Resampling and Frequency Conversion
What resampling means
Section titled “What resampling means”Resampling converts a time series from one frequency to another:
- Downsampling — high frequency → low frequency (minute data → daily totals). This always needs an aggregation, because many points collapse into one bucket.
- Upsampling — low frequency → high frequency (weekly data → daily rows). This creates new rows, which start out empty unless you fill them.
.resample() has a similar API to .groupby(): call resample() to define the
buckets, then call an aggregation like .sum() or .mean().
Downsampling: many points into one bucket
Section titled “Downsampling: many points into one bucket”import pandas as pd
idx = pd.date_range("2024-01-01", periods=12, freq="min")
ts = pd.Series(range(12), index=idx)
print(ts.resample("5min").sum())Two choices control how buckets are formed:
closed— which edge of each interval is inclusive ("left"or"right").label— whether the result is labeled with the left or right edge.
print(ts.resample("5min", closed="right").sum())
print(ts.resample("5min", closed="right", label="right").sum())OHLC: a finance-flavored aggregation
Section titled “OHLC: a finance-flavored aggregation”For price data, a common summary per bucket is open, high, low, close — computed in one call:
print(ts.resample("5min").ohlc())Upsampling: creating new rows
Section titled “Upsampling: creating new rows”Going from weekly to daily creates rows that don’t exist yet — they start as NaN
unless you tell pandas how to fill them:
weekly = pd.DataFrame(
{"a": [1, 2]},
index=pd.date_range("2024-01-03", periods=2, freq="W-WED"),
)
print(weekly.resample("D").asfreq()) # new rows are NaN
print(weekly.resample("D").ffill()) # new rows carry the last known value forwardresample vs groupby
Section titled “resample vs groupby”Both split data into buckets and apply a function — the difference is what defines a bucket:
groupby("column")groups by the values in a column.resample("2D")groups by fixed time intervals, regardless of how many rows fall into each one.
sales = pd.DataFrame(
{"amount": [10, 20, 30, 40, 50]},
index=pd.date_range("2024-01-01", periods=5, freq="D"),
)
print(sales.resample("2D").sum())Common pitfalls
Section titled “Common pitfalls”- Downsampling needs an aggregation (
.sum(),.mean(),.ohlc(), …); calling.resample("5min")alone just returns a lazy resampler object, not data. - The default for
closed/labelis"left"for most frequencies but"right"for a handful ("ME","YE","QE","W") — always check the output on real data. - Upsampling with
.asfreq()introducesNaN; you must explicitly choose.ffill()or another fill strategy if you don’t want gaps.
Visualize it
Section titled “Visualize it”flowchart LR A["High-frequency series
(e.g. 1-minute data)"] -->|"resample('5min').sum()"| B["Downsampled
fewer rows, aggregated"] C["Low-frequency series
(e.g. weekly data)"] -->|"resample('D').asfreq()"| D["Upsampled
more rows, mostly NaN"] D -->|".ffill()"| E["Upsampled + filled
no gaps"]
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Downsample Minute Data to 5-Minute Sums
Section titled “Exercise 1 – Downsample Minute Data to 5-Minute Sums”Exercise 2 – Upsample and Forward-Fill
Section titled “Exercise 2 – Upsample and Forward-Fill”Exercise 3 – Compute OHLC in One Call
Section titled “Exercise 3 – Compute OHLC in One Call”Resampling collapses data into buckets. Rolling and Moving Window Functions instead slides a window across the data without collapsing rows — great for smoothing noisy series.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading