Part II: Classical Forecasting and Time Series Analysis
Chapter 3: Statistical Foundations

Stationarity and Unit Roots

"They keep telling me to return to the mean. I have looked everywhere and I cannot find one. Every shock I absorb, I keep forever, and I am quietly proud of my long memory."

A Unit Root That Refuses to Revert to the Mean
Big Picture

Stationarity is the single property that lets a model learned on the past be trusted on the future. If the statistical law generating a series keeps changing, then yesterday's estimates describe a world that no longer exists, and every confidence interval, coefficient, and forecast is borrowed against a future that will not honor the loan. This section defines stationarity precisely, in both its strict and its working (weak, covariance) form, then isolates the one defect that breaks the assumption most violently: a unit root, the signature of a series that accumulates its shocks forever instead of forgetting them. We build the random walk from a single line of recursion, watch its variance grow without bound, learn to detect a unit root with the ADF and KPSS tests read as a pair, witness two independent random walks manufacture a correlation out of nothing, and finally repair a non-stationary series by differencing it the right number of times. Every later model in this part, the AR and MA pieces of Chapter 5, the cointegrated systems of Chapter 6, the state-space filters of Chapter 7, quietly assumes that this repair has already been done.

The previous section, Section 3.1, treated a time series as a single realization of a stochastic process and built the vocabulary of expectation, autocovariance, and the ergodic hope that one long sample can stand in for the ensemble. That hope has a price, and stationarity is the price. We can only estimate a mean from one trajectory if the mean is the same at every point in time; we can only estimate an autocovariance from one trajectory if the autocovariance depends on the gap between two times and not on where in the series we happen to look. This section makes those requirements exact, shows what goes wrong when they fail, and gives the standard recipe for restoring them. The thread running through it is a warning that Chapter 2 first raised at evaluation time and that returns here at modeling time: non-stationarity is the quiet way that a temporal analysis fools itself, and the cure is to detect it deliberately rather than to discover it in production.

1. Strict Versus Weak (Covariance) Stationarity Intermediate

A stochastic process $\{X_t\}$ is strictly stationary when its entire probabilistic law is invariant under a shift of the time origin. Formally, for every finite set of indices $t_1 < t_2 < \dots < t_k$ and every lag $\tau$, the joint distribution satisfies

$$F_{X_{t_1},\,\dots,\,X_{t_k}}(x_1,\dots,x_k) \;=\; F_{X_{t_1+\tau},\,\dots,\,X_{t_k+\tau}}(x_1,\dots,x_k).$$

Nothing about the joint behavior of any group of observations depends on when they were observed, only on their spacing. This is a beautiful and almost useless condition, because verifying it requires knowing every finite-dimensional distribution, and estimating those from one finite sample is hopeless. Strict stationarity is the ideal we rarely test and the assumption we sometimes prove holds for a model we have constructed.

The workhorse weakens this to its first two moments. A process is weakly stationary (equivalently covariance stationary or second-order stationary) when three conditions hold for all $t$ and all lags $h$:

$$\mathbb{E}[X_t] = \mu \;\;(\text{constant}), \qquad \operatorname{Var}(X_t) = \sigma^2 < \infty \;\;(\text{constant, finite}), \qquad \operatorname{Cov}(X_t, X_{t+h}) = \gamma(h).$$

The mean does not drift, the variance neither explodes nor vanishes, and the autocovariance between two observations depends only on the lag $h$ that separates them, never on the absolute time $t$. That third condition is the one that makes estimation possible: because $\gamma(h)$ is the same wherever we measure it, we can average over the whole series to estimate it, which is exactly the ergodic bargain of Section 3.1. The function $\gamma(h)$ is the autocovariance function, its normalized form $\rho(h) = \gamma(h)/\gamma(0)$ is the autocorrelation function (the subject of Section 3.3), and weak stationarity is precisely the condition under which these two functions are well defined as properties of the process rather than artifacts of a window. The notation $\mu$, $\sigma^2$, $\gamma(h)$ follows the unified table in Appendix A.

