Repository: statsmodels/statsmodels

Issue:
ic in ARResults, aic, bic, hqic, fpe inconsistent definition?
aic, bic do not include k_trend, while fpe does.

Is this consistent?

The comments about the llf version of Stata count k_trend as estimated parameters.

(based on code included in htmlhelp for 0.4.1)



Discussion:
ARMAResults uses

-2_self.llf + np.log(nobs)_(self.df_model+1)

which includes trends

Hmm, yes. This may be inconsistent. It looks like I used stata's VAR to get the estimation results for AR and R's VARselect for the lag order selection, which we agree with. I'm inclined to just switch this to the ARIMA definition. In any event, I think we should penalize for estimating both k_trend and + 1 for estimating the variance.

Is the +1 for variance commonly used?   For linear model, I think we only have the mean parameters in the penalization term.

It looks like it, at least for MLE models since we do actually estimate the variance (even if it's concentrated out). For the linear regression models, no I don't think so. The only package that has pure AR (with information criteria) is R and I think that's why I used it. Stata and gretl are ARIMA only.

Eg., you get the same as stata and gretl arima with

-2 \* self.llf + 2*(self.k_ar+self.k_trend+1)


Resolving patch:
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
@@ -629,10 +629,10 @@ class ARResults(tsbase.TimeSeriesModelResults):
 
     aic : float
         Akaike Information Criterion using Lutkephol's definition.
-        :math:`log(sigma) + 2*(1+k_ar)/nobs`
+        :math:`log(sigma) + 2*(1 + k_ar + k_trend)/nobs`
     bic : float
         Bayes Information Criterion
-        :math:`\\log(\\sigma) + (1+k_ar)*\\log(nobs)/nobs`
+        :math:`\\log(\\sigma) + (1 + k_ar + k_trend)*\\log(nobs)/nobs`
     bse : array
         The standard errors of the estimated parameters. If `method` is 'cmle',
         then the standard errors that are returned are the OLS standard errors
@@ -702,7 +702,8 @@ def __init__(self, model, params, normalized_cov_params=None, scale=1.):
             trendorder = k_trend - 1
         self.trendorder = 1
         #TODO: cmle vs mle?
-        self.df_resid = self.model.df_resid = n_totobs - k_ar - k_trend
+        self.df_model = k_ar + k_trend
+        self.df_resid = self.model.df_resid = n_totobs - self.df_model
 
     @cache_writable()
     def sigma2(self):
@@ -737,7 +738,7 @@ def aic(self):
         # Lutkepohl
         #return np.log(self.sigma2) + 1./self.model.nobs * self.k_ar
         # Include constant as estimated free parameter and double the loss
-        return np.log(self.sigma2) + 2 * (1 + self.k_ar)/self.nobs
+        return np.log(self.sigma2) + 2 * (1 + self.df_model)/self.nobs
         # Stata defintion
         #nobs = self.nobs
         #return -2 * self.llf/nobs + 2 * (self.k_ar+self.k_trend)/nobs
@@ -749,7 +750,7 @@ def hqic(self):
         # return np.log(self.sigma2)+ 2 * np.log(np.log(nobs))/nobs * self.k_ar
         # R uses all estimated parameters rather than just lags
         return np.log(self.sigma2) + 2 * np.log(np.log(nobs))/nobs * \
-                (1 + self.k_ar)
+                (1 + self.df_model)
         # Stata
         #nobs = self.nobs
         #return -2 * self.llf/nobs + 2 * np.log(np.log(nobs))/nobs * \
@@ -758,10 +759,9 @@ def hqic(self):
     @cache_readonly
     def fpe(self):
         nobs = self.nobs
-        k_ar = self.k_ar
-        k_trend = self.k_trend
+        df_model = self.df_model
         #Lutkepohl
-        return ((nobs+k_ar+k_trend)/(nobs-k_ar-k_trend))*self.sigma2
+        return ((nobs+df_model)/(nobs-df_model))*self.sigma2
 
     @cache_readonly
     def bic(self):
@@ -769,7 +769,7 @@ def bic(self):
         # Lutkepohl
         #return np.log(self.sigma2) + np.log(nobs)/nobs * self.k_ar
         # Include constant as est. free parameter
-        return np.log(self.sigma2) + (1 + self.k_ar) * np.log(nobs)/nobs
+        return np.log(self.sigma2) + (1 + self.df_model) * np.log(nobs)/nobs
         # Stata
         # return -2 * self.llf/nobs + np.log(nobs)/nobs * (self.k_ar + \
         #       self.k_trend)


Test patch:
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
@@ -213,11 +213,33 @@ def setupClass(cls):
         for lag in range(1,16+1):
             endog_tmp = endog[16-lag:]
             r = AR(endog_tmp).fit(maxlag=lag)
-            results.append([r.aic, r.hqic, r.bic, r.fpe])
-        cls.res1 = np.asarray(results).T.reshape(4,-1, order='C')
+            # See issue #324 for why we're doing these corrections vs. R
+            # results
+            k_ar = r.k_ar
+            k_trend = r.k_trend
+            log_sigma2 = np.log(r.sigma2)
+            #import ipdb; ipdb.set_trace()
+            aic = r.aic
+            aic = (aic - log_sigma2) * (1 + k_ar)/(1 + k_ar + k_trend)
+            aic += log_sigma2
+
+            hqic = r.hqic
+            hqic = (hqic - log_sigma2) * (1 + k_ar)/(1 + k_ar + k_trend)
+            hqic += log_sigma2
+
+            bic = r.bic
+            bic = (bic - log_sigma2) * (1 + k_ar)/(1 + k_ar + k_trend)
+            bic += log_sigma2
+
+
+            results.append([aic, hqic, bic, r.fpe])
+        res1 = np.asarray(results).T.reshape(4,-1, order='C')
+        # aic correction to match R
+        cls.res1 = res1
         cls.res2 = results_ar.ARLagResults("const").ic
 
     def test_ic(self):
+
         npt.assert_almost_equal(self.res1, self.res2, DECIMAL_6)
 
 def test_ar_dates():