Type at least 2 letters. Enter opens the first result, Esc closes.

    Time Series and Forecasting · Lesson 7 · ARIMA and state-space models

    State-space models and the Kalman filter

    Advanced 110 min 5 interactive figures 4 videos Colab notebook

    Before this lesson: simple exponential smoothing (lesson 04), AR models and the backshift notation (lesson 05), forecast intervals and ARIMA(0,1,1) (lesson 06). From the probability-and-statistics track: the multivariate Gaussian and its conditional distribution, maximum likelihood, and Bayes' rule with a prior and a posterior. Covariance matrices are positive semidefinite (linear-algebra lesson 07). Time: about 110 minutes, plus the notebook.


    Why this matters #

    • One framework for many models. Local level, trend, seasonal, ARMA and regression models with changing coefficients are all state-space models. One algorithm filters, forecasts and computes the likelihood for all of them. statsmodels computes the exact likelihood of ARIMA and SARIMA models with a Kalman filter (its SARIMAX class is a state-space model).
    • Missing data and irregular times need no special code: the filter skips the update when there is no observation.
    • Tracking and navigation. The Kalman filter (Kalman, 1960) fuses GPS, inertial sensors and odometry in aircraft, phones and robots. It is the base of SLAM and of data assimilation in weather forecasting (ensemble Kalman filter, see the ml-for-science track).
    • Deep learning. The layers of S4 and Mamba are linear state-space recurrences (deep-learning lesson 16). They use the state equation of this lesson without noise and learn FF and HH by gradient descent.

    1. Predict first #

    2. The picture #

    A hidden state seen through noise #

    A state-space model has a state xtx_t that we do not observe and an observation yty_t that we do observe. The state carries everything about the past that matters for the future. The state moves by a linear rule FF plus noise; the observation is a linear function HH of the state plus noise:

    ⋯⟶xt−1→  F  xt→  F  xt+1⟶⋯↓H↓H↓Hyt−1ytyt+1\begin{array}{ccccccc} \cdots \longrightarrow & x_{t-1} & \xrightarrow{\;F\;} & x_t & \xrightarrow{\;F\;} & x_{t+1} & \longrightarrow \cdots \\ & \big\downarrow H & & \big\downarrow H & & \big\downarrow H & \\ & y_{t-1} & & y_t & & y_{t+1} & \end{array}

    The arrows are the only dependencies: xtx_t depends on the past only through xt−1x_{t-1} (a Markov chain), and yty_t depends only on xtx_t. This is the same graph as a hidden Markov model; here the state is a continuous vector and everything is linear and Gaussian.

    The example of the figure. A target moves along a line. Its state is xt=(position,velocity)x_t = (\text{position}, \text{velocity}). In each time step the position grows by the velocity, and a small random acceleration changes both. A sensor measures the position only, with noise of standard deviation 3. The velocity is never measured.

    Interactive figure: loading…

    Press Step several times. Each press does one half of the cycle. After predict, the orange point and bar show the one-step prediction and its ±2σ\pm 2\sigma range: the range is wider than before. After update, the aqua point moves toward the new blue observation and the range shrinks. The yellow line is the current step; the dashed blue line is the true position. Then choose missing at t = 40 to 60 and press Run: the band grows in the grey gap and collapses at t=61t = 61. Watch the lower chart: the velocity is never observed, but the filter estimates it. Use the Try buttons to give the filter a wrong QQ or RR and compare the RMSE readouts. View: whole series shows all 100 steps.

    The filter repeats two steps.

    1. Predict. Move the last estimate forward with the motion rule: new position = old position + velocity. The uncertainty grows, because the random acceleration adds the variance QQ.
    2. Update. A new observation yty_t arrives. The difference between it and the predicted position is the innovation νt\nu_t (the "surprise"). Move the estimate by a fraction of the innovation. The uncertainty shrinks, because we have one more measurement.

    The fraction is the Kalman gain KtK_t. It is not a tuning knob: the filter computes it from the two variances. With the true QQ and RR, the filtered position has an RMSE well below the RMSE of the raw observations: the filter averages the noise away and still follows the turns of the target.

    One update is Bayes' rule for two Gaussians #

    Look at one update in one dimension. Before yty_t arrives, the prediction says xt∼N(m,P)x_t \sim \mathcal N(m, P): this is the prior. The sensor says yt=xt+vty_t = x_t + v_t with vt∼N(0,R)v_t \sim \mathcal N(0, R): as a function of xtx_t, this is the likelihood. Bayes' rule multiplies the two. The product of two Gaussian densities in xx is again a Gaussian: the posterior.

    Interactive figure: loading…

    Move the three sliders. The orange curve is the prior (the prediction, mean m=10m = 10), the dashed blue curve is the likelihood of the observation yy, and the aqua curve is the posterior. Make RR small: the posterior jumps to yy and K→1K \to 1. Make RR large: the posterior stays at the prior and K→0K \to 0. Notice that the posterior is always narrower than both other curves.

    The posterior mean is a weighted average of the prediction and the observation. Each one gets a weight proportional to its precision (one over its variance). So the more precise source wins, and the combined estimate is more precise than either source.

    Quick check

    In the scalar filter, the observation noise variance RR grows to infinity while the prediction variance Pt∣t−1P_{t\mid t-1} stays finite. What does the update step do?

    Quick check

    The prediction is xt∼N(10,4)x_t \sim \mathcal N(10, 4). You observe yt=14y_t = 14 with noise variance R=4R = 4. What is the posterior?

    3. Definitions and main results #

    3.1 The linear Gaussian state-space model #

    Definition. A linear Gaussian state-space model for a series y1,…,yny_1, \dots, y_n (each yty_t a vector of size kk) has a hidden state xtx_t (a vector of size dd) and two equations:

    xt=Fxt−1+wt,wt∼N(0,Q),yt=Hxt+vt,vt∼N(0,R).\begin{aligned} x_t &= F x_{t-1} + w_t, & w_t &\sim \mathcal N(0, Q), \\ y_t &= H x_t + v_t, & v_t &\sim \mathcal N(0, R). \end{aligned}

    The first line is the state equation (or transition equation); the second is the observation equation (or measurement equation).

    • FF (d×dd \times d) is the transition matrix; HH (k×dk \times d) is the observation matrix.
    • QQ is the process noise (state noise) covariance; RR is the observation noise covariance. Both are positive semidefinite.
    • Assumptions: the noises w1,w2,…w_1, w_2, \dots and v1,v2,…v_1, v_2, \dots are independent of each other, independent over time, and independent of the initial state x0∼N(x^0∣0,P0∣0)x_0 \sim \mathcal N(\hat x_{0\mid 0}, P_{0\mid 0}).

    FF, HH, QQ and RR may depend on tt (then we write FtF_t, and so on); every result below still holds. A known input utu_t can be added, xt=Fxt−1+But+wtx_t = F x_{t-1} + B u_t + w_t; this only shifts the predicted mean.

    Examples. Each model below is one choice of the four matrices.

    Model State xtx_t FF HH QQ RR
    local level (random walk plus noise) level ℓt\ell_t 11 11 ση2\sigma_\eta^2 σε2\sigma_\varepsilon^2
    local linear trend (ℓt,bt)(\ell_t, b_t) level and slope (1101)\begin{pmatrix}1&1\\0&1\end{pmatrix} (10)\begin{pmatrix}1&0\end{pmatrix} diag⁡(σξ2,σζ2)\operatorname{diag}(\sigma_\xi^2, \sigma_\zeta^2) σε2\sigma_\varepsilon^2
    constant velocity (tracking) (position, velocity) (1101)\begin{pmatrix}1&1\\0&1\end{pmatrix} (10)\begin{pmatrix}1&0\end{pmatrix} q(1/41/21/21)q\begin{pmatrix}1/4&1/2\\1/2&1\end{pmatrix} σv2\sigma_v^2
    seasonal, period m=4m = 4 (st,st−1,st−2)(s_t, s_{t-1}, s_{t-2}) (−1−1−1100010)\begin{pmatrix}-1&-1&-1\\1&0&0\\0&1&0\end{pmatrix} (100)\begin{pmatrix}1&0&0\end{pmatrix} diag⁡(σω2,0,0)\operatorname{diag}(\sigma_\omega^2, 0, 0) σε2\sigma_\varepsilon^2
    AR(2), companion form (yt,yt−1)(y_t, y_{t-1}) (ϕ1ϕ210)\begin{pmatrix}\phi_1&\phi_2\\1&0\end{pmatrix} (10)\begin{pmatrix}1&0\end{pmatrix} diag⁡(σ2,0)\operatorname{diag}(\sigma^2, 0) 00

    How to read the rows:

    • Local level. ℓt=ℓt−1+ηt\ell_t = \ell_{t-1} + \eta_t is a random walk; yt=ℓt+εty_t = \ell_t + \varepsilon_t adds measurement noise. This is the model behind SES (section 3.4).
    • Local linear trend. ℓt=ℓt−1+bt−1+ξt\ell_t = \ell_{t-1} + b_{t-1} + \xi_t and bt=bt−1+ζtb_t = b_{t-1} + \zeta_t: the slope also moves slowly. This is the stochastic version of Holt's linear method (lesson 04).
    • Constant velocity. The same FF. The random acceleration at∼N(0,q)a_t \sim \mathcal N(0, q) adds at/2a_t/2 to the position and ata_t to the velocity, so wt=at (1/2,1)⊤w_t = a_t\,(1/2, 1)^\top and Q=q (1/2,1)⊤(1/2,1)Q = q\,(1/2, 1)^\top(1/2, 1).
    • Seasonal. The first row says st=−(st−1+st−2+st−3)+ωts_t = -(s_{t-1} + s_{t-2} + s_{t-3}) + \omega_t: four consecutive seasonal values sum to zero, up to noise. To get "level + season", stack the level and the seasonal state into one vector, put FF in block-diagonal form and use H=(1,1,0,0)H = (1, 1, 0, 0).
    • AR(2). The first row of Fxt−1F x_{t-1} is ϕ1yt−1+ϕ2yt−2\phi_1 y_{t-1} + \phi_2 y_{t-2}; the second row copies yt−1y_{t-1} down. The eigenvalues of FF are the inverses of the roots of ϕ(z)\phi(z) (lesson 05), so the model is stationary exactly when they lie inside the unit circle. Every ARMA(p,qp, q) model has such a form, with a state of size max⁡(p,q+1)\max(p, q + 1) (Durbin and Koopman, ch. 3).
    Interactive figure: loading…

    Switch between the models. Each model is only a different set of matrices FF, HH, QQ, RR, and the same Kalman filter works for all of them. The chart shows one simulated sample: the blue observations, the aqua hidden level, and, for the seasonal model, the violet hidden seasonal part. Press New sample to see another path from the same model.

    3.2 The scalar update, step by step #

    Start with the local level model. All quantities are numbers. We write x^t∣s\hat x_{t\mid s} for the mean of xtx_t given y1,…,ysy_1, \dots, y_s and Pt∣sP_{t\mid s} for its variance.

    Predict. Suppose xt−1∣y1:t−1∼N(x^t−1∣t−1,Pt−1∣t−1)x_{t-1} \mid y_{1:t-1} \sim \mathcal N(\hat x_{t-1\mid t-1}, P_{t-1\mid t-1}). Then xt=xt−1+wtx_t = x_{t-1} + w_t with independent wt∼N(0,Q)w_t \sim \mathcal N(0, Q), so

    x^t∣t−1=x^t−1∣t−1,Pt∣t−1=Pt−1∣t−1+Q.\hat x_{t\mid t-1} = \hat x_{t-1\mid t-1}, \qquad P_{t\mid t-1} = P_{t-1\mid t-1} + Q .

    Update. Write m=x^t∣t−1m = \hat x_{t\mid t-1} and P=Pt∣t−1P = P_{t\mid t-1}. Then:

    1. Prior times likelihood, as a function of xx: p(x∣y1:t)∝exp⁡ ⁣(−(x−m)22P)exp⁡ ⁣(−(yt−x)22R)p(x \mid y_{1:t}) \propto \exp\!\big(-\tfrac{(x - m)^2}{2P}\big)\exp\!\big(-\tfrac{(y_t - x)^2}{2R}\big).
    2. The exponent is −12[(x−m)2P+(x−yt)2R]-\tfrac12\big[\tfrac{(x - m)^2}{P} + \tfrac{(x - y_t)^2}{R}\big]. Expand and keep the terms with xx: −12[x2(1P+1R)−2x(mP+ytR)]+const-\tfrac12\big[x^2\big(\tfrac1P + \tfrac1R\big) - 2x\big(\tfrac mP + \tfrac{y_t}R\big)\big] + \text{const}.
    3. A Gaussian N(μ,V)\mathcal N(\mu, V) has exponent −12[x2V−2xμV]+const-\tfrac12\big[\tfrac{x^2}{V} - \tfrac{2x\mu}{V}\big] + \text{const}. Compare the coefficients (this is "completing the square"):
    1V=1P+1R,μV=mP+ytR.\frac1V = \frac1P + \frac1R, \qquad \frac{\mu}{V} = \frac mP + \frac{y_t}R .
    1. So the posterior variance is V=PRP+RV = \dfrac{PR}{P + R} and the posterior mean is μ=V(mP+ytR)=Rm+PytP+R\mu = V\big(\tfrac mP + \tfrac{y_t}R\big) = \dfrac{Rm + Py_t}{P + R}.
    2. Write K=PP+RK = \dfrac{P}{P + R}. Then RP+R=1−K\dfrac{R}{P + R} = 1 - K, and step 4 becomes
    x^t∣t=(1−K) m+K yt=m+K (yt−m),Pt∣t=(1−K) P.\begin{aligned}\hat x_{t\mid t} &= (1 - K)\,m + K\,y_t = m + K\,(y_t - m), \\ P_{t\mid t} &= (1 - K)\,P .\end{aligned}

    The first form says: a weighted average of prediction and observation. The second form says: the prediction plus the gain times the innovation νt=yt−m\nu_t = y_t - m. Step 3 says that precisions add: 1/Pt∣t=1/Pt∣t−1+1/R1/P_{t\mid t} = 1/P_{t\mid t-1} + 1/R. So Pt∣tP_{t\mid t} is smaller than both Pt∣t−1P_{t\mid t-1} and RR.

    3.3 The Kalman filter #

    For the vector model the same two steps hold with matrices.

    Which covariance is which. Pt∣t−1P_{t\mid t-1} is the uncertainty of the state before we see yty_t (the prediction). Pt∣tP_{t\mid t} is the uncertainty after we see it (the filtered estimate). StS_t is the uncertainty of the observation yty_t before we see it: state uncertainty mapped by HH, plus sensor noise. With d=k=1d = k = 1 and F=H=1F = H = 1 the box is exactly section 3.2.

    Theorem (the Kalman filter is exact). Under the assumptions of section 3.1, xt∣y1:t−1∼N(x^t∣t−1,Pt∣t−1)x_t \mid y_{1:t-1} \sim \mathcal N(\hat x_{t\mid t-1}, P_{t\mid t-1}) and xt∣y1:t∼N(x^t∣t,Pt∣t)x_t \mid y_{1:t} \sim \mathcal N(\hat x_{t\mid t}, P_{t\mid t}) for every tt, and yt∣y1:t−1∼N(Hx^t∣t−1,St)y_t \mid y_{1:t-1} \sim \mathcal N(H\hat x_{t\mid t-1}, S_t).

    Proof by Gaussian conditioning

    We use one fact about jointly Gaussian vectors (a,b)(a, b): the conditional distribution of aa given bb is Gaussian with mean μa+ΣabΣbb−1(b−μb)\mu_a + \Sigma_{ab}\Sigma_{bb}^{-1}(b - \mu_b) and covariance Σaa−ΣabΣbb−1Σba\Sigma_{aa} - \Sigma_{ab}\Sigma_{bb}^{-1}\Sigma_{ba} (the multivariate Gaussian lesson of the probability-and-statistics track). The proof is by induction over tt.

    1. Start. x0∼N(x^0∣0,P0∣0)x_0 \sim \mathcal N(\hat x_{0\mid 0}, P_{0\mid 0}) by assumption.
    2. Predict. Assume xt−1∣y1:t−1∼N(x^t−1∣t−1,Pt−1∣t−1)x_{t-1} \mid y_{1:t-1} \sim \mathcal N(\hat x_{t-1\mid t-1}, P_{t-1\mid t-1}). The noise wtw_t is independent of xt−1x_{t-1} and of y1:t−1y_{1:t-1}. A linear map of a Gaussian plus an independent Gaussian is Gaussian, with mean Fx^t−1∣t−1F\hat x_{t-1\mid t-1} and covariance FPt−1∣t−1F⊤+QFP_{t-1\mid t-1}F^\top + Q.
    3. Joint of state and observation. Given y1:t−1y_{1:t-1}, the pair (xt,yt)(x_t, y_t) with yt=Hxt+vty_t = Hx_t + v_t is jointly Gaussian (vtv_t is independent of everything else), with
    μ=(x^t∣t−1Hx^t∣t−1),Σ=(Pt∣t−1Pt∣t−1H⊤HPt∣t−1HPt∣t−1H⊤+R).\mu = \begin{pmatrix}\hat x_{t\mid t-1}\\ H\hat x_{t\mid t-1}\end{pmatrix}, \qquad \Sigma = \begin{pmatrix} P_{t\mid t-1} & P_{t\mid t-1}H^\top \\ HP_{t\mid t-1} & HP_{t\mid t-1}H^\top + R\end{pmatrix}.

    The lower-right block is StS_t. This already proves the claim about yt∣y1:t−1y_t \mid y_{1:t-1}. 4. Condition on yty_t. With a=xta = x_t and b=ytb = y_t, the fact above gives the mean x^t∣t−1+Pt∣t−1H⊤St−1(yt−Hx^t∣t−1)=x^t∣t−1+Ktνt\hat x_{t\mid t-1} + P_{t\mid t-1}H^\top S_t^{-1}(y_t - H\hat x_{t\mid t-1}) = \hat x_{t\mid t-1} + K_t\nu_t and the covariance Pt∣t−1−Pt∣t−1H⊤St−1HPt∣t−1=(I−KtH)Pt∣t−1P_{t\mid t-1} - P_{t\mid t-1}H^\top S_t^{-1}HP_{t\mid t-1} = (I - K_tH)P_{t\mid t-1}. This is the update step, and the induction continues. ■\blacksquare

    Two remarks. First, the covariances Pt∣t−1P_{t\mid t-1}, Pt∣tP_{t\mid t}, StS_t and the gain KtK_t do not depend on the data yty_t: they can be computed before the data arrive. Second, without the Gaussian assumption (noises only uncorrelated, with the same means and covariances), the same recursion still gives the best linear estimate of xtx_t in mean squared error.

    Why the velocity changes when only the position is observed. In the constant-velocity model, H=(1,0)H = (1, 0), so Kt=Pt∣t−1H⊤/StK_t = P_{t\mid t-1}H^\top/S_t is the first column of Pt∣t−1P_{t\mid t-1} divided by StS_t. The second entry of that column is the covariance of position and velocity. The predict step makes it nonzero (a fast target moves far), so a surprise in the position also corrects the velocity. The worked example in section 4 shows this with numbers.

    Numerical form and cost

    The form Pt∣t=(I−KtH)Pt∣t−1P_{t\mid t} = (I - K_tH)P_{t\mid t-1} can lose symmetry and positive semidefiniteness in floating point. The Joseph form Pt∣t=(I−KtH)Pt∣t−1(I−KtH)⊤+KtRKt⊤P_{t\mid t} = (I - K_tH)P_{t\mid t-1}(I - K_tH)^\top + K_tRK_t^\top is a sum of two positive semidefinite matrices, so it stays positive semidefinite, and it is correct for any gain KtK_t, not only the optimal one. Square-root filters propagate a Cholesky factor of PP instead (linear-algebra lesson 07). Solve with StS_t (Cholesky) instead of forming St−1S_t^{-1}. One step costs O(d3+k3)O(d^3 + k^3) operations; this is why a state of size 10810^8 (a weather model) needs the ensemble methods of the ml-for-science track.

    3.4 Steady state: the local level filter is exponential smoothing #

    For the local level model the covariance recursion does not involve the data. Combine predict and update into one map for Pt=Pt∣t−1P_t = P_{t\mid t-1}:

    Pt+1=g(Pt),g(P)=PRP+R+Q.P_{t+1} = g(P_t), \qquad g(P) = \frac{PR}{P + R} + Q .

    Theorem (steady-state gain). Let Q>0Q > 0, R>0R > 0 and q=Q/Rq = Q/R (the signal-to-noise ratio). For every start P0∣0≥0P_{0\mid 0} \ge 0, Pt∣t−1P_{t\mid t-1} converges to P=12(Q+Q2+4QR)P = \tfrac12\big(Q + \sqrt{Q^2 + 4QR}\big), and the gain converges to

    K∞=PP+R=q2+4q−q2.K_\infty = \frac{P}{P + R} = \frac{\sqrt{q^2 + 4q} - q}{2}.

    Proof.

    1. Fixed point. P=g(P)P = g(P) means P(P+R)=PR+Q(P+R)P(P + R) = PR + Q(P + R), that is P2−QP−QR=0P^2 - QP - QR = 0. The only positive root is P=12(Q+Q2+4QR)P = \tfrac12\big(Q + \sqrt{Q^2 + 4QR}\big).
    2. Gain. Divide by RR: p=P/Rp = P/R solves p2=qp+qp^2 = qp + q, so p=12(q+q2+4q)p = \tfrac12\big(q + \sqrt{q^2 + 4q}\big). The claim K∞=p/(p+1)=p−qK_\infty = p/(p + 1) = p - q is equivalent to p=(p−q)(p+1)=p2+p−qp−qp = (p - q)(p + 1) = p^2 + p - qp - q, which is p2=qp+qp^2 = qp + q. So K∞=p−q=12(q2+4q−q)K_\infty = p - q = \tfrac12\big(\sqrt{q^2 + 4q} - q\big).
    3. Convergence. P1=P0∣0+Q≥QP_1 = P_{0\mid 0} + Q \ge Q and g(P)≥Qg(P) \ge Q for every P≥0P \ge 0, so every Pt≥QP_t \ge Q, and the fixed point is ≥Q\ge Q too. The derivative is g′(P)=R2/(P+R)2g'(P) = R^2/(P + R)^2, so on [Q,∞)[Q, \infty) it is at most R2/(Q+R)2=1/(1+q)2<1R^2/(Q + R)^2 = 1/(1 + q)^2 < 1. By the mean value theorem,
    ∣Pt+1−P∣=∣g(Pt)−g(P)∣≤(1+q)−2 ∣Pt−P∣,|P_{t+1} - P| = |g(P_t) - g(P)| \le (1 + q)^{-2}\,|P_t - P| ,

    so the error shrinks geometrically. ■\blacksquare

    The link to SES. With a constant gain KK, the update is

    x^t∣t=x^t−1∣t−1+K(yt−x^t−1∣t−1)=Kyt+(1−K) x^t−1∣t−1.\hat x_{t\mid t} = \hat x_{t-1\mid t-1} + K(y_t - \hat x_{t-1\mid t-1}) = K y_t + (1 - K)\,\hat x_{t-1\mid t-1} .

    This is the SES recursion ℓt=αyt+(1−α)ℓt−1\ell_t = \alpha y_t + (1 - \alpha)\ell_{t-1} of lesson 04 with α=K∞\alpha = K_\infty. The forecasts agree too: both are flat, y^t+h∣t=x^t∣t\hat y_{t+h\mid t} = \hat x_{t\mid t}. So SES is the steady-state Kalman filter of the local level model, and α\alpha is decided by the signal-to-noise ratio qq:

    q=Q/Rq = Q/R 0.01 0.1 0.5 1 10
    K∞=αK_\infty = \alpha 0.095 0.270 0.500 0.618 0.916

    Small qq (the level moves little, the noise is large) gives a small α\alpha: average over a long past. Large qq gives α\alpha near 1: trust the last observation. As q→0q \to 0, K∞≈qK_\infty \approx \sqrt q; as q→∞q \to \infty, K∞→1K_\infty \to 1.

    Interactive figure: loading…

    Move qq and the start variance P0P_0. The upper chart shows the gain KtK_t (aqua): it starts near 1 when P0P_0 is large and settles at K∞K_\infty (dashed line) after a few steps. The lower chart runs the Kalman filter (aqua) and SES with α=K∞\alpha = K_\infty (orange, dashed) on the same blue data, both started at 0. Notice that the two lines differ only at the start; the readout shows the gap going to 0. With small qq the start-up takes longer.

    Quick check

    In a local level model the state noise and the observation noise have the same variance, Q=RQ = R. Which SES parameter α\alpha gives the steady-state Kalman filter?

    SES intervals and ARIMA(0,1,1)

    In steady state the innovations νt\nu_t have constant variance σ2=S=P+R\sigma^2 = S = P + R and are white noise. From yt=x^t−1∣t−1+νty_t = \hat x_{t-1\mid t-1} + \nu_t and x^t∣t=x^t−1∣t−1+ανt\hat x_{t\mid t} = \hat x_{t-1\mid t-1} + \alpha\nu_t we get yt−yt−1=νt−(1−α)νt−1y_t - y_{t-1} = \nu_t - (1 - \alpha)\nu_{t-1}: an ARIMA(0,1,1) with θ=α−1\theta = \alpha - 1, as in lesson 06. The hh-step forecast variance of the state-space model is Pt∣t+hQ+RP_{t\mid t} + hQ + R. It equals the ARIMA formula σ2(1+(h−1)α2)\sigma^2\big(1 + (h - 1)\alpha^2\big): both are σ2\sigma^2 for h=1h = 1, and each extra step adds QQ in one form and σ2α2=P2/(P+R)\sigma^2\alpha^2 = P^2/(P + R) in the other. These are equal, because the fixed-point equation says Q=P−PR/(P+R)=P2/(P+R)Q = P - PR/(P + R) = P^2/(P + R).

    3.5 Forecasting and missing data #

    Forecasting. To forecast from origin tt, run the predict step hh times without updates:

    x^t+h∣t=F x^t+h−1∣t,Pt+h∣t=FPt+h−1∣tF⊤+Q,\begin{aligned} \hat x_{t+h\mid t} &= F\,\hat x_{t+h-1\mid t}, \\ P_{t+h\mid t} &= F P_{t+h-1\mid t} F^\top + Q, \end{aligned}

    so x^t+h∣t=Fhx^t∣t\hat x_{t+h\mid t} = F^h\hat x_{t\mid t}. The forecast of the observation is y^t+h∣t=Hx^t+h∣t\hat y_{t+h\mid t} = H\hat x_{t+h\mid t} with covariance HPt+h∣tH⊤+RHP_{t+h\mid t}H^\top + R. Under the model, a 95 % prediction interval for a scalar yt+hy_{t+h} is y^t+h∣t±1.96HPt+h∣tH⊤+R\hat y_{t+h\mid t} \pm 1.96\sqrt{HP_{t+h\mid t}H^\top + R}. For the local level model, Pt+h∣t=Pt∣t+hQP_{t+h\mid t} = P_{t\mid t} + hQ: the variance grows linearly in hh, and the fan widens like h\sqrt h. These intervals assume that F,H,Q,RF, H, Q, R are known; using estimated values makes them a little too narrow (lesson 10 checks the coverage of intervals).

    Missing data. If yty_t is missing, skip the update: x^t∣t=x^t∣t−1\hat x_{t\mid t} = \hat x_{t\mid t-1} and Pt∣t=Pt∣t−1P_{t\mid t} = P_{t\mid t-1}. This is exact, because "no observation" means "condition on nothing new". For the local level model the variance grows by QQ in each missing step. At the first observation after the gap, Pt∣t−1P_{t\mid t-1} is large, so the gain is near 1 and the estimate jumps to the data. The likelihood (next section) simply has no term for a missing time. If only some entries of a vector yty_t are missing, keep the rows of HH and RR that belong to the observed entries.

    Irregular time steps. If the time between observations is Δt\Delta_t, use FF and QQ for that step. For the local level model, a random walk observed after Δt\Delta_t units has Qt=Δt ση2Q_t = \Delta_t\,\sigma_\eta^2.

    Quick check

    A local level model has Q=2Q = 2 and R=10R = 10. The observations at times 31 to 35 are missing, and P30∣30=5P_{30\mid 30} = 5. What is P35∣35P_{35\mid 35}?

    3.6 The likelihood: the prediction-error decomposition #

    To estimate unknown parameters θ\theta (for example QQ and RR, or the ϕi\phi_i of an AR model), we need the likelihood p(y1,…,yn∣θ)p(y_1, \dots, y_n \mid \theta). The observations are dependent, so it is not a product of marginal densities. The chain rule of probability gives

    p(y1,…,yn)=∏t=1np(yt∣y1:t−1),p(y_1, \dots, y_n) = \prod_{t=1}^{n} p(y_t \mid y_{1:t-1}),

    and the theorem of section 3.3 says yt∣y1:t−1∼N(Hx^t∣t−1,St)y_t \mid y_{1:t-1} \sim \mathcal N(H\hat x_{t\mid t-1}, S_t). So

    log⁡L(θ)=−12∑t=1n(log⁡det⁡(2πSt)+νt⊤St−1νt).\log L(\theta) = -\frac12\sum_{t=1}^{n}\Big(\log\det(2\pi S_t) + \nu_t^\top S_t^{-1}\nu_t\Big).

    This is the prediction-error decomposition: the filter turns dependent observations into innovations νt\nu_t that are independent with known covariances StS_t. One pass of the filter gives the exact Gaussian log-likelihood in O(n)O(n) steps. Maximise it with a numerical optimiser; write variances as Q=eθ1Q = e^{\theta_1}, R=eθ2R = e^{\theta_2} so that they stay positive.

    Checking the model. If the model is correct, the standardised innovations νt/St\nu_t/\sqrt{S_t} are independent N(0,1)\mathcal N(0, 1). Check them like residuals: plot them, look at their ACF and run the Ljung–Box test (lesson 02).

    The Nile example (Durbin and Koopman, ch. 2). The annual flow of the Nile at Aswan, 1871–1970, in 108 m310^8\,\text{m}^3, fitted with a local level model. Maximising the likelihood gives R^=15 099\hat R = 15\,099 and Q^=1 469\hat Q = 1\,469, so q^=0.097\hat q = 0.097 and K∞=0.267K_\infty = 0.267: the level moves slowly compared with the year-to-year noise. The maximum log-likelihood is −632.55-632.55 (with the diffuse start of section 3.7).

    Interactive figure: loading…

    Move qq, then press Go to the maximum. For each qq, the figure sets RR to its best value R^(q)\hat R(q) and shows the aqua level with its ±2σ\pm 2\sigma band over the blue data, and a 10-year orange forecast fan after the yellow origin (1970). The lower chart is the log-likelihood as a function of qq; its maximum is at q≈0.097q \approx 0.097. Notice the drop of the level around 1899 (the first Aswan dam was built from 1898 to 1902). Switch to smoother: the band becomes narrower. Data: Durbin and Koopman (2012), via statsmodels.

    3.7 Initialisation #

    The filter needs x^0∣0\hat x_{0\mid 0} and P0∣0P_{0\mid 0}. Often we know nothing about the start. A diffuse prior uses a very large P0∣0P_{0\mid 0}. For the local level model, P1∣0P_{1\mid 0} is then huge, K1≈1K_1 \approx 1, and so x^1∣1≈y1\hat x_{1\mid 1} \approx y_1 and P1∣1≈RP_{1\mid 1} \approx R: the first estimate is the first observation, with the uncertainty of one observation. The first innovation has an almost infinite variance and carries no information, so the diffuse log-likelihood leaves out the first term.

    In practice: (1) the exact diffuse start for the local level model is x^1∣1=y1\hat x_{1\mid 1} = y_1, P1∣1=RP_{1\mid 1} = R, and the likelihood starts at t=2t = 2 (this lesson and Durbin and Koopman use it); or (2) set P0∣0P_{0\mid 0} to a very large number, for example 10710^7 for the Nile data, and leave out the first likelihood term (an approximate diffuse start). Different starts change the estimates a little. For the Nile data, statsmodels 0.15 UnobservedComponents gives R^=15 078\hat R = 15\,078, Q^=1 479\hat Q = 1\,479. The first filtered values have wide bands for the same reason: early on, there is little data. For a stationary state (such as an AR part) use instead its stationary distribution as the start.

    3.8 Smoothing: using the whole series #

    The filter estimates xtx_t from y1,…,yty_1, \dots, y_t. After the whole series is observed, we can estimate each xtx_t from all of y1,…,yny_1, \dots, y_n: this is smoothing. The Rauch–Tung–Striebel (RTS) smoother runs backward after the filter, for t=n−1,…,1t = n - 1, \dots, 1:

    Gt=Pt∣tF⊤Pt+1∣t−1,x^t∣n=x^t∣t+Gt(x^t+1∣n−x^t+1∣t),Pt∣n=Pt∣t+Gt(Pt+1∣n−Pt+1∣t)Gt⊤.\begin{aligned} G_t &= P_{t\mid t}F^\top P_{t+1\mid t}^{-1}, \\ \hat x_{t\mid n} &= \hat x_{t\mid t} + G_t\big(\hat x_{t+1\mid n} - \hat x_{t+1\mid t}\big), \\ P_{t\mid n} &= P_{t\mid t} + G_t\big(P_{t+1\mid n} - P_{t+1\mid t}\big)G_t^\top . \end{aligned}

    It starts at t=nt = n, where smoother and filter agree. In words: the smoother corrects the filtered estimate by what the future revealed about the prediction error at t+1t + 1.

    Smoothed variances are smaller. Pt∣n⪯Pt∣tP_{t\mid n} \preceq P_{t\mid t} (in the order of positive semidefinite matrices): conditioning a Gaussian on more data never increases its covariance. On the Nile data, the smoothed variance in the middle of the series is about 2 330, against 4 030 for the filter.

    Filter or smoother? The smoother uses future data. Use it to describe the past: decomposition into trend and season, filling gaps, finding the date of a level shift, or anomaly analysis (lesson 11). Never use it to make or evaluate forecasts: in a backtest (lesson 03), a smoothed state at the forecast origin has seen the test period. That is leakage.

    Quick check

    You backtest a local level model with a rolling origin. At each origin you need the state estimate to start the forecast. Which estimate do you use?

    Beyond linear and Gaussian
    • Extended Kalman filter (EKF). For xt=f(xt−1)+wtx_t = f(x_{t-1}) + w_t, yt=h(xt)+vty_t = h(x_t) + v_t, linearise ff and hh around the current estimate (Jacobians) and run the Kalman steps. Works when the nonlinearity is mild over the size of the uncertainty.
    • Unscented Kalman filter (UKF). Push 2d+12d + 1 chosen "sigma points" through ff and hh and match the mean and covariance. No Jacobians; often more accurate than the EKF.
    • Particle filter. Represent the distribution by many weighted samples; predict by simulating the model, update by weighting with the likelihood, and resample. Works for any model, but needs many particles when the state has many dimensions.
    • Ensemble Kalman filter and 4D-Var. For states with millions of entries (weather), estimate PP from an ensemble of model runs, or solve a variational problem over a time window. See the ml-for-science track.
    • Deep state-space layers. S4 and Mamba use xt=Aˉxt−1+Bˉutx_t = \bar Ax_{t-1} + \bar Bu_t, yt=Cxt+Duty_t = Cx_t + Du_t (deep-learning lesson 16): Aˉ\bar A is our FF, CC is our HH, utu_t is a known input. There is no noise and no update step; the matrices are learned by gradient descent.

    Answers to section 1. (1) About 2.1 m. Precisions add: 1/9+1/9=2/91/9 + 1/9 = 2/9, so the variance is 4.54.5 and the standard deviation is 4.5≈2.12\sqrt{4.5} \approx 2.12 m, which is 3/23/\sqrt 2. (2) During the gap only the predict step runs, so the variance grows by QQ (local level) or faster (models with a velocity) at every step. At the first observation after the gap the gain is large, so the estimate jumps toward the data and the variance drops at once. (3) Yes: the local level model (random walk plus noise). SES with α=K∞\alpha = K_\infty is its steady-state Kalman filter, and α\alpha is decided by the signal-to-noise ratio q=Q/Rq = Q/R.

    4. Worked example #

    (a) Three steps of the local level filter. Q=1Q = 1, R=2R = 2, start x^0∣0=10\hat x_{0\mid 0} = 10, P0∣0=3P_{0\mid 0} = 3, observations y1=13y_1 = 13, y2=11y_2 = 11, y3=14y_3 = 14.

    Step t=1t = 1.

    1. Predict: x^1∣0=10\hat x_{1\mid 0} = 10, P1∣0=3+1=4P_{1\mid 0} = 3 + 1 = 4.
    2. Innovation: ν1=13−10=3\nu_1 = 13 - 10 = 3, with variance S1=4+2=6S_1 = 4 + 2 = 6.
    3. Gain: K1=4/6=0.667K_1 = 4/6 = 0.667.
    4. Update: x^1∣1=10+0.667⋅3=12\hat x_{1\mid 1} = 10 + 0.667 \cdot 3 = 12, P1∣1=(1−0.667)⋅4=1.333P_{1\mid 1} = (1 - 0.667) \cdot 4 = 1.333.

    Step t=2t = 2.

    1. Predict: x^2∣1=12\hat x_{2\mid 1} = 12, P2∣1=1.333+1=2.333P_{2\mid 1} = 1.333 + 1 = 2.333.
    2. Innovation: ν2=11−12=−1\nu_2 = 11 - 12 = -1, S2=2.333+2=4.333S_2 = 2.333 + 2 = 4.333.
    3. Gain: K2=2.333/4.333=0.538K_2 = 2.333/4.333 = 0.538.
    4. Update: x^2∣2=12−0.538=11.462\hat x_{2\mid 2} = 12 - 0.538 = 11.462, P2∣2=0.462⋅2.333=1.077P_{2\mid 2} = 0.462 \cdot 2.333 = 1.077.

    Step t=3t = 3.

    1. Predict: x^3∣2=11.462\hat x_{3\mid 2} = 11.462, P3∣2=2.077P_{3\mid 2} = 2.077.
    2. Innovation: ν3=14−11.462=2.538\nu_3 = 14 - 11.462 = 2.538, S3=4.077S_3 = 4.077.
    3. Gain: K3=2.077/4.077=0.509K_3 = 2.077/4.077 = 0.509.
    4. Update: x^3∣3=11.462+0.509⋅2.538=12.755\hat x_{3\mid 3} = 11.462 + 0.509 \cdot 2.538 = 12.755, P3∣3=1.019P_{3\mid 3} = 1.019.

    Steady state. The gains 0.667,0.538,0.5090.667, 0.538, 0.509 approach K∞K_\infty. Here q=1/2q = 1/2, so

    K∞=12(1/4+2−1/2)=12(1.5−0.5)=0.5,K_\infty = \tfrac12\Big(\sqrt{1/4 + 2} - 1/2\Big) = \tfrac12(1.5 - 0.5) = 0.5,

    with P=12(1+1+8)=2P = \tfrac12(1 + \sqrt{1 + 8}) = 2. The filter becomes SES with α=0.5\alpha = 0.5.

    Forecast from t=3t = 3. y^3+h∣3=12.755\hat y_{3+h\mid 3} = 12.755 for every hh. The variance of y3+hy_{3+h} is P3∣3+hQ+R=1.019+h+2P_{3\mid 3} + hQ + R = 1.019 + h + 2: 4.0194.019, 5.0195.019, 6.0196.019 for h=1,2,3h = 1, 2, 3. The 95 % interval for h=1h = 1 is 12.755±1.964.019=12.755±3.9312.755 \pm 1.96\sqrt{4.019} = 12.755 \pm 3.93.

    Log-likelihood. Each term is −12(log⁡(2πSt)+νt2/St)-\tfrac12\big(\log(2\pi S_t) + \nu_t^2/S_t\big): −2.565-2.565, −1.768-1.768, −2.412-2.412, so log⁡L=−6.744\log L = -6.744.

    Missing y2y_2. Skip the update at t=2t = 2: x^2∣2=12\hat x_{2\mid 2} = 12, P2∣2=P2∣1=2.333P_{2\mid 2} = P_{2\mid 1} = 2.333. Then P3∣2=3.333P_{3\mid 2} = 3.333, K3=3.333/5.333=0.625K_3 = 3.333/5.333 = 0.625 (larger than before) and x^3∣3=12+0.625⋅(14−12)=13.25\hat x_{3\mid 3} = 12 + 0.625 \cdot (14 - 12) = 13.25.

    (b) One step of constant-velocity tracking. F=(1101)F = \begin{pmatrix}1&1\\0&1\end{pmatrix}, H=(1,0)H = (1, 0), Q=diag⁡(0,1)Q = \operatorname{diag}(0, 1), R=1R = 1, start x^0∣0=(0,1)\hat x_{0\mid 0} = (0, 1) (position 0, velocity 1), P0∣0=IP_{0\mid 0} = I. We observe y1=2.5y_1 = 2.5.

    1. Predict the mean: x^1∣0=Fx^0∣0=(0+1,1)=(1,1)\hat x_{1\mid 0} = F\hat x_{0\mid 0} = (0 + 1, 1) = (1, 1).
    2. Predict the covariance: FP0∣0F⊤=FF⊤=(2111)FP_{0\mid 0}F^\top = FF^\top = \begin{pmatrix}2&1\\1&1\end{pmatrix}, and adding QQ gives P1∣0=(2112)P_{1\mid 0} = \begin{pmatrix}2&1\\1&2\end{pmatrix}. The off-diagonal 1 is new: position and velocity are now correlated.
    3. Innovation: ν1=2.5−1=1.5\nu_1 = 2.5 - 1 = 1.5; S1=HP1∣0H⊤+R=2+1=3S_1 = HP_{1\mid 0}H^\top + R = 2 + 1 = 3.
    4. Gain: K1=P1∣0H⊤/S1=(2,1)⊤/3K_1 = P_{1\mid 0}H^\top/S_1 = (2, 1)^\top/3, that is (0.667,0.333)⊤(0.667, 0.333)^\top.
    5. Update: x^1∣1=(1,1)+1.5 (0.667,0.333)=(2,1.5)\hat x_{1\mid 1} = (1, 1) + 1.5\,(0.667, 0.333) = (2, 1.5).
    6. Covariance:
    P1∣1=P1∣0−K1HP1∣0=(2112)−13(4221)=(2/31/31/35/3).P_{1\mid 1} = P_{1\mid 0} - K_1HP_{1\mid 0} = \begin{pmatrix}2&1\\1&2\end{pmatrix} - \frac13\begin{pmatrix}4&2\\2&1\end{pmatrix} = \begin{pmatrix}2/3&1/3\\1/3&5/3\end{pmatrix}.

    The target is further ahead than predicted, so the filter raises the velocity from 1 to 1.5, although the velocity was not measured. The position variance fell from 2 to 2/32/3; the velocity variance fell only from 2 to 5/35/3.

    5. In code #

    One function covers every model of this lesson. It treats a row of NaN as a missing observation.

    import numpy as np
    from scipy.optimize import minimize
    
    def kalman_filter(y, F, H, Q, R, x0, P0):
        """Filtered means/covariances and log-likelihood. y: (n, k) array; NaN rows are missing."""
        x, P = np.asarray(x0, float), np.asarray(P0, float)
        xs, Ps, loglik = [], [], 0.0
        for yt in y:
            x, P = F @ x, F @ P @ F.T + Q                   # predict
            if not np.any(np.isnan(yt)):                     # update (skip if missing)
                nu = yt - H @ x                              # innovation
                S = H @ P @ H.T + R                          # its covariance
                K = P @ H.T @ np.linalg.inv(S)               # Kalman gain
                x, P = x + K @ nu, (np.eye(len(x)) - K @ H) @ P
                loglik -= 0.5 * (np.log(np.linalg.det(2 * np.pi * S)) + nu @ np.linalg.solve(S, nu))
            xs.append(x); Ps.append(P)
        return np.array(xs), np.array(Ps), loglik
    
    # worked example (a): local level, Q = 1, R = 2, prior N(10, 3)
    I = np.eye(1)
    y = np.array([[13.], [11.], [14.]])
    xs, Ps, ll = kalman_filter(y, I, I, 1 * I, 2 * I, [10.], 3 * I)
    print(xs.ravel().round(4), Ps.ravel().round(4), round(ll, 4))
    # [12.     11.4615 12.7547] [1.3333 1.0769 1.0189] -6.7442
    y_miss = np.array([[13.], [np.nan], [14.]])
    print(kalman_filter(y_miss, I, I, I, 2 * I, [10.], 3 * I)[0].ravel().round(4))
    # [12.   12.   13.25]
    
    # worked example (b): one step of constant-velocity tracking
    F = np.array([[1., 1.], [0., 1.]]); H = np.array([[1., 0.]])
    xs, Ps, _ = kalman_filter(np.array([[2.5]]), F, H, np.diag([0., 1.]), np.eye(1), [0., 1.], np.eye(2))
    print(xs[0], Ps[0].round(4).tolist())
    # [2.  1.5] [[0.6667, 0.3333], [0.3333, 1.6667]]
    
    # Nile: maximum likelihood for (Q, R); NILE holds the 100 values (they are in the notebook)
    def neg_loglik(theta):
        Q, R = np.exp(theta)                       # log scale keeps the variances positive
        # exact diffuse start: x_{1|1} = y_1, P_{1|1} = R; the likelihood starts at t = 2
        return -kalman_filter(NILE[1:, None], I, I, Q * I, R * I, [NILE[0]], R * I)[2]
    res = minimize(neg_loglik, x0=np.log([1000., 10000.]), method="Nelder-Mead")
    Q_hat, R_hat = np.exp(res.x)
    q = Q_hat / R_hat
    print(round(Q_hat), round(R_hat), round(-res.fun, 2))         # 1469 15099 -632.55
    print(round(q, 4), round((np.sqrt(q**2 + 4*q) - q) / 2, 3))   # 0.0973 0.267
    

    For real work, statsmodels has the same model as sm.tsa.UnobservedComponents(y, "local level") and general models with sm.tsa.statespace.MLEModel. The notebook compares the results in an optional cell.

    6. Where it shows up #

    • Classical forecasting software. statsmodels fits ARIMA, SARIMAX, unobserved-components and dynamic factor models through the Kalman filter; this is how it handles missing values and computes the exact likelihood. The R package KFAS and the structural models of Harvey (1989) work the same way.
    • Structural time-series models. Write a series as level + trend + season + regression, each a block of the state, and read the smoothed components. Time-varying regression coefficients (yt=zt⊤βt+vty_t = z_t^\top\beta_t + v_t with βt\beta_t a random walk) are a state-space model with Ht=zt⊤H_t = z_t^\top; economists use them for changing relations and nowcasting.
    • Navigation and robotics. GPS + inertial sensor fusion, target tracking with radar, and the EKF in SLAM.
    • Weather and climate. Data assimilation combines a physical model forecast with millions of observations; the ensemble Kalman filter and 4D-Var are the large-scale versions of the update step (see the ml-for-science track).
    • Machine learning. Probabilistic state-space models with a neural network inside (deep Kalman filters, DeepState) use the same predict–update structure. S4 and Mamba layers use the linear state equation without noise (deep-learning lesson 16). A Gaussian process with a Matérn kernel in time can be written as a state-space model, so a Kalman filter computes it in O(n)O(n) instead of O(n3)O(n^3) (Hartikainen and Särkkä, 2010).

    7. Common mistakes #

    • Mixing up the state and the observation. xtx_t is hidden; yty_t is measured. The filtered level x^t∣t\hat x_{t\mid t} is not the data, and the interval for yt+hy_{t+h} must add RR to the state variance.
    • Mixing up the covariances. Pt∣t−1P_{t\mid t-1} (before yty_t), Pt∣tP_{t\mid t} (after yty_t) and StS_t (of the observation) are three different things. The gain uses Pt∣t−1P_{t\mid t-1}, not Pt∣tP_{t\mid t}.
    • Thinking the gain is a free parameter. KtK_t follows from F,H,Q,RF, H, Q, R and P0∣0P_{0\mid 0}. To get a smoother or a more reactive filter, change QQ or RR (or better, estimate them).
    • Too small a QQ. With QQ near 0 the filter "trusts the model" and becomes slow; it can lag behind a real change for a long time. The innovations then have long runs of one sign: check them.
    • Using the smoother for forecasts or in a backtest. It uses future data. Only the filter is allowed at a forecast origin.
    • Forgetting the start. A small P0∣0P_{0\mid 0} with a wrong x^0∣0\hat x_{0\mid 0} makes the first estimates wrong for a long time, and the first likelihood terms can dominate the fit. Use a diffuse start when you know nothing.

    8. Check yourself #

    1. Write the model zt=ϕzt−1+εtz_t = \phi z_{t-1} + \varepsilon_t, yt=zt+vty_t = z_t + v_t (an AR(1) measured with noise) in state-space form.
      AnswerState xt=ztx_t = z_t, F=ϕF = \phi, H=1H = 1, Q=σε2Q = \sigma_\varepsilon^2, R=σv2R = \sigma_v^2. With ∣ϕ∣<1|\phi| < 1 a natural start is the stationary distribution: x^0∣0=0\hat x_{0\mid 0} = 0, P0∣0=σε2/(1−ϕ2)P_{0\mid 0} = \sigma_\varepsilon^2/(1 - \phi^2) (lesson 02).
    2. In the local level model set Q=0Q = 0 and use a diffuse start. What does the filter compute?
      AnswerThe running mean. With Q=0Q = 0 the level is a constant μ\mu. After the diffuse start, x^1∣1=y1\hat x_{1\mid 1} = y_1 and P1∣1=RP_{1\mid 1} = R. By induction, if Pt−1∣t−1=R/(t−1)P_{t-1\mid t-1} = R/(t-1), then Kt=R/(t−1)R/(t−1)+R=1/tK_t = \frac{R/(t-1)}{R/(t-1) + R} = 1/t and x^t∣t=x^t−1∣t−1+1t(yt−x^t−1∣t−1)\hat x_{t\mid t} = \hat x_{t-1\mid t-1} + \frac1t(y_t - \hat x_{t-1\mid t-1}), the update of the mean of y1,…,yty_1, \dots, y_t, with Pt∣t=R/tP_{t\mid t} = R/t. The gain goes to 0, which matches K∞=0K_\infty = 0 for q=0q = 0.
    3. In worked example (b), why did the velocity estimate change although H=(1,0)H = (1, 0) measures only the position?
      AnswerThe predict step made position and velocity correlated: P1∣0P_{1\mid 0} has off-diagonal entry 1. The gain is P1∣0H⊤/S1P_{1\mid 0}H^\top/S_1, the first column of P1∣0P_{1\mid 0} over S1S_1, so its second entry is 1/3≠01/3 \ne 0. A position surprise of +1.5+1.5 therefore raises the velocity by 1.5/3=0.51.5/3 = 0.5.
    4. Why does the likelihood use the innovations νt\nu_t and not the observations yty_t themselves?
      AnswerThe yty_t are dependent, so their joint density is not a product of marginals. The chain rule writes it as a product of one-step conditional densities p(yt∣y1:t−1)p(y_t \mid y_{1:t-1}), and each one is the Gaussian density of the innovation νt\nu_t with covariance StS_t. Under the model the innovations are independent, so the log-likelihood is a simple sum.
    5. For the Nile data the filter at 1970 gives x^1970∣1970=798.4\hat x_{1970\mid 1970} = 798.4 and P1970∣1970=4 032P_{1970\mid 1970} = 4\,032, with Q^=1 469\hat Q = 1\,469 and R^=15 099\hat R = 15\,099. Give the 95 % prediction interval for the flow in 1971.
      AnswerThe variance is P1970∣1970+Q+R=4 032+1 469+15 099=20 600P_{1970\mid 1970} + Q + R = 4\,032 + 1\,469 + 15\,099 = 20\,600, so the standard deviation is 143.5143.5. The interval is 798±1.96⋅143.5=798±281798 \pm 1.96 \cdot 143.5 = 798 \pm 281, about [517,1080][517, 1080]. Most of the width comes from RR, the year-to-year noise, not from the uncertainty about the level.
    6. At which time do the filter and the smoother give the same estimate, and why?
      AnswerAt the last time t=nt = n. There, both condition on the same data y1,…,yny_1, \dots, y_n, so x^n∣n\hat x_{n\mid n} is both the filtered and the smoothed estimate. The RTS recursion starts from it and goes backward.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. Write the two equations of a linear Gaussian state-space model.

    2. What does the predict step of the Kalman filter compute?

    3. Give the Kalman gain in the vector case.

    4. What does the update step compute?

    5. Prior N(10,4)\mathcal N(10, 4), observation y=14y = 14 with R=4R = 4. What is the posterior?

    6. What is the steady-state gain of the local level model?

    7. How are SES and the Kalman filter related?

    8. How does the Kalman filter handle a missing observation?

    9. State the prediction-error decomposition of the log-likelihood.

    10. Why may only the filter, not the smoother, be used at a forecast origin?

    Open the review →

    9. Interview angle #

    • "Explain the Kalman filter in one minute." — A hidden state follows a linear model with noise and is measured with noise. Predict: push the estimate and its covariance through the model. Update: correct with the innovation times the gain, which weighs prediction uncertainty against sensor noise. For linear Gaussian models it is the exact posterior.
    • "What is the intuition for the Kalman gain?" — It is the relative trust in the measurement: P/(P+R)P/(P + R) in one dimension. Precisions add. Large RR → small gain; large prediction uncertainty → gain near 1.
    • "How do you choose QQ and RR?" — Estimate them by maximum likelihood with the prediction-error decomposition, then check that the standardised innovations look like white noise with variance 1. Hand tuning is a last resort.
    • "What is the link between SES and the Kalman filter?" — SES is the steady-state filter of the local level model, with α\alpha fixed by q=Q/Rq = Q/R. ARIMA(0,1,1) with θ=α−1\theta = \alpha - 1 gives the same forecasts.
    • "Filter vs smoother vs forecast?" — x^t∣t\hat x_{t\mid t}, x^t∣n\hat x_{t\mid n} and x^t+h∣t\hat x_{t+h\mid t}. Only the filter and the forecast are allowed in real-time use and in backtests.
    • "What do you do for a nonlinear model?" — EKF (linearise), UKF (sigma points) or a particle filter (samples); for very large states the ensemble Kalman filter.

    10. Practice #

    The notebook for this lesson runs in Colab. It has five exercises, each with a check cell and a folded solution:

    1. Write kalman_filter(y, F, H, Q, R, x0, P0) that returns filtered means, covariances and the log-likelihood and treats np.nan as missing; check it on worked example (a) and against Bayes' rule computed on a grid.
    2. Track a constant-velocity target from position-only observations and compare the RMSE of the filtered position with the RMSE of the raw observations.
    3. Compute the steady-state gain of the local level model and show that SES with α=K∞\alpha = K_\infty gives the same values as the filter after the start-up.
    4. Fit the local level model to the Nile data: maximise the log-likelihood over (log⁡Q,log⁡R)(\log Q, \log R) with scipy.optimize.minimize and compare with R^≈15 099\hat R \approx 15\,099, Q^≈1 469\hat Q \approx 1\,469.
    5. Write rts_smoother and check that the smoothed variances are never larger than the filtered ones.

    11. Watch and read #

    The predict and update steps as pictures of Gaussians; matches section 2.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The first video of a long series with small numerical examples; useful if you want more worked steps.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    State-space models from the time-series view (DTU course 02417), with the same two equations.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The control-engineering view: the filter as an optimal observer; uses continuous time.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play