Checking the Model, Seeing the Season

Module 5 · Residual diagnostics and seasonality

Gary Cornwall

Econ 6376 · The George Washington University

Where we are: the last knob on the board

yt=α+δt+∑j=1pϕjyt−j+∑l=1qθlϵt−l+ϵty_t=\alpha+\delta t+\sum_{j=1}^{p}\phi_j y_{t-j}+\sum_{l=1}^{q}\theta_l\epsilon_{t-l}+\epsilon_t

Term L3 L4 L5
α\alpha, δt\delta t on on on
ϕjyt−j\phi_j y_{t-j}, all pp on on on
θlϵt−l\theta_l \epsilon_{t-l}, all qq on on on
Seasonal block — the same structure at lag ss off off turning on today
ϵt\epsilon_t on on on

Two halves, one picture. Part A: learn to check a model — residuals are the final jury. Part B: the structure Module 4’s residuals kept pointing at — what seasonality is, and three ways to treat it.

By the end of today every slider on the mixing board is on.

Last time ended on a spike. Reproduce it.

library(forecast)
csv <- read.csv("../../data/UNRATE.csv")   # cached FRED pull
csv <- subset(csv, observation_date >= "1960-01-01" &
                   observation_date <= "2019-12-01")
unrate   <- ts(csv$UNRATE, start = c(1960, 1), frequency = 12)
d_unrate <- diff(unrate)

fit_l4 <- Arima(d_unrate, order = c(1, 0, 2),   # Module 4's pick,
                include.mean = FALSE)   # same zero-mean policy
e_hat  <- residuals(fit_l4)
ggAcf(e_hat, lag.max = 36)
acf(e_hat, lag.max = 36, plot = FALSE)$acf[c(13, 25, 37)]
ρ̂e(12)\hat\rho_e(12)−0.145
ρ̂e(24)\hat\rho_e(24)−0.125
ρ̂e(36)\hat\rho_e(36)−0.130
band, 1.96/T1.96/\sqrt{T} (≈ 2/T2/\sqrt{T})±0.073

Same window as Module 4: 1960-01 to 2019-12, T=719T = 719 differences. The low lags are quiet. The spikes every twelve months survive.

What would you need to see to sign off on this model?

Cached: the Module 4 residual ACF

The principle: residuals should behave like innovations

  • ϵt\epsilon_t is the innovation — the DGP’s shock. Unobserved.
  • et=yt−ŷte_t = y_t - \hat y_t is the residual — what the fitted model leaves. Observed.
  • If the model is right, ete_t should behave like ϵt∼WN(0,σ2)\epsilon_t \sim \text{WN}(0,\sigma^2). Not is white noise — no finite sample is — but could plausibly be.
  • If it does not, there is nothing to fix in the data. There is something to fix in the model.

Four checks, one ranked line:

serial correlation → heteroskedasticity → zero mean → normality

The first is the whole of today. The second is a preview of Module 14 — eyeball it for now. The last two are cheap, and almost never the real problem.

Serial correlation: the definition, then the rule

Term Meaning Belongs to
Autocorrelation (dependence, memory, persistence) A property of a series, measured by its ACF — any series, residuals included the data
Serial correlation The diagnostic verdict that a fitted model’s residuals ete_t show autocorrelation the model, relative to the data
Residual seasonality Seasonal autocorrelation left in a published seasonally adjusted series the adjustment procedure — itself a model

“Serial correlation is not a property of the data — it is a property of your model relative to the data. It means your model is wrong.”

Enders, Hamilton, and Hyndman & Athanasopoulos use autocorrelation and serial correlation interchangeably, and so does most applied practice. Recognize that usage in the wild; the course’s line is deliberate.

Neither is a property of the data.

Reading residuals against the Module 3 table

Residual ACF pattern First candidate diagnosis — a proposal, not a verdict
Mostly inside ±2/T\pm 2/\sqrt{T}, no systematic pattern no visible evidence of remaining serial correlation
Spike at lag 1 only an omitted short-run AR or MA term
Geometric decay from lag 1 omitted low-order AR dynamics
Cutoff after a few lags omitted low-order MA dynamics
Spikes at s,2s,3ss, 2s, 3s (here 12,24,3612, 24, 36) omitted dependence at the seasonal frequency — today’s transition
Large correlations that persist, not decay insufficient differencing; reassess the transformation

Same table as Module 3, different question: there it told you what to fit; here, what your fit missed. Propose the next candidate, refit, re-check — a diagnosis earns its place only if the revised fit removes the pattern without creating a new one.

Misspecify and watch: predict the residual ACF before every move

Pick a DGP you know — AR(2), MA(2), or the two glued together — fit an ARMA(p,qp,q), read the residual ACF and the Ljung-Box verdict. Predict before every move.

