pandas·Lesson 22·12 min·0/4 exercises

resample and rolling

One changes how many points there are. The other changes what each one says.

.resample().rolling().expanding().ewm()min_periodsfrequency aliases

Watch it happen

Play it through, or step back and forth yourself.

Thirty-five days of cups sold. There's a weekly rhythm in there — weekends are busier — but the day-to-day noise makes it hard to see.

The idea

Both of these smooth a noisy series, and people reach for whichever they saw first. The distinction is simple and worth getting right:

resample changes how many points there are. rolling keeps every point and changes its value.

resample — groupby for time

sales.resample("W").mean()     # 35 daily points -> 6 weekly points
sales.resample("ME").sum()     # month-end totals
sales.resample("QE").max()     # quarterly peaks

It buckets the index into calendar periods and aggregates each one — a groupby that knows what a week is. It requires a DatetimeIndex, which is why lesson 21 spent so long on putting dates in the index.

Check the ends

Five weeks of data gives six weekly buckets here, which surprises people the first time. The buckets follow the calendar, not your start date: weeks end on Sunday by default, and 2026-03-01 happens to be a Sunday, so it forms a bucket containing one day. The final bucket is short for the same reason.

So the first and last points of any resample are usually computed from a partial period, and will look wrong on a chart. Either drop them, or anchor the frequency deliberately — resample("W-MON") ends weeks on Monday instead.

The aliases are worth a look: D day, W week, ME month end, MS month start, QE quarter, YE year, h hour. (pandas 2.2 renamed M to ME; older code and tutorials still use the short forms.)

Up as well as down

sales.resample("12h").ffill()     # invent hourly rows, carry values forward
sales.resample("12h").interpolate()

Downsampling aggregates and needs a function. Upsampling creates timestamps that didn't exist and leaves them NaN until you say how to fill them. Be deliberate: ffill claims the value held steady, interpolate claims it moved smoothly, and both are assertions about data you don't have.

rolling — a moving window

sales.rolling(7).mean()      # 7-day moving average — still 35 points
sales.rolling(7).sum()
sales.rolling(7).std()

Each point becomes the average of the seven days ending there. The output is the same length as the input, so you can plot it against the original or subtract one from the other.

The first six values are NaN, because a 7-day window needs seven days behind it. min_periods=1 starts computing immediately with whatever it has — convenient, and it hides the fact that the early values are averages of one or two points.

For irregularly spaced data, give rolling a time rather than a count:

sales.rolling("7D").mean()   # the last 7 days, however many rows that is

The rest of the family

sales.expanding().mean()      # everything so far — a running average
sales.ewm(span=7).mean()      # exponentially weighted — recent days count more

ewm is the one to reach for when you want smoothing without a hard cut-off: every past point contributes, with decaying weight. No leading NaNs either.

Which to use

resample when you want a different grain — daily data, weekly report. rolling when you want to smooth without losing resolution — a 7-day average you can still plot per day.

And they compose. A common pattern is resample to a regular grid first, then roll over it, so the window means a fixed span of time rather than a fixed number of possibly-uneven rows.

One thing to watch: rolling looks backwards, which is what you want for anything that will feed a model. A centred window (center=True) uses future values, which is fine for a chart and leakage in a feature.

See it run

The lesson's code, ready to run and to fiddle with.

Putting the kettle on…

Starting up…

Worked example

not graded

Already written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.

trysales.rolling(7, min_periods=1).mean() and see the leading NaNs disappear.

Press Run — the output appears here.

Your turn

4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Return the weekly mean of sales.

your answer

Return the total cups per week rather than the mean.

your answer

Return the 7-day moving average of sales — same length as the input.

your answer

The same 7-day average, but with no leading NaNs — start computing from the first day.

your answer