Hide keyboard shortcuts

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 

3 

4Author: Chad Fulton 

5License: Simplified-BSD 

6""" 

7from warnings import warn 

8 

9import numpy as np 

10import pandas as pd 

11 

12from statsmodels.compat.pandas import Appender 

13 

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 

18 

19from statsmodels.tsa.arima.specification import SARIMAXSpecification 

20from statsmodels.tsa.arima.params import SARIMAXParams 

21from statsmodels.tsa.tsatools import lagmat 

22 

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) 

29 

30 

31class SARIMAX(MLEModel): 

32 r""" 

33 Seasonal AutoRegressive Integrated Moving Average with eXogenous regressors 

34 model 

35 

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. 

115 

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. 

206 

207 Notes 

208 ----- 

209 The SARIMA model is specified :math:`(p, d, q) \times (P, D, Q)_s`. 

210 

211 .. math:: 

212 

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 

215 

216 In terms of a univariate structural model, this can be represented as 

217 

218 .. math:: 

219 

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 

223 

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). 

227 

228 In terms of this model, regression with SARIMA errors can be represented 

229 easily as 

230 

231 .. math:: 

232 

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 

236 

237 this model is the one used when exogenous regressors are provided. 

238 

239 Note that the reduced form lag polynomials will be written as: 

240 

241 .. math:: 

242 

243 \Phi (L) \equiv \phi_p (L) \tilde \phi_P (L^s) \\ 

244 \Theta (L) \equiv \theta_q (L) \tilde \theta_Q (L^s) 

245 

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. 

251 

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. 

259 

260 The Harvey representation is convenient because it allows integrating 

261 differencing into the state vector to allow using all observations for 

262 estimation. 

263 

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. 

268 

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). 

272 

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. 

282 

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: 

287 

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. 

294 

295 Detailed information about state space models can be found in [1]_. Some 

296 specific references are: 

297 

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. 

311 

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 """ 

318 

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): 

327 

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) 

334 

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 

340 

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 

352 

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.') 

360 

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() 

365 

366 self._params.ma_params = 1 

367 self.polynomial_ma = self._params.ma_poly.coef 

368 self._polynomial_ma = self.polynomial_ma.copy() 

369 

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() 

373 

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() 

377 

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) 

386 

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 

398 

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 

406 

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 

412 

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') 

422 

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 

431 

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 

437 

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 

452 

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 

460 

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 

467 

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) 

473 

474 # Handle non-default loglikelihood burn 

475 self._loglikelihood_burn = kwargs.get('loglikelihood_burn', None) 

476 

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 

487 

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) 

493 

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 

501 

502 # Internally used in several locations 

503 self._k_states_diff = ( 

504 self._k_diff + self.seasonal_periods * self._k_seasonal_diff 

505 ) 

506 

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 

512 

513 # Initialize the statespace 

514 super(SARIMAX, self).__init__( 

515 endog, exog=exog, k_states=k_states, k_posdef=k_posdef, **kwargs 

516 ) 

517 

518 # Set the filter to concentrate out the scale if requested 

519 if self.concentrate_scale: 

520 self.ssm.filter_concentrated = True 

521 

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 

525 

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. 

533 

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 ??? 

542 

543 # Initialize the state 

544 if self.ssm.initialization is None: 

545 self.initialize_default() 

546 

547 def prepare_data(self): 

548 endog, exog = super(SARIMAX, self).prepare_data() 

549 

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) 

561 

562 # Reset the ModelData datasets and cache 

563 self.data.endog, self.data.exog = ( 

564 self.data._convert_endog_exog(endog, exog)) 

565 

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:] 

578 

579 # Reset the nobs 

580 self.nobs = endog.shape[0] 

581 

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) 

586 

587 return endog, exog 

588 

589 def initialize(self): 

590 """ 

591 Initialize the SARIMAX model. 

592 

593 Notes 

594 ----- 

595 These initialization steps must occur following the parent class 

596 __init__ function calls. 

