UnobservedComponentsResult#

class cultivars.univariate.unobserved_components.UnobservedComponentsResult(*, endog, trend, has_cycle, seasonal_period, sigma2_irregular, sigma2_level, sigma2_slope, cycle_rho, cycle_freq, sigma2_cycle, sigma2_seasonal, llf, nobs, n_params, _smoothed_state, _smoothed_state_cov, _slices)[source]#

A fitted structural decomposition: components, variances and bands.

The observed series is the sum of unobserved components, each with its own dynamics and its own innovation,

\[y_t = \mu_t + \psi_t + \gamma_t + \varepsilon_t, \qquad \varepsilon_t \sim N(0, \sigma^2_\varepsilon),\]

where the trend \(\mu_t\) is a local level \(\mu_{t+1} = \mu_t + \eta_t\), a local linear trend \(\mu_{t+1} = \mu_t + \beta_t + \eta_t\), \(\beta_{t+1} = \beta_t + \zeta_t\), or the smooth trend that sets \(\eta_t \equiv 0\) so the level is an integrated random walk; the cycle is the damped rotation

\[\begin{split}\begin{pmatrix} \psi_{t+1} \\ \psi^*_{t+1} \end{pmatrix} = \rho \begin{pmatrix} \cos\lambda_c & \sin\lambda_c \\ -\sin\lambda_c & \cos\lambda_c \end{pmatrix} \begin{pmatrix} \psi_t \\ \psi^*_t \end{pmatrix} + \begin{pmatrix} \kappa_t \\ \kappa^*_t \end{pmatrix}, \qquad 0 < \rho < 1,\end{split}\]

with period \(2\pi / \lambda_c\); and the seasonal is the trigonometric form, one such rotation without damping at each harmonic \(\lambda_j = 2\pi j / s\), \(j = 1, \ldots, \lfloor s/2 \rfloor\), every harmonic sharing one innovation variance \(\sigma^2_\omega\). Everything is estimated by exact maximum likelihood through the Kalman filter, and every component path reported here is the full-sample smoothed estimate \(E[\cdot \mid y_1, \ldots, y_n]\) with its smoothed standard deviation, so the bands widen at both ends of the sample – the behaviour an ad-hoc filter hides.

The record is immutable; it carries the smoothed state and its covariance privately and rebuilds the system matrices on demand, so the component properties, state_space, forecast and simulate all read the same fitted variances. Information criteria, the summary and the likelihood-ratio test come from the mixins.

Note

The likelihood is exact Gaussian under an approximate-diffuse initialization: the nonstationary states (level, slope, seasonal) start with variance 1e6 and the cycle starts at its stationary covariance. llf is therefore comparable across specifications on the same sample – the ranking the information_criteria express – but differs from an exact-diffuse likelihood by a constant that depends on the number of diffuse states, so it should not be compared with likelihoods from software that uses the exact-diffuse initialization. The cycle frequency is estimated on (0.05, pi - 0.05), so the cycle period lies between about two and about 126 observations; a slower swing is absorbed by the trend. n_params counts the variances and the two cycle shape parameters – 2 for a local level, 3 for a local linear trend, 2 for a smooth trend, plus 3 for the cycle and 1 for the seasonal.

Variables:
  • endog – The observed series, shape (n,).

  • trend – The trend specification ("level", "lltrend" or "smooth").

  • has_cycle – Whether a damped stochastic cycle was estimated.

  • seasonal_period – The trigonometric seasonal period, or None.

  • sigma2_irregular – The irregular (observation noise) variance \(\sigma^2_\varepsilon\).

  • sigma2_level – The level innovation variance \(\sigma^2_\eta\), or None for the smooth trend, whose level has no innovation of its own.

  • sigma2_slope – The slope innovation variance \(\sigma^2_\zeta\), or None for the local level.

  • cycle_rho – The cycle damping \(\rho\), or None.

  • cycle_freq – The cycle frequency \(\lambda_c\) in radians per observation, or None.

  • sigma2_cycle – The cycle innovation variance \(\sigma^2_\kappa\), or None.

  • sigma2_seasonal – The seasonal innovation variance \(\sigma^2_\omega\), or None.

  • llf – The exact Gaussian log-likelihood at the optimum.

  • nobs – The number of observations.

  • n_params – The number of free parameters the likelihood was maximized over.

