Estimating Unobserved Components (Potential GDP, NAIRU)
“The Kalman filter is the Swiss Army knife of state estimation: it handles missing data, real-time updating, and likelihood evaluation — all in a unified recursive algorithm.”
Cross-reference: Principles Ch. 6 (potential GDP and the output gap as unobserved); Appendix B (DSGE estimation via MLE with Kalman filter) [P:Ch.6, P:AppB]
20.1 The State-Space Form¶
Many macroeconomic concepts are not directly observed: potential output, the natural rate of unemployment (NAIRU), the natural rate of interest, and long-run inflation expectations are all inherently unobservable quantities inferred from observed data. The state-space model provides the unified statistical framework for this inference; the Kalman filter is the optimal algorithm for doing so recursively in real time.
Definition 20.1 (Linear Gaussian State-Space Model). The state-space model consists of two equations:
where:
is the observation vector (observed variables: GDP, unemployment, interest rates).
is the state vector (unobserved variables: potential output, NAIRU, natural rate).
is the observation matrix (links states to observables).
is the transition matrix (law of motion of the state).
is the measurement noise covariance.
is the state noise covariance (process noise).
and are constant terms (intercepts).
Assumption: and are mutually uncorrelated at all leads and lags; the initial state .
The state-space form is extremely general: every ARMA model, every DSGE model (after log-linearization), and every unobserved components (UC) model can be written in this form. Chapter 28 will show that the log-linearized DSGE model reduces to exactly this state-space form, with the Kalman filter providing the likelihood.
20.2 The Kalman Filter Algorithm¶
The Kalman filter solves the linear filtering problem: given observations , what is the best (minimum mean squared error) estimate of the unobserved state ?
Definition 20.2 (Filtered Estimate). The filtered mean and filtered covariance are the posterior mean and variance of the state conditional on all information up to time .
The predicted mean and predicted covariance are the prior to the -th observation.
Algorithm 20.1 (Kalman Filter).
Initialization: , .
For :
Prediction step (propagate the prior through the transition equation):
Prediction error and variance:
Kalman gain:
Update step (incorporate the new observation):
In APL, the prediction and update steps are single matrix expressions:
⍝ APL — Kalman filter: prediction and update steps
⎕IO←0 ⋄ ⎕ML←1
⍝ Prediction step: propagate state and covariance
⍝ a_pred = F × a + c
⍝ P_pred = F × P × F' + Q
predict ← {F Q c a P ← ⍵
a_new ← (F +.× a) + c
P_new ← (F +.× P +.× ⍉F) + Q
a_new P_new}
⍝ Update step: incorporate new observation
⍝ v = y - H×a - d (innovation)
⍝ Fvar = H × P × H' + R (innovation variance)
⍝ K = P × H' × Fvar^{-1} (Kalman gain)
⍝ a_upd = a + K×v
⍝ P_upd = (I - K×H) × P
update ← {H R d y a P ← ⍵
v ← y - (H +.× a) + d ⍝ innovation
Fvar ← (H +.× P +.× ⍉H) + R ⍝ innovation variance
K ← P +.× (⍉H) +.× ⌹Fvar ⍝ Kalman gain: P H' F^{-1}
n ← ≢a
I_n ← =⍨⍳n
a_upd ← a + K +.× v
P_upd ← (I_n - K +.× H) +.× P
a_upd P_upd v Fvar} ⍝ return updated state, cov, innovation, innov varThe full Kalman filter iterates these two steps over observations, storing the filtered estimates and the innovations .
20.3 Derivation of the Kalman Gain as MMSE Estimator¶
Theorem 20.1 (Optimality of the Kalman Gain). The Kalman gain minimizes the mean squared error of the updated state estimate among all linear functions of .
Proof. The general linear update: for some matrix . The update error:
Since :
The MSE matrix:
Taking the derivative with respect to and setting to zero:
Solving: , so .
The Joseph form for numerical stability: The update equation can become non-positive-definite due to numerical errors. The numerically stable Joseph form is:
This is algebraically equivalent but guaranteed to remain symmetric positive semi-definite.
20.4 The Kalman Smoother¶
The Kalman filter produces filtered estimates — based on information up to time . The Kalman smoother produces smoothed estimates — based on all observations. Smoothed estimates are generally more accurate and are required for computing the log-likelihood efficiently.
Algorithm 20.2 (Rauch–Tung–Striebel Smoother).
Run the Kalman filter forward to obtain for .
Then run backward for :
with terminal condition and from the filter.
Interpretation: The smoother revises the filtered estimate at each date using information from all subsequent observations. For the output gap, the smoother tells us what we would have estimated for 2006Q2 if we had known the entire 2007–2012 sequence of GDP data — a retrospective revision.
20.5 Maximum Likelihood Estimation via the Kalman Filter¶
The Kalman filter generates the prediction error decomposition of the log-likelihood — the key formula connecting the filter to parameter estimation.
Theorem 20.2 (Prediction Error Decomposition of Log-Likelihood). Under the Gaussian assumption, the log-likelihood of the state-space model parameters given observations is:
where and are the prediction errors and their covariances generated by the Kalman filter.
Proof sketch. By the chain rule of conditional probability:
Each conditional is Gaussian with mean (the predicted observation) and variance . Taking logs gives the stated formula.
Practical estimation: MLE maximizes over model parameters . The gradient can be computed analytically (or by automatic differentiation) and used with a quasi-Newton optimizer (BFGS, Chapter 24). The Kalman filter evaluates at each candidate in time — fast enough for models with state dimension .
20.6 Macroeconomic Applications¶
20.6.1 Estimating the Output Gap¶
The simplest unobserved components (UC) model for the output gap:
State vector: . Observation: .
The four unknowns are the signal-to-noise ratios and AR coefficients . MLE via the Kalman filter estimates these from GDP data.
20.6.2 The Laubach–Williams Natural Rate Model¶
Laubach and Williams (2003) estimate the natural rate of interest as an unobserved state in a system that links to the output gap and inflation:
The system is cast in state-space form with unobserved and estimated by MLE. The LW estimates show declining from approximately 3.5% in the early 1980s to near 0% by 2015 — consistent with the secular stagnation narrative of Principles Ch. 39 [P:Ch.39.4].
20.7 Worked Example: Output Gap via UC Model¶
Python¶
import numpy as np
from scipy.optimize import minimize
# Simulate U.S.-like GDP data for demonstration
np.random.seed(42)
T = 200
tau = np.zeros(T); g = np.zeros(T); c = np.zeros(T)
g[0] = 0.005 # quarterly growth rate
sig_tau, sig_g, sig_c = 0.002, 0.001, 0.010
phi1, phi2 = 1.5, -0.6
for t in range(1, T):
g[t] = g[t-1] + sig_g * np.random.randn()
tau[t] = tau[t-1] + g[t-1] + sig_tau * np.random.randn()
c[t] = (phi1*c[t-1] + phi2*(c[t-2] if t>1 else 0)
+ sig_c * np.random.randn())
y = tau + c + 0.003*np.random.randn(T) # observed GDP (with tiny meas. noise)
def kalman_filter(y, F, H, Q, R, a0, P0):
T, p = y.shape if y.ndim > 1 else (len(y), 1)
m = len(a0)
y = y.reshape(T, -1)
a = a0.copy(); P = P0.copy()
log_lik = 0.0
a_filter = np.zeros((T, m)); P_filter = np.zeros((T, m, m))
for t in range(T):
# Predict
a_pred = F @ a
P_pred = F @ P @ F.T + Q
# Innovation
v = y[t] - H @ a_pred
Fv = H @ P_pred @ H.T + R
# Log-likelihood contribution
sign, logdet = np.linalg.slogdet(Fv)
log_lik -= 0.5 * (logdet + v.T @ np.linalg.solve(Fv, v) + p*np.log(2*np.pi))
# Update
K = P_pred @ H.T @ np.linalg.inv(Fv)
a = a_pred + K @ v
P = (np.eye(m) - K @ H) @ P_pred
a_filter[t] = a; P_filter[t] = P
return log_lik, a_filter, P_filter
# Set up UC state-space model
# State: [tau, g, c, c_lag]'
m = 4
F = np.array([[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, phi1, phi2],
[0, 0, 1, 0]])
H = np.array([[1, 0, 1, 0]]) # y = tau + c
R = np.array([[1e-6]]) # nearly zero measurement error
def neg_log_lik(params):
sq, sg, sc = np.exp(params[:3]) # log-parameterize for positivity
Q = np.diag([sq**2, sg**2, sc**2, 0])
a0 = np.array([y[0], 0.005, 0, 0])
P0 = np.diag([1.0, 0.01, 0.1, 0.1])
ll, _, _ = kalman_filter(y, F, H, Q, R, a0, P0)
return -ll
# Estimate parameters
x0 = np.log([0.002, 0.001, 0.010])
res = minimize(neg_log_lik, x0, method='Nelder-Mead',
options={'xatol':1e-6, 'fatol':1e-6, 'maxiter':2000})
sig_hat = np.exp(res.x)
print(f"Estimated σ_τ={sig_hat[0]:.4f}, σ_g={sig_hat[1]:.4f}, σ_c={sig_hat[2]:.4f}")
print(f"True: σ_τ={sig_tau:.4f}, σ_g={sig_g:.4f}, σ_c={sig_c:.4f}")
# Extract filtered output gap
Q_hat = np.diag([sig_hat[0]**2, sig_hat[1]**2, sig_hat[2]**2, 0])
a0 = np.array([y[0], 0.005, 0, 0]); P0 = np.diag([1.0,0.01,0.1,0.1])
_, a_f, _ = kalman_filter(y, F, H, Q_hat, R, a0, P0)
output_gap = a_f[:,2] # cycle component
import matplotlib.pyplot as plt
fig,(ax1,ax2) = plt.subplots(2,1,figsize=(12,7),sharex=True)
ax1.plot(y,'b-',alpha=0.6,label='Observed GDP'); ax1.plot(a_f[:,0],'r-',lw=2,label='Filtered trend')
ax1.set_title('UC Model: Trend and Cycle Decomposition'); ax1.legend()
ax2.plot(output_gap,'g-',label='Output gap (filtered)'); ax2.plot(c,'k--',alpha=0.5,label='True cycle')
ax2.axhline(0,color='k',lw=0.5); ax2.set_title('Output Gap'); ax2.legend()
plt.tight_layout(); plt.show()Julia¶
using LinearAlgebra, Optim
function kalman_filter(y, F, H, Q, R, a0, P0)
T = length(y); m = length(a0); p = size(H, 1)
a = copy(a0); P = copy(P0); log_lik = 0.0
a_store = zeros(T, m)
for t in 1:T
a_pred = F * a; P_pred = F * P * F' + Q
v = [y[t]] - H * a_pred
Fv = H * P_pred * H' + R
log_lik -= 0.5*(log(det(Fv)) + dot(v, Fv\v) + p*log(2π))
K = P_pred * H' * inv(Fv)
a = a_pred + K * v; P = (I(m) - K*H) * P_pred
a_store[t,:] = a
end
log_lik, a_store
end
println("Kalman filter implemented. Use Optim.jl to maximize log-likelihood over parameters.")R¶
# R — Kalman filter via KFAS
library(KFAS)
# Simple local level model (trend extraction)
T <- 200; set.seed(42)
y <- cumsum(rnorm(T, 0.005, 0.002)) + rnorm(T, 0, 0.01) # trend + noise
model <- SSModel(y ~ SSMtrend(2, Q=list(matrix(NA),matrix(NA))),
H=matrix(NA))
fit <- fitSSM(model, inits=c(log(0.001), log(0.001), log(0.01)))
out <- KFS(fit$model)
plot(out$alphahat[,1], type='l', col='red', main='Filtered trend (KFAS)')
lines(y, col='blue', alpha=0.5)
legend('topleft', c('Observed','Filtered trend'), col=c('blue','red'), lty=1)20.8 Programming Exercises¶
Exercise 20.1 (APL — Full Kalman Filter Loop)¶
Implement the complete Kalman filter in APL as a dfn that takes (y F H Q R a0 P0) and returns the filtered states and log-likelihood. Use {predict_step ⍵} and {update_step ⍵} as helper dfns called within the main loop. The log-likelihood accumulation should use ln_det F_t + v'F^{-1}v at each step. Verify on simulated data from a known state-space model.
Exercise 20.2 (Python — Laubach–Williams Replication)¶
Implement a simplified Laubach–Williams (2003) model:
Cast in state-space form with state and observations . Estimate parameters by MLE via the Kalman filter. Plot the estimated natural rate path over 1960–2019 and compare to the LW published estimates.
Exercise 20.3 (Julia — Kalman Smoother)¶
Extend the Kalman filter implementation to include the RTS smoother (backward pass). Compare: (a) filtered output gap (using only information up to ); (b) smoothed output gap (using all information). Show that the smoothed estimates have lower uncertainty (smaller diagonal elements) than the filtered estimates.
Exercise 20.4 — DSGE Likelihood ()¶
The log-linearized NK model from Chapter 18 can be written in state-space form with state , observation (output gap and inflation observed with measurement error). (a) Write the , , , matrices explicitly in terms of the NK model parameters. (b) Simulate 100 periods of data from the model with . (c) Use the Kalman filter to compute for and plot the likelihood surface. Verify the MLE is near the true value.
20.9 Chapter Summary¶
Key results:
The state-space model has a measurement equation and transition equation ; virtually all dynamic models reduce to this form.
The Kalman filter is a two-step recursion: predict step (, ) and update step (, ).
The Kalman gain is the MMSE linear estimator (proved as Theorem 20.1).
The RTS smoother revises filtered estimates backward in time, producing more accurate estimates using all observations.
The prediction error log-likelihood enables MLE estimation of state-space model parameters via the Kalman filter.
In APL:
P_pred ← (F+.×P+.×⍉F)+QandK ← P+.×(⍉H)+.×⌹Fv— the prediction and gain steps as single expressions; the full filter as{update predict ⍵}⍣T⊢init_state.
Next: Chapter 21 — GMM and Maximum Likelihood