Three lessons from the simulation

  1. The diagnostic tools work. When the residuals carry real structure, Ljung-Box flags it without knowing the DGP, and the residual ACF shape proposes what to add — a candidate, not a verdict.

  2. The fit will not volunteer the problem. Fit the one-block-short model with Arima() and it prints coefficients, standard errors, an AIC, a log-likelihood. None of them says “short”. Stop at the IC winner without checking residuals and you own a confidently wrong model. Module 1’s spurious-regression Monte Carlo, in a new setting: the printout lies, and only a dependence-aware check catches it.

  3. The structure came from the model. The DGP was perfectly well behaved. The serial correlation in the residuals was generated by the fit. The correct orders leave white noise — and extra orders leave white noise too, at a price diagnostics cannot see and only parsimony can. Change the fit and the structure is gone; the data never moved.

Serial correlation is a model report card, not a disease the data has.

Ljung-Box, on one slide

Q(h)=T(T+2)∑k=1hρ̂k2T−k∼aχh−fitdf2Q(h)=T(T+2)\sum_{k=1}^{h}\frac{\hat\rho_k^{\,2}}{T-k} \;\stackrel{a}{\sim}\;\chi^2_{\,h-\text{fitdf}}

  • ρ̂k\hat\rho_k is the sample autocorrelation of the residuals at lag kk; a joint test that the first hh of them are all zero.
  • fitdf =p+q= p+q for a non-seasonal ARMA fit, p+q+P+Qsp+q+P+Q_s for a SARIMA fit, 00 for a raw series with no model. You spent those degrees of freedom estimating; the test charges for them.
  • Choose hh before you look: h=10h=10 for non-seasonal residuals, h=24h=24 for monthly seasonal residuals. Report both on monthly data. Hunting across hh until one rejects is pp-hacking with a chi-square.
  • Large pp-value is good news. In regression you want to reject. In diagnostics you want to fail to reject: no structure found.
Box.test(e_hat, lag = 10, type = "Ljung-Box", fitdf = 3)   # p + q = 1 + 2
Box.test(e_hat, lag = 24, type = "Ljung-Box", fitdf = 3)
checkresiduals(fit_l4)                                     # the all-in-one from forecast

Verdict on the Module 4 fit

Box.test(e_hat, lag = 10, type = "Ljung-Box", fitdf = 3)
Box.test(e_hat, lag = 24, type = "Ljung-Box", fitdf = 3)
checkresiduals(fit_l4)
h=10h=10: Q=3.76Q = 3.76, χ72\chi^2_{7}p = 0.807 · pass
h=24h=24: Q=46.1Q = 46.1, χ212\chi^2_{21}p = 0.0012 · fail

The two horizons disagree, and the disagreement is the lesson. Over ten lags these residuals are white noise. Widen the window to lag 24 and the joint statistic sees what the eye saw: a test that never looks at lag 12 cannot reject because of lag 12.

The residual ACF shows no decay from lag 1, no cutoff, no spike at lag 1 — the Module 3 table’s first four rows are empty.

What it shows is spikes at 12, 24, 36. Every twelve months. Same sign each time.

Spikes that recur at regular intervals. What are those?

Cached: checkresiduals() on the Module 4 fit

You can see it

UNRATENSA — the same civilian unemployment rate, not seasonally adjusted. What the survey measures each month.

ρ̂(12)\hat\rho(12) of Δ\DeltaUNRATENSA0.804
ρ̂(24)\hat\rho(24)0.777
ρ̂(36)\hat\rho(36)0.742
band±0.073
ρ̂(12)\hat\rho(12) of Δ\DeltaUNRATE, published−0.089

No model has been fitted. This is the series’ own autocorrelation. The lag-12 spike in the Module 4 residuals was serial correlation — a verdict on a fit. Same picture, different owner.

Ten times the band, and barely decaying from one year to the next. Hold that thought.

Nobody agrees what it is — but you can see it

Tradition Core object Representative work
Calendar means (regularity and repetition) Seasonal means across a calendar partition Falkner (1924); Kallek (1978)
Seasonal unit roots and stability (the ARIMA polynomial) Roots of the AR polynomial at the seasonal frequencies Hylleberg, Engle, Granger & Yoo (1990); Canova & Hansen (1995)
Unobserved components (signal extraction) A latent seasonal component StS_t identified by a restriction Hillmer & Tiao (1982); Bell & Hillmer (1984); X-13ARIMA-SEATS
Spectral (frequency domain) A functional of the spectral density at the seasonal frequencies Nerlove (1964); Granger (1978)

Each is internally consistent and answers a well-posed question. None nests the others. Analysts consult several at once and quietly assemble a superset nobody has written down.

“Some of us looking for answers regarding seasonal analysis may feel as if we’re in a dark room looking for a black cat. That’s bad, but it’s not as bad as it could be. Pity the philosophers who are in a dark room looking for a black cat that isn’t there.” — Zellner et al. (1978, p. 451)

The taxonomy: where the seasonal unit root sits

Time series Non-seasonal Seasonal Deterministic a fixed calendar pattern dummies, sinusoids Stochastic a pattern that drifts: seasonal ARMA, seasonal differencing Seasonal unit roots: the extreme-persistence boundary

