Re-bucket time-series data into regular periods — daily, monthly, quarterly, yearly.
resample is groupby for time. Where groupby splits rows by the value in a column, resample splits them by when they happened, filling in the calendar for you. Events that arrive irregularly — earthquakes, sales, sensor readings — become a tidy row per month whether or not anything happened in a given month.
Official documentation: DataFrame.resample
The forms worth knowing
resample needs a DatetimeIndex, so it almost always follows set_index:
df.set_index('time').resample('ME').sum() # month end
df.set_index('time').resample('D').mean() # daily
df.set_index('time').resample('QE').size() # quarter end, count of rows
df.set_index('time').resample('YE').max() # year end
# Combine with agg for several answers at once
df.set_index('time').resample('ME').agg(
quakes=('mag', 'size'),
strongest=('mag', 'max'),
)
The frequency codes
Nobody remembers these, and Pandas 3 changed several of them. The ones worth knowing:
| Code | Means | Example |
|---|---|---|
s |
second | .resample('30s') — half-minute buckets |
min |
minute | .resample('15min') — quarter-hourly |
h |
hour | .resample('6h') — four buckets a day |
D |
calendar day | .resample('D') |
B |
business day | .resample('B') — skips weekends |
W |
week, ending Sunday | .resample('2W') — fortnightly |
ME |
month end | .resample('ME') — labelled 2024-01-31 |
MS |
month start | .resample('MS') — labelled 2024-01-01 |
QE |
quarter end | .resample('QE') |
QS |
quarter start | .resample('QS') |
YE |
year end | .resample('YE') |
YS |
year start | .resample('YS') |
Any code takes a multiplier, which is where 2W and 6h above come from.
The end/start pairs are the ones that catch people. ME and MS produce the same buckets — they differ only in whether each one is labelled with the first or last day of the month. That difference then shows up on every chart axis you draw afterwards.
Seven codes were retired in Pandas 3 and now raise ValueError: Invalid frequency rather than warning: T, H, S, M, Q, Y and A. They became min, h, s, ME, QE, YE and YE. Almost every tutorial written before 2025 uses the old spellings, so this is the first thing to fix when porting code that used to work.
The full list — including anchored offsets like W-FRI and business-quarter variants — is in the Pandas user guide: Offset aliases. Worth bookmarking; it is the page everyone re-finds every few months.
A worked example, on real data
The USGS publishes every earthquake it records as CSV, through a public API with no key — the source Bamboo Weekly #3 worked with.
Earthquakes arrive whenever they arrive. To ask "how did 2024 unfold, month by month?" you need them bucketed:
import pandas as pd
url = ('https://earthquake.usgs.gov/fdsnws/event/1/query.csv'
'?starttime=2024-01-01&endtime=2024-12-31&minmagnitude=6')
(
pd.read_csv(url, usecols=['time', 'mag'], parse_dates=['time'])
.set_index('time')
.resample('ME')
.agg(quakes=('mag', 'size'),
strongest=('mag', 'max'))
.head(6)
)
Which gives:
quakes strongest
time
2024-01-31 00:00:00+00:00 12 7.5
2024-02-29 00:00:00+00:00 4 6.3
2024-03-31 00:00:00+00:00 8 6.9
2024-04-30 00:00:00+00:00 12 7.4
2024-05-31 00:00:00+00:00 8 6.6
2024-06-30 00:00:00+00:00 7 7.2
Three steps: make the timestamp the index, bucket by month, then aggregate. The index labels are month ends — that is what the E in ME means, and it is why February reads 2024-02-29 rather than 2024-02-28. It was a leap year, and resample knew.
Three mistakes people make
Using the retired 'M' and 'Y' codes. In Pandas 3 these raise ValueError: Invalid frequency: M rather than warning. If you are porting code that worked last year, this is the first thing to fix.
Calling resample without a DatetimeIndex. It has to know which column is time. Either set_index first, or pass on= — df.resample('ME', on='time') — but the index form chains better and makes the intent obvious.
Forgetting that empty periods still appear. A month with no events produces a row with 0 or NaN, which is usually the point — a gap in the calendar is a finding. If you only want periods that contain data, groupby on .dt.to_period('M') gives you that instead.
Watch it
Resampling? How offsets are changing in Pandas 3 covers the frequency-code change directly, and Pandas time series superpowers: Why datetime indexes matter explains why the index has to be a datetime in the first place.
Practice it
Work through a resample exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/resample/
Go deeper
Bamboo Weekly is the practice. If you want the structured version — full courses with downloadable Jupyter notebooks, plus live Pandas office hours when you get stuck — that is LernerPython+Data. A paid Bamboo Weekly subscription is included with it.
See it on real data
Below are the 47 Bamboo Weekly exercises that use resample on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #180: Movies
- Bamboo Weekly #178: Harmful algal bloom
- Bamboo Weekly #175: Inflation
- Bamboo Weekly #173: IPOs
- Bamboo Weekly #170: Port of Long Beach
- Bamboo Weekly #167: Oil prices
- Bamboo Weekly #161: Missiles in Israel
- Bamboo Weekly #155: Gold
- Bamboo Weekly #153: Venezuela
- Bamboo Weekly #152: Congestion pricing
- Bamboo Weekly #150: Kalshi
- Bamboo Weekly #146: Thanksgiving travel
- Bamboo Weekly #145: Economic indicators
- Bamboo Weekly #142: Hurricanes
- Bamboo Weekly #141: Argentina
- Bamboo Weekly #134: Taiwan weather
- Bamboo Weekly #131: Canadian border crossings
- Bamboo Weekly #128: Extreme heat
- Bamboo Weekly #127: European comparisons
- Bamboo Weekly #125: Shrinking dollars
- Bamboo Weekly #116: Philadelphia Fed survey
- Bamboo Weekly #113: US airport traffic
- Bamboo Weekly #109: Cacao nibs
- Bamboo Weekly #107: Consumer confidence
- Bamboo Weekly #106: Flu season
- Bamboo Weekly #104: Aviation accidents
- Bamboo Weekly #103: CDC data
- Bamboo Weekly #102: WordPress
- Bamboo Weekly #100: Sports betting
- Bamboo Weekly #98: Retail sales
- Bamboo Weekly #96: Taylor Swift
- Bamboo Weekly #89: Housing
- Bamboo Weekly #84: Central banks
- Bamboo Weekly #83: Gasoline prices
- Bamboo Weekly #82: Broadband
- Bamboo Weekly #78: Stock markets
- Bamboo Weekly #73: Avocado hand
- Bamboo Weekly #58: NATO
- Bamboo Weekly #52: Border encounters
- Bamboo Weekly #50: Red Sea shipping
- Bamboo Weekly #47: Minimum wage
- Bamboo Weekly #43: Financial protection
- Bamboo Weekly #39: WeWork
- Bamboo Weekly #33: Fracking
- Bamboo Weekly #28: Pret a Manger
- Bamboo Weekly #23: Misery index
- Bamboo Weekly #12: Tourism
Part of the Pandas Methods Index. See also practice by skill.