SARIMAX#
- class cultivars.univariate.box_jenkins.SARIMAX(endog, *, order, seasonal_order=(0, 0, 0, 0), trend='c', exog=None)[source]
Bases:
_BoxJenkinsModel[ARMAResult]Seasonal ARIMA with exogenous regressors – the general specification.
The model on the differenced series \(w_t = (1 - L)^d (1 - L^s)^D y_t\) is
\[\phi(L)\,\Phi(L^s)\,(w_t - x_t'\beta) = \theta(L)\,\Theta(L^s)\,\varepsilon_t, \qquad \varepsilon_t \sim N(0, \sigma^2),\]with \(\phi(L) = 1 - \phi_1 L - \cdots - \phi_p L^p\), \(\theta(L) = 1 + \theta_1 L + \cdots + \theta_q L^q\), their seasonal counterparts in \(L^s\), and \(x_t\) the deterministic terms of
trendfollowed by the columns ofexog, differenced alongside the series. The moving-average sign convention is the plus one: a simulated \(e_t - 0.4 e_{t-1}\) is estimated as \(\theta_1 \approx -0.4\). Estimation is exact maximum likelihood through the Harvey state-space form and the Kalman filter, with the autoregressive and moving-average polynomials kept stationary and invertible by the partial-autocorrelation reparameterization; starting values come from a regression of the differenced series on \(x_t\) and a short autoregression on its residuals.Every other class in this module narrows this one. Here alone
exogis optional, because this is the form a user reaches for when they want the full surface rather than a named special case;ARIMAXis the non-seasonal variant that makes regressors mandatory.- Parameters:
endog (
ArrayLike) – The endogenous series, one-dimensional, of length at least \(p + d + q + s(P + D + Q) + 2\).order (
tuple[int,int,int]) – Non-seasonal(p, d, q), non-negative integers.seasonal_order (
tuple[int,int,int,int]) – Seasonal(P, D, Q, s);smust be at least 2 whenever any ofP,D,Qis positive.trend (
str) – Deterministic specification ("n","c","ct").exog (
ArrayLike|None) – Optional exogenous regressors,(nobs, k)or(nobs,), differenced alongsideendog.
- Raises:
SpecificationError – If an order tuple has the wrong length or a negative entry, seasonal terms are requested with a period below 2, or the trend is unrecognized.
DimensionError – If the series is not one-dimensional, is too short for the specification, or
exogdoes not have one row per observation.
Note
Regressors enter as an observation intercept, so this is a regression with SARIMA errors, not a transfer-function model:
exogshifts the level of \(w_t\) and the ARMA structure describes what remains. The differencing thatdandDapply toendogis applied toexogtoo, so a regressor in levels against a series in first differences must be supplied in levels, and its coefficient reads on the differenced scale. A result with regressors cannotsimulate– their future is not part of the model.See also
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.
Example
The airline model on a series simulated from it:
>>> import numpy as np >>> rng = np.random.default_rng(0) >>> e = rng.standard_normal(400) >>> y = np.zeros(400) >>> for t in range(13, 400): ... y[t] = ( ... y[t - 1] + y[t - 12] - y[t - 13] ... + e[t] - 0.4 * e[t - 1] - 0.6 * e[t - 12] + 0.24 * e[t - 13] ... ) >>> res = SARIMAX(y, order=(0, 1, 1), seasonal_order=(0, 1, 1, 12), trend="n").fit() >>> res.nobs, res.ma_params.round(1).tolist(), res.seasonal_ma_params.round(1).tolist() (387, [-0.4], [-0.6]) >>> res.is_invertible True
A regression with AR(1) errors recovers its slopes:
>>> X = rng.standard_normal((400, 2)) >>> noise = rng.standard_normal(400) >>> reg = SARIMAX(X @ [1.0, -2.0] + noise, order=(1, 0, 0), exog=X).fit() >>> reg.k_exog, reg.beta[1:].round(1).tolist() (2, [1.0, -2.0])