Repository: statsmodels/statsmodels

Issue:
SARIMAX ar_params/ma_params giving incorrect results for non-consecutive lags
When using non-consecutive lags with SARIMAX, i.e. tuples for p or q, the resultant ar_params and ma_params aren't cut off correctly from all the others. I haven't tested with the combination, or with P/Q, but likely the same happens there.

statsmodels full_version == '0.8.0'

Example below.

```
>>> import statsmodels.api as sm
>>> import numpy as np
>>> import pandas as pd
>>> dta = sm.datasets.sunspots.load_pandas().data
>>> sss = lambda x : pd.Timestamp(str(int(x)) + "-01-01")
>>> dta = dta.set_index(pd.DatetimeIndex(dta["YEAR"].apply(sss)))
>>> del dta['YEAR']
>>> endog = dta['SUNACTIVITY']
>>> order1 = (3,0,0)
>>> mod1 = sm.tsa.SARIMAX(endog, order=order1).fit(disp=0)
>>> order2 = ((1,0,1), 0, 0)
>>> mod2 = sm.tsa.SARIMAX(endog, order=order2).fit(disp=0)
>>> order3 = ((0,0,1), 0, 0)
>>> mod3 = sm.tsa.SARIMAX(endog, order=order3).fit(disp=0)
>>> print(mod1.params)
ar.L1       1.559285
ar.L2      -0.787745
ar.L3       0.130519
sigma2    349.730362
dtype: float64
>>> print(mod2.params)
ar.L1       1.148138
ar.L3      -0.279352
sigma2    428.185631
dtype: float64
>>> print(mod3.params)
ar.L3        0.616523
sigma2    2516.393448
dtype: float64
>>> print(mod1.arparams)
[ 1.55928508 -0.78774548  0.13051884]
>>> print(mod2.arparams)
[  1.14813810e+00  -2.79352080e-01   4.28185631e+02]
>>> print(mod3.arparams)
[  6.16522707e-01   2.51639345e+03]
```

Off-topic: what is the correct term for this kind of AR process? Just a constrained AR(max_lag)? Partial autoregression?


Discussion:

Resolving patch:
diff --git a/statsmodels/tsa/statespace/sarimax.py b/statsmodels/tsa/statespace/sarimax.py
--- a/statsmodels/tsa/statespace/sarimax.py
+++ b/statsmodels/tsa/statespace/sarimax.py
@@ -1785,9 +1785,19 @@ def __init__(self, model, params, filter_results, cov_type='opg',
         self.param_terms = self.model.param_terms
         start = end = 0
         for name in self.param_terms:
-            end += self.model_orders[name]
+            if name == 'ar':
+                k = self.model.k_ar_params
+            elif name == 'ma':
+                k = self.model.k_ma_params
+            elif name == 'seasonal_ar':
+                k = self.model.k_seasonal_ar_params
+            elif name == 'seasonal_ma':
+                k = self.model.k_seasonal_ma_params
+            else:
+                k = self.model_orders[name]
+            end += k
             setattr(self, '_params_%s' % name, self.params[start:end])
-            start += self.model_orders[name]
+            start += k
 
         # Handle removing data
         self._data_attr_model.extend(['orig_endog', 'orig_exog'])
@@ -1832,18 +1842,40 @@ def mafreq(self):
     def arparams(self):
         """
         (array) Autoregressive parameters actually estimated in the model.
-        Does not include parameters whose values are constrained to be zero.
+        Does not include seasonal autoregressive parameters (see
+        `seasonalarparams`) or parameters whose values are constrained to be
+        zero.
         """
         return self._params_ar
 
+    @cache_readonly
+    def seasonalarparams(self):
+        """
+        (array) Seasonal autoregressive parameters actually estimated in the
+        model. Does not include nonseasonal autoregressive parameters (see
+        `arparams`) or parameters whose values are constrained to be zero.
+        """
+        return self._params_seasonal_ar
+
     @cache_readonly
     def maparams(self):
         """
         (array) Moving average parameters actually estimated in the model.
-        Does not include parameters whose values are constrained to be zero.
+        Does not include seasonal moving average parameters (see
+        `seasonalmaparams`) or parameters whose values are constrained to be
+        zero.
         """
         return self._params_ma
 
+    @cache_readonly
+    def seasonalmaparams(self):
+        """
+        (array) Seasonal moving average parameters actually estimated in the
+        model. Does not include nonseasonal moving average parameters (see
+        `maparams`) or parameters whose values are constrained to be zero.
+        """
+        return self._params_seasonal_ma
+
     def get_prediction(self, start=None, end=None, dynamic=False, index=None,
                        exog=None, **kwargs):
         """


Test patch:
diff --git a/statsmodels/tsa/statespace/tests/test_sarimax.py b/statsmodels/tsa/statespace/tests/test_sarimax.py
--- a/statsmodels/tsa/statespace/tests/test_sarimax.py
+++ b/statsmodels/tsa/statespace/tests/test_sarimax.py
@@ -1776,6 +1776,13 @@ def setup_class(cls, *args, **kwargs):
         kwargs['exog'] = (endog - np.floor(endog))**2
         super(Test_sarimax_exogenous, cls).setup_class(50, *args, **kwargs)
 
+    def test_results_params(self):
+        result = self.model.filter(self.true_params)
+        assert_allclose(self.true_params[1:4], result.arparams)
+        assert_allclose(self.true_params[4:6], result.maparams)
+        assert_allclose(self.true_params[6:9], result.seasonalarparams)
+        assert_allclose(self.true_params[9:11], result.seasonalmaparams)
+
 class Test_sarimax_exogenous_not_hamilton(SARIMAXCoverageTest):
     # // SARIMAX and exogenous
     # arima wpi x, arima(3,2,2) sarima(3,2,2,4) noconstant vce(oim)