Rolling and Moving Window Functions
Why moving windows matter
Real time series are noisy — daily sales bounce around, stock prices jitter minute to minute. A moving window function recomputes a statistic (mean, std, …) over a small sliding slice of the data, smoothing out that noise so the underlying trend is easier to see.
Pandas gives you three flavors:
.rolling(window).rolling(window)— a fixed-size window that slides forward..expanding().expanding()— a window that starts small and grows to include everything seen so far..ewm(span=...).ewm(span=...)— an exponentially weighted window that favors recent observations.
Rolling mean and std
import pandas as pd
s = pd.Series(
[10, 12, 13, 12, 15, 20, 18, 22, 25, 30],
index=pd.date_range("2024-01-01", periods=10, freq="D"),
)
print(s.rolling(3).mean()) # 3-day moving averageimport pandas as pd
s = pd.Series(
[10, 12, 13, 12, 15, 20, 18, 22, 25, 30],
index=pd.date_range("2024-01-01", periods=10, freq="D"),
)
print(s.rolling(3).mean()) # 3-day moving averagerolling(3)rolling(3) behaves like groupbygroupby, but instead of grouping by a key, it creates a
sliding 3-row window. Notice the first two values are NaNNaN — there aren’t 3 rows yet
to average.
min_periods: dealing with a short warm-up
By default, a rolling window needs every slot filled before it produces a value.
min_periodsmin_periods relaxes that, useful right at the start of a series:
print(s.rolling(3, min_periods=1).mean()) # no leading NaN this timeprint(s.rolling(3, min_periods=1).mean()) # no leading NaN this timeExpanding windows
An expanding window is like a rolling window whose size keeps growing — it always starts at the beginning of the series and includes everything up to the current row:
print(s.expanding().mean()) # cumulative average up to each pointprint(s.expanding().mean()) # cumulative average up to each pointExponentially weighted windows (ewm)
Instead of weighting every value in the window equally, ewmewm gives more weight to
recent observations — it “reacts” faster to a change than a simple moving average:
print(s.ewm(span=3).mean())print(s.ewm(span=3).mean())Rolling correlation (two series at once)
Some window functions — like correlation — need two series. Calling .rolling(...).rolling(...)
on one series and then .corr(other).corr(other) computes a rolling correlation between them:
bench = pd.Series(
[1, 1.2, 1.1, 1.3, 1.5, 1.6, 1.4, 1.7, 1.9, 2.0],
index=s.index,
)
rolling_corr = s.pct_change().rolling(4, min_periods=2).corr(bench.pct_change())
print(rolling_corr)bench = pd.Series(
[1, 1.2, 1.1, 1.3, 1.5, 1.6, 1.4, 1.7, 1.9, 2.0],
index=s.index,
)
rolling_corr = s.pct_change().rolling(4, min_periods=2).corr(bench.pct_change())
print(rolling_corr)Common pitfalls
- Without
min_periodsmin_periods, the firstwindow - 1window - 1rows of a rolling result are alwaysNaNNaN. - Rolling window functions automatically skip missing data, but they still require
min_periodsmin_periodsvalid values to produce output. ewmewmdoesn’t have a fixed “window size” the wayrollingrollingdoes —spanspanjust controls how quickly old observations lose influence.
Visualize it
flowchart LR A["Raw noisy series"] --> B["rolling(3).mean()
fixed-size sliding window"] A --> C["expanding().mean()
grows from the start"] A --> D["ewm(span=3).mean()
recent points weighted more"] B --> E["Smoothed line"] C --> E D --> E
🧪 Try It Yourself
Exercise 1 – Compute a Rolling Mean
Exercise 2 – Avoid Leading NaN With min_periods
Exercise 3 – Compute an Expanding Mean
Next
That completes the Pandas phase. Head into Phase 4 — Data Preprocessing and Cleaning, starting with Understanding Data Quality, to apply these skills to real, messier datasets.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