Key Insight: Weak Stationarity Is the Minimum Price of Learning From One Trajectory

We almost never observe an ensemble of parallel universes; we observe one history. The only way one history can teach us about the process behind it is if the process repeats its statistical behavior across time, so that different stretches of the same series act like independent looks at the same law. Constant mean, constant variance, and lag-only autocovariance are exactly the conditions that turn a single trajectory into a usable sample. Strip them away and the series stops being a sample of anything; it becomes a unique, unrepeatable event, and statistics has nothing to say about a sample of size one.

Strict stationarity implies weak stationarity whenever the second moment is finite, but the converse fails: a process can have a constant mean, constant variance, and lag-only autocovariance while its higher moments drift, so it is weakly but not strictly stationary. The two notions coincide for one important family, the Gaussian process, because a multivariate normal law is determined entirely by its mean vector and covariance matrix, so fixing the first two moments fixes everything. Most classical time-series theory lives in the weakly stationary world and leans on Gaussian or near-Gaussian behavior to make that world the strictly stationary one. The models in the chapters ahead each assume weak stationarity of something: an AR($p$) process assumes the series itself is stationary (which constrains its coefficients), a moving-average MA($q$) process is stationary by construction, an ARIMA model assumes the series becomes stationary after differencing (this section's subject), and a GARCH model in Chapter 5 assumes the conditional variance is stationary even when the level is not.

2. Why Stationarity Matters Beginner

Stationarity is what makes the past a guide to the future. A forecast is an extrapolation of an estimated law beyond the data, and the extrapolation is only valid if the law that held during estimation still holds during the forecast horizon. When a series is stationary, the mean we measured is the mean that will persist, the variance bounds the spread we will see, and the autocovariance structure we fitted is the dependence the future will obey. When a series is non-stationary, every one of those estimates is attached to a moving target, and the model confidently predicts a world that has already moved on.

The failure is not merely a loss of accuracy; it corrupts the inference machinery itself. Ordinary least squares, the workhorse behind regression-based forecasting, derives its standard errors, t-statistics, and p-values under assumptions that include a stable second-moment structure. Apply that machinery to a non-stationary regressor and the standard errors are computed against a variance that grows with the sample. The t-statistics inflate, the p-values shrink far too small, and the model declares significant relationships that are pure accidents of trending. Subsection four makes this concrete with two independent random walks that a regression insists are strongly related. This is the same family of self-deception that Section 2.6 warned about for evaluation: there, a leaky split let the future grade the past; here, a non-stationary series lets a spurious trend masquerade as a signal. Both are ways a temporal analysis reports a number it cannot deliver, and both are defended by the same discipline of checking the assumption before trusting the result. The connection runs deeper, because a backtest over a non-stationary series can look honest fold by fold and still mislead, since the regime each fold learns is not the regime the next fold faces.

Fun Fact: The Mean You Cannot Average To

For a stationary series, the sample mean converges to the true mean as you collect more data, the most basic promise statistics makes. For a random walk, the sample mean converges to nothing at all: it wanders as the walk wanders, and doubling your data does not pin it down, it just gives the walk more room to roam. You can compute the average of a random walk to fifteen decimal places, and it will still be a meaningless number, because there is no fixed mean underneath for the average to find.

3. The Random Walk and Unit Roots Intermediate

The canonical non-stationary process is the random walk,

$$X_t = X_{t-1} + \varepsilon_t, \qquad \varepsilon_t \overset{\text{iid}}{\sim} (0, \sigma^2),$$

where each step adds a fresh zero-mean shock $\varepsilon_t$ to the previous value. It is the simplest possible model of a quantity with no anchor: a price, an accumulated position, a drifting sensor bias. As Figure 3.2.1 contrasts, such a walk has no spring pulling it home, unlike a stationary series tethered to its mean. Unrolling the recursion from a fixed start $X_0$ reveals what the walk really is,

$$X_t = X_0 + \sum_{s=1}^{t} \varepsilon_s,$$
Two cartoon wanderers near a faint center line: one drifts freely away forever with no pull back, the other is held by a stretchy spring to a post on the line, contrasting a unit-root random walk that never reverts with a stationary series tethered to its mean.
Figure 3.2.1: A unit root is a wanderer with no spring: nothing pulls it back to a center, so it drifts forever, while a stationary series is gently tethered and always reverts toward its mean.

a running sum of every shock the process has ever received. No shock is ever discounted or forgotten; each one is added to the level and stays there forever. This is the defining feature of a unit-root process: shocks are permanent. The variance follows immediately, since the shocks are independent,

$$\operatorname{Var}(X_t) = \operatorname{Var}\!\Big(\sum_{s=1}^{t} \varepsilon_s\Big) = \sum_{s=1}^{t} \operatorname{Var}(\varepsilon_s) = t\,\sigma^2,$$

which grows without bound as $t$ increases. A process whose variance depends on $t$ cannot be weakly stationary, and the violation is structural, not a matter of degree: the spread of the random walk widens like $\sqrt{t}$ with no equilibrium it tends toward.

The general lens for this is the lag operator $L$, defined by $L X_t = X_{t-1}$. The random walk written with $L$ is $(1 - L)X_t = \varepsilon_t$, and more generally a first-order autoregression is

$$X_t = \phi\,X_{t-1} + \varepsilon_t \quad\Longleftrightarrow\quad (1 - \phi L)\,X_t = \varepsilon_t.$$

The behavior of the process is governed entirely by the root of the characteristic polynomial $1 - \phi z = 0$, which sits at $z = 1/\phi$. The stationarity condition is that this root lies strictly outside the unit circle, $|z| > 1$, equivalently $|\phi| < 1$. When $|\phi| < 1$ the process is a stationary AR(1): it has a fixed mean of zero, a finite variance $\sigma^2/(1-\phi^2)$, and a memory that decays geometrically, so a shock today fades as $\phi^h$ after $h$ steps and the series reverts to its mean. When $\phi = 1$ the root sits exactly on the unit circle at $z = 1$, the polynomial $1 - \phi z$ has a unit root, and the process is the random walk, with permanent shocks and no mean reversion. The knife edge between a forgetful, mean-reverting world and a perfect-memory, wandering one is the single value $\phi = 1$.

Numeric Example: Shock Persistence at Three Values of Phi

Suppose a single unit shock, $\varepsilon = 1$, hits an AR(1) process $X_t = \phi X_{t-1} + \varepsilon_t$ at time zero, with no further shocks. Its effect $h$ steps later is $\phi^h$. Compare three regimes after $h = 10$ steps.

For a strongly mean-reverting process, $\phi = 0.5$: the remaining effect is $0.5^{10} \approx 0.00098$, essentially gone, so the process has all but forgotten the shock. For a persistent but stationary process, $\phi = 0.9$: the remaining effect is $0.9^{10} \approx 0.349$, so roughly a third of the shock survives ten steps and the half-life is about $\ln(0.5)/\ln(0.9) \approx 6.6$ steps. For the unit-root process, $\phi = 1.0$: the remaining effect is $1^{10} = 1$, the entire shock survives, and it will survive at every horizon forever. The accumulated long-run effect of one shock is $\sum_{h=0}^{\infty}\phi^h = 1/(1-\phi)$, which equals $2$ at $\phi = 0.5$, equals $10$ at $\phi = 0.9$, and diverges at $\phi = 1$. The unit root is exactly the point where the geometric series stops converging.

Code 3.2.1 simulates the three regimes and confirms numerically that the unit-root variance grows linearly with $t$ while the stationary variances settle to their theoretical ceilings. We average over many independent paths to estimate the variance at each time, which is the ensemble view that subsection one said we usually cannot afford but can always simulate.

import numpy as np

def ar1_paths(phi, T, n_paths, sigma=1.0, seed=0):
    """Simulate n_paths of an AR(1): X_t = phi * X_{t-1} + eps_t, starting at 0."""
    rng = np.random.default_rng(seed)
    eps = rng.normal(0.0, sigma, size=(n_paths, T))   # one shock per step per path
    X = np.zeros((n_paths, T))
    for t in range(1, T):
        X[:, t] = phi * X[:, t - 1] + eps[:, t]        # the AR(1) recursion
    return X

T, N = 200, 20000
for phi in (0.5, 0.9, 1.0):
    X = ar1_paths(phi, T, N)
    var_T = X[:, -1].var()                             # empirical Var(X_T) across paths
    if phi < 1:
        theory = 1.0 / (1.0 - phi**2)                  # stationary AR(1) variance ceiling
        print(f"phi={phi}: Var(X_T)~{var_T:6.2f}  theory={theory:6.2f}  (stationary)")
    else:
        print(f"phi={phi}: Var(X_T)~{var_T:6.2f}  theory={T:6.2f}  (= T*sigma^2, grows)")
Code 3.2.1: Monte Carlo simulation of AR(1) paths at three values of $\phi$, estimating the variance of $X_T$ across twenty thousand independent trajectories. The stationary cases ($\phi < 1$) match the closed-form ceiling $\sigma^2/(1-\phi^2)$; the unit-root case matches $T\sigma^2$, which grows with the horizon.
phi=0.5: Var(X_T)~  1.34  theory=  1.33  (stationary)
phi=0.9: Var(X_T)~  5.27  theory=  5.26  (stationary)
phi=1.0: Var(X_T)~199.4   theory=200.00  (= T*sigma^2, grows)
Output 3.2.1: At $\phi = 0.5$ and $\phi = 0.9$ the terminal variance sits at its stationary ceiling and would not change if we ran the series longer. At $\phi = 1.0$ the terminal variance is essentially $T = 200$, so doubling the horizon would double it, the linear-in-$t$ growth derived above and the structural reason a unit-root series can never be stationary.

4. Testing for Unit Roots and Stationarity Advanced

Eyeballing a plot is not enough, because a persistent stationary AR(1) with $\phi = 0.97$ and a true random walk look almost identical over a few hundred points. Two formal tests dominate practice, and they are most powerful when read together because their null hypotheses are opposite. The Augmented Dickey-Fuller (ADF) test takes the null hypothesis that a unit root is present (the series is non-stationary). It estimates the regression

$$\Delta X_t = \alpha + \beta t + \gamma\,X_{t-1} + \sum_{j=1}^{p}\delta_j\,\Delta X_{t-j} + \varepsilon_t,$$

where the lagged differences ($p$ of them) are the "augmentation" that soaks up short-run autocorrelation. The coefficient of interest is $\gamma$: a unit root corresponds to $\gamma = 0$ (rewriting $X_t = \phi X_{t-1} + \dots$ gives $\gamma = \phi - 1$, so $\phi = 1 \Leftrightarrow \gamma = 0$). The test statistic is the t-ratio on $\hat\gamma$, but it does not follow a normal distribution under the null; it follows the non-standard Dickey-Fuller distribution, so the critical values are more negative than the usual ones. A large negative ADF statistic (below the critical value, small p-value) rejects the unit root, evidence the series is stationary.

The Kwiatkowski-Phillips-Schmidt-Shin (KPSS) test reverses the burden of proof: its null hypothesis is that the series is stationary (around a level or a deterministic trend), and the alternative is a unit root. It decomposes the series into a deterministic trend, a random walk, and a stationary error, and tests whether the random-walk component has zero variance. A large KPSS statistic (small p-value) rejects stationarity. Because ADF and KPSS frame the question from opposite sides, crossing them gives a four-way diagnosis, summarized in Table 3.2.1, and the agreement of two tests with opposite nulls is far more convincing than either alone.

ADF resultKPSS resultJoint readingWhat to do
Rejects unit rootDoes not reject stationaritySeries is stationaryModel as is
Does not reject unit rootRejects stationaritySeries has a unit rootDifference it
Does not reject unit rootDoes not reject stationarityInconclusive, weak evidenceGather more data; inspect
Rejects unit rootRejects stationarityLikely trend-stationaryDe-trend, not difference
Table 3.2.1: Reading the ADF and KPSS tests as a pair. Because the two carry opposite null hypotheses, their agreement (rows one and two) is a strong verdict, while their disagreement (rows three and four) flags an ambiguous or trend-stationary case that differencing alone would mishandle. The fourth row is the classic trend-stationary signature: remove the deterministic trend rather than take a difference.

The most vivid reason to take all of this seriously is spurious regression. Regress one independent random walk on another, two series with no causal or statistical link whatsoever, and ordinary least squares will routinely report a large coefficient, a high $R^2$, and a t-statistic so large that the relationship looks overwhelmingly significant. The trends wander, sometimes in loose visual sympathy, and OLS mistakes shared wandering for shared meaning. Code 3.2.2 demonstrates this from scratch over many trials and shows how often the naive regression is fooled.

import numpy as np

def spurious_trial(T, rng):
    """Regress one random walk on an INDEPENDENT random walk; return |t-stat| and R^2."""
    x = np.cumsum(rng.normal(size=T))          # random walk 1
    y = np.cumsum(rng.normal(size=T))          # random walk 2, fully independent of x
    X = np.column_stack([np.ones(T), x])       # design matrix [intercept, x]
    beta, *_ = np.linalg.lstsq(X, y, rcond=None)
    resid = y - X @ beta
    s2 = resid @ resid / (T - 2)               # residual variance
    se = np.sqrt(s2 * np.linalg.inv(X.T @ X)[1, 1])   # std error of the slope
    t_stat = beta[1] / se
    ss_tot = ((y - y.mean())**2).sum()
    r2 = 1.0 - (resid @ resid) / ss_tot
    return abs(t_stat), r2

rng = np.random.default_rng(1)
trials = 2000
ts, r2s = zip(*(spurious_trial(200, rng) for _ in range(trials)))
ts, r2s = np.array(ts), np.array(r2s)

# Under a TRUE null with stationary data, |t| > 1.96 should happen ~5% of the time.
print(f"fraction with |t| > 1.96 : {(ts > 1.96).mean():.1%}   (should be ~5%)")
print(f"median R^2 of nonsense  : {np.median(r2s):.3f}")
Code 3.2.2: A from-scratch spurious-regression demonstration. Two independent random walks are regressed on each other across two thousand trials, with the slope's t-statistic and $R^2$ computed by hand so the inference machinery is fully exposed. A valid test should reject at the 5 percent level only 5 percent of the time.
fraction with |t| > 1.96 : 74.8%   (should be ~5%)
median R^2 of nonsense  : 0.235
Output 3.2.2: The naive regression declares a "significant" relationship in roughly three quarters of trials between series that are utterly unrelated, fifteen times the nominal 5 percent rate, with a median $R^2$ near one quarter. This is the inference corruption of subsection two made quantitative: unit-root regressors break the standard errors, and ordinary significance testing becomes worthless until the series are made stationary or the relationship is modeled as cointegration (Chapter 6).
Warning: A High R-Squared Between Trending Series Is Evidence of Nothing

The single most common mistake in applied time-series regression is to fit a model on two trending (non-stationary) variables, see an $R^2$ of 0.9 and a tiny p-value, and conclude a relationship exists. As Code 3.2.2 shows, pure noise produces exactly this picture. Before you regress one time series on another, test both for unit roots. If both are non-stationary, either difference them first (and regress the changes) or test explicitly for cointegration, the special case where a linear combination is stationary even though the levels are not. Never trust a regression between two series until you know each one's order of integration.

5. Achieving Stationarity: Differencing and the Order of Integration Intermediate

If a unit root is the disease, differencing is the cure. The first difference operator is $\Delta X_t = X_t - X_{t-1} = (1-L)X_t$. Applying it to the random walk gives $\Delta X_t = \varepsilon_t$, white noise, which is stationary. A series that becomes stationary after differencing $d$ times, but not after $d-1$, is called integrated of order $d$, written $X_t \sim I(d)$. A stationary series is $I(0)$; a random walk is $I(1)$; a series whose changes themselves wander (an accelerating trend) may be $I(2)$ and need differencing twice. The integration order $d$ is exactly the $d$ in ARIMA($p,d,q$): it counts how many differences turn the series into something the stationary AR and MA machinery of Chapter 5 can model.

Two refinements matter in practice. Seasonal differencing removes a periodic unit root with the operator $\Delta_m X_t = X_t - X_{t-m}$, subtracting the value one full season ago (for monthly data with yearly seasonality, $m = 12$), and is the right tool when the non-stationarity repeats on a fixed calendar cycle rather than wandering freely. Variance-stabilizing transforms address a different failure: when the spread of a series grows with its level (multiplicative seasonality, exponential-looking growth), no amount of differencing fixes the changing variance, but a logarithm or a Box-Cox transform does, turning multiplicative structure into additive structure that differencing can then handle. The standard recipe is therefore transform first to stabilize variance, then difference to stabilize the mean.

Key Insight: Difference As Little As Possible, And Never Out of Habit

Differencing is not free. Each difference removes one observation, amplifies high-frequency noise, and can introduce a non-invertible moving-average unit root if applied to an already-stationary series. Over-differencing an $I(0)$ series produces an $I(-1)$-like artifact whose differenced form has a spurious negative autocorrelation at lag one and an inflated variance, which then misleads the model-identification step of Section 3.3. The discipline is to difference the minimum number of times the ADF and KPSS tests jointly demand, and to stop the moment the series tests stationary. Apply the test, difference, re-test, and halt; do not difference a second time just because the first felt productive.

Code 3.2.3 implements first differencing and the integration-order search from scratch, then re-tests with an ADF call from statsmodels as the library half of the pair. The from-scratch routine repeatedly differences until a hand-rolled ADF-style check stops rejecting stationarity, and the library version collapses the whole detect-and-difference cycle into a couple of calls.

import numpy as np
from statsmodels.tsa.stattools import adfuller

# ---- from scratch: difference until stationary, counting the integration order ----
def difference(series):
    """First difference: returns X_t - X_{t-1}, one element shorter."""
    return series[1:] - series[:-1]

def integration_order(series, max_d=3, alpha=0.05):
    """Find the smallest d such that the d-times-differenced series tests stationary."""
    s = np.asarray(series, dtype=float)
    for d in range(max_d + 1):
        pvalue = adfuller(s, autolag="AIC")[1]   # ADF p-value on the current series
        if pvalue < alpha:                       # reject unit root -> stationary
            return d, pvalue
        s = difference(s)                        # not stationary: difference once more
    return max_d, pvalue

rng = np.random.default_rng(7)
rw = np.cumsum(rng.normal(size=400))             # an I(1) random walk

d, p = integration_order(rw)
print(f"detected integration order d = {d}  (ADF p after differencing = {p:.4f})")

# sanity: the random walk itself is non-stationary, its first difference is not
print(f"ADF p-value, levels        : {adfuller(rw)[1]:.4f}  (>0.05 -> unit root)")
print(f"ADF p-value, 1st difference: {adfuller(difference(rw))[1]:.4f}  (<0.05 -> stationary)")
Code 3.2.3: From-scratch first differencing and an integration-order search, with the stationarity verdict delegated to statsmodels.adfuller. The loop differences the series until the ADF p-value drops below 0.05, returning the order $d$ at which stationarity is reached.
detected integration order d = 1  (ADF p after differencing = 0.0000)
ADF p-value, levels        : 0.6312  (>0.05 -> unit root)
ADF p-value, 1st difference: 0.0000  (<0.05 -> stationary)
Output 3.2.3: The random walk is correctly identified as $I(1)$: in levels the ADF test cannot reject a unit root (p = 0.63), but after one difference it rejects emphatically (p < 0.0001). The search stops at $d = 1$, applying the minimum-differencing discipline of the Key Insight rather than differencing further.
Library Shortcut: One Call Picks the Differencing Order For You

The entire detect-difference-retest loop in Code 3.2.3 is a single function call in pmdarima: pmdarima.arima.ndiffs(series, test="adf") returns the integration order $d$ directly, and nsdiffs(series, m=12, test="ocsb") returns the seasonal differencing order $D$. Feed the chosen orders straight into pmdarima.auto_arima, which then searches the ARIMA($p,d,q$)($P,D,Q$)$_m$ grid for you, or use Nixtla's statsforecast.AutoARIMA, which runs the unit-root tests and the order search internally. The roughly fifteen lines of hand-rolled differencing logic above collapse to one line; the library handles the test-statistic distributions, the lag-length selection, the seasonal case, and the over-differencing guard that we had to reason about manually. The from-scratch version exists so you know precisely what ndiffs decided and why, never as the code you ship.

Practical Example: The Energy Forecaster That Forecast a Flat Line

Who: A data scientist on the demand-planning team of a regional electricity utility, building a day-ahead load forecaster.

Situation: The raw series was hourly megawatt demand over six years, with a strong daily and weekly cycle, a slow upward trend from population growth, and a spread that visibly widened in the high-demand summers.

Problem: A textbook ARMA model fitted on the raw series produced forecasts that decayed toward a constant within a day and badly under-predicted peak load, despite excellent in-sample fit statistics.

Dilemma: The in-sample $R^2$ was high, so the obvious instinct was to add more AR and MA terms. But more terms only made the over-fitting worse and the forecasts no less flat. The real question was whether the series even satisfied the stationarity the ARMA model assumed.

Decision: Before touching model order, the team ran the full diagnostic battery: a log transform to address the level-dependent variance, then ADF and KPSS on the result, then seasonal and ordinary differencing guided by ndiffs and nsdiffs.

How: KPSS rejected stationarity on the raw and the logged-but-undifferenced series; ADF could not reject a unit root, the row-two diagnosis of Table 3.2.1. A log transform stabilized the summer-widening variance, one seasonal difference at $m = 24$ removed the daily cycle's seasonal unit root, and one ordinary difference removed the growth trend. After the transform-and-difference pipeline, both tests agreed the series was stationary, and an ARIMA fitted on it captured the structure the raw-series model had missed.

Result: The forecasts stopped collapsing to a constant and tracked the daily peaks, cutting peak-hour error by roughly a third. The high $R^2$ on the original model had been the spurious-fit signature of subsection four: a non-stationary regressor flattering an over-parameterized fit.

Lesson: Stationarity diagnosis is not a preliminary chore to rush past; it determines the entire model. The fix here was not a better model but the right transform and the right number of differences, decided by tests rather than by adding parameters until the in-sample fit looked good.

Research Frontier: Stationarity in the Age of Foundation Forecasters (2024 to 2026)

The unit-root question has not gone away; it has migrated into deep learning. The non-stationary Transformer line (the NeurIPS 2022 "Non-stationary Transformers" and its successors through 2024 to 2025) shows that naively normalizing a non-stationary series before a neural forecaster can erase the very dynamics worth predicting, and proposes de-stationary attention that removes non-stationarity for stability while restoring it inside the attention scores, a learned analogue of the difference-then-undo logic of this section. Reversible instance normalization (RevIN) does something similar at the input and output: it removes each window's mean and variance before the network and adds them back after, a per-window stationarization that is now standard in models like PatchTST and in the temporal foundation models of Chapter 15 (Chronos, TimesFM, Moirai). The frontier question for 2025 to 2026 is how zero-shot foundation forecasters should handle series whose integration order they have never seen, since a model pretrained mostly on stationary or once-differenced data can stumble on a genuinely $I(2)$ regime. The classical ADF and KPSS tests are increasingly used not to difference the data but to diagnose what preprocessing a neural model needs, so the hundred-year-old unit-root theory has become a preprocessing oracle for billion-parameter forecasters.

Fun Fact: Dickey and Fuller Needed a Distribution That Did Not Exist

When David Dickey and Wayne Fuller built their test in the 1970s, the obstacle was that the t-statistic on $\hat\gamma$ under a unit root does not follow the normal or even the Student-t distribution that every regression textbook promised. The usual asymptotics simply break when the regressor is a random walk. They had to tabulate the correct critical values by Monte Carlo simulation, generating random walks on the era's computers and recording where the statistic landed. The "Dickey-Fuller distribution" your software quietly looks up today is the frozen output of that simulation, a reminder that non-stationarity breaks even the distribution of the test meant to detect it.

Exercise 3.2.1: Which Stationarity, and What Breaks Without It Conceptual

For each process, state whether it is strictly stationary, weakly stationary, both, or neither, and justify in one or two sentences. (a) i.i.d. standard normal noise. (b) i.i.d. draws from a Cauchy distribution. (c) A stationary AR(1) with $\phi = 0.7$ and Gaussian innovations. (d) A random walk with drift, $X_t = 0.1 + X_{t-1} + \varepsilon_t$. For part (b), explain which of the three weak-stationarity conditions fails and why no amount of data can rescue an estimate of its mean, connecting to the constant-finite-variance requirement of subsection one.

Exercise 3.2.2: Reproduce the Variance Law and the Spurious Regression Coding

Using Code 3.2.1 as a base, plot the empirical $\operatorname{Var}(X_t)$ against $t$ for $\phi \in \{0.8, 0.95, 1.0\}$ and overlay the theoretical curves ($\sigma^2/(1-\phi^2)$ for the stationary cases, $t\sigma^2$ for the unit root). Then extend Code 3.2.2 to record how the spurious-regression rejection rate changes as the series length $T$ grows from 50 to 1000. Does the false-positive rate shrink toward 5 percent as you collect more data, the way it would for stationary series, or not? Explain what your result says about the danger of "just getting more data" when the regressors have unit roots.

Exercise 3.2.3: Diagnose and Difference a Real Series Analysis

Take a series with a clear trend and seasonality (for example monthly airline passengers, retail sales, or one of the running datasets from Appendix E). Run ADF and KPSS on the raw series, on its log, on its first difference, and on a seasonal-then-ordinary difference, recording the four-way verdict of Table 3.2.1 at each stage. Report the integration order $d$ (and seasonal order $D$) at which both tests finally agree on stationarity, then deliberately difference one extra time and describe the over-differencing artifact (the negative lag-one autocorrelation and inflated variance of the Key Insight) that appears in the result.

Exercise 3.2.4: When Differencing Is the Wrong Cure Open-Ended

Construct a trend-stationary series, $X_t = a + b\,t + \eta_t$ with stationary $\eta_t$, and a difference-stationary series (a random walk with drift) that have the same visual upward slope. Show that ADF-with-trend rejects the unit root for the first but not the second, the row-four versus row-two distinction of Table 3.2.1. Then argue, with reference to how the forecast error variance behaves at long horizons, why differencing a trend-stationary series and de-trending a difference-stationary series are both mistakes, and propose a decision rule a practitioner could follow to choose between de-trending and differencing.