597 """ 

598 super(SARIMAX, self).initialize() 

599 

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:] 

611 

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 ) 

626 

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 ) 

638 

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:]) 

644 

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' 

653 

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 

660 

661 init = Initialization( 

662 self.k_states, 

663 approximate_diffuse_variance=approximate_diffuse_variance) 

664 

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) 

680 

681 self.ssm.initialization = init 

682 

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 ] 

692 

693 if len(design) == 0: 

694 design = np.r_[0] 

695 

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 

711 

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 

722 

723 @property 

724 def initial_transition(self): 

725 """Initial transition matrix""" 

726 transition = np.zeros((self.k_states, self.k_states)) 

727 

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) 

733 

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 

741 

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 ) 

749 

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 

758 

759 # T_c^* 

760 transition[start:end, start:end] = seasonal_companion 

761 

762 # i 

763 for i in range(d + 1, self._k_seasonal_diff): 

764 transition[start, end + self.seasonal_periods - 1] = 1 

765 

766 # \iota 

767 transition[start, self._k_states_diff] = 1 

768 

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 

784 

785 return transition 

786 

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] 

797 

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 

811 

812 def clone(self, endog, exog=None, **kwargs): 

813 return self._clone_from_init_kwds(endog, exog=exog, **kwargs) 

814 

815 @property 

816 def _res_classes(self): 

817 return {'fit': (SARIMAXResults, SARIMAXResultsWrapper)} 

818 

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) 

825 

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 

828 

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) 

840 

841 # Run an ARMA(p,q) model using the just computed residuals as 

842 # data 

843 Y = endog[r:] 

844 

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]] 

857 

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)] 

882 

883 # Default output 

884 params_trend = [] 

885 params_ar = [] 

886 params_ma = [] 

887 params_variance = [] 

888 

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() 

902 

903 return (params_trend, params_ar, params_ma, 

904 params_variance) 

905 

906 @property 

907 def start_params(self): 

908 """ 

909 Starting parameters for maximum likelihood estimation 

910 """ 

911 

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() 

929 

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] 

939 

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 = [] 

947 

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') 

954 

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 

966 

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 

978 

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')) 

985 

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 

997 

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 

1009 

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 [] 

1025 

1026 # We want to bound the starting variance away from zero 

1027 params_variance = np.atleast_1d(max(np.array(params_variance), 1e-10)) 

1028 

1029 # Remove state variance as parameter if scale is concentrated out 

1030 if self.concentrate_scale: 

1031 params_variance = [] 

1032 

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 ] 

1045 

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 

1055 

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 

1076 

1077 params_complete = [ 

1078 'trend', 'exog', 'ar', 'ma', 'seasonal_ar', 'seasonal_ma', 

1079 'exog_variance', 'measurement_variance', 'variance' 

1080 ] 

1081 

1082 @property 

1083 def param_terms(self): 

1084 """ 

1085 List of parameters actually included in the model, in sorted order. 

1086 

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') 

1099 

1100 return params 

1101 

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 ] 

1113 

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)] 

1125 

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)] 

1129 

1130 return names 

1131 

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 } 

1151 

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) 

1158 

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) 

1165 

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 } 

1180 

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) 

1192 

1193 # Exogenous coefficients 

1194 if self._k_exog > 0: 

1195 names['exog'] = self.exog_names 

1196 

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) 

1203 

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) 

1210 

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) 

1219 

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) 

1228 

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) 

1238 

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) 

1248 

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 ] 

1261 

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] 

1272 

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] 

1277 

1278 return names 

1279 

1280 def transform_params(self, unconstrained): 

