Python for Data course menu
Dates and Time Series in pandas: to_datetime, dt and resample
Work with dates in pandas: parse dates, extract year, month and weekday with .dt, date arithmetic with Timedelta, date ranges, filtering by date, a DatetimeIndex, and resampling daily data to weekly or monthly totals.
On this page
Sales per month, sign-ups per weekday, days since last purchase: time is part of most business questions. pandas has a full set of tools for dates once a column has the datetime type.
Parsing dates#
import pandas as pd
orders = pd.DataFrame({
"ordered": ["2026-01-05", "2026-01-28", "2026-02-11", "2026-03-02", "2026-03-30"],
"amount": [250, 120, 560, 90, 310],
})
orders["ordered"] = pd.to_datetime(orders["ordered"])
print(orders.dtypes)ordered datetime64[ns]
amount int64
dtype: objectISO dates (YYYY-MM-DD) parse without a format. For anything else, pass format= (see Data Types).
The dt accessor#
.dt gives every date part as a column:
import pandas as pd
orders = pd.DataFrame({
"ordered": pd.to_datetime(["2026-01-05", "2026-01-28", "2026-02-11", "2026-03-02", "2026-03-30"]),
"amount": [250, 120, 560, 90, 310],
})
orders["year"] = orders["ordered"].dt.year
orders["month"] = orders["ordered"].dt.month
orders["weekday"] = orders["ordered"].dt.day_name()
orders["quarter"] = orders["ordered"].dt.quarter
orders["month_label"] = orders["ordered"].dt.strftime("%b %Y")
print(orders)
print(orders.groupby("month_label", sort=False)["amount"].sum()) ordered amount year month weekday quarter month_label
0 2026-01-05 250 2026 1 Monday 1 Jan 2026
1 2026-01-28 120 2026 1 Wednesday 1 Jan 2026
2 2026-02-11 560 2026 2 Wednesday 1 Feb 2026
3 2026-03-02 90 2026 3 Monday 1 Mar 2026
4 2026-03-30 310 2026 3 Monday 1 Mar 2026
month_label
Jan 2026 370
Feb 2026 560
Mar 2026 400
Name: amount, dtype: int64strftime formats dates as text with the same codes used for parsing.
Date arithmetic#
Subtracting dates gives a Timedelta; adding a Timedelta or DateOffset moves dates:
import pandas as pd
ordered = pd.to_datetime(pd.Series(["2026-01-05", "2026-02-11", "2026-03-30"]))
today = pd.Timestamp("2026-04-01")
print((today - ordered).dt.days.tolist()) # days since each order
print((ordered + pd.Timedelta(days=7)).dt.date.tolist()) # due a week later
print((ordered + pd.DateOffset(months=1)).dt.date.tolist()) # same day next month
print(ordered.dt.to_period("M").tolist())[86, 49, 2]
[datetime.date(2026, 1, 12), datetime.date(2026, 2, 18), datetime.date(2026, 4, 6)]
[datetime.date(2026, 2, 5), datetime.date(2026, 3, 11), datetime.date(2026, 4, 30)]
[Period('2026-01', 'M'), Period('2026-02', 'M'), Period('2026-03', 'M')]DateOffset(months=1) respects calendar months, which a fixed number of days cannot. to_period("M") labels each date with its month.
Date ranges#
import pandas as pd
print(pd.date_range("2026-03-01", periods=5, freq="D").date.tolist())
print(pd.date_range("2026-01-01", "2026-06-30", freq="MS").strftime("%b").tolist())
print(pd.date_range("2026-03-02", periods=3, freq="W-MON").date.tolist())[datetime.date(2026, 3, 1), datetime.date(2026, 3, 2), datetime.date(2026, 3, 3), datetime.date(2026, 3, 4), datetime.date(2026, 3, 5)]
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
[datetime.date(2026, 3, 2), datetime.date(2026, 3, 9), datetime.date(2026, 3, 16)]Frequency codes: D day, W-MON weekly on Mondays, MS month start, ME month end, QE quarter end, h hour. (Older pandas used M and Q; recent versions want ME and QE for month and quarter ends.)
A DatetimeIndex and filtering#
Setting the date as the index makes time-based selection easy:
import numpy as np
import pandas as pd
rng = np.random.default_rng(3)
daily = pd.Series(rng.integers(80, 160, size=90),
index=pd.date_range("2026-01-01", periods=90, freq="D"), name="visitors")
print(daily.head(3))
print(daily.loc["2026-02"].sum()) # all of February
print(daily.loc["2026-03-10":"2026-03-12"]) # a date range (inclusive)
print(daily[daily.index.dayofweek >= 5].mean().round(1)) # weekends only2026-01-01 144
2026-01-02 86
2026-01-03 94
Freq: D, Name: visitors, dtype: int64
3331
2026-03-10 154
2026-03-11 96
2026-03-12 128
Freq: D, Name: visitors, dtype: int64
119.1Resampling#
resample is groupby for time: it groups by calendar periods. Daily data can become weekly or monthly totals in one line:
import numpy as np
import pandas as pd
rng = np.random.default_rng(3)
daily = pd.Series(rng.integers(80, 160, size=90),
index=pd.date_range("2026-01-01", periods=90, freq="D"), name="visitors")
monthly = daily.resample("ME").sum()
monthly.index = monthly.index.strftime("%b")
print(monthly)
print(daily.resample("W-SUN").mean().round(1).head(3))Jan 3566
Feb 3331
Mar 3784
Name: visitors, dtype: int64
2026-01-04 105.5
2026-01-11 112.7
2026-01-18 118.1
Freq: W-SUN, Name: visitors, dtype: float64Grouping without a DatetimeIndex#
If the date is a regular column, group by a period instead:
import pandas as pd
orders = pd.DataFrame({
"ordered": pd.to_datetime(["2026-01-05", "2026-01-28", "2026-02-11", "2026-03-02", "2026-03-30"]),
"amount": [250, 120, 560, 90, 310],
})
print(orders.groupby(orders["ordered"].dt.to_period("M"))["amount"].agg(["sum", "count"]))
print(orders.groupby(pd.Grouper(key="ordered", freq="QE"))["amount"].sum()) sum count
ordered
2026-01 370 2
2026-02 560 1
2026-03 400 2
ordered
2026-03-31 1330
Freq: QE-DEC, Name: amount, dtype: int64Common mistakes#
Practice#
Practice exercise
Sign-up dates are ["2026-01-03", "2026-01-15", "2026-02-01", "2026-02-14", "2026-02-20", "2026-04-02"]. Print the number of sign-ups per month including months with none (January to April), and the weekday on which most sign-ups happened.
Show hint
Make a Series of 1s indexed by the dates and resample("ME").sum(). For weekdays, dt.day_name().value_counts().idxmax().
Show solution
import pandas as pd
dates = pd.to_datetime(pd.Series(["2026-01-03", "2026-01-15", "2026-02-01", "2026-02-14", "2026-02-20", "2026-04-02"]))
per_month = pd.Series(1, index=dates).resample("ME").sum()
per_month.index = per_month.index.strftime("%b")
print(per_month)
print(dates.dt.day_name().value_counts().idxmax())Jan 2
Feb 3
Mar 0
Apr 1
dtype: int64
SaturdayMarch appears with 0 because resample creates every month in the range.
Key takeaway#
Finished this lesson?