Extracting ISO Week Numbers from Datetime Series in Pandas

Fundamental Behavior of dt.week

The dt.week accessor, applied to a Pandas Series with datetime64 values, yields an integer representing the calendar week of the year for each timestamp. This calculation follows the ISO 8601 definition: Monday marks the start of the week, and the first week of the year is the one containing January 4th (or equivalently, the week that includes the year's first Thursday). The returned integer ranges from 1 to 53.

import pandas as pd

ts = pd.to_datetime(["2024-01-01", "2024-12-31", "2026-01-01"])
week_nums = ts.week
# Result: [1, 1, 1]  (2024-01-01 is Monday and belongs to week 1; 2026-01-01 is Thursday, also week 1)
print(week_nums.tolist())

Building a Weekly Aggregation Pipeline

A common scenario involves grouping records by week to perform statistical summaries. The snippet below creates a DataFrame, enriches it with week numbers, and computes totals per week.

import numpy as np

dates = pd.date_range("2024-06-01", periods=100, freq="D")
df = pd.DataFrame({
    "recorded": dates,
    "amount": np.random.randint(50, 300, size=100)
})

df["iso_week"] = df["recorded"].dt.week
weekly_stats = df.groupby("iso_week", as_index=False).agg(
    total_sales=("amount", "sum"),
    avg_sales=("amount", "mean"),
    transaction_count=("amount", "count")
)

print(weekly_stats.head())

To ensure weeks are uniquely identified across years, combine year and week into a single feature:

df["year_week"] = df["recorded"].dt.strftime("%G-W%V")
weekly_by_year = df.groupby("year_week")["amount"].sum().reset_index()
print(weekly_by_year.head())

Handling Non-Standard Week Definitions

Series.dt.week always uses ISO rules. When analysis requires Sunday as the first day, or defines the first week differently, shift the dates before extraction and optionally reverse the shift afterward.

# Trick for Sunday-based weeks: Treat Sunday as if it were Monday
seconds_in_day = pd.Timedelta(days=1)
adjusted = df["recorded"] - seconds_in_day
df["sunday_based_week"] = adjusted.dt.week

This adjustment re-maps Sundays into the previous Monday's ISO week. For a more robust solution, use pd.Timestamp arithmetic together with conditional logic, or adopt a dedicated calendar library when requirements are complex.

Diagnosing Common Issues

Non-datetime Columns

If datetime64 is missing, the .dt accessor raises AttributeError. Convert explicitly:

# When reading CSV
frame = pd.read_csv("records.csv", parse_dates=["timestamp_column"])
# Or after loading
frame["timestamp_column"] = pd.to_datetime(frame["timestamp_column"])

Mixed or Malformed Dates

Unparseable strings result in NaT values. Use errors='coerce' and drop or fill missing values:

dirty_dates = pd.Series(["2025/08/12", "not-a-date", "2025-08-13"])
converted = pd.to_datetime(dirty_dates, errors="coerce")
valid_mask = converted.notna()
clean_weeks = converted[valid_mask].dt.week

Time Zone Awareness

Timestamps with different UTC offsets may produce incorrect week numbers when viewed from a local perspective. Localizee all timestamps to a single time zone first:

localized = df["utc_timestamp"].dt.tz_convert("America/Chicago")
df["week_number_local"] = localized.dt.week

Year-End Edge Cases

Week numbers transition at year boundaries according to ISO rules, sometimes causing confusion. Validate year-week combinations by extracting both components:

df["iso_year"], df["iso_week"] = df["recorded"].dt.isocalendar().year, df["recorded"].dt.isocalendar().week
print(df[["recorded", "iso_year", "iso_week"]].drop_duplicates().head(10))

Tags: Pandas datetime dt.week iso-8601 time-series

Posted on Fri, 17 Jul 2026 17:00:09 +0000 by ctsttom