The seasonal unit root is not a third species. It is the stochastic case pushed to the limit where the seasonal pattern never mean-reverts — the random walk is the AR(1) at ϕ1=1\phi_1 = 1 (Module 2), and this is the seasonal analogue at Φ1=1\Phi_1 = 1. The differencing decision DD is the decision about which side of that boundary the series is on.

There is no clean “isn’t”

Students want a checklist: seasonality is X; calendar effects, trading days, holidays are not seasonality. The course declines, and the reason matters.

  • A definition is a line you draw. Choose one — seasonal means, seasonal unit roots, a latent component, a spectral peak — and whatever lands inside that line is seasonal by construction, whether or not you would have called it that beforehand.
  • Trading-day variation is the standard case: the number of Mondays in a month varies with a rhythm close to, but not at, the annual frequency. Under a spectral definition it leaks into the seasonal frequencies; under a calendar-means definition it lands in the monthly means. The definition decides, and different definitions decide differently. Moving holidays likewise.
  • These effects are not statistically separable from seasonality without an extra identifying assumption — which is exactly what X-13 supplies when it pre-regresses them out.
  • The one safe non-example: monthly sampling alone does not make a series seasonal. A random walk observed every month has a seasonal frequency at which structure could live. It does not put structure there.
Hold on to “the line you draw”. The Deeper Dive at the end of today draws one explicitly — a partition of the frequency axis — and then asks a testable question relative to it.

The triage: three paths

Diagnosis or need Path What it commits you to
The pattern is deterministic — fixed, exogenous, calendar-driven Path 1 · seasonal dummies (or Fourier terms) in a regression a fixed shape; if the pattern drifts, the residuals will say so
The pattern is stochastic — it drifts, and you want to model it and forecast with it Path 2 · SARIMA — seasonal ARMA terms, with seasonal differencing if there is a seasonal unit root a joint model of both bands of the console
“I need it gone, not modeled” — a series you can read, compare month to month, or feed to a non-seasonal model Path 3 · seasonal adjustment — decompose, remove the seasonal, publish the rest trusting a procedure that is itself a model

The three are not rivals. Agencies run path 3 to publish the series you read in the news; forecasters run path 2 on the raw or the published series; path 1 is the cheap first question — is the pattern fixed?

A journalist comparing this month to last month — which path? A forecaster building next year’s fan chart?

All three, next, on one series: the raw unemployment rate.

Path 1: seasonal dummies

On the first difference — the course has treated the unemployment rate as I(1)I(1) since Module 2 — regress on eleven month indicators plus an intercept, with month index mm and January (m=1m=1) as the reference:

Δyt=α+∑m=212βm𝟙{montht=m}+ϵt\Delta y_t=\alpha+\sum_{m=2}^{12}\beta_m\,\mathbb{1}\{\text{month}_t=m\}+\epsilon_t

  • What it assumes: a fixed monthly pattern. The same January jump in 1965 and in 2019. Eleven parameters β2,…,β12\beta_2,\ldots,\beta_{12}, any shape. (The indicator is written 𝟙{⋅}\mathbb{1}\{\cdot\} on purpose: DD is today’s seasonal differencing order, and jj is the AR lag index.)
  • What the residuals will tell you: if the pattern really is fixed, the residual ACF is clean at 12, 24, 36. If it drifts, the seasonal spikes survive — and that verdict sends you to path 2.
  • The parsimonious cousin, one line: replace the eleven dummies with a few sine–cosine pairs from forecast::fourier(). Fewer parameters, same fixedness — and fixedness, not parsimony, is what is about to be tested.
seasonal_dummies <- function(y, ref = 1) { ... }     # helpers/seasonality.R: the (s - 1) indicator columns

Path 1, live: fit the dummies, read the residuals

nsa <- read.csv("../../data/UNRATENSA.csv")
nsa <- subset(nsa, observation_date >= "1960-01-01" &
                   observation_date <= "2019-12-01")
unratensa   <- ts(nsa$UNRATENSA, start = c(1960, 1),
                  frequency = 12)
d_unratensa <- diff(unratensa)

source("../../helpers/seasonality.R")   # seasonal_dummies()
X_month <- seasonal_dummies(d_unratensa)   # Jan = reference
fit_dum <- lm(d_unratensa ~ X_month)
summary(fit_dum)$r.squared                 # 0.72
e_dum <- ts(residuals(fit_dum), frequency = 12,
            start = start(d_unratensa))
ggAcf(e_dum, lag.max = 36)
Box.test(e_dum, lag = 24, type = "Ljung-Box", fitdf = 0)

Before the plot: the dummies absorb R2=0.72R^2 = 0.72. Will lag 12 clear the band?

residual ρ̂e(12)\hat\rho_e(12)0.350
residual ρ̂e(24)\hat\rho_e(24)0.298
residual ρ̂e(36)\hat\rho_e(36)0.223
band±0.073

Honestly: the fitdf =0=0 Ljung-Box also rejects for non-seasonal reasons — lags 2, 3, 5, 7, 11 breach too, the Module 4 dynamics a dummy regression cannot carry. Give it ARMA(1,2) errors and only the seasonal lags do the rejecting: h=10h=10 passes (p=0.30p=0.30), h=24h=24 fails.

