13 · Rolling & calendar features
Rolling-window stats summarise recent behaviour and calendar fields expose seasonality directly — two feature families that supercharge a lagged frame (if you avoid leakage).
rolling(w).mean()/std()/min()/max() compress the recent window into trend and volatility signals, while DatetimeIndex fields (dayofweek, month, is_weekend) hand the model seasonality for free — but rolling features must be shifted so they use only PAST data.
Without this:
Raw lags alone miss smoothed trend and volatility; without calendar features the model must rediscover weekly/monthly patterns from scratch. And an unshifted rolling window leaks the present into your features and inflates offline scores.
Lag features give the model raw recent values. Two more families make those features far richer.
Rolling-window statistics summarise the last w observations into a single number: rolling(w).mean() is a smoothed local level (recent trend), rolling(w).std() is recent volatility, and .min()/.max() capture the recent range. Instead of feeding 14 noisy daily lags, you can feed a 7-day rolling mean (signal) plus a 7-day rolling std (regime/volatility) — denser, less noisy features.
Calendar features come straight off the DatetimeIndex: .dayofweek (0=Mon … 6=Sun), .month, .quarter, .day, and derived flags like is_weekend. These hand seasonality to the model explicitly — a tree can split "if dayofweek ≥ 5 then weekend behaviour" without having to infer the 7-day cycle from lags.
There is one trap that ruins everything: leakage. A plain rolling(7).mean() on row t includes y[t] itself — the very value you're trying to predict. Use that as a feature and your offline score looks magical and your live score collapses. The fix is to shift the rolling feature by 1 (or compute it on already-shifted lags) so each row's rolling stats use only data strictly before t. Calendar features are safe — the date of a future point is known in advance — but any statistic computed from the target must look strictly into the past.
Python (in browser)
Rolling stats on shift(1)-ed values plus calendar fields enrich the lagged frame; the leakage check proves why the shift matters.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
You add a feature df['roll7'] = df['y'].rolling(7).mean() and your CV error drops dramatically, but the live model fails. What went wrong?
- rolling(w).mean/std/min/max compress the recent window into trend, volatility, and range signals.
- Calendar fields (dayofweek, month, is_weekend) off the DatetimeIndex hand seasonality to the model explicitly and are always leakage-safe.
- Shift rolling features by 1 so each row uses only PAST data — an unshifted window includes the target and leaks.
Rolling means/stds and calendar dummies are the standard feature recipe for retail demand, energy load, and traffic forecasting with gradient boosting.
If you remove it: Drop rolling/calendar features and the model loses smoothed trend, volatility context, and explicit seasonality; leak them and your offline metrics become a comforting lie.