Repository: statsmodels/statsmodels

Issue:
incorrect name of panel hac
Fixes https://github.com/statsmodels/statsmodels/issues/2578



Discussion:
[![Coverage Status](https://coveralls.io/builds/5503957/badge)](https://coveralls.io/builds/5503957)

Coverage remained the same at 85.378% when pulling **01def9dff8eee17e62ea57748acce3aefac78174 on theaverageguy:Issue#2578** into **eed4ae5a90a1d1ac312c24bbebf2bff88fe87da9 on statsmodels:master**.


Resolving patch:
diff --git a/statsmodels/base/covtype.py b/statsmodels/base/covtype.py
--- a/statsmodels/base/covtype.py
+++ b/statsmodels/base/covtype.py
@@ -114,7 +114,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
               #TODO: we need more options here
 
     Reminder:
-    `use_correction` in "nw-groupsum" and "nw-panel" is not bool,
+    `use_correction` in "hac-groupsum" and "hac-panel" is not bool,
     needs to be in [False, 'hac', 'cluster']
 
     TODO: Currently there is no check for extra or misspelled keywords,
@@ -124,6 +124,14 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
 
     import statsmodels.stats.sandwich_covariance as sw
 
+    #normalize names
+    if cov_type == 'nw-panel':
+        cov_type = 'hac-panel'
+    if cov_type == 'nw-groupsum':
+        cov_type = 'hac-groupsum'
+    if 'kernel' in kwds:
+            kwds['weights_func'] = kwds.pop('kernel')
+
     # TODO: make separate function that returns a robust cov plus info
     use_self = kwds.pop('use_self', False)
     if use_self:
@@ -142,7 +150,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
     res.use_t = use_t
 
     adjust_df = False
-    if cov_type in ['cluster', 'nw-panel', 'nw-groupsum']:
+    if cov_type in ['cluster', 'hac-panel', 'hac-groupsum']:
         df_correction = kwds.get('df_correction', None)
         # TODO: check also use_correction, do I need all combinations?
         if df_correction is not False: # i.e. in [None, True]:
@@ -170,6 +178,8 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
     elif cov_type == 'HAC':
         maxlags = kwds['maxlags']   # required?, default in cov_hac_simple
         res.cov_kwds['maxlags'] = maxlags
+        weights_func = kwds.get('weights_func', sw.weights_bartlett)
+        res.cov_kwds['weights_func'] = weights_func
         use_correction = kwds.get('use_correction', False)
         res.cov_kwds['use_correction'] = use_correction
         res.cov_kwds['description'] = ('Standard Errors are heteroscedasticity ' +
@@ -177,6 +187,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
              'sample correction') % (maxlags, ['without', 'with'][use_correction])
 
         res.cov_params_default = sw.cov_hac_simple(self, nlags=maxlags,
+                                             weights_func=weights_func,
                                              use_correction=use_correction)
     elif cov_type == 'cluster':
         #cluster robust standard errors, one- or two-way
@@ -218,7 +229,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         res.cov_kwds['description'] = ('Standard Errors are robust to' +
                             'cluster correlation ' + '(' + cov_type + ')')
 
-    elif cov_type == 'nw-panel':
+    elif cov_type == 'hac-panel':
         #cluster robust standard errors
         res.cov_kwds['time'] = time = kwds['time']
         #TODO: nlags is currently required
@@ -239,7 +250,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
                                             use_correction=use_correction)
         res.cov_kwds['description'] = ('Standard Errors are robust to' +
                             'cluster correlation ' + '(' + cov_type + ')')
-    elif cov_type == 'nw-groupsum':
+    elif cov_type == 'hac-groupsum':
         # Driscoll-Kraay standard errors
         res.cov_kwds['time'] = time = kwds['time']
         #TODO: nlags is currently required
diff --git a/statsmodels/regression/linear_model.py b/statsmodels/regression/linear_model.py
--- a/statsmodels/regression/linear_model.py
+++ b/statsmodels/regression/linear_model.py
@@ -1915,7 +1915,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
                   #TODO: we need more options here
 
         Reminder:
-        `use_correction` in "nw-groupsum" and "nw-panel" is not bool,
+        `use_correction` in "hac-groupsum" and "hac-panel" is not bool,
         needs to be in [False, 'hac', 'cluster']
 
         TODO: Currently there is no check for extra or misspelled keywords,
@@ -1925,6 +1925,14 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
 
         import statsmodels.stats.sandwich_covariance as sw
 
+        #normalize names
+        if cov_type == 'nw-panel':
+            cov_type = 'hac-panel'
+        if cov_type == 'nw-groupsum':
+            cov_type = 'hac-groupsum'
+        if 'kernel' in kwds:
+            kwds['weights_func'] = kwds.pop('kernel')
+
         # TODO: make separate function that returns a robust cov plus info
         use_self = kwds.pop('use_self', False)
         if use_self:
@@ -1942,7 +1950,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         res.use_t = use_t
 
         adjust_df = False
-        if cov_type in ['cluster', 'nw-panel', 'nw-groupsum']:
+        if cov_type in ['cluster', 'hac-panel', 'hac-groupsum']:
             df_correction = kwds.get('df_correction', None)
             # TODO: check also use_correction, do I need all combinations?
             if df_correction is not False: # i.e. in [None, True]:
@@ -1973,6 +1981,8 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         elif cov_type == 'HAC':
             maxlags = kwds['maxlags']   # required?, default in cov_hac_simple
             res.cov_kwds['maxlags'] = maxlags
+            weights_func = kwds.get('weights_func', sw.weights_bartlett)
+            res.cov_kwds['weights_func'] = weights_func
             use_correction = kwds.get('use_correction', False)
             res.cov_kwds['use_correction'] = use_correction
             res.cov_kwds['description'] = ('Standard Errors are heteroscedasticity ' +
@@ -1980,6 +1990,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
                  'sample correction') % (maxlags, ['without', 'with'][use_correction])
 
             res.cov_params_default = sw.cov_hac_simple(self, nlags=maxlags,
+                                                 weights_func=weights_func,
                                                  use_correction=use_correction)
         elif cov_type == 'cluster':
             #cluster robust standard errors, one- or two-way
@@ -2021,7 +2032,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
             res.cov_kwds['description'] = ('Standard Errors are robust to' +
                                 'cluster correlation ' + '(' + cov_type + ')')
 
-        elif cov_type == 'nw-panel':
+        elif cov_type == 'hac-panel':
             #cluster robust standard errors
             res.cov_kwds['time'] = time = kwds['time']
             #TODO: nlags is currently required
@@ -2042,7 +2053,7 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
                                                 use_correction=use_correction)
             res.cov_kwds['description'] = ('Standard Errors are robust to' +
                                 'cluster correlation ' + '(' + cov_type + ')')