Is the pattern fixed? Month effects by half, 1960–89 against 1990–2019: F(11,695)=12.5F(11,695)=12.5, p≈10−21p\approx10^{-21}. June halves (+0.84→+0.43+0.84\to+0.43); July flips sign (−0.33→+0.02-0.33\to+0.02).

The fixed part is absorbed. A drifting part remains. On to path 2.

Cached: the dummy residuals

Cached: the fitted monthly pattern, by half of the window

Path 2: the seasonal band on the console

The non-seasonal band works at lags 1,2,3,…1, 2, 3, \ldots — the knobs since Module 3. A seasonal process has a second band at lags s,2s,3s,…s, 2s, 3s, \ldots with the same kinds of knobs: AR, MA, and differencing.

Φp(L)ΦP(Ls)(1−L)d(1−Ls)Dyt=Θq(L)ΘQ(Ls)ϵt\Phi_p(L)\,\Phi_P(L^s)\,(1-L)^d\,(1-L^s)^D\,y_t=\Theta_q(L)\,\Theta_Q(L^s)\,\epsilon_t

Φp(L)=1−ϕ1L−⋯−ϕpLp\Phi_p(L)=1-\phi_1L-\cdots-\phi_pL^p

ΦP(Ls)=1−Φ1Ls−⋯−ΦPLPs\Phi_P(L^s)=1-\Phi_1L^s-\cdots-\Phi_PL^{Ps}

Θq(L)=1+θ1L+⋯+θqLq\Theta_q(L)=1+\theta_1L+\cdots+\theta_qL^q

ΘQ(Ls)=1+Θ1Ls+⋯+ΘQLQs\Theta_Q(L^s)=1+\Theta_1L^s+\cdots+\Theta_QL^{Qs}

  • Lowercase (p,d,q)(p,d,q), ϕ\phi, θ\theta: the base band. Uppercase (P,D,Q)(P,D,Q), Φ\Phi, Θ\Theta: the seasonal band. Minus signs on AR, plus signs on MA, in both bands.
  • ss is the period: 12 monthly, 4 quarterly, 7 daily-with-a-weekly-cycle. ARIMA(1,1,1)(0,1,1)12(1,1,1)(0,1,1)_{12} reads: base band, seasonal band, monthly.
  • (1−Ls)D(1-L^s)^D is seasonal differencing, Δsyt=yt−yt−s\Delta_s y_t = y_t - y_{t-s} — the Module 2 operator at the seasonal frequency. New today.
  • When the seasonal MA order appears alone in prose it is written QsQ_s, so it does not collide with the Ljung-Box QQ.

Multiplicative structure: lag 13 for free

The bands multiply. Take the smallest case with both on, ARIMA(1,0,0)(1,0,0)12(1,0,0)(1,0,0)_{12}:

(1−ϕ1L)(1−Φ1L12)yt=ϵt(1-\phi_1L)(1-\Phi_1L^{12})\,y_t=\epsilon_t

1−ϕ1L−Φ1L12+ϕ1Φ1L131-\phi_1L-\Phi_1L^{12}+\phi_1\Phi_1L^{13}

  • Coefficients at lags 1, 12, and 13. The lag-13 term is not a free parameter — it is the product ϕ1Φ1\phi_1\Phi_1, forced by the multiplication.
  • Lag-12 and lag-13 dynamics for two parameters instead of thirteen. That is what makes SARIMA parsimonious next to a long AR that reaches lag 12 by sheer depth.
  • The cross-frequency dynamics come for free as products of low-order terms. If the residuals disagree with the product, a free lag-13 term (an additive seasonal) is the less restrictive alternative — but for most macro applications the product works.

sarima_simulator() in helpers/simulators.R does exactly this expansion before running the master-equation loop. (Worked in the notes’ Deeper Dive §5.7.2.)

Reading the seasonal ACF: decaying or flat?

Decaying spikes at s,2s,3ss, 2s, 3s: a stationary seasonal ARMA. Model the band, keep D=0D=0.

Flat spikes near one: a seasonal unit root. Difference once at the seasonal frequency, D=1D=1, then re-read.

Do not reach for D=2D=2 without fresh evidence of a second seasonal unit root.

The benchmark, Box & Jenkins’s airline model, ARIMA(0,1,1)(0,1,1)12(0,1,1)(0,1,1)_{12}:

(1−L)(1−L12)yt=(1+θ1L)(1+Θ1L12)ϵt(1-L)(1-L^{12})\,y_t=(1+\theta_1L)(1+\Theta_1L^{12})\,\epsilon_t

Two differences, two parameters. A benchmark to improve on, not an answer.

Path 2, live: four candidates, one decision

fit_air  <- Arima(unratensa, order = c(0, 1, 1), seasonal = list(order = c(0, 1, 1), period = 12))
fit_d0   <- Arima(unratensa, order = c(1, 1, 2), seasonal = list(order = c(1, 0, 1), period = 12))
fit_auto <- auto.arima(unratensa)
fit_win  <- Arima(unratensa, order = c(1, 1, 2), seasonal = list(order = c(0, 1, 1), period = 12))
source("../../helpers/diagnostics.R")   # lb_both()
lb_both(fit_win); checkresiduals(fit_win)

