cultivars.univariate.box_jenkins#
ARMA / ARIMA / SARIMA / SARIMAX – one engine behind four public fronts.
All four are the same estimator. The conditional mean is the multiplicative seasonal ARMA
with \(\theta(L) = 1 + \theta_1 L + \cdots\) (the plus sign convention), cast in the Harvey state-space form so the likelihood is exact and runs through the Kalman filter,
with \(v_t\) and \(F_t\) the filter’s innovations and their
variances. Integration is applied by differencing before estimation;
deterministic terms and exogenous regressors enter the observation
intercept, which makes ARIMAX and SARIMAX nothing more than
ARIMA and SARIMA with exog supplied. Stationarity and
invertibility are enforced structurally by the partial-autocorrelation
reparameterization, so the optimizer searches an unconstrained space
and the reported diagnostics verify the transform rather than describe
the data – a unit root fitted this way lands at a root of 0.999 with
Stationary: True, and d is the right answer, not a different
optimizer.
Two commitments shape the surface. First, SARIMAX is the real
specification and the other four names are narrowing constructors over
it, not separate models: a user who reaches for ARMA cannot pass a
differencing order, one who reaches for SARIMA need not spell out
(0, 0, 0, 0), and one who reaches for ARIMAX must supply
regressors, so an import path reads as the specification it is and the
four fits of one model return identical likelihoods. Second, both
stability verdicts are assessed on the multiplied polynomials
\(\phi(L)\Phi(L^s)\) and \(\theta(L)\Theta(L^s)\); checking the
non-seasonal block alone would pass a specification with an explosive
seasonal root, and the result exposes the expanded polynomials so the
verdict can be audited.
Layout. SARIMAX validates order and seasonal_order
through validate_order_tuple, the trend through validate_choice
against Trend, and the regressors through
validate_exog, all on the _BoxJenkinsModel base in
_internals; fit builds a _BoxJenkinsObjective whose
state_space maps a parameter vector to a
_LinearGaussianStateSpace through _from_arma, and hands it to
_maximize_likelihood. ARIMAX, SARIMA,
ARIMA and ARMA fix or forbid arguments and delegate.
The packed _BoxJenkinsFit is assembled into ARMAResult by
_from_fit; expand_ar and expand_ma in _core multiply
the polynomials for the stability and invertibility checks and for
simulate, which runs _simulate_arma and undoes the differencing
with _integrate; combined_difference applies it, and
deterministic_columns and n_deterministic lay out the trend
block. The pure autoregression with conditional least squares is
autoregression; the long-memory
generalization fractional_integration; the
permanent-transitory reading of an ARIMA(p, 1, q) result
BeveridgeNelsonDecomposition.
References
Box, G. E. P., Jenkins, G. M., Reinsel, G. C., & Ljung, G. M. (2015). Time Series Analysis: Forecasting and Control, 5th ed. Wiley.
Harvey, A. C. (1989). Forecasting, Structural Time Series Models and the Kalman Filter. Cambridge University Press.
Durbin, J., & Koopman, S. J. (2012). Time Series Analysis by State Space Methods, 2nd ed., chapter 3. Oxford University Press.
Monahan, J. F. (1984). A note on enforcing stationarity in ARMA models. Biometrika, 71(2), 403-404.
Example
One model, four fronts, one likelihood; then order selection on a common sample:
>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> e = rng.standard_normal(500)
>>> y = np.zeros(500)
>>> for t in range(1, 500):
... y[t] = 0.6 * y[t - 1] + e[t] + 0.3 * e[t - 1]
>>> fits = (
... ARMA(y, order=(1, 1)).fit(),
... ARIMA(y, order=(1, 0, 1)).fit(),
... SARIMA(y, order=(1, 0, 1), seasonal_order=(0, 0, 0, 0)).fit(),
... SARIMAX(y, order=(1, 0, 1)).fit(),
... )
>>> len({round(fit.llf, 6) for fit in fits})
1
>>> candidates = [ARMA(y, order=(p, q)).fit() for p in (1, 2) for q in (0, 1)]
>>> table = candidates[0].compare(*candidates[1:], criterion="bic")
>>> table.rows[0][0] in {"ARIMA(1, 0, 1)", "ARIMA(2, 0, 0)"}
True
Classes#
ARIMA(p, d, q): non-seasonal differencing, no seasonal block, no regressors. |
|
ARIMA(p, d, q) with exogenous regressors, no seasonal block. |
|
Stationary ARMA(p, q): no differencing, no seasonal block, no regressors. |
|
Multiplicative seasonal ARIMA, without exogenous regressors. |
|
Seasonal ARIMA with exogenous regressors -- the general specification. |
|
A fitted (seasonal) ARIMA, optionally with exogenous regressors. |