Original source
---
id: time-series
title: Time Series Fundamentals
code: TS
subtitle: Check stationarity, build lag features, validate walk-forward, beat naive baselines. Random walks and Markov chains.
hue: 170
sources: phases/02-ml-fundamentals/15-time-series, phases/01-math-foundations/22-stochastic-processes, phases/02-ml-fundamentals/16-anomaly-detection
source_hash: 32f26254a2e7a0f0
---
## From series to honest forecast
type: architecture
from: phases/02-ml-fundamentals/15-time-series/docs/en.md, phases/02-ml-fundamentals/15-time-series/code/time_series.py
```arch
node | raw | 1,1 | Raw series | plot it first
node | stat | 2,1 | Stationarity check | rolling mean and std
node | diff | 3,1 | Differencing | at most two rounds
node | lags | 4,1 | Lag features | values at t-1 or earlier
node | wf | 4,2 | walk_forward_split | train before test
node | model | 3,2 | Model | Ridge, SimpleAR, ARIMA
node | base | 2,2 | Baselines | persistence, seasonal naive
node | score | 1,2 | Score | MAE, RMSE, MAPE
edge | raw | stat |
edge | stat | diff | drifts
edge | diff | lags |
edge | lags | wf |
edge | wf | model | folds
edge | model | base | must beat
edge | base | score |
```
## Walk-forward validation
type: flow
from: phases/02-ml-fundamentals/15-time-series/docs/en.md
1. **Train**: fit on data up to time t.
2. **Predict**: forecast t+1, or t+1 to t+k for multi-step.
3. **Slide**: move the window forward.
4. **Repeat**: each test fold comes after all its training data.
5. **Expanding window**: training set grows. Use it when old data stays relevant.
6. **Sliding window**: fixed size. Use it when the world changes and old data hurts.
## Tasks in code
type: task-table
from: phases/02-ml-fundamentals/15-time-series/code/time_series.py
| Task | Code |
|---|---|
| Remove a trend | `difference(series, order=1)` |
| Rolling stats and a verdict | `check_stationarity(series, window=50)` |
| ACF up to a lag | `autocorrelation(series, max_lag=20)` |
| Series to X and y | `make_lag_features(series, n_lags)` |
| Time-ordered folds | `walk_forward_split(n_samples, n_splits=5, min_train=50)` |
| AR on lag features | `SimpleAR(n_lags=5).fit_series(series)` |
| Recursive multi-step | `forecast(last_values, n_steps)` |
| Percent error, skips zeros | `mape(y_true, y_pred)` |
## Library versions
type: annotated-code
lang: python
from: phases/02-ml-fundamentals/15-time-series/docs/en.md
```python
X, y = make_lag_features(series, n_lags=10)
for train_idx, test_idx in walk_forward_split(len(X)):
model = Ridge(alpha=1.0)
model.fit(X[train_idx], y[train_idx])
predictions = model.predict(X[test_idx])
model = ARIMA(train_series, order=(5, 1, 2)) # p, d, q
fitted = model.fit()
forecast = fitted.forecast(steps=30) # 30 steps ahead
tscv = TimeSeriesSplit(n_splits=5) # sklearn walk-forward
scores = cross_val_score(model, X, y, cv=TimeSeriesSplit(n_splits=5))
```
## Core definitions
type: concepts
from: phases/02-ml-fundamentals/15-time-series/docs/en.md
- **Components**: trend, seasonality and residual. A white-noise residual means the split worked.
- **Stationary**: mean, variance and autocorrelation do not change over time.
- **Differencing**: $\text{diff}[t] = \text{value}[t] - \text{value}[t-1]$
- **ACF**: correlation of the series with a lagged copy. Use lags up to where it is negligible.
- **PACF**: autocorrelation at lag k after the effect of shorter lags is removed.
- **ARIMA(p, d, q)**: p past values, d rounds of differencing, q past errors.
- **MAE / RMSE / MAPE**: mean absolute error, squared error root, percent error.
- **Target alignment**: target at t. Every feature uses t-1 or earlier.
## Pick an approach
type: command-table
from: phases/02-ml-fundamentals/15-time-series/docs/en.md
| Command | What it does |
|---|---|
| Lag features + ML | Many external features. Strongest start with boosting |
| ARIMA | One univariate series, short term. SARIMA for seasons |
| Exponential smoothing | Simple trend + seasonality with Holt-Winters |
| Neural networks | Long sequences, many series |
| Recursive forecast | Feeds predictions back. Use for 1-5 steps |
| Direct forecast | One model per horizon. Use for long horizons |
| Multi-output | One model outputs all horizons |
## Defaults and numbers
type: defaults
from: phases/02-ml-fundamentals/15-time-series/docs/en.md, phases/02-ml-fundamentals/15-time-series/code/time_series.py
| Setting | Default | Note |
|---|---|---|
| `check_stationarity` window | 50 | Rolling window size |
| Mean shift limit | 0.5 | Times the series std, half against half |
| Variance ratio limit | 2.0 | Above it, flagged non-stationary |
| ADF p-value | 0.05 | Below it, reject non-stationary |
| `max_lag` | 20 | Autocorrelation default |
| `n_splits`, `min_train` | 5 | min_train=50 rows before the first test |
| Rolling windows | 7 and 14 | Mean, std, min, max in days |
| Weekly seasonality | lag 7 | Possibly 14 too |
## Mistakes and fixes
type: troubleshooting
from: phases/02-ml-fundamentals/15-time-series/docs/en.md, phases/02-ml-fundamentals/15-time-series/code/time_series.py
| Symptom | Cause | Fix |
|---|---|---|
| Random split MSE lower | Future leakage | Walk-forward or temporal split |
| Near-perfect backtest | Feature at time t included | Audit every feature alignment |
| Misses the seasonal pattern | Test set shorter than one cycle | Hold out a full seasonal cycle |
| Revenue doubles, model lags | Absolute scale changed | Model percentage change |
| Overfit with many lags | "More history is better" | Pick lags from the ACF |
| Linear model drifts | Trend not removed | Difference first, model second |
| Loses to seasonal naive | Leakage, wrong eval, or random series | Fix the bug before tuning |
| Errors rise in production | World changed | Retrain on recent data |
## Stochastic processes in code
type: annotated-code
lang: python
from: phases/01-math-foundations/22-stochastic-processes/code/stochastic.py, phases/01-math-foundations/22-stochastic-processes/docs/en.md
```python
positions = random_walk_1d(n_steps, seed=None) # std grows as sqrt(n)
mc = MarkovChain(P, state_names=names) # rows of P sum to 1
pi = mc.stationary_distribution() # left eigenvector, eigenvalue 1
states = mc.simulate(start_state=0, n_steps=100000, seed=42)
traj = langevin_dynamics(grad_U, x0, dt, temperature, n_steps)
samples, acc_rate = metropolis_hastings(
target_log_prob=bimodal_log_prob,
proposal_std=2.0, x0=np.array([0.0]), n_samples=100000, seed=42)
samples_flat = samples[10000:, 0] # burn-in discarded
```
## Random walks and Markov chains
type: concepts
from: phases/01-math-foundations/22-stochastic-processes/docs/en.md
- **Random walk**: mean 0, standard deviation $\sqrt{n}$. 10000 steps gives about 100.
- **Markov property**: the next state depends only on the current state.
- **Stationary distribution**: $\pi P = \pi$. Weather chain gives [0.55, 0.18, 0.27].
- **Convergence**: needs an irreducible and aperiodic chain.
- **Spectral gap**: 1 minus the second eigenvalue. A gap of 0.2 mixes in about 5 steps.
- **Brownian step**: $B(t + dt) = B(t) + \sqrt{dt} \, z$
- **Langevin**: $x_{t+1} = x_t - dt \, \nabla U(x_t) + \sqrt{2 T \, dt} \, z_t$
- **Metropolis-Hastings**: accept with $\min(1, a)$. Aim for about 23% acceptance.
## Anomalies in time
type: tips
from: phases/02-ml-fundamentals/16-anomaly-detection/docs/en.md
- 90 degrees is normal in summer and anomalous in winter. Contextual anomalies need time features.
- Collective anomalies need sequence-aware methods. Fifty failed logins in a row is an attack.
- Add rolling statistics, ratios and time-since-last-event features.
- Rolling statistics over 30 days need 30 days of buffered history at scoring time.
- Track the anomaly score distribution. A fixed threshold drifts out of date.
## Questions
type: qa
from: phases/02-ml-fundamentals/15-time-series/docs/en.md
- **Why not k-fold?** Samples are not independent. Random folds leak the future into training.
- **How many differences?** Most real series need at most two rounds.
- **How many lags?** Use the ACF. Spikes at lag 12 on monthly data mean yearly seasonality.
- **Do trees need differencing?** Tree models handle trends. Linear models need stationarity.
- **Skewed revenue?** Forecast in log space, then exponentiate.
- **Which baseline first?** Last value, then seasonal naive, then moving average.