Before the table: which of the four will put a coefficient on a boundary?

Candidate Seasonal coefficient LB pp (10 / 24) Verdict
Airline (0,1,1)(0,1,1)12(0,1,1)(0,1,1)_{12} Θ̂1=−0.78\hat\Theta_1=-0.78, interior 10−1310^{-13} / 10−1110^{-11} fails both: the (0,1,1)(0,1,1) block is too thin — residual ACF 0.21, 0.14, 0.12, 0.11 at lags 2–5
D=0D=0 (1,1,2)(1,0,1)12(1,1,2)(1,0,1)_{12} Φ̂1=0.994\hat\Phi_1=\mathbf{0.994} 0.97 / 0.76 passes and wins the D=0D=0 table, but the seasonal AR root, in z=L12z=L^{12}, has modulus 1.006, within 0.01 of the unit circle: a seasonal unit root in disguise
auto.arima() (2,0,2)(2,1,2)12(2,0,2)(2,1,2)_{12} d=0d=0, D=1D=1, eight coefficients 0.27 / 0.47 passes; a third differencing class, so its AICc is on neither table — a very good intern, read its work
Winner (1,1,2)(0,1,1)12(1,1,2)(0,1,1)_{12} Θ̂1=−0.75\hat\Theta_1=-0.75, interior 0.88 / 0.42 passes both horizons, every coefficient interior, same differencing as the airline

Information criteria compare only within a differencing class

A D=1D=1 likelihood is evaluated on the seasonally differenced series — a different, shorter series than a D=0D=0 model uses — and d=0d=0 is different again. The four candidates fall into three classes, and rows are comparable only within a class.

The (d,D)=(1,1)(d,D)=(1,1) class, the airline model against the winner:

AIC BIC LB pp (10 / 24)
airline (0,1,1)(0,1,1)12(0,1,1)(0,1,1)_{12} −155.7 −142.0 10−1310^{-13} / 10−1110^{-11}
(1,1,2)(0,1,1)12(1,1,2)(0,1,1)_{12} −206.2 −183.4 0.88 / 0.42

The winner beats the airline by 50 AIC / 41 BIC points — the whole gap is the non-seasonal block.

  • In the (1,0)(1,0) class the seasonal ARMA(1,1) band wins by hundreds of points over Module 4’s bare block — and its winner is the model with Φ̂1=0.994\hat\Phi_1=0.994.
  • auto.arima() sits alone in the (0,1)(0,1) class; its AICc is on neither table.

The tables cannot pick among candidates 2, 3, 4. Diagnostics and the coefficients do.

And a check nobody planned: X-13’s own automatic model selection, run on the next path with no guidance, chose the same orders as the winner.

Cached: the four residual ACFs

DD is a commitment, and the data push back from both sides

One seasonal difference too many

On the published, already adjusted UNRATE, impose D=1D=1 — the airline model:

−0.99999
Θ̂1\hat\Theta_1, airline on UNRATE

The seasonal MA coefficient is pinned at the non-invertibility boundary: the Module 3 over-differencing signature. The model is trying to undo a difference the data did not need — the adjustment procedure had, in effect, already taken it.

One seasonal difference too few

On the raw UNRATENSA, leave D=0D=0 and model the band with seasonal ARMA terms:

+0.994
Φ̂1\hat\Phi_1, (1,1,2)(1,0,1)12(1,1,2)(1,0,1)_{12} on UNRATENSA

The seasonal AR coefficient is pinned at one — the root in z=L12z=L^{12} has modulus 1.006, within 0.01 of the unit circle. The model is trying to manufacture a difference the data did need. Diagnostics pass; the coefficient tells the truth.

Either boundary estimate says: revisit DD, not the ARMA orders.

Neither shows up in an IC table, because IC cannot compare across differencing orders — a D=1D=1 likelihood is evaluated on a different, shorter series. When candidates disagree about differencing, decide on diagnostics and coefficients, and on out-of-sample forecasts (Module 6).

Path 3: what seasonal adjustment does

A forecaster wants to model the seasonal band. A statistical agency, a journalist, a policymaker wants it gone, so this month can be compared with last month without the January jump in the way.

yt=trend-cyclet+seasonalt+irregularty_t=\text{trend-cycle}_t+\text{seasonal}_t+\text{irregular}_t

  • What it does. Treat the series as three latent pieces (the unobserved-components tradition). Estimate the seasonal piece with a model. Subtract it. Publish the rest: trend-cycle plus irregular. The seasonal piece is what the procedure decided was seasonal — under its definition, with its identifying restriction.
  • Who does it. The BLS, the Census Bureau, the BEA and their counterparts abroad run X-13ARIMA-SEATS (Census Bureau). It fits a regARIMA model — a SARIMA plus regressors for trading days, leap years, holidays and outliers — extends the series at both ends, and extracts the seasonal by X-11 filters or a SEATS model-based decomposition. Every option has a default; agencies tune them per series.
  • Why you must recognize it. Most headline macro series you will ever download — the unemployment rate, payrolls, retail sales, GDP, CPI — are its outputs.

