10 · Moving averages
Averaging a sliding window of recent points smooths noise and gives the simplest possible forecaster. The window size trades smoothness against lag — the central tension of every smoother.
A moving average replaces each point with the mean of its window. As a smoother it removes noise; as a forecaster it predicts the next steps as the average of the last k points. A bigger window smooths more but lags more — there is no free lunch.
Without this:
Without a baseline smoother you have no cheap noise filter and no naive forecast to beat — and a model that can't beat a moving average isn't earning its complexity.
The moving average (MA) is the most basic time-series tool there is, and it wears two hats.
As a smoother, it replaces each point with the mean of a sliding window of width k: the point at time t becomes the average of t-k+1 … t. Noise (which averages toward zero) gets damped while the underlying level and slow trend survive. In pandas this is one line: ts.rolling(window=k).mean().
As a naive forecaster, the MA says: my best guess for the next step is the average of the last k observations. Repeat that average for every step in the horizon and you have a flat forecast — crude, but it's the baseline every fancier model must beat.
The choice of k is the whole game, and it's a tradeoff with no escape:
- Small
k(e.g. 3) — barely smooths, follows the data closely, reacts fast, but lets noise through. - Large
k(e.g. 14) — very smooth, kills noise, but introduces lag: the smoothed line trails turns in the series by roughly(k-1)/2steps. On an upward trend a wide MA systematically reads low.
A weighted moving average (WMA) softens this by giving recent points more weight (a triangular or exponential weighting) so the average reacts faster than its window width suggests — the idea exponential smoothing pushes to its logical end in the next lesson.
Below we smooth a noisy trending series with two window sizes, quantify how much noise each removes versus how much lag it adds, and build a naive MA forecast for the next steps.
Python (in browser)
rolling().mean() smooths a noisy series. A wider window lowers the roughness (more noise averaged away) but trails turning points by more steps — and the naive MA forecast is just a flat average of the last k points.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Let's make the lag-versus-smoothness tradeoff visible. We plot the noisy series with both moving averages overlaid: the short window (3) hugs the data and stays jittery, while the long window (14) is glassy-smooth but visibly drags behind every rise and fall. There is no setting that gives you both — picking k is choosing where on that spectrum you want to live.
Python (in browser)
The same series under a narrow (3) and wide (14) moving average. The wide one is smoother but visibly trails turning points — smoothness and responsiveness pull in opposite directions.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
You widen a moving-average window from 3 to 21 days. What happens?
- A moving average smooths by replacing each point with the mean of a sliding window (ts.rolling(k).mean()).
- As a forecaster it's a flat naive baseline: the next steps equal the average of the last k points — the bar every model must clear.
- Window size trades smoothness against lag: bigger k removes more noise but trails turns by ~(k-1)/2 steps. There is no free lunch.
Rolling means power dashboards (7-day averages), feature engineering (rolling stats as model inputs), and the baseline forecasts you benchmark ARIMA and ML models against.
If you remove it: Without a moving-average baseline you can't tell whether a complex model is actually adding value or just dressing up the recent average.