Repository: statsmodels/statsmodels

Issue:
order selection for AR model raises exception
I have time series data:

``` pytb
>>> ts.describe()
count     1093.000000
mean     26814.123139
std      17044.026136
min        699.098200
25%      14313.080000
50%      21469.620000
75%      36069.570000
max      96611.610000
dtype: float64
>>> ts.index
<class 'pandas.tseries.index.DatetimeIndex'>
[2005-07-01, ..., 2008-06-30]
Length: 1093, Freq: None, Timezone: None
```

and I want to see suggested order for supposed AR process:

``` pytb
>>> ar = sm.tsa.AR(ts)
>>> ar.select_order(maxlag=12, ic='aic')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/statsmodels-0.6.0-py2.7-linux-x86_64.egg/statsmodels/tsa/ar_model.py", line 435, in select_order
    maxiter=100, disp=0)
  File "/usr/local/lib/python2.7/dist-packages/statsmodels-0.6.0-py2.7-linux-x86_64.egg/statsmodels/tsa/ar_model.py", line 584, in fit
    params = self._transparams(params)
  File "/usr/local/lib/python2.7/dist-packages/statsmodels-0.6.0-py2.7-linux-x86_64.egg/statsmodels/tsa/ar_model.py", line 94, in _transparams
    newparams = params.copy()
AttributeError: 'NoneType' object has no attribute 'copy'
```

Apparently `params = mlefit.params` is passed to `fit()` as `None`, which as such doesn't have `copy()` method. So why does this happen, are there some prerequisites to using this function, not explained in the docs?

Thanks



Discussion:
Thanks for the report. Will look in to it. Fully reproducible example

```
np.random.seed(12345)
y = sm.tsa.arma_generate_sample([1, -.75, .3], [1], 100)
ts = pd.TimeSeries(y, index=pd.DatetimeIndex(start='1/1/1990', periods=100, freq='M'))
ar = sm.tsa.AR(ts)
ar.select_order(maxlag=12, ic='aic')
```

As a workaround, you might want to use the ARMA version.

```
sm.tsa.arma_order_select_ic(y, max_ar=12, max_ma=0, ic='aic')
```

This is actually a pretty boneheaded bug. I'm not sure any of the models will work correctly if `full_output = 0`. Wish we had caught it a few days ago. Might be worth a 0.6.2, though maybe I'd wait for any other reports.

Ok, thanks for the fast reply, confirmation and workaround.