Recognition level, deliberately: no simulate step, no spec file, one call.

Path 3, live: one call, and the reveal

library(seasonal)            # wraps X-13ARIMA-SEATS
fit_x13 <- seas(unratensa)   # no options, no spec file
summary(fit_x13)             # chose (1 1 2)(0 1 1), SEATS,
                             # no log, leap year + weekday,
                             # and a level shift at 1975-01
sa_x13 <- final(fit_x13)
autoplot(cbind(published = unrate, x13 = sa_x13))
cor(sa_x13, unrate)
mean(abs(sa_x13 - unrate)); max(abs(sa_x13 - unrate))

Before the overlay: how close will one default call get to the published line?

correlation with published UNRATE0.9985
mean |difference|0.065 pp
max |difference| (1976-02)0.37 pp
months within 0.1 pp80%

The two adjusted lines are one line. The gaps are what a default call against a production run should leave — BLS adjusts concurrently, with its own span, regressors and revision history. One X-13 call reproduces the published series. Not equals.

The UNRATE series you have used since Module 2 is the output of this procedure.

Every ACF, ADF test, ARMA fit and information criterion in Modules 2–4 was computed on trend-cycle-plus-irregular from an X-13 run at the BLS, not on what the survey measured. Not a complaint — it changes what you are modeling.

Cached: the overlay

The cost

Adjustment is a model. It fits a SARIMA, imposes an identifying restriction, and removes what that restriction calls seasonal. Like any model it can be wrong relative to the data — so the rule from Part A applies to it exactly as it applies to your own fits.

  • The adjustment procedure can have serial correlation of its own. When it shows up at the seasonal lags of the published series, the course’s name for it is residual seasonality — seasonal autocorrelation left in a series that has already been adjusted.
  • The published series carries the procedure’s choices as well as the data’s dynamics. A model fitted to it is partly modeling the procedure.
  • The field’s tests and its filters answer different questions. A diagnostic from one tradition can flag seasonality that a filter built in another was never designed to remove — and the two do not speak the same language.

You cannot diagnose residual seasonality without saying what seasonality is.

A test for “seasonality left over” needs a definition to test against — the one thing the field has never agreed on. The Deeper Dive that follows is one way of drawing that line so it can be tested. It is optional and never assessed.

Two representations of one series

Deeper Dive · not assessable

Every finite series {xt}t=1T\{x_t\}_{t=1}^{T} has two equivalent descriptions, connected by the discrete Fourier transform at the Fourier frequencies ωj=2πj/T\omega_j = 2\pi j/T:

dx(ωj)=∑t=1Txte−iωjtd_x(\omega_j)=\sum_{t=1}^{T}x_t\,e^{-i\omega_j t}

xt=1T∑j=0T−1dx(ωj)eiωjtx_t=\frac{1}{T}\sum_{j=0}^{T-1}d_x(\omega_j)\,e^{i\omega_j t}

  • The time domain orders observations by calendar position — the language of every ACF and lag polynomial in this course.
  • The frequency domain reallocates the same variance by rate of repetition: every two months, every six, every year, every decade.
  • Lossless. The second formula recovers the series exactly.

The series is a mixture. The DFT is the centrifuge that separates it into pure tones. For monthly data the annual cycle lives at ω=π/6\omega=\pi/6, with harmonics at π/3\pi/3, π/2\pi/2, 2π/32\pi/3, 5π/65\pi/6, π\pi.

Time to frequency: the instructor’s animation, local file. Press play.

The same seasonality in both domains

Deeper Dive · not assessable

The periodogram IT(ωj)=1T|dx(ωj)|2I_T(\omega_j)=\tfrac{1}{T}\lvert d_x(\omega_j)\rvert^2 is the sample estimate of how variance is spread across frequencies.

Here it is for Δ\DeltaUNRATENSA — the series whose ACF spiked at 12, 24, 36. The spikes sit on the dashed lines: the same seasonality, by rate of repetition.

Which line is tallest? π/3\pi/3 — the semiannual harmonic, a January jump and a June jump — not the annual π/6\pi/6. The lag-12 spike cannot tell you that; the periodogram shows it at a glance.

Model-free: deterministic, stochastic and unit-root seasonality all show up the same way.

A definition you can test

Deeper Dive · not assessable

Draw the line before looking at the data:

  1. Choose the cycles of interest. Monthly data, annual period: the six harmonics Ω={π/6,π/3,π/2,2π/3,5π/6,π}\Omega=\{\pi/6,\pi/3,\pi/2,2\pi/3,5\pi/6,\pi\}.
  2. Partition (0,π](0,\pi] into MM equal-width bins B1,…,BMB_1,\ldots,B_M. A bin is seasonal if it contains a harmonic; call that set ℳ1\mathcal M_1, the rest ℳ0\mathcal M_0.
  3. Compare the tallest peaks. With bm(f)b_m(f) the highest value of the spectral density ff inside bin mm,

