A time series is stationary if its statistical properties — mean, variance, and autocovariance — do not change over time. Most classical models (ARIMA, ETS) require stationarity. If your series has a trend or changing variance, you must transform it first.
| Test | Null Hypothesis | Action |
|---|---|---|
| ADF (Augmented Dickey-Fuller) | Unit root (non-stationary) | p < 0.05 → stationary |
| KPSS | Stationary | p < 0.05 → non-stationary |
| Phillips-Perron | Unit root | Robust to serial correlation |
A thousand pairs of completely independent random walks. Correlating their levels gives an average absolute correlation of 0.43, and 41% of pairs exceed 0.5 — strong relationships that do not exist. Correlating their day-to-day changes gives 0.05. Non-stationary series trend, and two trending series look related; this is Granger and Newbold’s “spurious regression”.
- Stationarity tests have low power on short series; failing to reject a unit root is not proof of one.
- Differencing removes the trend and the spurious correlation, but also the long-run level information; cointegrated series need an error-correction model instead.
- Many real series are only stationary piecewise; a structural break looks like a unit root to the test (see Changepoint Detection).
rng = np.random.default_rng(0)
corr_levels, corr_diffs = [], []
for _ in range(1_000): # pairs of independent random walks
a, b = rng.normal(size=(2, 250)).cumsum(axis=1)
corr_levels.append(abs(np.corrcoef(a, b)[0, 1]))
corr_diffs.append(abs(np.corrcoef(np.diff(a), np.diff(b))[0, 1]))
levels, diffs = np.mean(corr_levels), np.mean(corr_diffs)
share_big = np.mean(np.array(corr_levels) > 0.5)Assumes import numpy as np and import pandas as pd. Each snippet simulates or makes up its own series, as the comments say.
The same idea with a library
# Python — test for stationarity from statsmodels.tsa.stattools import adfuller, kpss result = adfuller(series, autolag='AIC') print(f"ADF stat: {result[0]:.4f}, p-value: {result[1]:.4f}") result_kpss = kpss(series, regression='c', nlags='auto') print(f"KPSS stat: {result_kpss[0]:.4f}, p-value: {result_kpss[1]:.4f}")
- C. W. J. Granger & P. Newbold, “Spurious Regressions in Econometrics”, Journal of Econometrics 2(2), 1974
- D. A. Dickey & W. A. Fuller, “Distribution of the Estimators for Autoregressive Time Series with a Unit Root”, Journal of the American Statistical Association 74(366), 1979
- Forecasting: Principles and Practice (3rd ed.), R. J. Hyndman & G. Athanasopoulos, OTexts, 2021
Last reviewed October 2026 · How this site checks its content · Report a mistake
1. Two independent random walks often show a strong correlation. Why?
Show answer
C. Granger & Newbold (1974) showed that regressions of one random walk on another look significant far too often. Differencing removes the problem.
2. Which of these is closest to stationary?
Show answer
C. Prices wander (a unit root); returns fluctuate around a roughly stable mean, though their volatility changes over time.
3. An augmented Dickey–Fuller test gives p = 0.40. What follows?
Show answer
A. The ADF null hypothesis is a unit root. A large p-value means no evidence against it — not proof of it.