Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/statsmodels/tsa/statespace/sarimax.py : 8%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2SARIMAX Model
4Author: Chad Fulton
5License: Simplified-BSD
6"""
7from warnings import warn
9import numpy as np
10import pandas as pd
12from statsmodels.compat.pandas import Appender
14from statsmodels.tools.tools import Bunch
15from statsmodels.tools.data import _is_using_pandas
16from statsmodels.tools.decorators import cache_readonly
17import statsmodels.base.wrapper as wrap
19from statsmodels.tsa.arima.specification import SARIMAXSpecification
20from statsmodels.tsa.arima.params import SARIMAXParams
21from statsmodels.tsa.tsatools import lagmat
23from .initialization import Initialization
24from .mlemodel import MLEModel, MLEResults, MLEResultsWrapper
25from .tools import (
26 companion_matrix, diff, is_invertible, constrain_stationary_univariate,
27 unconstrain_stationary_univariate,
28 prepare_exog, prepare_trend_spec, prepare_trend_data)
31class SARIMAX(MLEModel):
32 r"""
33 Seasonal AutoRegressive Integrated Moving Average with eXogenous regressors
34 model
36 Parameters
37 ----------
38 endog : array_like
39 The observed time-series process :math:`y`
40 exog : array_like, optional
41 Array of exogenous regressors, shaped nobs x k.
42 order : iterable or iterable of iterables, optional
43 The (p,d,q) order of the model for the number of AR parameters,
44 differences, and MA parameters. `d` must be an integer
45 indicating the integration order of the process, while
46 `p` and `q` may either be an integers indicating the AR and MA
47 orders (so that all lags up to those orders are included) or else
48 iterables giving specific AR and / or MA lags to include. Default is
49 an AR(1) model: (1,0,0).
50 seasonal_order : iterable, optional
51 The (P,D,Q,s) order of the seasonal component of the model for the
52 AR parameters, differences, MA parameters, and periodicity.
53 `d` must be an integer indicating the integration order of the process,
54 while `p` and `q` may either be an integers indicating the AR and MA
55 orders (so that all lags up to those orders are included) or else
56 iterables giving specific AR and / or MA lags to include. `s` is an
57 integer giving the periodicity (number of periods in season), often it
58 is 4 for quarterly data or 12 for monthly data. Default is no seasonal
59 effect.
60 trend : str{'n','c','t','ct'} or iterable, optional
61 Parameter controlling the deterministic trend polynomial :math:`A(t)`.
62 Can be specified as a string where 'c' indicates a constant (i.e. a
63 degree zero component of the trend polynomial), 't' indicates a
64 linear trend with time, and 'ct' is both. Can also be specified as an
65 iterable defining the polynomial as in `numpy.poly1d`, where
66 `[1,1,0,1]` would denote :math:`a + bt + ct^3`. Default is to not
67 include a trend component.
68 measurement_error : bool, optional
69 Whether or not to assume the endogenous observations `endog` were
70 measured with error. Default is False.
71 time_varying_regression : bool, optional
72 Used when an explanatory variables, `exog`, are provided provided
73 to select whether or not coefficients on the exogenous regressors are
74 allowed to vary over time. Default is False.
75 mle_regression : bool, optional
76 Whether or not to use estimate the regression coefficients for the
77 exogenous variables as part of maximum likelihood estimation or through
78 the Kalman filter (i.e. recursive least squares). If
79 `time_varying_regression` is True, this must be set to False. Default
80 is True.
81 simple_differencing : bool, optional
82 Whether or not to use partially conditional maximum likelihood
83 estimation. If True, differencing is performed prior to estimation,
84 which discards the first :math:`s D + d` initial rows but results in a
85 smaller state-space formulation. See the Notes section for important
86 details about interpreting results when this option is used. If False,
87 the full SARIMAX model is put in state-space form so that all
88 datapoints can be used in estimation. Default is False.
89 enforce_stationarity : bool, optional
90 Whether or not to transform the AR parameters to enforce stationarity
91 in the autoregressive component of the model. Default is True.
92 enforce_invertibility : bool, optional
93 Whether or not to transform the MA parameters to enforce invertibility
94 in the moving average component of the model. Default is True.
95 hamilton_representation : bool, optional
96 Whether or not to use the Hamilton representation of an ARMA process
97 (if True) or the Harvey representation (if False). Default is False.
98 concentrate_scale : bool, optional
99 Whether or not to concentrate the scale (variance of the error term)
100 out of the likelihood. This reduces the number of parameters estimated
101 by maximum likelihood by one, but standard errors will then not
102 be available for the scale parameter.
103 trend_offset : int, optional
104 The offset at which to start time trend values. Default is 1, so that
105 if `trend='t'` the trend is equal to 1, 2, ..., nobs. Typically is only
106 set when the model created by extending a previous dataset.
107 use_exact_diffuse : bool, optional
108 Whether or not to use exact diffuse initialization for non-stationary
109 states. Default is False (in which case approximate diffuse
110 initialization is used).
111 **kwargs
112 Keyword arguments may be used to provide default values for state space
113 matrices or for Kalman filtering options. See `Representation`, and
114 `KalmanFilter` for more details.
116 Attributes
117 ----------
118 measurement_error : bool
119 Whether or not to assume the endogenous
120 observations `endog` were measured with error.
121 state_error : bool
122 Whether or not the transition equation has an error component.
123 mle_regression : bool
124 Whether or not the regression coefficients for
125 the exogenous variables were estimated via maximum
126 likelihood estimation.
127 state_regression : bool
128 Whether or not the regression coefficients for
129 the exogenous variables are included as elements
130 of the state space and estimated via the Kalman
131 filter.
132 time_varying_regression : bool
133 Whether or not coefficients on the exogenous
134 regressors are allowed to vary over time.
135 simple_differencing : bool
136 Whether or not to use partially conditional maximum likelihood
137 estimation.
138 enforce_stationarity : bool
139 Whether or not to transform the AR parameters
140 to enforce stationarity in the autoregressive
141 component of the model.
142 enforce_invertibility : bool
143 Whether or not to transform the MA parameters
144 to enforce invertibility in the moving average
145 component of the model.
146 hamilton_representation : bool
147 Whether or not to use the Hamilton representation of an ARMA process.
148 trend : str{'n','c','t','ct'} or iterable
149 Parameter controlling the deterministic
150 trend polynomial :math:`A(t)`. See the class
151 parameter documentation for more information.
152 polynomial_ar : ndarray
153 Array containing autoregressive lag polynomial
154 coefficients, ordered from lowest degree to highest.
155 Initialized with ones, unless a coefficient is
156 constrained to be zero (in which case it is zero).
157 polynomial_ma : ndarray
158 Array containing moving average lag polynomial
159 coefficients, ordered from lowest degree to highest.
160 Initialized with ones, unless a coefficient is
161 constrained to be zero (in which case it is zero).
162 polynomial_seasonal_ar : ndarray
163 Array containing seasonal moving average lag
164 polynomial coefficients, ordered from lowest degree
165 to highest. Initialized with ones, unless a
166 coefficient is constrained to be zero (in which
167 case it is zero).
168 polynomial_seasonal_ma : ndarray
169 Array containing seasonal moving average lag
170 polynomial coefficients, ordered from lowest degree
171 to highest. Initialized with ones, unless a
172 coefficient is constrained to be zero (in which
173 case it is zero).
174 polynomial_trend : ndarray
175 Array containing trend polynomial coefficients,
176 ordered from lowest degree to highest. Initialized
177 with ones, unless a coefficient is constrained to be
178 zero (in which case it is zero).
179 k_ar : int
180 Highest autoregressive order in the model, zero-indexed.
181 k_ar_params : int
182 Number of autoregressive parameters to be estimated.
183 k_diff : int
184 Order of integration.
185 k_ma : int
186 Highest moving average order in the model, zero-indexed.
187 k_ma_params : int
188 Number of moving average parameters to be estimated.
189 seasonal_periods : int
190 Number of periods in a season.
191 k_seasonal_ar : int
192 Highest seasonal autoregressive order in the model, zero-indexed.
193 k_seasonal_ar_params : int
194 Number of seasonal autoregressive parameters to be estimated.
195 k_seasonal_diff : int
196 Order of seasonal integration.
197 k_seasonal_ma : int
198 Highest seasonal moving average order in the model, zero-indexed.
199 k_seasonal_ma_params : int
200 Number of seasonal moving average parameters to be estimated.
201 k_trend : int
202 Order of the trend polynomial plus one (i.e. the constant polynomial
203 would have `k_trend=1`).
204 k_exog : int
205 Number of exogenous regressors.
207 Notes
208 -----
209 The SARIMA model is specified :math:`(p, d, q) \times (P, D, Q)_s`.
211 .. math::
213 \phi_p (L) \tilde \phi_P (L^s) \Delta^d \Delta_s^D y_t = A(t) +
214 \theta_q (L) \tilde \theta_Q (L^s) \zeta_t
216 In terms of a univariate structural model, this can be represented as
218 .. math::
220 y_t & = u_t + \eta_t \\
221 \phi_p (L) \tilde \phi_P (L^s) \Delta^d \Delta_s^D u_t & = A(t) +
222 \theta_q (L) \tilde \theta_Q (L^s) \zeta_t
224 where :math:`\eta_t` is only applicable in the case of measurement error
225 (although it is also used in the case of a pure regression model, i.e. if
226 p=q=0).
228 In terms of this model, regression with SARIMA errors can be represented
229 easily as
231 .. math::
233 y_t & = \beta_t x_t + u_t \\
234 \phi_p (L) \tilde \phi_P (L^s) \Delta^d \Delta_s^D u_t & = A(t) +
235 \theta_q (L) \tilde \theta_Q (L^s) \zeta_t
237 this model is the one used when exogenous regressors are provided.
239 Note that the reduced form lag polynomials will be written as:
241 .. math::
243 \Phi (L) \equiv \phi_p (L) \tilde \phi_P (L^s) \\
244 \Theta (L) \equiv \theta_q (L) \tilde \theta_Q (L^s)
246 If `mle_regression` is True, regression coefficients are treated as
247 additional parameters to be estimated via maximum likelihood. Otherwise
248 they are included as part of the state with a diffuse initialization.
249 In this case, however, with approximate diffuse initialization, results
250 can be sensitive to the initial variance.
252 This class allows two different underlying representations of ARMA models
253 as state space models: that of Hamilton and that of Harvey. Both are
254 equivalent in the sense that they are analytical representations of the
255 ARMA model, but the state vectors of each have different meanings. For
256 this reason, maximum likelihood does not result in identical parameter
257 estimates and even the same set of parameters will result in different
258 loglikelihoods.
260 The Harvey representation is convenient because it allows integrating
261 differencing into the state vector to allow using all observations for
262 estimation.
264 In this implementation of differenced models, the Hamilton representation
265 is not able to accommodate differencing in the state vector, so
266 `simple_differencing` (which performs differencing prior to estimation so
267 that the first d + sD observations are lost) must be used.
269 Many other packages use the Hamilton representation, so that tests against
270 Stata and R require using it along with simple differencing (as Stata
271 does).
273 If `filter_concentrated = True` is used, then the scale of the model is
274 concentrated out of the likelihood. A benefit of this is that there the
275 dimension of the parameter vector is reduced so that numerical maximization
276 of the log-likelihood function may be faster and more stable. If this
277 option in a model with measurement error, it is important to note that the
278 estimated measurement error parameter will be relative to the scale, and
279 is named "snr.measurement_error" instead of "var.measurement_error". To
280 compute the variance of the measurement error in this case one would
281 multiply `snr.measurement_error` parameter by the scale.
283 If `simple_differencing = True` is used, then the `endog` and `exog` data
284 are differenced prior to putting the model in state-space form. This has
285 the same effect as if the user differenced the data prior to constructing
286 the model, which has implications for using the results:
288 - Forecasts and predictions will be about the *differenced* data, not about
289 the original data. (while if `simple_differencing = False` is used, then
290 forecasts and predictions will be about the original data).
291 - If the original data has an Int64Index, a new RangeIndex will be created
292 for the differenced data that starts from one, and forecasts and
293 predictions will use this new index.
295 Detailed information about state space models can be found in [1]_. Some
296 specific references are:
298 - Chapter 3.4 describes ARMA and ARIMA models in state space form (using
299 the Harvey representation), and gives references for basic seasonal
300 models and models with a multiplicative form (for example the airline
301 model). It also shows a state space model for a full ARIMA process (this
302 is what is done here if `simple_differencing=False`).
303 - Chapter 3.6 describes estimating regression effects via the Kalman filter
304 (this is performed if `mle_regression` is False), regression with
305 time-varying coefficients, and regression with ARMA errors (recall from
306 above that if regression effects are present, the model estimated by this
307 class is regression with SARIMA errors).
308 - Chapter 8.4 describes the application of an ARMA model to an example
309 dataset. A replication of this section is available in an example
310 IPython notebook in the documentation.
312 References
313 ----------
314 .. [1] Durbin, James, and Siem Jan Koopman. 2012.
315 Time Series Analysis by State Space Methods: Second Edition.
316 Oxford University Press.
317 """
319 def __init__(self, endog, exog=None, order=(1, 0, 0),
320 seasonal_order=(0, 0, 0, 0), trend=None,
321 measurement_error=False, time_varying_regression=False,
322 mle_regression=True, simple_differencing=False,
323 enforce_stationarity=True, enforce_invertibility=True,
324 hamilton_representation=False, concentrate_scale=False,
325 trend_offset=1, use_exact_diffuse=False, dates=None,
326 freq=None, missing='none', **kwargs):
328 self._spec = SARIMAXSpecification(
329 endog, exog=exog, order=order, seasonal_order=seasonal_order,
330 trend=trend, enforce_stationarity=None, enforce_invertibility=None,
331 concentrate_scale=concentrate_scale, dates=dates, freq=freq,
332 missing=missing)
333 self._params = SARIMAXParams(self._spec)
335 # Save given orders
336 order = self._spec.order
337 seasonal_order = self._spec.seasonal_order
338 self.order = order
339 self.seasonal_order = seasonal_order
341 # Model parameters
342 self.seasonal_periods = seasonal_order[3]
343 self.measurement_error = measurement_error
344 self.time_varying_regression = time_varying_regression
345 self.mle_regression = mle_regression
346 self.simple_differencing = simple_differencing
347 self.enforce_stationarity = enforce_stationarity
348 self.enforce_invertibility = enforce_invertibility
349 self.hamilton_representation = hamilton_representation
350 self.concentrate_scale = concentrate_scale
351 self.use_exact_diffuse = use_exact_diffuse
353 # Enforce non-MLE coefficients if time varying coefficients is
354 # specified
355 if self.time_varying_regression and self.mle_regression:
356 raise ValueError('Models with time-varying regression coefficients'
357 ' must integrate the coefficients as part of the'
358 ' state vector, so that `mle_regression` must'
359 ' be set to False.')
361 # Lag polynomials
362 self._params.ar_params = -1
363 self.polynomial_ar = self._params.ar_poly.coef
364 self._polynomial_ar = self.polynomial_ar.copy()
366 self._params.ma_params = 1
367 self.polynomial_ma = self._params.ma_poly.coef
368 self._polynomial_ma = self.polynomial_ma.copy()
370 self._params.seasonal_ar_params = -1
371 self.polynomial_seasonal_ar = self._params.seasonal_ar_poly.coef
372 self._polynomial_seasonal_ar = self.polynomial_seasonal_ar.copy()
374 self._params.seasonal_ma_params = 1
375 self.polynomial_seasonal_ma = self._params.seasonal_ma_poly.coef
376 self._polynomial_seasonal_ma = self.polynomial_seasonal_ma.copy()
378 # Deterministic trend polynomial
379 self.trend = trend
380 self.trend_offset = trend_offset
381 self.polynomial_trend, self.k_trend = prepare_trend_spec(self.trend)
382 self._polynomial_trend = self.polynomial_trend.copy()
383 self._k_trend = self.k_trend
384 # (we internally use _k_trend for mechanics so that the public
385 # attribute can be overridden by subclasses)
387 # Model orders
388 # Note: k_ar, k_ma, k_seasonal_ar, k_seasonal_ma do not include the
389 # constant term, so they may be zero.
390 # Note: for a typical ARMA(p,q) model, p = k_ar_params = k_ar - 1 and
391 # q = k_ma_params = k_ma - 1, although this may not be true for models
392 # with arbitrary log polynomials.
393 self.k_ar = self._spec.max_ar_order
394 self.k_ar_params = self._spec.k_ar_params
395 self.k_diff = int(order[1])
396 self.k_ma = self._spec.max_ma_order
397 self.k_ma_params = self._spec.k_ma_params
399 self.k_seasonal_ar = (self._spec.max_seasonal_ar_order *
400 self._spec.seasonal_periods)
401 self.k_seasonal_ar_params = self._spec.k_seasonal_ar_params
402 self.k_seasonal_diff = int(seasonal_order[1])
403 self.k_seasonal_ma = (self._spec.max_seasonal_ma_order *
404 self._spec.seasonal_periods)
405 self.k_seasonal_ma_params = self._spec.k_seasonal_ma_params
407 # Make internal copies of the differencing orders because if we use
408 # simple differencing, then we will need to internally use zeros after
409 # the simple differencing has been performed
410 self._k_diff = self.k_diff
411 self._k_seasonal_diff = self.k_seasonal_diff
413 # We can only use the Hamilton representation if differencing is not
414 # performed as a part of the state space
415 if (self.hamilton_representation and not (self.simple_differencing or
416 self._k_diff == self._k_seasonal_diff == 0)):
417 raise ValueError('The Hamilton representation is only available'
418 ' for models in which there is no differencing'
419 ' integrated into the state vector. Set'
420 ' `simple_differencing` to True or set'
421 ' `hamilton_representation` to False')
423 # Model order
424 # (this is used internally in a number of locations)
425 self._k_order = max(self.k_ar + self.k_seasonal_ar,
426 self.k_ma + self.k_seasonal_ma + 1)
427 if self._k_order == 1 and self.k_ar + self.k_seasonal_ar == 0:
428 # Handle time-varying regression
429 if self.time_varying_regression:
430 self._k_order = 0
432 # Exogenous data
433 (self._k_exog, exog) = prepare_exog(exog)
434 # (we internally use _k_exog for mechanics so that the public attribute
435 # can be overridden by subclasses)
436 self.k_exog = self._k_exog
438 # Redefine mle_regression to be true only if it was previously set to
439 # true and there are exogenous regressors
440 self.mle_regression = (
441 self.mle_regression and exog is not None and self._k_exog > 0
442 )
443 # State regression is regression with coefficients estimated within
444 # the state vector
445 self.state_regression = (
446 not self.mle_regression and exog is not None and self._k_exog > 0
447 )
448 # If all we have is a regression (so k_ar = k_ma = 0), then put the
449 # error term as measurement error
450 if self.state_regression and self._k_order == 0:
451 self.measurement_error = True
453 # Number of states
454 k_states = self._k_order
455 if not self.simple_differencing:
456 k_states += (self.seasonal_periods * self._k_seasonal_diff +
457 self._k_diff)
458 if self.state_regression:
459 k_states += self._k_exog
461 # Number of positive definite elements of the state covariance matrix
462 k_posdef = int(self._k_order > 0)
463 # Only have an error component to the states if k_posdef > 0
464 self.state_error = k_posdef > 0
465 if self.state_regression and self.time_varying_regression:
466 k_posdef += self._k_exog
468 # Diffuse initialization can be more sensistive to the variance value
469 # in the case of state regression, so set a higher than usual default
470 # variance
471 if self.state_regression:
472 kwargs.setdefault('initial_variance', 1e10)
474 # Handle non-default loglikelihood burn
475 self._loglikelihood_burn = kwargs.get('loglikelihood_burn', None)
477 # Number of parameters
478 self.k_params = (
479 self.k_ar_params + self.k_ma_params +
480 self.k_seasonal_ar_params + self.k_seasonal_ma_params +
481 self._k_trend +
482 self.measurement_error +
483 int(not self.concentrate_scale)
484 )
485 if self.mle_regression:
486 self.k_params += self._k_exog
488 # We need to have an array or pandas at this point
489 self.orig_endog = endog
490 self.orig_exog = exog
491 if not _is_using_pandas(endog, None):
492 endog = np.asanyarray(endog)
494 # Update the differencing dimensions if simple differencing is applied
495 self.orig_k_diff = self._k_diff
496 self.orig_k_seasonal_diff = self._k_seasonal_diff
497 if (self.simple_differencing and
498 (self._k_diff > 0 or self._k_seasonal_diff > 0)):
499 self._k_diff = 0
500 self._k_seasonal_diff = 0
502 # Internally used in several locations
503 self._k_states_diff = (
504 self._k_diff + self.seasonal_periods * self._k_seasonal_diff
505 )
507 # Set some model variables now so they will be available for the
508 # initialize() method, below
509 self.nobs = len(endog)
510 self.k_states = k_states
511 self.k_posdef = k_posdef
513 # Initialize the statespace
514 super(SARIMAX, self).__init__(
515 endog, exog=exog, k_states=k_states, k_posdef=k_posdef, **kwargs
516 )
518 # Set the filter to concentrate out the scale if requested
519 if self.concentrate_scale:
520 self.ssm.filter_concentrated = True
522 # Set as time-varying model if we have time-trend or exog
523 if self._k_exog > 0 or len(self.polynomial_trend) > 1:
524 self.ssm._time_invariant = False
526 # Initialize the fixed components of the statespace model
527 self.ssm['design'] = self.initial_design
528 self.ssm['state_intercept'] = self.initial_state_intercept
529 self.ssm['transition'] = self.initial_transition
530 self.ssm['selection'] = self.initial_selection
531 if self.concentrate_scale:
532 self.ssm['state_cov', 0, 0] = 1.
534 # update _init_keys attached by super
535 self._init_keys += ['order', 'seasonal_order', 'trend',
536 'measurement_error', 'time_varying_regression',
537 'mle_regression', 'simple_differencing',
538 'enforce_stationarity', 'enforce_invertibility',
539 'hamilton_representation', 'concentrate_scale',
540 'trend_offset'] + list(kwargs.keys())
541 # TODO: I think the kwargs or not attached, need to recover from ???
543 # Initialize the state
544 if self.ssm.initialization is None:
545 self.initialize_default()
547 def prepare_data(self):
548 endog, exog = super(SARIMAX, self).prepare_data()
550 # Perform simple differencing if requested
551 if (self.simple_differencing and
552 (self.orig_k_diff > 0 or self.orig_k_seasonal_diff > 0)):
553 # Save the original length
554 orig_length = endog.shape[0]
555 # Perform simple differencing
556 endog = diff(endog.copy(), self.orig_k_diff,
557 self.orig_k_seasonal_diff, self.seasonal_periods)
558 if exog is not None:
559 exog = diff(exog.copy(), self.orig_k_diff,
560 self.orig_k_seasonal_diff, self.seasonal_periods)
562 # Reset the ModelData datasets and cache
563 self.data.endog, self.data.exog = (
564 self.data._convert_endog_exog(endog, exog))
566 # Reset indexes, if provided
567 new_length = self.data.endog.shape[0]
568 if self.data.row_labels is not None:
569 self.data._cache['row_labels'] = (
570 self.data.row_labels[orig_length - new_length:])
571 if self._index is not None:
572 if self._index_int64:
573 self._index = pd.RangeIndex(start=1, stop=new_length + 1)
574 elif self._index_generated:
575 self._index = self._index[:-(orig_length - new_length)]
576 else:
577 self._index = self._index[orig_length - new_length:]
579 # Reset the nobs
580 self.nobs = endog.shape[0]
582 # Cache the arrays for calculating the intercept from the trend
583 # components
584 self._trend_data = prepare_trend_data(
585 self.polynomial_trend, self._k_trend, self.nobs, self.trend_offset)
587 return endog, exog
589 def initialize(self):
590 """
591 Initialize the SARIMAX model.
593 Notes
594 -----
595 These initialization steps must occur following the parent class
596 __init__ function calls.
597 """
598 super(SARIMAX, self).initialize()
600 # Cache the indexes of included polynomial orders (for update below)
601 # (but we do not want the index of the constant term, so exclude the
602 # first index)
603 self._polynomial_ar_idx = np.nonzero(self.polynomial_ar)[0][1:]
604 self._polynomial_ma_idx = np.nonzero(self.polynomial_ma)[0][1:]
605 self._polynomial_seasonal_ar_idx = np.nonzero(
606 self.polynomial_seasonal_ar
607 )[0][1:]
608 self._polynomial_seasonal_ma_idx = np.nonzero(
609 self.polynomial_seasonal_ma
610 )[0][1:]
612 # Save the indices corresponding to the reduced form lag polynomial
613 # parameters in the transition and selection matrices so that they
614 # do not have to be recalculated for each update()
615 start_row = self._k_states_diff
616 end_row = start_row + self.k_ar + self.k_seasonal_ar
617 col = self._k_states_diff
618 if not self.hamilton_representation:
619 self.transition_ar_params_idx = (
620 np.s_['transition', start_row:end_row, col]
621 )
622 else:
623 self.transition_ar_params_idx = (
624 np.s_['transition', col, start_row:end_row]
625 )
627 start_row += 1
628 end_row = start_row + self.k_ma + self.k_seasonal_ma
629 col = 0
630 if not self.hamilton_representation:
631 self.selection_ma_params_idx = (
632 np.s_['selection', start_row:end_row, col]
633 )
634 else:
635 self.design_ma_params_idx = (
636 np.s_['design', col, start_row:end_row]
637 )
639 # Cache indices for exog variances in the state covariance matrix
640 if self.state_regression and self.time_varying_regression:
641 idx = np.diag_indices(self.k_posdef)
642 self._exog_variance_idx = ('state_cov', idx[0][-self._k_exog:],
643 idx[1][-self._k_exog:])
645 def initialize_default(self, approximate_diffuse_variance=None):
646 """Initialize default"""
647 if approximate_diffuse_variance is None:
648 approximate_diffuse_variance = self.ssm.initial_variance
649 if self.use_exact_diffuse:
650 diffuse_type = 'diffuse'
651 else:
652 diffuse_type = 'approximate_diffuse'
654 # Set the loglikelihood burn parameter, if not given in constructor
655 if self._loglikelihood_burn is None:
656 k_diffuse_states = self.k_states
657 if self.enforce_stationarity:
658 k_diffuse_states -= self._k_order
659 self.loglikelihood_burn = k_diffuse_states
661 init = Initialization(
662 self.k_states,
663 approximate_diffuse_variance=approximate_diffuse_variance)
665 if self.enforce_stationarity:
666 # Differencing operators are at the beginning
667 init.set((0, self._k_states_diff), diffuse_type)
668 # Stationary component in the middle
669 init.set((self._k_states_diff,
670 self._k_states_diff + self._k_order),
671 'stationary')
672 # Regression components at the end
673 init.set((self._k_states_diff + self._k_order,
674 self._k_states_diff + self._k_order + self._k_exog),
675 diffuse_type)
676 # If we're not enforcing a stationarity, then we cannot initialize a
677 # stationary component
678 else:
679 init.set(None, diffuse_type)
681 self.ssm.initialization = init
683 @property
684 def initial_design(self):
685 """Initial design matrix"""
686 # Basic design matrix
687 design = np.r_[
688 [1] * self._k_diff,
689 ([0] * (self.seasonal_periods - 1) + [1]) * self._k_seasonal_diff,
690 [1] * self.state_error, [0] * (self._k_order - 1)
691 ]
693 if len(design) == 0:
694 design = np.r_[0]
696 # If we have exogenous regressors included as part of the state vector
697 # then the exogenous data is incorporated as a time-varying component
698 # of the design matrix
699 if self.state_regression:
700 if self._k_order > 0:
701 design = np.c_[
702 np.reshape(
703 np.repeat(design, self.nobs),
704 (design.shape[0], self.nobs)
705 ).T,
706 self.exog
707 ].T[None, :, :]
708 else:
709 design = self.exog.T[None, :, :]
710 return design
712 @property
713 def initial_state_intercept(self):
714 """Initial state intercept vector"""
715 # TODO make this self._k_trend > 1 and adjust the update to take
716 # into account that if the trend is a constant, it is not time-varying
717 if self._k_trend > 0:
718 state_intercept = np.zeros((self.k_states, self.nobs))
719 else:
720 state_intercept = np.zeros((self.k_states,))
721 return state_intercept
723 @property
724 def initial_transition(self):
725 """Initial transition matrix"""
726 transition = np.zeros((self.k_states, self.k_states))
728 # Exogenous regressors component
729 if self.state_regression:
730 start = -self._k_exog
731 # T_\beta
732 transition[start:, start:] = np.eye(self._k_exog)
734 # Autoregressive component
735 start = -(self._k_exog + self._k_order)
736 end = -self._k_exog if self._k_exog > 0 else None
737 else:
738 # Autoregressive component
739 start = -self._k_order
740 end = None
742 # T_c
743 if self._k_order > 0:
744 transition[start:end, start:end] = companion_matrix(self._k_order)
745 if self.hamilton_representation:
746 transition[start:end, start:end] = np.transpose(
747 companion_matrix(self._k_order)
748 )
750 # Seasonal differencing component
751 # T^*
752 if self._k_seasonal_diff > 0:
753 seasonal_companion = companion_matrix(self.seasonal_periods).T
754 seasonal_companion[0, -1] = 1
755 for d in range(self._k_seasonal_diff):
756 start = self._k_diff + d * self.seasonal_periods
757 end = self._k_diff + (d + 1) * self.seasonal_periods
759 # T_c^*
760 transition[start:end, start:end] = seasonal_companion
762 # i
763 for i in range(d + 1, self._k_seasonal_diff):
764 transition[start, end + self.seasonal_periods - 1] = 1
766 # \iota
767 transition[start, self._k_states_diff] = 1
769 # Differencing component
770 if self._k_diff > 0:
771 idx = np.triu_indices(self._k_diff)
772 # T^**
773 transition[idx] = 1
774 # [0 1]
775 if self.seasonal_periods > 0:
776 start = self._k_diff
777 end = self._k_states_diff
778 transition[:self._k_diff, start:end] = (
779 ([0] * (self.seasonal_periods - 1) + [1]) *
780 self._k_seasonal_diff)
781 # [1 0]
782 column = self._k_states_diff
783 transition[:self._k_diff, column] = 1
785 return transition
787 @property
788 def initial_selection(self):
789 """Initial selection matrix"""
790 if not (self.state_regression and self.time_varying_regression):
791 if self.k_posdef > 0:
792 selection = np.r_[
793 [0] * (self._k_states_diff),
794 [1] * (self._k_order > 0), [0] * (self._k_order - 1),
795 [0] * ((1 - self.mle_regression) * self._k_exog)
796 ][:, None]
798 if len(selection) == 0:
799 selection = np.zeros((self.k_states, self.k_posdef))
800 else:
801 selection = np.zeros((self.k_states, 0))
802 else:
803 selection = np.zeros((self.k_states, self.k_posdef))
804 # Typical state variance
805 if self._k_order > 0:
806 selection[0, 0] = 1
807 # Time-varying regression coefficient variances
808 for i in range(self._k_exog, 0, -1):
809 selection[-i, -i] = 1
810 return selection
812 def clone(self, endog, exog=None, **kwargs):
813 return self._clone_from_init_kwds(endog, exog=exog, **kwargs)
815 @property
816 def _res_classes(self):
817 return {'fit': (SARIMAXResults, SARIMAXResultsWrapper)}
819 @staticmethod
820 def _conditional_sum_squares(endog, k_ar, polynomial_ar, k_ma,
821 polynomial_ma, k_trend=0, trend_data=None,
822 warning_description=None):
823 k = 2 * k_ma
824 r = max(k + k_ma, k_ar)
826 k_params_ar = 0 if k_ar == 0 else len(polynomial_ar.nonzero()[0]) - 1
827 k_params_ma = 0 if k_ma == 0 else len(polynomial_ma.nonzero()[0]) - 1
829 residuals = None
830 if k_ar + k_ma + k_trend > 0:
831 try:
832 # If we have MA terms, get residuals from an AR(k) model to use
833 # as data for conditional sum of squares estimates of the MA
834 # parameters
835 if k_ma > 0:
836 Y = endog[k:]
837 X = lagmat(endog, k, trim='both')
838 params_ar = np.linalg.pinv(X).dot(Y)
839 residuals = Y - np.dot(X, params_ar)
841 # Run an ARMA(p,q) model using the just computed residuals as
842 # data
843 Y = endog[r:]
845 X = np.empty((Y.shape[0], 0))
846 if k_trend > 0:
847 if trend_data is None:
848 raise ValueError('Trend data must be provided if'
849 ' `k_trend` > 0.')
850 X = np.c_[X, trend_data[:(-r if r > 0 else None), :]]
851 if k_ar > 0:
852 cols = polynomial_ar.nonzero()[0][1:] - 1
853 X = np.c_[X, lagmat(endog, k_ar)[r:, cols]]
854 if k_ma > 0:
855 cols = polynomial_ma.nonzero()[0][1:] - 1
856 X = np.c_[X, lagmat(residuals, k_ma)[r-k:, cols]]
858 # Get the array of [ar_params, ma_params]
859 params = np.linalg.pinv(X).dot(Y)
860 residuals = Y - np.dot(X, params)
861 except ValueError:
862 if warning_description is not None:
863 warning_description = ' for %s' % warning_description
864 else:
865 warning_description = ''
866 warn('Too few observations to estimate starting parameters%s.'
867 ' All parameters except for variances will be set to'
868 ' zeros.' % warning_description)
869 # Typically this will be raised if there are not enough
870 # observations for the `lagmat` calls.
871 params = np.zeros(k_trend + k_ar + k_ma, dtype=endog.dtype)
872 if len(endog) == 0:
873 # This case usually happens when there are not even enough
874 # observations for a complete set of differencing
875 # operations (no hope of fitting, just set starting
876 # variance to 1)
877 residuals = np.ones(k_params_ma * 2 + 1, dtype=endog.dtype)
878 else:
879 residuals = np.r_[
880 np.zeros(k_params_ma * 2, dtype=endog.dtype),
881 endog - np.mean(endog)]
883 # Default output
884 params_trend = []
885 params_ar = []
886 params_ma = []
887 params_variance = []
889 # Get the params
890 offset = 0
891 if k_trend > 0:
892 params_trend = params[offset:k_trend + offset]
893 offset += k_trend
894 if k_ar > 0:
895 params_ar = params[offset:k_params_ar + offset]
896 offset += k_params_ar
897 if k_ma > 0:
898 params_ma = params[offset:k_params_ma + offset]
899 offset += k_params_ma
900 if residuals is not None:
901 params_variance = (residuals[k_params_ma:]**2).mean()
903 return (params_trend, params_ar, params_ma,
904 params_variance)
906 @property
907 def start_params(self):
908 """
909 Starting parameters for maximum likelihood estimation
910 """
912 # Perform differencing if necessary (i.e. if simple differencing is
913 # false so that the state-space model will use the entire dataset)
914 trend_data = self._trend_data
915 if not self.simple_differencing and (
916 self._k_diff > 0 or self._k_seasonal_diff > 0):
917 endog = diff(self.endog, self._k_diff,
918 self._k_seasonal_diff, self.seasonal_periods)
919 if self.exog is not None:
920 exog = diff(self.exog, self._k_diff,
921 self._k_seasonal_diff, self.seasonal_periods)
922 else:
923 exog = None
924 trend_data = trend_data[:endog.shape[0], :]
925 else:
926 endog = self.endog.copy()
927 exog = self.exog.copy() if self.exog is not None else None
928 endog = endog.squeeze()
930 # Although the Kalman filter can deal with missing values in endog,
931 # conditional sum of squares cannot
932 if np.any(np.isnan(endog)):
933 mask = ~np.isnan(endog).squeeze()
934 endog = endog[mask]
935 if exog is not None:
936 exog = exog[mask]
937 if trend_data is not None:
938 trend_data = trend_data[mask]
940 # Regression effects via OLS
941 params_exog = []
942 if self._k_exog > 0:
943 params_exog = np.linalg.pinv(exog).dot(endog)
944 endog = endog - np.dot(exog, params_exog)
945 if self.state_regression:
946 params_exog = []
948 # Non-seasonal ARMA component and trend
949 (params_trend, params_ar, params_ma,
950 params_variance) = self._conditional_sum_squares(
951 endog, self.k_ar, self.polynomial_ar, self.k_ma,
952 self.polynomial_ma, self._k_trend, trend_data,
953 warning_description='ARMA and trend')
955 # If we have estimated non-stationary start parameters but enforce
956 # stationarity is on, start with 0 parameters and warn
957 invalid_ar = (
958 self.k_ar > 0 and
959 self.enforce_stationarity and
960 not is_invertible(np.r_[1, -params_ar])
961 )
962 if invalid_ar:
963 warn('Non-stationary starting autoregressive parameters'
964 ' found. Using zeros as starting parameters.')
965 params_ar *= 0
967 # If we have estimated non-invertible start parameters but enforce
968 # invertibility is on, raise an error
969 invalid_ma = (
970 self.k_ma > 0 and
971 self.enforce_invertibility and
972 not is_invertible(np.r_[1, params_ma])
973 )
974 if invalid_ma:
975 warn('Non-invertible starting MA parameters found.'
976 ' Using zeros as starting parameters.')
977 params_ma *= 0
979 # Seasonal Parameters
980 _, params_seasonal_ar, params_seasonal_ma, params_seasonal_variance = (
981 self._conditional_sum_squares(
982 endog, self.k_seasonal_ar, self.polynomial_seasonal_ar,
983 self.k_seasonal_ma, self.polynomial_seasonal_ma,
984 warning_description='seasonal ARMA'))
986 # If we have estimated non-stationary start parameters but enforce
987 # stationarity is on, warn and set start params to 0
988 invalid_seasonal_ar = (
989 self.k_seasonal_ar > 0 and
990 self.enforce_stationarity and
991 not is_invertible(np.r_[1, -params_seasonal_ar])
992 )
993 if invalid_seasonal_ar:
994 warn('Non-stationary starting seasonal autoregressive'
995 ' Using zeros as starting parameters.')
996 params_seasonal_ar *= 0
998 # If we have estimated non-invertible start parameters but enforce
999 # invertibility is on, raise an error
1000 invalid_seasonal_ma = (
1001 self.k_seasonal_ma > 0 and
1002 self.enforce_invertibility and
1003 not is_invertible(np.r_[1, params_seasonal_ma])
1004 )
1005 if invalid_seasonal_ma:
1006 warn('Non-invertible starting seasonal moving average'
1007 ' Using zeros as starting parameters.')
1008 params_seasonal_ma *= 0
1010 # Variances
1011 params_exog_variance = []
1012 if self.state_regression and self.time_varying_regression:
1013 # TODO how to set the initial variance parameters?
1014 params_exog_variance = [1] * self._k_exog
1015 if (self.state_error and type(params_variance) == list and
1016 len(params_variance) == 0):
1017 if not (type(params_seasonal_variance) == list and
1018 len(params_seasonal_variance) == 0):
1019 params_variance = params_seasonal_variance
1020 elif self._k_exog > 0:
1021 params_variance = np.inner(endog, endog)
1022 else:
1023 params_variance = np.inner(endog, endog) / self.nobs
1024 params_measurement_variance = 1 if self.measurement_error else []
1026 # We want to bound the starting variance away from zero
1027 params_variance = np.atleast_1d(max(np.array(params_variance), 1e-10))
1029 # Remove state variance as parameter if scale is concentrated out
1030 if self.concentrate_scale:
1031 params_variance = []
1033 # Combine all parameters
1034 return np.r_[
1035 params_trend,
1036 params_exog,
1037 params_ar,
1038 params_ma,
1039 params_seasonal_ar,
1040 params_seasonal_ma,
1041 params_exog_variance,
1042 params_measurement_variance,
1043 params_variance
1044 ]
1046 @property
1047 def endog_names(self, latex=False):
1048 """Names of endogenous variables"""
1049 diff = ''
1050 if self.k_diff > 0:
1051 if self.k_diff == 1:
1052 diff = r'\Delta' if latex else 'D'
1053 else:
1054 diff = (r'\Delta^%d' if latex else 'D%d') % self.k_diff
1056 seasonal_diff = ''
1057 if self.k_seasonal_diff > 0:
1058 if self.k_seasonal_diff == 1:
1059 seasonal_diff = ((r'\Delta_%d' if latex else 'DS%d') %
1060 (self.seasonal_periods))
1061 else:
1062 seasonal_diff = ((r'\Delta_%d^%d' if latex else 'D%dS%d') %
1063 (self.k_seasonal_diff, self.seasonal_periods))
1064 endog_diff = self.simple_differencing
1065 if endog_diff and self.k_diff > 0 and self.k_seasonal_diff > 0:
1066 return (('%s%s %s' if latex else '%s.%s.%s') %
1067 (diff, seasonal_diff, self.data.ynames))
1068 elif endog_diff and self.k_diff > 0:
1069 return (('%s %s' if latex else '%s.%s') %
1070 (diff, self.data.ynames))
1071 elif endog_diff and self.k_seasonal_diff > 0:
1072 return (('%s %s' if latex else '%s.%s') %
1073 (seasonal_diff, self.data.ynames))
1074 else:
1075 return self.data.ynames
1077 params_complete = [
1078 'trend', 'exog', 'ar', 'ma', 'seasonal_ar', 'seasonal_ma',
1079 'exog_variance', 'measurement_variance', 'variance'
1080 ]
1082 @property
1083 def param_terms(self):
1084 """
1085 List of parameters actually included in the model, in sorted order.
1087 TODO Make this an OrderedDict with slice or indices as the values.
1088 """
1089 model_orders = self.model_orders
1090 # Get basic list from model orders
1091 params = [
1092 order for order in self.params_complete
1093 if model_orders[order] > 0
1094 ]
1095 # k_exog may be positive without associated parameters if it is in the
1096 # state vector
1097 if 'exog' in params and not self.mle_regression:
1098 params.remove('exog')
1100 return params
1102 @property
1103 def param_names(self):
1104 """
1105 List of human readable parameter names (for parameters actually
1106 included in the model).
1107 """
1108 params_sort_order = self.param_terms
1109 model_names = self.model_names
1110 return [
1111 name for param in params_sort_order for name in model_names[param]
1112 ]
1114 @property
1115 def state_names(self):
1116 # TODO: we may be able to revisit these states to get somewhat more
1117 # informative names, but ultimately probably not much better.
1118 # TODO: alternatively, we may be able to get better for certain models,
1119 # like pure AR models.
1120 k_ar_states = self._k_order
1121 if not self.simple_differencing:
1122 k_ar_states += (self.seasonal_periods * self._k_seasonal_diff +
1123 self._k_diff)
1124 names = ['state.%d' % i for i in range(k_ar_states)]
1126 if self._k_exog > 0 and self.state_regression:
1127 names += ['beta.%s' % self.exog_names[i]
1128 for i in range(self._k_exog)]
1130 return names
1132 @property
1133 def model_orders(self):
1134 """
1135 The orders of each of the polynomials in the model.
1136 """
1137 return {
1138 'trend': self._k_trend,
1139 'exog': self._k_exog,
1140 'ar': self.k_ar,
1141 'ma': self.k_ma,
1142 'seasonal_ar': self.k_seasonal_ar,
1143 'seasonal_ma': self.k_seasonal_ma,
1144 'reduced_ar': self.k_ar + self.k_seasonal_ar,
1145 'reduced_ma': self.k_ma + self.k_seasonal_ma,
1146 'exog_variance': self._k_exog if (
1147 self.state_regression and self.time_varying_regression) else 0,
1148 'measurement_variance': int(self.measurement_error),
1149 'variance': int(self.state_error and not self.concentrate_scale),
1150 }
1152 @property
1153 def model_names(self):
1154 """
1155 The plain text names of all possible model parameters.
1156 """
1157 return self._get_model_names(latex=False)
1159 @property
1160 def model_latex_names(self):
1161 """
1162 The latex names of all possible model parameters.
1163 """
1164 return self._get_model_names(latex=True)
1166 def _get_model_names(self, latex=False):
1167 names = {
1168 'trend': None,
1169 'exog': None,
1170 'ar': None,
1171 'ma': None,
1172 'seasonal_ar': None,
1173 'seasonal_ma': None,
1174 'reduced_ar': None,
1175 'reduced_ma': None,
1176 'exog_variance': None,
1177 'measurement_variance': None,
1178 'variance': None,
1179 }
1181 # Trend
1182 if self._k_trend > 0:
1183 trend_template = 't_%d' if latex else 'trend.%d'
1184 names['trend'] = []
1185 for i in self.polynomial_trend.nonzero()[0]:
1186 if i == 0:
1187 names['trend'].append('intercept')
1188 elif i == 1:
1189 names['trend'].append('drift')
1190 else:
1191 names['trend'].append(trend_template % i)
1193 # Exogenous coefficients
1194 if self._k_exog > 0:
1195 names['exog'] = self.exog_names
1197 # Autoregressive
1198 if self.k_ar > 0:
1199 ar_template = '$\\phi_%d$' if latex else 'ar.L%d'
1200 names['ar'] = []
1201 for i in self.polynomial_ar.nonzero()[0][1:]:
1202 names['ar'].append(ar_template % i)
1204 # Moving Average
1205 if self.k_ma > 0:
1206 ma_template = '$\\theta_%d$' if latex else 'ma.L%d'
1207 names['ma'] = []
1208 for i in self.polynomial_ma.nonzero()[0][1:]:
1209 names['ma'].append(ma_template % i)
1211 # Seasonal Autoregressive
1212 if self.k_seasonal_ar > 0:
1213 seasonal_ar_template = (
1214 '$\\tilde \\phi_%d$' if latex else 'ar.S.L%d'
1215 )
1216 names['seasonal_ar'] = []
1217 for i in self.polynomial_seasonal_ar.nonzero()[0][1:]:
1218 names['seasonal_ar'].append(seasonal_ar_template % i)
1220 # Seasonal Moving Average
1221 if self.k_seasonal_ma > 0:
1222 seasonal_ma_template = (
1223 '$\\tilde \\theta_%d$' if latex else 'ma.S.L%d'
1224 )
1225 names['seasonal_ma'] = []
1226 for i in self.polynomial_seasonal_ma.nonzero()[0][1:]:
1227 names['seasonal_ma'].append(seasonal_ma_template % i)
1229 # Reduced Form Autoregressive
1230 if self.k_ar > 0 or self.k_seasonal_ar > 0:
1231 reduced_polynomial_ar = reduced_polynomial_ar = -np.polymul(
1232 self.polynomial_ar, self.polynomial_seasonal_ar
1233 )
1234 ar_template = '$\\Phi_%d$' if latex else 'ar.R.L%d'
1235 names['reduced_ar'] = []
1236 for i in reduced_polynomial_ar.nonzero()[0][1:]:
1237 names['reduced_ar'].append(ar_template % i)
1239 # Reduced Form Moving Average
1240 if self.k_ma > 0 or self.k_seasonal_ma > 0:
1241 reduced_polynomial_ma = np.polymul(
1242 self.polynomial_ma, self.polynomial_seasonal_ma
1243 )
1244 ma_template = '$\\Theta_%d$' if latex else 'ma.R.L%d'
1245 names['reduced_ma'] = []
1246 for i in reduced_polynomial_ma.nonzero()[0][1:]:
1247 names['reduced_ma'].append(ma_template % i)
1249 # Exogenous variances
1250 if self.state_regression and self.time_varying_regression:
1251 if not self.concentrate_scale:
1252 exog_var_template = ('$\\sigma_\\text{%s}^2$' if latex
1253 else 'var.%s')
1254 else:
1255 exog_var_template = (
1256 '$\\sigma_\\text{%s}^2 / \\sigma_\\zeta^2$' if latex
1257 else 'snr.%s')
1258 names['exog_variance'] = [
1259 exog_var_template % exog_name for exog_name in self.exog_names
1260 ]
1262 # Measurement error variance
1263 if self.measurement_error:
1264 if not self.concentrate_scale:
1265 meas_var_tpl = (
1266 '$\\sigma_\\eta^2$' if latex else 'var.measurement_error')
1267 else:
1268 meas_var_tpl = (
1269 '$\\sigma_\\eta^2 / \\sigma_\\zeta^2$' if latex
1270 else 'snr.measurement_error')
1271 names['measurement_variance'] = [meas_var_tpl]
1273 # State variance
1274 if self.state_error and not self.concentrate_scale:
1275 var_tpl = '$\\sigma_\\zeta^2$' if latex else 'sigma2'
1276 names['variance'] = [var_tpl]
1278 return names
1280 def transform_params(self, unconstrained):
1281 """
1282 Transform unconstrained parameters used by the optimizer to constrained
1283 parameters used in likelihood evaluation.
1285 Used primarily to enforce stationarity of the autoregressive lag
1286 polynomial, invertibility of the moving average lag polynomial, and
1287 positive variance parameters.
1289 Parameters
1290 ----------
1291 unconstrained : array_like
1292 Unconstrained parameters used by the optimizer.
1294 Returns
1295 -------
1296 constrained : array_like
1297 Constrained parameters used in likelihood evaluation.
1299 Notes
1300 -----
1301 If the lag polynomial has non-consecutive powers (so that the
1302 coefficient is zero on some element of the polynomial), then the
1303 constraint function is not onto the entire space of invertible
1304 polynomials, although it only excludes a very small portion very close
1305 to the invertibility boundary.
1306 """
1307 unconstrained = np.array(unconstrained, ndmin=1)
1308 constrained = np.zeros(unconstrained.shape, unconstrained.dtype)
1310 start = end = 0
1312 # Retain the trend parameters
1313 if self._k_trend > 0:
1314 end += self._k_trend
1315 constrained[start:end] = unconstrained[start:end]
1316 start += self._k_trend
1318 # Retain any MLE regression coefficients
1319 if self.mle_regression:
1320 end += self._k_exog
1321 constrained[start:end] = unconstrained[start:end]
1322 start += self._k_exog
1324 # Transform the AR parameters (phi) to be stationary
1325 if self.k_ar_params > 0:
1326 end += self.k_ar_params
1327 if self.enforce_stationarity:
1328 constrained[start:end] = (
1329 constrain_stationary_univariate(unconstrained[start:end])
1330 )
1331 else:
1332 constrained[start:end] = unconstrained[start:end]
1333 start += self.k_ar_params
1335 # Transform the MA parameters (theta) to be invertible
1336 if self.k_ma_params > 0:
1337 end += self.k_ma_params
1338 if self.enforce_invertibility:
1339 constrained[start:end] = (
1340 -constrain_stationary_univariate(unconstrained[start:end])
1341 )
1342 else:
1343 constrained[start:end] = unconstrained[start:end]
1344 start += self.k_ma_params
1346 # Transform the seasonal AR parameters (\tilde phi) to be stationary
1347 if self.k_seasonal_ar > 0:
1348 end += self.k_seasonal_ar_params
1349 if self.enforce_stationarity:
1350 constrained[start:end] = (
1351 constrain_stationary_univariate(unconstrained[start:end])
1352 )
1353 else:
1354 constrained[start:end] = unconstrained[start:end]
1355 start += self.k_seasonal_ar_params
1357 # Transform the seasonal MA parameters (\tilde theta) to be invertible
1358 if self.k_seasonal_ma_params > 0:
1359 end += self.k_seasonal_ma_params
1360 if self.enforce_invertibility:
1361 constrained[start:end] = (
1362 -constrain_stationary_univariate(unconstrained[start:end])
1363 )
1364 else:
1365 constrained[start:end] = unconstrained[start:end]
1366 start += self.k_seasonal_ma_params
1368 # Transform the standard deviation parameters to be positive
1369 if self.state_regression and self.time_varying_regression:
1370 end += self._k_exog
1371 constrained[start:end] = unconstrained[start:end]**2
1372 start += self._k_exog
1373 if self.measurement_error:
1374 constrained[start] = unconstrained[start]**2
1375 start += 1
1376 end += 1
1377 if self.state_error and not self.concentrate_scale:
1378 constrained[start] = unconstrained[start]**2
1379 # start += 1
1380 # end += 1
1382 return constrained
1384 def untransform_params(self, constrained):
1385 """
1386 Transform constrained parameters used in likelihood evaluation
1387 to unconstrained parameters used by the optimizer
1389 Used primarily to reverse enforcement of stationarity of the
1390 autoregressive lag polynomial and invertibility of the moving average
1391 lag polynomial.
1393 Parameters
1394 ----------
1395 constrained : array_like
1396 Constrained parameters used in likelihood evaluation.
1398 Returns
1399 -------
1400 constrained : array_like
1401 Unconstrained parameters used by the optimizer.
1403 Notes
1404 -----
1405 If the lag polynomial has non-consecutive powers (so that the
1406 coefficient is zero on some element of the polynomial), then the
1407 constraint function is not onto the entire space of invertible
1408 polynomials, although it only excludes a very small portion very close
1409 to the invertibility boundary.
1410 """
1411 constrained = np.array(constrained, ndmin=1)
1412 unconstrained = np.zeros(constrained.shape, constrained.dtype)
1414 start = end = 0
1416 # Retain the trend parameters
1417 if self._k_trend > 0:
1418 end += self._k_trend
1419 unconstrained[start:end] = constrained[start:end]
1420 start += self._k_trend
1422 # Retain any MLE regression coefficients
1423 if self.mle_regression:
1424 end += self._k_exog
1425 unconstrained[start:end] = constrained[start:end]
1426 start += self._k_exog
1428 # Transform the AR parameters (phi) to be stationary
1429 if self.k_ar_params > 0:
1430 end += self.k_ar_params
1431 if self.enforce_stationarity:
1432 unconstrained[start:end] = (
1433 unconstrain_stationary_univariate(constrained[start:end])
1434 )
1435 else:
1436 unconstrained[start:end] = constrained[start:end]
1437 start += self.k_ar_params
1439 # Transform the MA parameters (theta) to be invertible
1440 if self.k_ma_params > 0:
1441 end += self.k_ma_params
1442 if self.enforce_invertibility:
1443 unconstrained[start:end] = (
1444 unconstrain_stationary_univariate(-constrained[start:end])
1445 )
1446 else:
1447 unconstrained[start:end] = constrained[start:end]
1448 start += self.k_ma_params
1450 # Transform the seasonal AR parameters (\tilde phi) to be stationary
1451 if self.k_seasonal_ar > 0:
1452 end += self.k_seasonal_ar_params
1453 if self.enforce_stationarity:
1454 unconstrained[start:end] = (
1455 unconstrain_stationary_univariate(constrained[start:end])
1456 )
1457 else:
1458 unconstrained[start:end] = constrained[start:end]
1459 start += self.k_seasonal_ar_params
1461 # Transform the seasonal MA parameters (\tilde theta) to be invertible
1462 if self.k_seasonal_ma_params > 0:
1463 end += self.k_seasonal_ma_params
1464 if self.enforce_invertibility:
1465 unconstrained[start:end] = (
1466 unconstrain_stationary_univariate(-constrained[start:end])
1467 )
1468 else:
1469 unconstrained[start:end] = constrained[start:end]
1470 start += self.k_seasonal_ma_params
1472 # Untransform the standard deviation
1473 if self.state_regression and self.time_varying_regression:
1474 end += self._k_exog
1475 unconstrained[start:end] = constrained[start:end]**0.5
1476 start += self._k_exog
1477 if self.measurement_error:
1478 unconstrained[start] = constrained[start]**0.5
1479 start += 1
1480 end += 1
1481 if self.state_error and not self.concentrate_scale:
1482 unconstrained[start] = constrained[start]**0.5
1483 # start += 1
1484 # end += 1
1486 return unconstrained
1488 def _validate_can_fix_params(self, param_names):
1489 super(SARIMAX, self)._validate_can_fix_params(param_names)
1490 model_names = self.model_names
1492 items = [
1493 ('ar', 'autoregressive', self.enforce_stationarity,
1494 '`enforce_stationarity=True`'),
1495 ('seasonal_ar', 'seasonal autoregressive',
1496 self.enforce_stationarity, '`enforce_stationarity=True`'),
1497 ('ma', 'moving average', self.enforce_invertibility,
1498 '`enforce_invertibility=True`'),
1499 ('seasonal_ma', 'seasonal moving average',
1500 self.enforce_invertibility, '`enforce_invertibility=True`')]
1502 for name, title, condition, condition_desc in items:
1503 names = set(model_names[name] or [])
1504 fix_all = param_names.issuperset(names)
1505 fix_any = len(param_names.intersection(names)) > 0
1506 if condition and fix_any and not fix_all:
1507 raise ValueError('Cannot fix individual %s parameters when'
1508 ' %s. Must either fix all %s parameters or'
1509 ' none.' % (title, condition_desc, title))
1511 def update(self, params, transformed=True, includes_fixed=False,
1512 complex_step=False):
1513 """
1514 Update the parameters of the model
1516 Updates the representation matrices to fill in the new parameter
1517 values.
1519 Parameters
1520 ----------
1521 params : array_like
1522 Array of new parameters.
1523 transformed : bool, optional
1524 Whether or not `params` is already transformed. If set to False,
1525 `transform_params` is called. Default is True..
1527 Returns
1528 -------
1529 params : array_like
1530 Array of parameters.
1531 """
1532 params = self.handle_params(params, transformed=transformed,
1533 includes_fixed=includes_fixed)
1535 params_trend = None
1536 params_exog = None
1537 params_ar = None
1538 params_ma = None
1539 params_seasonal_ar = None
1540 params_seasonal_ma = None
1541 params_exog_variance = None
1542 params_measurement_variance = None
1543 params_variance = None
1545 # Extract the parameters
1546 start = end = 0
1547 end += self._k_trend
1548 params_trend = params[start:end]
1549 start += self._k_trend
1550 if self.mle_regression:
1551 end += self._k_exog
1552 params_exog = params[start:end]
1553 start += self._k_exog
1554 end += self.k_ar_params
1555 params_ar = params[start:end]
1556 start += self.k_ar_params
1557 end += self.k_ma_params
1558 params_ma = params[start:end]
1559 start += self.k_ma_params
1560 end += self.k_seasonal_ar_params
1561 params_seasonal_ar = params[start:end]
1562 start += self.k_seasonal_ar_params
1563 end += self.k_seasonal_ma_params
1564 params_seasonal_ma = params[start:end]
1565 start += self.k_seasonal_ma_params
1566 if self.state_regression and self.time_varying_regression:
1567 end += self._k_exog
1568 params_exog_variance = params[start:end]
1569 start += self._k_exog
1570 if self.measurement_error:
1571 params_measurement_variance = params[start]
1572 start += 1
1573 end += 1
1574 if self.state_error and not self.concentrate_scale:
1575 params_variance = params[start]
1576 # start += 1
1577 # end += 1
1579 # Update lag polynomials
1580 if self.k_ar > 0:
1581 if self._polynomial_ar.dtype == params.dtype:
1582 self._polynomial_ar[self._polynomial_ar_idx] = -params_ar
1583 else:
1584 polynomial_ar = self._polynomial_ar.real.astype(params.dtype)
1585 polynomial_ar[self._polynomial_ar_idx] = -params_ar
1586 self._polynomial_ar = polynomial_ar
1588 if self.k_ma > 0:
1589 if self._polynomial_ma.dtype == params.dtype:
1590 self._polynomial_ma[self._polynomial_ma_idx] = params_ma
1591 else:
1592 polynomial_ma = self._polynomial_ma.real.astype(params.dtype)
1593 polynomial_ma[self._polynomial_ma_idx] = params_ma
1594 self._polynomial_ma = polynomial_ma
1596 if self.k_seasonal_ar > 0:
1597 idx = self._polynomial_seasonal_ar_idx
1598 if self._polynomial_seasonal_ar.dtype == params.dtype:
1599 self._polynomial_seasonal_ar[idx] = -params_seasonal_ar
1600 else:
1601 polynomial_seasonal_ar = (
1602 self._polynomial_seasonal_ar.real.astype(params.dtype)
1603 )
1604 polynomial_seasonal_ar[idx] = -params_seasonal_ar
1605 self._polynomial_seasonal_ar = polynomial_seasonal_ar
1607 if self.k_seasonal_ma > 0:
1608 idx = self._polynomial_seasonal_ma_idx
1609 if self._polynomial_seasonal_ma.dtype == params.dtype:
1610 self._polynomial_seasonal_ma[idx] = params_seasonal_ma
1611 else:
1612 polynomial_seasonal_ma = (
1613 self._polynomial_seasonal_ma.real.astype(params.dtype)
1614 )
1615 polynomial_seasonal_ma[idx] = params_seasonal_ma
1616 self._polynomial_seasonal_ma = polynomial_seasonal_ma
1618 # Get the reduced form lag polynomial terms by multiplying the regular
1619 # and seasonal lag polynomials
1620 # Note: that although the numpy np.polymul examples assume that they
1621 # are ordered from highest degree to lowest, whereas our are from
1622 # lowest to highest, it does not matter.
1623 if self.k_seasonal_ar > 0:
1624 reduced_polynomial_ar = -np.polymul(
1625 self._polynomial_ar, self._polynomial_seasonal_ar
1626 )
1627 else:
1628 reduced_polynomial_ar = -self._polynomial_ar
1629 if self.k_seasonal_ma > 0:
1630 reduced_polynomial_ma = np.polymul(
1631 self._polynomial_ma, self._polynomial_seasonal_ma
1632 )
1633 else:
1634 reduced_polynomial_ma = self._polynomial_ma
1636 # Observation intercept
1637 # Exogenous data with MLE estimation of parameters enters through a
1638 # time-varying observation intercept (is equivalent to simply
1639 # subtracting it out of the endogenous variable first)
1640 if self.mle_regression:
1641 self.ssm['obs_intercept'] = np.dot(self.exog, params_exog)[None, :]
1643 # State intercept (Harvey) or additional observation intercept
1644 # (Hamilton)
1645 # SARIMA trend enters through the a time-varying state intercept,
1646 # associated with the first row of the stationary component of the
1647 # state vector (i.e. the first element of the state vector following
1648 # any differencing elements)
1649 if self._k_trend > 0:
1650 data = np.dot(self._trend_data, params_trend).astype(params.dtype)
1651 if not self.hamilton_representation:
1652 self.ssm['state_intercept', self._k_states_diff, :] = data
1653 else:
1654 # The way the trend enters in the Hamilton representation means
1655 # that the parameter is not an ``intercept'' but instead the
1656 # mean of the process. The trend values in `data` are meant for
1657 # an intercept, and so must be transformed to represent the
1658 # mean instead
1659 if self.hamilton_representation:
1660 data /= np.sum(-reduced_polynomial_ar)
1662 # If we already set the observation intercept for MLE
1663 # regression, just add to it
1664 if self.mle_regression:
1665 self.ssm.obs_intercept += data[None, :]
1666 # Otherwise set it directly
1667 else:
1668 self.ssm['obs_intercept'] = data[None, :]
1670 # Observation covariance matrix
1671 if self.measurement_error:
1672 self.ssm['obs_cov', 0, 0] = params_measurement_variance
1674 # Transition matrix
1675 if self.k_ar > 0 or self.k_seasonal_ar > 0:
1676 self.ssm[self.transition_ar_params_idx] = reduced_polynomial_ar[1:]
1677 elif not self.ssm.transition.dtype == params.dtype:
1678 # This is required if the transition matrix is not really in use
1679 # (e.g. for an MA(q) process) so that it's dtype never changes as
1680 # the parameters' dtype changes. This changes the dtype manually.
1681 self.ssm['transition'] = self.ssm['transition'].real.astype(
1682 params.dtype)
1684 # Selection matrix (Harvey) or Design matrix (Hamilton)
1685 if self.k_ma > 0 or self.k_seasonal_ma > 0:
1686 if not self.hamilton_representation:
1687 self.ssm[self.selection_ma_params_idx] = (
1688 reduced_polynomial_ma[1:]
1689 )
1690 else:
1691 self.ssm[self.design_ma_params_idx] = reduced_polynomial_ma[1:]
1693 # State covariance matrix
1694 if self.k_posdef > 0:
1695 if not self.concentrate_scale:
1696 self['state_cov', 0, 0] = params_variance
1697 if self.state_regression and self.time_varying_regression:
1698 self.ssm[self._exog_variance_idx] = params_exog_variance
1700 return params
1702 def _get_extension_time_varying_matrices(
1703 self, params, exog, out_of_sample, extend_kwargs=None,
1704 transformed=True, includes_fixed=False, **kwargs):
1705 """
1706 Get time-varying state space system matrices for extended model
1708 Notes
1709 -----
1710 We need to override this method for SARIMAX because we need some
1711 special handling in the `simple_differencing=True` case.
1712 """
1714 # Get the appropriate exog for the extended sample
1715 exog = self._validate_out_of_sample_exog(exog, out_of_sample)
1717 # Get the tmp endog, exog
1718 if self.simple_differencing:
1719 nobs = self.data.orig_endog.shape[0] + out_of_sample
1720 tmp_endog = np.zeros((nobs, self.k_endog))
1721 if exog is not None:
1722 tmp_exog = np.c_[self.data.orig_exog.T, exog.T].T
1723 else:
1724 tmp_exog = None
1725 else:
1726 tmp_endog = np.zeros((out_of_sample, self.k_endog))
1727 tmp_exog = exog
1729 # Create extended model
1730 if extend_kwargs is None:
1731 extend_kwargs = {}
1732 if not self.simple_differencing and self._k_trend > 0:
1733 extend_kwargs.setdefault(
1734 'trend_offset', self.trend_offset + self.nobs)
1735 mod_extend = self.clone(
1736 endog=tmp_endog, exog=tmp_exog, **extend_kwargs)
1737 mod_extend.update(params, transformed=transformed,
1738 includes_fixed=includes_fixed)
1740 # Retrieve the extensions to the time-varying system matrices
1741 # and put them in kwargs
1742 for name in self.ssm.shapes.keys():
1743 if name == 'obs':
1744 continue
1745 if getattr(self.ssm, name).shape[-1] > 1:
1746 mat = getattr(mod_extend.ssm, name)
1747 kwargs[name] = mat[..., -out_of_sample:]
1749 return kwargs
1752class SARIMAXResults(MLEResults):
1753 """
1754 Class to hold results from fitting an SARIMAX model.
1756 Parameters
1757 ----------
1758 model : SARIMAX instance
1759 The fitted model instance
1761 Attributes
1762 ----------
1763 specification : dictionary
1764 Dictionary including all attributes from the SARIMAX model instance.
1765 polynomial_ar : ndarray
1766 Array containing autoregressive lag polynomial coefficients,
1767 ordered from lowest degree to highest. Initialized with ones, unless
1768 a coefficient is constrained to be zero (in which case it is zero).
1769 polynomial_ma : ndarray
1770 Array containing moving average lag polynomial coefficients,
1771 ordered from lowest degree to highest. Initialized with ones, unless
1772 a coefficient is constrained to be zero (in which case it is zero).
1773 polynomial_seasonal_ar : ndarray
1774 Array containing seasonal autoregressive lag polynomial coefficients,
1775 ordered from lowest degree to highest. Initialized with ones, unless
1776 a coefficient is constrained to be zero (in which case it is zero).
1777 polynomial_seasonal_ma : ndarray
1778 Array containing seasonal moving average lag polynomial coefficients,
1779 ordered from lowest degree to highest. Initialized with ones, unless
1780 a coefficient is constrained to be zero (in which case it is zero).
1781 polynomial_trend : ndarray
1782 Array containing trend polynomial coefficients, ordered from lowest
1783 degree to highest. Initialized with ones, unless a coefficient is
1784 constrained to be zero (in which case it is zero).
1785 model_orders : list of int
1786 The orders of each of the polynomials in the model.
1787 param_terms : list of str
1788 List of parameters actually included in the model, in sorted order.
1790 See Also
1791 --------
1792 statsmodels.tsa.statespace.kalman_filter.FilterResults
1793 statsmodels.tsa.statespace.mlemodel.MLEResults
1794 """
1795 def __init__(self, model, params, filter_results, cov_type=None,
1796 **kwargs):
1797 super(SARIMAXResults, self).__init__(model, params, filter_results,
1798 cov_type, **kwargs)
1800 self.df_resid = np.inf # attribute required for wald tests
1802 # Save _init_kwds
1803 self._init_kwds = self.model._get_init_kwds()
1805 # Save model specification
1806 self.specification = Bunch(**{
1807 # Set additional model parameters
1808 'seasonal_periods': self.model.seasonal_periods,
1809 'measurement_error': self.model.measurement_error,
1810 'time_varying_regression': self.model.time_varying_regression,
1811 'simple_differencing': self.model.simple_differencing,
1812 'enforce_stationarity': self.model.enforce_stationarity,
1813 'enforce_invertibility': self.model.enforce_invertibility,
1814 'hamilton_representation': self.model.hamilton_representation,
1815 'concentrate_scale': self.model.concentrate_scale,
1816 'trend_offset': self.model.trend_offset,
1818 'order': self.model.order,
1819 'seasonal_order': self.model.seasonal_order,
1821 # Model order
1822 'k_diff': self.model.k_diff,
1823 'k_seasonal_diff': self.model.k_seasonal_diff,
1824 'k_ar': self.model.k_ar,
1825 'k_ma': self.model.k_ma,
1826 'k_seasonal_ar': self.model.k_seasonal_ar,
1827 'k_seasonal_ma': self.model.k_seasonal_ma,
1829 # Param Numbers
1830 'k_ar_params': self.model.k_ar_params,
1831 'k_ma_params': self.model.k_ma_params,
1833 # Trend / Regression
1834 'trend': self.model.trend,
1835 'k_trend': self.model.k_trend,
1836 'k_exog': self.model.k_exog,
1838 'mle_regression': self.model.mle_regression,
1839 'state_regression': self.model.state_regression,
1840 })
1842 # Polynomials
1843 self.polynomial_trend = self.model._polynomial_trend
1844 self.polynomial_ar = self.model._polynomial_ar
1845 self.polynomial_ma = self.model._polynomial_ma
1846 self.polynomial_seasonal_ar = self.model._polynomial_seasonal_ar
1847 self.polynomial_seasonal_ma = self.model._polynomial_seasonal_ma
1848 self.polynomial_reduced_ar = np.polymul(
1849 self.polynomial_ar, self.polynomial_seasonal_ar
1850 )
1851 self.polynomial_reduced_ma = np.polymul(
1852 self.polynomial_ma, self.polynomial_seasonal_ma
1853 )
1855 # Distinguish parameters
1856 self.model_orders = self.model.model_orders
1857 self.param_terms = self.model.param_terms
1858 start = end = 0
1859 for name in self.param_terms:
1860 if name == 'ar':
1861 k = self.model.k_ar_params
1862 elif name == 'ma':
1863 k = self.model.k_ma_params
1864 elif name == 'seasonal_ar':
1865 k = self.model.k_seasonal_ar_params
1866 elif name == 'seasonal_ma':
1867 k = self.model.k_seasonal_ma_params
1868 else:
1869 k = self.model_orders[name]
1870 end += k
1871 setattr(self, '_params_%s' % name, self.params[start:end])
1872 start += k
1874 # Handle removing data
1875 self._data_attr_model.extend(['orig_endog', 'orig_exog'])
1877 def extend(self, endog, exog=None, **kwargs):
1878 kwargs.setdefault('trend_offset', self.nobs + 1)
1879 return super(SARIMAXResults, self).extend(endog, exog=exog, **kwargs)
1881 @cache_readonly
1882 def arroots(self):
1883 """
1884 (array) Roots of the reduced form autoregressive lag polynomial
1885 """
1886 return np.roots(self.polynomial_reduced_ar)**-1
1888 @cache_readonly
1889 def maroots(self):
1890 """
1891 (array) Roots of the reduced form moving average lag polynomial
1892 """
1893 return np.roots(self.polynomial_reduced_ma)**-1
1895 @cache_readonly
1896 def arfreq(self):
1897 """
1898 (array) Frequency of the roots of the reduced form autoregressive
1899 lag polynomial
1900 """
1901 z = self.arroots
1902 if not z.size:
1903 return
1904 return np.arctan2(z.imag, z.real) / (2 * np.pi)
1906 @cache_readonly
1907 def mafreq(self):
1908 """
1909 (array) Frequency of the roots of the reduced form moving average
1910 lag polynomial
1911 """
1912 z = self.maroots
1913 if not z.size:
1914 return
1915 return np.arctan2(z.imag, z.real) / (2 * np.pi)
1917 @cache_readonly
1918 def arparams(self):
1919 """
1920 (array) Autoregressive parameters actually estimated in the model.
1921 Does not include seasonal autoregressive parameters (see
1922 `seasonalarparams`) or parameters whose values are constrained to be
1923 zero.
1924 """
1925 return self._params_ar
1927 @cache_readonly
1928 def seasonalarparams(self):
1929 """
1930 (array) Seasonal autoregressive parameters actually estimated in the
1931 model. Does not include nonseasonal autoregressive parameters (see
1932 `arparams`) or parameters whose values are constrained to be zero.
1933 """
1934 return self._params_seasonal_ar
1936 @cache_readonly
1937 def maparams(self):
1938 """
1939 (array) Moving average parameters actually estimated in the model.
1940 Does not include seasonal moving average parameters (see
1941 `seasonalmaparams`) or parameters whose values are constrained to be
1942 zero.
1943 """
1944 return self._params_ma
1946 @cache_readonly
1947 def seasonalmaparams(self):
1948 """
1949 (array) Seasonal moving average parameters actually estimated in the
1950 model. Does not include nonseasonal moving average parameters (see
1951 `maparams`) or parameters whose values are constrained to be zero.
1952 """
1953 return self._params_seasonal_ma
1955 @Appender(MLEResults.summary.__doc__)
1956 def summary(self, alpha=.05, start=None):
1957 # Create the model name
1959 # See if we have an ARIMA component
1960 order = ''
1961 if self.model.k_ar + self.model.k_diff + self.model.k_ma > 0:
1962 if self.model.k_ar == self.model.k_ar_params:
1963 order_ar = self.model.k_ar
1964 else:
1965 order_ar = list(self.model._spec.ar_lags)
1966 if self.model.k_ma == self.model.k_ma_params:
1967 order_ma = self.model.k_ma
1968 else:
1969 order_ma = list(self.model._spec.ma_lags)
1970 # If there is simple differencing, then that is reflected in the
1971 # dependent variable name
1972 k_diff = 0 if self.model.simple_differencing else self.model.k_diff
1973 order = '(%s, %d, %s)' % (order_ar, k_diff, order_ma)
1974 # See if we have an SARIMA component
1975 seasonal_order = ''
1976 has_seasonal = (
1977 self.model.k_seasonal_ar +
1978 self.model.k_seasonal_diff +
1979 self.model.k_seasonal_ma
1980 ) > 0
1981 if has_seasonal:
1982 tmp = int(self.model.k_seasonal_ar / self.model.seasonal_periods)
1983 if tmp == self.model.k_seasonal_ar_params:
1984 order_seasonal_ar = (
1985 int(self.model.k_seasonal_ar / self.model.seasonal_periods)
1986 )
1987 else:
1988 order_seasonal_ar = list(self.model._spec.seasonal_ar_lags)
1989 tmp = int(self.model.k_seasonal_ma / self.model.seasonal_periods)
1990 if tmp == self.model.k_ma_params:
1991 order_seasonal_ma = tmp
1992 else:
1993 order_seasonal_ma = list(self.model._spec.seasonal_ma_lags)
1994 # If there is simple differencing, then that is reflected in the
1995 # dependent variable name
1996 k_seasonal_diff = self.model.k_seasonal_diff
1997 if self.model.simple_differencing:
1998 k_seasonal_diff = 0
1999 seasonal_order = ('(%s, %d, %s, %d)' %
2000 (str(order_seasonal_ar), k_seasonal_diff,
2001 str(order_seasonal_ma),
2002 self.model.seasonal_periods))
2003 if not order == '':
2004 order += 'x'
2005 model_name = (
2006 '%s%s%s' % (self.model.__class__.__name__, order, seasonal_order)
2007 )
2008 return super(SARIMAXResults, self).summary(
2009 alpha=alpha, start=start, title='SARIMAX Results',
2010 model_name=model_name
2011 )
2014class SARIMAXResultsWrapper(MLEResultsWrapper):
2015 _attrs = {}
2016 _wrap_attrs = wrap.union_dicts(MLEResultsWrapper._wrap_attrs,
2017 _attrs)
2018 _methods = {}
2019 _wrap_methods = wrap.union_dicts(MLEResultsWrapper._wrap_methods,
2020 _methods)
2021wrap.populate_wrapper(SARIMAXResultsWrapper, SARIMAXResults) # noqa:E305