Δ(f)=maxm∈ℳ1bm(f)−maxm∈ℳ0bm(f)\Delta(f)=\max_{m\in\mathcal M_1}b_m(f)\;-\;\max_{m\in\mathcal M_0}b_m(f)

A series is seasonal, relative to the chosen cycles and partition, if and only if Δ>0\Delta>0.

In plain language: the chosen cycles are demonstrably larger than every other cycle. Relative, not absolute, so a narrow spike at π/6\pi/6 and a diffuse hump around π/3\pi/3 count alike, with no commitment to what generated the peak. Whatever lands in a seasonal bin counts, trading-day leakage included — the “no clean isn’t” made operational.

One line on notation. Δ\Delta has meant the difference operator since Module 1, Δyt=yt−yt−1\Delta y_t = y_t - y_{t-1}. This Δ\Delta is a different object — a difference of two peak heights — and its sample version Δ̂\hat\Delta is a test statistic. The tell: Δ̂\hat\Delta never acts on a yty_t.

The test: a null distribution in closed form

Deeper Dive · not assessable

Replace ff with the periodogram and take the same two maxima:

Δ̂=maxm∈ℳ1bm(IT)−maxm∈ℳ0bm(IT)\hat\Delta=\max_{m\in\mathcal M_1}b_m(I_T)-\max_{m\in\mathcal M_0}b_m(I_T)

Under white noise with innovation variance σ2\sigma^2, periodogram ordinates are approximately independent exponentials; the max of NN of them is Gumbel, and a difference of two Gumbels with a common scale is Logistic. With N1N_1 seasonal and N0N_0 non-seasonal ordinates,

Δ̂|H0⇒Logistic(σ2logN1N0,σ2)\hat\Delta\;\big|\;H_0\;\Rightarrow\;\text{Logistic}\!\left(\sigma^2\log\frac{N_1}{N_0},\;\sigma^2\right)

Closed form: no simulation, no bandwidth, no kernel. (The paper writes τ\tau; the course writes σ2\sigma^2.)

Prewhiten first with a BIC-chosen non-seasonal ARIMA, so ordinary autocorrelation cannot colour the non-seasonal maximum; standardize to unit variance, so σ2=1\sigma^2=1.

remotes::install_github("grycrnwll/freqseas")   # once
library(freqseas)
test_nsa <- seas_test(unratensa)                # defaults
summary(test_nsa)

Δ̂\hat\Delta on UNRATENSA122.2
5% critical value2.19
MM bins · whitener19 · d=1d=1, AR(2)
verdictreject: seasonal

Adjustment as the logical conjugate

Deeper Dive · not assessable

Most shops run a test from one tradition and a filter from another, so the filter removes an object the test never measured. The requirement here: the adjustment removes exactly the excess the test detects, so re-running the test on the output fails to reject by construction. Stochastic spectral imputation (SSI), in three moves:

  1. Setup. DFT the whitened series. Seasonal-bin ordinates are the targets; quiet ordinates below a chosen quantile of the non-seasonal floor are the donors.
  2. Impute. Reset each target’s magnitude to a randomly drawn donor’s level, keeping the phase — filled from the noise floor, not set to zero, so no spectral hole.
  3. Finalize. Enforce conjugate symmetry, invert the DFT, recolour through the whitener, restore the levels.
adj_ssi <- seas_adjust(unratensa, phase_rule = "zero",
                       target_set = "bins", seed = 6376)
ssi_nsa <- adjusted(adj_ssi)     # classic SSI
seas_test(ssi_nsa)               # the same test, on the output
Δ̂\hat\Delta after adjustment−2.72
pp0.877 · fail to reject
first 12 monthsleft unadjusted
One honest line. The package’s default branch is a different, experimental object: on a series the test classifies as a broad “band” it divides by an estimated gain and imputes nothing — and on UNRATENSA its own post-test rejects (p≈3×10−10p\approx 3\times10^{-10}). The “by construction” claim attaches to the classic call above.

SSI on UNRATENSA

Deeper Dive · not assessable Classic SSI on UNRATENSA, 1960–2019, replayed from precomputed freqseas draws. Press Adjust.

Epilogue: residual seasonality

Residual Seasonality in Core Consumer Price InflationFederal Reserve Board · 2014
"The Phrase of the Day is 'Residual Seasonality'"Wall Street Journal · 2015
U.S. says probe found problem in seasonal adjustment of GDP dataReuters · 2016
Residual Seasonality in GDP Growth Remains after Latest BEA ImprovementsCleveland Fed · 2019
Assessing residual seasonality in published outputsUK Office for National Statistics · 2025
Residual Seasonality in Some Components of PCE InflationCleveland Fed · 2026

Back to the series Module 4 handed us. Its ARMA(1,2) on the published UNRATE showed serial correlation at the seasonal lags — a verdict on that fit. The series it was fitted to is the output of an adjustment procedure, so the course’s name for that leftover is residual seasonality: the procedure’s own serial correlation, showing up in the data it published. The seasonal ARMA terms of ARIMA(1,1,2)(1,0,1)12(1,1,2)(1,0,1)_{12} are what absorbed it.