Resolving patch:
diff --git a/statsmodels/base/model.py b/statsmodels/base/model.py
--- a/statsmodels/base/model.py
+++ b/statsmodels/base/model.py
@@ -426,9 +426,7 @@ def fit(self, start_params=None, method='newton', maxiter=100,
 
         #NOTE: this is for fit_regularized and should be generalized
         cov_params_func = kwargs.setdefault('cov_params_func', None)
-        if not full_output: # xopt should be None and retvals is argmin
-            xopt = retvals
-        elif cov_params_func:
+        if cov_params_func:
             Hinv = cov_params_func(self, xopt, retvals)
         elif method == 'newton' and full_output:
             Hinv = np.linalg.inv(-retvals['Hessian']) / nobs
diff --git a/statsmodels/base/optimizer.py b/statsmodels/base/optimizer.py
--- a/statsmodels/base/optimizer.py
+++ b/statsmodels/base/optimizer.py
@@ -183,12 +183,6 @@ def _fit(self, objective, gradient, start_params, fargs, kwargs,
                             retall=retall, full_output=full_output,
                             hess=hessian)
 
-        # this is stupid TODO: just change this to something sane
-        # no reason to copy scipy here
-        if not full_output: # xopt should be None and retvals is argmin
-            xopt = retvals
-            retvals = None
-
         optim_settings = {'optimizer': method, 'start_params': start_params,
                         'maxiter': maxiter, 'full_output': full_output,
                         'disp': disp, 'fargs': fargs, 'callback': callback,
@@ -274,8 +268,8 @@ def _fit_newton(f, score, start_params, fargs, kwargs, disp=True,
             retvals.update({'allvecs': history})
 
     else:
-        retvals = newparams
-        xopt = None
+        xopt = newparams
+        retvals = None
 
     return xopt, retvals
 
@@ -303,7 +297,8 @@ def _fit_bfgs(f, score, start_params, fargs, kwargs, disp=True,
         if retall:
             retvals.update({'allvecs': allvecs})
     else:
-        xopt = None
+        xopt = retvals
+        retvals = None
 
     return xopt, retvals
 
@@ -394,7 +389,8 @@ def _fit_lbfgs(f, score, start_params, fargs, kwargs, disp=True,
         retvals = {'fopt': fopt, 'gopt': gopt, 'fcalls': fcalls,
                    'warnflag': warnflag, 'converged': converged}
     else:
-        xopt = None
+        xopt = retvals[0]
+        retvals = None
 
     return xopt, retvals
 
@@ -421,7 +417,8 @@ def _fit_nm(f, score, start_params, fargs, kwargs, disp=True,
         if retall:
             retvals.update({'allvecs': allvecs})
     else:
-        xopt = None
+        xopt = retvals
+        retvals = None
 
     return xopt, retvals
 
@@ -448,7 +445,8 @@ def _fit_cg(f, score, start_params, fargs, kwargs, disp=True,
             retvals.update({'allvecs': allvecs})
 
     else:
-        xopt = None
+        xopt = retvals
+        retvals = None
 
     return xopt, retvals
 
@@ -477,7 +475,8 @@ def _fit_ncg(f, score, start_params, fargs, kwargs, disp=True,
         if retall:
             retvals.update({'allvecs': allvecs})
     else:
-        xopt = None
+        xopt = retvals
+        retvals = None
 
     return xopt, retvals
 
@@ -507,7 +506,8 @@ def _fit_powell(f, score, start_params, fargs, kwargs, disp=True,
         if retall:
             retvals.update({'allvecs': allvecs})
     else:
-        xopt = None
+        xopt = retvals
+        retvals = None
 
     return xopt, retvals
 
@@ -533,18 +533,20 @@ def _fit_basinhopping(f, score, start_params, fargs, kwargs, disp=True,
     if method and method != 'L-BFGS-B': # l_bfgs_b doesn't take a hessian
         minimizer_kwargs['hess'] = hess
 
-    res = optimize.basinhopping(f, start_params,
-                                minimizer_kwargs=minimizer_kwargs,
-                                niter=niter, niter_success=niter_success,
-                                T=T, stepsize=stepsize, disp=disp,
-                                callback=callback, interval=interval)
+    retvals = optimize.basinhopping(f, start_params,
+                                    minimizer_kwargs=minimizer_kwargs,
+                                    niter=niter, niter_success=niter_success,
+                                    T=T, stepsize=stepsize, disp=disp,
+                                    callback=callback, interval=interval)
     if full_output:
-        xopt, fopt, niter, fcalls = res.x, res.fun, res.nit, res.nfev
-        converged = 'completed successfully' in res.message[0]
+        xopt, fopt, niter, fcalls = map(lambda x : getattr(retvals, x),
+                                        ['x', 'fun', 'nit', 'nfev'])
+        converged = 'completed successfully' in retvals.message[0]
         retvals = {'fopt': fopt, 'iterations': niter,
                    'fcalls': fcalls, 'converged': converged}
 
     else:
-        xopt = None
+        xopt = retvals.x
+        retvals = None
 
     return xopt, retvals
diff --git a/statsmodels/tsa/ar_model.py b/statsmodels/tsa/ar_model.py
--- a/statsmodels/tsa/ar_model.py
+++ b/statsmodels/tsa/ar_model.py
@@ -595,7 +595,7 @@ def fit(self, maxlag=None, method='cmle', ic=None, trend='c',
         pinv_exog = np.linalg.pinv(X)
         normalized_cov_params = np.dot(pinv_exog, pinv_exog.T)
         arfit = ARResults(self, params, normalized_cov_params)
-        if method == 'mle':
+        if method == 'mle' and full_output:
             arfit.mle_retvals = mlefit.mle_retvals
             arfit.mle_settings = mlefit.mle_settings
         return ARResultsWrapper(arfit)


Test patch:
diff --git a/statsmodels/base/tests/test_optimize.py b/statsmodels/base/tests/test_optimize.py
--- a/statsmodels/base/tests/test_optimize.py
+++ b/statsmodels/base/tests/test_optimize.py
@@ -1,7 +1,8 @@
 from numpy.testing import assert_
 from statsmodels.base.optimizer import (_fit_newton, _fit_nm,
                                         _fit_bfgs, _fit_cg,
-                                        _fit_ncg, _fit_powell)
+                                        _fit_ncg, _fit_powell,
+                                        _fit_lbfgs, _fit_basinhopping)
 
 fit_funcs = {
     'newton': _fit_newton,
@@ -9,9 +10,16 @@
     'bfgs': _fit_bfgs,
     'cg': _fit_cg,
     'ncg': _fit_ncg,
-    'powell': _fit_powell
+    'powell': _fit_powell,
+    'lbfgs': _fit_lbfgs,
             }
 
+try:
+    from scipy.optimize import basinhopping
+    fit_funcs.update({'basinhopping': _fit_basinhopping})
+except ImportError:
+    pass
+
 
 def dummy_func(x):
     return x**2
@@ -34,16 +42,39 @@ def test_full_output_false():
     for method in fit_funcs:
         func = fit_funcs[method]
         if method == "newton":
-            xopts, retvals = func(dummy_func, dummy_score, [1], (), {},
+            xopt, retvals = func(dummy_func, dummy_score, [1], (), {},
                     hess=dummy_hess, full_output=False, disp=0)
 
         else:
-            xopts, retvals = func(dummy_func, dummy_score, [1], (), {},
+            xopt, retvals = func(dummy_func, dummy_score, [1], (), {},
                 full_output=False, disp=0)
-        assert_(xopts == None)
+        assert_(retvals is None)
         if method == "powell":
             #NOTE: I think I reported this? Might be version/optimize API
             # dependent
-            assert_(retvals.shape == () and retvals.size == 1)
+            assert_(xopt.shape == () and xopt.size == 1)
+        else:
+            assert_(len(xopt) == 1)
+
+
+def test_full_output():
+    for method in fit_funcs:
+        func = fit_funcs[method]
+        if method == "newton":
+            xopt, retvals = func(dummy_func, dummy_score, [1], (), {},
+                                 hess=dummy_hess, full_output=True, disp=0)
+
         else:
-            assert_(len(retvals)==1)
+            xopt, retvals = func(dummy_func, dummy_score, [1], (), {},
+                                 full_output=True, disp=0)
+
+        assert_(retvals is not None)
+        assert_('converged' in retvals)
+
+        if method == "powell":
+            #NOTE: I think I reported this? Might be version/optimize API
+            # dependent
+            assert_(xopt.shape == () and xopt.size == 1)
+        else:
+            assert_(len(xopt) == 1)
+
diff --git a/statsmodels/tsa/tests/test_ar.py b/statsmodels/tsa/tests/test_ar.py
--- a/statsmodels/tsa/tests/test_ar.py
+++ b/statsmodels/tsa/tests/test_ar.py
@@ -9,7 +9,7 @@
 from .results import results_ar
 import numpy as np
 import numpy.testing as npt
-from pandas import Series, Index
+from pandas import Series, Index, TimeSeries, DatetimeIndex
 
 DECIMAL_6 = 6
 DECIMAL_5 = 5
@@ -293,6 +293,16 @@ def test_ar_series():
     ar.bse
 
 
+def test_ar_select_order():
+    # 2118
+    np.random.seed(12345)
+    y = sm.tsa.arma_generate_sample([1, -.75, .3], [1], 100)
+    ts = TimeSeries(y, index=DatetimeIndex(start='1/1/1990', periods=100,
+                                           freq='M'))
+    ar = AR(ts)
+    res = ar.select_order(maxlag=12, ic='aic')
+    assert_(res == 2)
+
 #TODO: likelihood for ARX model?
 #class TestAutolagARX(object):
 #    def setup(self):