See also

References

Harvey, A. C. (1989). Forecasting, Structural Time Series Models and the Kalman Filter. Cambridge University Press, chs. 2 and 4.

Durbin, J., & Koopman, S. J. (2012). Time Series Analysis by State Space Methods (2nd ed.). Oxford University Press, ch. 3.

Example

A local level recovered from a noisy random walk, then a level plus a stochastic cycle whose period is read off the fit:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> level = np.cumsum(0.1 * rng.standard_normal(n))
>>> y = level + 0.5 * rng.standard_normal(n)
>>> res = UnobservedComponents(y, trend="level").fit()
>>> res.trend, res.has_cycle, res.seasonal_period, res.nobs, res.n_params
('level', False, None, 300, 2.0)
>>> bool(abs(res.sigma2_irregular - 0.25) < 0.1), bool(res.sigma2_level < 0.05)
(True, True)
>>> bool(np.mean((res.level - level) ** 2) < np.mean((y - level) ** 2))
True
>>> rng = np.random.default_rng(3)
>>> n = 300
>>> rho, freq = 0.95, 2 * np.pi / 20
>>> rotation = rho * np.array([[np.cos(freq), np.sin(freq)], [-np.sin(freq), np.cos(freq)]])
>>> cycle = np.zeros((n, 2))
>>> for t in range(1, n):
...     cycle[t] = rotation @ cycle[t - 1] + 0.3 * rng.standard_normal(2)
>>> walk = np.cumsum(0.05 * rng.standard_normal(n))
>>> y = walk + cycle[:, 0] + 0.3 * rng.standard_normal(n)
>>> res = UnobservedComponents(y, cycle=True).fit()
>>> bool(abs(res.cycle_period - 20) < 2), bool(abs(res.cycle_rho - 0.95) < 0.05)
(True, True)
>>> bool(np.corrcoef(res.cycle, cycle[:, 0])[0, 1] > 0.9)
True

Attributes#

endog

The observed series, shape (n,).

trend

"level", "lltrend" or "smooth".

has_cycle

Whether a damped stochastic cycle was estimated.

seasonal_period

The trigonometric seasonal period, or None.

sigma2_irregular

The irregular variance \(\sigma^2_\varepsilon\).

sigma2_level

The level innovation variance, or None for the smooth trend.

sigma2_slope

The slope innovation variance, or None for the local level.

cycle_rho

The cycle damping \(\rho\) in (0, 1), or None.

cycle_freq

The cycle frequency \(\lambda_c\) in radians, or None.

sigma2_cycle

The cycle innovation variance, or None.

sigma2_seasonal

The seasonal innovation variance shared by the harmonics, or None.

llf

The exact Gaussian log-likelihood at the optimum.

nobs

The number of observations.

n_params

The number of free parameters the likelihood was maximized over.

cycle

The smoothed cycle \(E[\psi_t \mid y]\), shape (n,).

cycle_period

The estimated cycle period \(2\pi / \lambda_c\) in observations.

information_criteria

AIC, BIC, and HQIC derived from the likelihood and parameter count.

irregular

The observed series minus the smoothed signal, shape (n,).

level

The smoothed trend level \(E[\mu_t \mid y]\), shape (n,).

level_std

The smoothed standard deviation of the level, shape (n,).

seasonal

The smoothed seasonal \(E[\gamma_t \mid y]\), shape (n,).

signal

level plus cycle plus seasonal, shape (n,).

slope

The smoothed trend slope \(E[\beta_t \mid y]\), shape (n,).

state_space

The fitted system, re-applicable to data it was not estimated on.

fittedvalues

resid

Methods#

compare(*others[, criterion])

Rank this result against others by an information criterion.

forecast(steps)

Out-of-sample forecasts with honest standard errors.

likelihood_ratio_test(unrestricted)

Test this result as the restricted model against a nesting one.

simulate([n, seed, burn])

A fresh sample path at the fitted variances.

summary()

The estimation summary, renderable as text, HTML, or a dataframe.

to_dict()

The aligned per-observation outputs as plain numpy arrays.

to_pandas(*[, index])

The aligned per-observation outputs as a pandas.DataFrame.

to_polars()

The aligned per-observation outputs as a polars.DataFrame.