The sign says which way. ρ̂12=−0.145\hat\rho_{12}=-0.145 and its echoes (−0.125-0.125, −0.130-0.130) are negative. Under a non-seasonal ARMA that is consistent with the published series carrying too little variation at the seasonal frequencies — a seasonal deficit, the signature of an adjustment that removed slightly more than the seasonal component. A dip, not a peak.

Two honest limits. This is consistent with an adjustment-side explanation, not a claim about how the BLS runs its procedure. And the numbers do not establish why the dip is there.

Epilogue: before and after, on the published UNRATE

Same series, two fits. The left picture asked Module 4’s question; the right one answers it — seasonal band on, seasonal lags inside the band, Ljung-Box passing at both horizons. When someone asks what residual diagnostics are for, hand them this pair.

Deeper Dive · not assessable The frequency domain agrees (notes §5.9.6): on the published series all six seasonal bins sit below their neighbours, ratio 0.75 — on the raw series the ratio is 18.4 — and Δ̂=0.44\hat\Delta=0.44, p=0.23p=0.23, fails to reject, because the test is one-sided for excess.

The master equation, fully lit

yt=α+δt+∑j=1pϕjyt−j+∑l=1qθlϵt−l+ϵty_t=\alpha+\delta t+\sum_{j=1}^{p}\phi_j y_{t-j}+\sum_{l=1}^{q}\theta_l\epsilon_{t-l}+\epsilon_t

— and the seasonal block, written out, is the same structure at lag ss, multiplied in:

Φp(L)ΦP(Ls)(1−L)d(1−Ls)Dyt=Θq(L)ΘQ(Ls)ϵt\Phi_p(L)\,\Phi_P(L^s)\,(1-L)^d(1-L^s)^D\,y_t=\Theta_q(L)\,\Theta_Q(L^s)\,\epsilon_t

Knob Module State
α\alpha, δt\delta t — level and trend L1, L2 on
ϕj\phi_j, θl\theta_l — the base band L3, L4 on
(1−L)d(1-L)^d — differencing at the base frequency L2 on
ΦP(Ls)\Phi_P(L^s), ΘQ(Ls)\Theta_Q(L^s), (1−Ls)D(1-L^s)^D — the seasonal band L5 on
ϵt\epsilon_t L1 on

The fitting toolkit built since Module 1 is complete. From here on, every module is about what you do with a fitted model, or about lifting it to several variables, or about letting the variance have a master equation of its own.

Key takeaways

  1. Residuals should behave like innovations. ete_t versus ϵt\epsilon_t is the whole foundation of diagnostics. Ljung-Box is the omnibus check: Q(h)∼aχh−fitdf2Q(h)\stackrel{a}{\sim}\chi^2_{h-\text{fitdf}}, h=10h=10 and h=24h=24 on monthly data, fitdf set honestly, and a large pp-value is good news.
  2. Serial correlation is the model’s, not the data’s. The data has autocorrelation. A fitted model earns the verdict serial correlation. An adjustment procedure that leaves seasonal autocorrelation in a published series earns residual seasonality.
  3. Seasonality is a line you draw. Four traditions, none nesting the others; whatever lands inside your definition is seasonal by construction. The one safe non-example: monthly sampling alone makes nothing seasonal.
  4. Three paths, and when each applies. Deterministic → dummies (on UNRATENSA: fixed part absorbed, drifting part left). Stochastic → SARIMA (winner (1,1,2)(0,1,1)12(1,1,2)(0,1,1)_{12}, by diagnostics and interior coefficients, because IC cannot compare across differencing orders). “Gone, not modeled” → adjustment (one X-13 call reproduces the published series). And DD is a commitment the data push back on from both sides.
  5. The Module 6 handoff: ARIMA(1,1,2)(1,0,1)12(1,1,2)(1,0,1)_{12} on the published, adjusted UNRATE — diagnostic-clean at both horizons, its seasonal ARMA terms absorbing the residual seasonality the adjustment left behind.

Next time: forecasting

Module 5 hands Module 6 a diagnostic-clean univariate model — the ARIMA(1,1,2)(1,0,1)12(1,1,2)(1,0,1)_{12} on UNRATE that passed Ljung-Box at both horizons.

You have a model. You have checked it. Now the question is what you use it for.

  • Forecasting: the recursive computation of ŷt+h∣t\hat y_{t+h\mid t} — point, interval, density.
  • Forecast uncertainty: why the interval grows with the horizon, built from the σ2\sigma^2 we have tracked since Module 1.
  • Train/test splits for time series: random splits are wrong; rolling windows are right.
  • The workflow: split → fit on the training window → forecast → evaluate on the test window. Today was fit + check. Modules 6 and 7 add the rest.

A forecast is a conditional mean plus honest uncertainty; classical ARMA gives you both.

Interactive lab machinery

This uncounted support slide keeps both widget factories in the document. Use the RevealJS menu to return to a widget if a browser reload interrupts an interaction.