1281 """ 

1282 Transform unconstrained parameters used by the optimizer to constrained 

1283 parameters used in likelihood evaluation. 

1284 

1285 Used primarily to enforce stationarity of the autoregressive lag 

1286 polynomial, invertibility of the moving average lag polynomial, and 

1287 positive variance parameters. 

1288 

1289 Parameters 

1290 ---------- 

1291 unconstrained : array_like 

1292 Unconstrained parameters used by the optimizer. 

1293 

1294 Returns 

1295 ------- 

1296 constrained : array_like 

1297 Constrained parameters used in likelihood evaluation. 

1298 

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) 

1309 

1310 start = end = 0 

1311 

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 

1317 

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 

1323 

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 

1334 

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 

1345 

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 

1356 

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 

1367 

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 

1381 

1382 return constrained 

1383 

1384 def untransform_params(self, constrained): 

1385 """ 

1386 Transform constrained parameters used in likelihood evaluation 

1387 to unconstrained parameters used by the optimizer 

1388 

1389 Used primarily to reverse enforcement of stationarity of the 

1390 autoregressive lag polynomial and invertibility of the moving average 

1391 lag polynomial. 

1392 

1393 Parameters 

1394 ---------- 

1395 constrained : array_like 

1396 Constrained parameters used in likelihood evaluation. 

1397 

1398 Returns 

1399 ------- 

1400 constrained : array_like 

1401 Unconstrained parameters used by the optimizer. 

1402 

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) 

1413 

1414 start = end = 0 

1415 

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 

1421 

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 

1427 

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 

1438 

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 

1449 

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 

1460 

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 

1471 

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 

1485 

1486 return unconstrained 

1487 

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 

1491 

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`')] 

1501 

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)) 

1510 

1511 def update(self, params, transformed=True, includes_fixed=False, 

1512 complex_step=False): 

1513 """ 

1514 Update the parameters of the model 

1515 

1516 Updates the representation matrices to fill in the new parameter 

1517 values. 

1518 

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.. 

1526 

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) 

1534 

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 

1544 

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 

1578 

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 

1587 

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 

1595 

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 

1606 

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 

1617 

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 

1635 

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, :] 

1642 

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) 

1661 

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, :] 

1669 

1670 # Observation covariance matrix 

1671 if self.measurement_error: 

1672 self.ssm['obs_cov', 0, 0] = params_measurement_variance 

1673 

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) 

1683 

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:] 

1692 

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 

1699 

1700 return params 

1701 

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 

1707 

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 """ 

1713 

1714 # Get the appropriate exog for the extended sample 

1715 exog = self._validate_out_of_sample_exog(exog, out_of_sample) 

1716 

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 

1728 

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) 

1739 

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:] 

1748 

1749 return kwargs 

1750 

1751 

1752class SARIMAXResults(MLEResults): 

1753 """ 

1754 Class to hold results from fitting an SARIMAX model. 

1755 

1756 Parameters 

1757 ---------- 

1758 model : SARIMAX instance 

1759 The fitted model instance 

1760 

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. 

1789 

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) 

1799 

1800 self.df_resid = np.inf # attribute required for wald tests 

1801 

1802 # Save _init_kwds 

1803 self._init_kwds = self.model._get_init_kwds() 

1804 

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, 

1817 

1818 'order': self.model.order, 

1819 'seasonal_order': self.model.seasonal_order, 

1820 

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, 

1828 

1829 # Param Numbers 

1830 'k_ar_params': self.model.k_ar_params, 

1831 'k_ma_params': self.model.k_ma_params, 

1832 

1833 # Trend / Regression 

1834 'trend': self.model.trend, 

1835 'k_trend': self.model.k_trend, 

1836 'k_exog': self.model.k_exog, 

1837 

1838 'mle_regression': self.model.mle_regression, 

1839 'state_regression': self.model.state_regression, 

1840 }) 

1841 

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 ) 

1854 

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 

1873 

1874 # Handle removing data 

1875 self._data_attr_model.extend(['orig_endog', 'orig_exog']) 

1876 

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) 

1880 

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 

1887 

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 

1894 

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) 

1905 

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) 

1916 

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 

1926 

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 

1935 

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 

1945 

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 

1954 

1955 @Appender(MLEResults.summary.__doc__) 

1956 def summary(self, alpha=.05, start=None): 

1957 # Create the model name 

1958 

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 ) 

2012 

2013 

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