Thomas Piacentini
← Atelier

Fiche

Time Series Fundamentals

Texte original · anglais

Check stationarity, build lag features, validate walk-forward, beat naive baselines. Random walks and Markov chains.

From series to honest forecast

driftsfoldsmust beatRaw seriesplot it firstStationarity checkrolling mean and stdDifferencingat most two roundsLag featuresvalues at t-1 or earlierwalk_forward_splittrain before testModelRidge, SimpleAR, ARIMABaselinespersistence, seasonalnaiveScoreMAE, RMSE, MAPE
Nodes and connections
NodeRoleKind
Raw seriesplot it first
Stationarity checkrolling mean and std
Differencingat most two rounds
Lag featuresvalues at t-1 or earlier
walk_forward_splittrain before test
ModelRidge, SimpleAR, ARIMA
Baselinespersistence, seasonal naive
ScoreMAE, RMSE, MAPE
FromToConnection
Raw seriesStationarity check
Stationarity checkDifferencingdrifts
DifferencingLag features
Lag featureswalk_forward_split
walk_forward_splitModelfolds
ModelBaselinesmust beat
BaselinesScore
Sources

Walk-forward validation

  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.
Sources

Tasks in code

TaskCode
Remove a trenddifference(series, order=1)
Rolling stats and a verdictcheck_stationarity(series, window=50)
ACF up to a lagautocorrelation(series, max_lag=20)
Series to X and ymake_lag_features(series, n_lags)
Time-ordered foldswalk_forward_split(n_samples, n_splits=5, min_train=50)
AR on lag featuresSimpleAR(n_lags=5).fit_series(series)
Recursive multi-stepforecast(last_values, n_steps)
Percent error, skips zerosmape(y_true, y_pred)
Sources

Library versions

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))
Sources

Core definitions

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.
Sources

Pick an approach

CommandWhat it does
Lag features + MLMany external features. Strongest start with boosting
ARIMAOne univariate series, short term. SARIMA for seasons
Exponential smoothingSimple trend + seasonality with Holt-Winters
Neural networksLong sequences, many series
Recursive forecastFeeds predictions back. Use for 1-5 steps
Direct forecastOne model per horizon. Use for long horizons
Multi-outputOne model outputs all horizons
Sources

Defaults and numbers

SettingDefaultNote
check_stationarity window50Rolling window size
Mean shift limit0.5Times the series std, half against half
Variance ratio limit2.0Above it, flagged non-stationary
ADF p-value0.05Below it, reject non-stationary
max_lag20Autocorrelation default
n_splits, min_train5min_train=50 rows before the first test
Rolling windows7 and 14Mean, std, min, max in days
Weekly seasonalitylag 7Possibly 14 too
Sources

Mistakes and fixes

SymptomCauseFix
Random split MSE lowerFuture leakageWalk-forward or temporal split
Near-perfect backtestFeature at time t includedAudit every feature alignment
Misses the seasonal patternTest set shorter than one cycleHold out a full seasonal cycle
Revenue doubles, model lagsAbsolute scale changedModel percentage change
Overfit with many lags"More history is better"Pick lags from the ACF
Linear model driftsTrend not removedDifference first, model second
Loses to seasonal naiveLeakage, wrong eval, or random seriesFix the bug before tuning
Errors rise in productionWorld changedRetrain on recent data
Sources

Stochastic processes in code

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
Sources

Random walks and Markov chains

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.
Sources

Anomalies in time

  • 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.
Sources

Questions

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.
Sources
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.
Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