-        elif cov_type == 'nw-groupsum':
+        elif cov_type == 'hac-groupsum':
             # Driscoll-Kraay standard errors
             res.cov_kwds['time'] = time = kwds['time']
             #TODO: nlags is currently required


Test patch:
diff --git a/statsmodels/discrete/tests/test_sandwich_cov.py b/statsmodels/discrete/tests/test_sandwich_cov.py
--- a/statsmodels/discrete/tests/test_sandwich_cov.py
+++ b/statsmodels/discrete/tests/test_sandwich_cov.py
@@ -16,9 +16,11 @@
 from statsmodels.regression.linear_model import OLS
 import statsmodels.stats.sandwich_covariance as sc
 from statsmodels.base.covtype import get_robustcov_results
+import statsmodels.stats.sandwich_covariance as sw
 from statsmodels.tools.tools import add_constant
 
-from numpy.testing import assert_allclose, assert_equal
+
+from numpy.testing import assert_allclose, assert_equal, assert_
 import statsmodels.tools._testing as smt
 
 
@@ -555,6 +557,113 @@ def setup_class(cls):
         cls.res2 = mod2.fit(cov_type='HC0')
 
 
+class TestGLMGaussHAC(CheckDiscreteGLM):
+
+    @classmethod
+    def setup_class(cls):
+
+        cls.cov_type = 'HAC'
+
+        kwds={'maxlags':2}
+        mod1 = GLM(endog, exog, family=families.Gaussian())
+        cls.res1 = mod1.fit(cov_type='HAC', cov_kwds=kwds)
+
+        mod2 = OLS(endog, exog)
+        cls.res2 = mod2.fit(cov_type='HAC', cov_kwds=kwds)
+
+
+class TestGLMGaussHACUniform(CheckDiscreteGLM):
+
+    @classmethod
+    def setup_class(cls):
+
+        cls.cov_type = 'HAC'
+
+        kwds={'kernel':sw.weights_uniform, 'maxlags':2}
+        mod1 = GLM(endog, exog, family=families.Gaussian())
+        cls.res1 = mod1.fit(cov_type='HAC', cov_kwds=kwds)
+
+        mod2 = OLS(endog, exog)
+        cls.res2 = mod2.fit(cov_type='HAC', cov_kwds=kwds)
+
+        #for debugging
+        cls.res3 = mod2.fit(cov_type='HAC', cov_kwds={'maxlags':2})
+
+
+    def test_cov_options(self):
+
+        # check keyword `weights_func
+        kwdsa = {'weights_func':sw.weights_uniform, 'maxlags':2}
+        res1a = self.res1.model.fit(cov_type='HAC', cov_kwds=kwdsa)
+        res2a = self.res2.model.fit(cov_type='HAC', cov_kwds=kwdsa)
+        assert_allclose(res1a.bse, self.res1.bse, rtol=1e-12)
+        assert_allclose(res2a.bse, self.res2.bse, rtol=1e-12)
+
+        # regression test for bse values
+        bse = np.array([  2.82203924,   4.60199596,  11.01275064])
+        assert_allclose(res1a.bse, bse, rtol=1e-6)
+
+        assert_(res1a.cov_kwds['weights_func'] is sw.weights_uniform)
+
+        kwdsb = {'kernel':sw.weights_bartlett, 'maxlags':2}
+        res1a = self.res1.model.fit(cov_type='HAC', cov_kwds=kwdsb)
+        res2a = self.res2.model.fit(cov_type='HAC', cov_kwds=kwdsb)
+        assert_allclose(res1a.bse, res2a.bse, rtol=1e-12)
+
+        # regression test for bse values
+        bse = np.array([  2.502264,  3.697807,  9.193303])
+        assert_allclose(res1a.bse, bse, rtol=1e-6)
+
+
+class TestGLMGaussHACPanel(CheckDiscreteGLM):
+
+    @classmethod
+    def setup_class(cls):
+        import statsmodels.stats.sandwich_covariance as sw
+        cls.cov_type = 'hac-panel'
+        # time index is just made up to have a test case
+        time = np.tile(np.arange(7), 5)[:-1]
+        mod1 = GLM(endog.copy(), exog.copy(), family=families.Gaussian())
+        kwds = dict(time=time,
+                    maxlags=2,
+                    kernel=sw.weights_uniform,
+                    use_correction='hac',
+                    df_correction=False)
+        cls.res1 = mod1.fit(cov_type='hac-panel', cov_kwds=kwds)
+        cls.res1b = mod1.fit(cov_type='nw-panel', cov_kwds=kwds)
+
+        mod2 = OLS(endog, exog)
+        cls.res2 = mod2.fit(cov_type='hac-panel', cov_kwds=kwds)
+
+    def test_kwd(self):
+        # test corrected keyword name
+        assert_allclose(self.res1b.bse, self.res1.bse, rtol=1e-12)
+
+
+class TestGLMGaussHACGroupsum(CheckDiscreteGLM):
+
+    @classmethod
+    def setup_class(cls):
+        cls.cov_type = 'hac-groupsum'
+        # time index is just made up to have a test case
+        time = np.tile(np.arange(7), 5)[:-1]
+        mod1 = GLM(endog, exog, family=families.Gaussian())
+        kwds = dict(time=time,
+                    maxlags=2,
+                    use_correction='hac',
+                    df_correction=False)
+        cls.res1 = mod1.fit(cov_type='hac-groupsum', cov_kwds=kwds)
+        cls.res1b = mod1.fit(cov_type='nw-groupsum', cov_kwds=kwds)
+
+        mod2 = OLS(endog, exog)
+        cls.res2 = mod2.fit(cov_type='hac-groupsum', cov_kwds=kwds)
+
+    def test_kwd(self):
+        # test corrected keyword name
+        assert_allclose(self.res1b.bse, self.res1.bse, rtol=1e-12)
+
+
+
 if __name__ == '__main__':
     tt = TestPoissonClu()
     tt.setup_class()
diff --git a/statsmodels/regression/tests/test_robustcov.py b/statsmodels/regression/tests/test_robustcov.py
--- a/statsmodels/regression/tests/test_robustcov.py
+++ b/statsmodels/regression/tests/test_robustcov.py
@@ -594,6 +594,17 @@ def setup(self):
         self.rtolh = 1e-10
 
 
+    def test_keyword(self):
+        # check corrected keyword
+        res_ols = self.res1.get_robustcov_results('hac-panel',
+                                                  time=self.time,
+                                                  maxlags=4,
+                                                  use_correction='hac',
+                                                  use_t=True,
+                                                  df_correction=False)
+        assert_allclose(res_ols.bse, self.res1.bse, rtol=1e-12)
+
+
 # TODO: low precision/agreement
 class TestOLSRobustCluster2G(CheckOLSRobustCluster, CheckOLSRobustNewMixin):
     # compare with `reg cluster`