Repository: statsmodels/statsmodels

Issue:
fixed scale in WLS
If weights are actual inverse variance for the residual, then the scale should be 1.

https://groups.google.com/d/msg/pystatsmodels/DqhoHzAxV3U/RS1xRIJAbtIJ
https://groups.google.com/d/msg/pystatsmodels/KCTCw-zDjN0/k0OgDZIdUycJ
and related discussions for scipy curve_fit

Stata has VWLS

implementation as a cov_type in #2137 



Discussion:

Resolving patch:
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
@@ -1658,6 +1658,8 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         use_t : bool
             If true, then the t distribution is used for inference.
             If false, then the normal distribution is used.
+            If `use_t` is None, then an appropriate default is used, which is
+            `true` if the cov_type is nonrobust, and `false` in all other cases.
         kwds : depends on cov_type
             Required or optional arguments for robust covariance calculation.
             see Notes below
@@ -1675,6 +1677,8 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         The following covariance types and required or optional arguments are
         currently available:
 
+        - 'fixed scale' and optional keyword argument 'scale' which uses
+            a predefined scale estimate with default equal to one.
         - 'HC0', 'HC1', 'HC2', 'HC3' and no keyword arguments:
             heteroscedasticity robust covariance
         - 'HAC' and keywords
@@ -1778,7 +1782,13 @@ def get_robustcov_results(self, cov_type='HC1', use_t=None, **kwds):
         # TODO: this should be outsourced in a function so we can reuse it in
         #       other models
         # TODO: make it DRYer   repeated code for checking kwds
-        if cov_type in ('HC0', 'HC1', 'HC2', 'HC3'):
+        if cov_type in ['fixed scale', 'fixed_scale']:
+            res.cov_kwds['description'] = ('Standard Errors are based on ' +
+                                           'fixed scale')
+
+            res.cov_kwds['scale'] = scale = kwds.get('scale', 1.)
+            res.cov_params_default = scale * res.normalized_cov_params
+        elif cov_type in ('HC0', 'HC1', 'HC2', 'HC3'):
             if kwds:
                 raise ValueError('heteroscedasticity robust covarians ' +
                                  'does not use keywords')


Test patch:
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
@@ -766,3 +766,59 @@ def test_all(self):
             ft2 = res2.f_test(mat)
             assert_allclose(ft1.fvalue, ft2.fvalue, rtol=1e-13)
             assert_allclose(ft1.pvalue, ft2.pvalue, rtol=1e-12)
+
+    def test_fixed_scale(self):
+        cov_type = 'fixed_scale'
+        kwds = {}
+        res1 = self.res_ols.get_robustcov_results(cov_type, **kwds)
+        res2 = self.res_wls.get_robustcov_results(cov_type, **kwds)
+        assert_allclose(res1.params, res2.params, rtol=1e-13)
+        assert_allclose(res1.cov_params(), res2.cov_params(), rtol=1e-13)
+        assert_allclose(res1.bse, res2.bse, rtol=1e-13)
+        assert_allclose(res1.pvalues, res2.pvalues, rtol=1e-13)
+
+        tt = res2.t_test(np.eye(len(res2.params)),
+                         cov_p=res2.normalized_cov_params)
+        assert_allclose(res2.cov_params(), res2.normalized_cov_params,
+                        rtol=1e-13)
+        assert_allclose(res2.bse, tt.sd, rtol=1e-13)
+        assert_allclose(res2.pvalues, tt.pvalue, rtol=1e-13)
+        assert_allclose(res2.tvalues, tt.tvalue, rtol=1e-13)
+
+        # using cov_type in fit
+        mod = self.res_wls.model
+        mod3 = WLS(mod.endog, mod.exog, weights=mod.weights)
+        res3 = mod3.fit(cov_type=cov_type, cov_kwds=kwds)
+        tt = res3.t_test(np.eye(len(res3.params)),
+                         cov_p=res3.normalized_cov_params)
+        assert_allclose(res3.cov_params(), res3.normalized_cov_params,
+                        rtol=1e-13)
+        assert_allclose(res3.bse, tt.sd, rtol=1e-13)
+        assert_allclose(res3.pvalues, tt.pvalue, rtol=1e-13)
+        assert_allclose(res3.tvalues, tt.tvalue, rtol=1e-13)
+
+
+def test_cov_type_fixed_scale():
+    # this is a unit test from scipy curvefit for `absolute_sigma` keyword
+    xdata = np.array([0, 1, 2, 3, 4, 5])
+    ydata = np.array([1, 1, 5, 7, 8, 12])
+    sigma = np.array([1, 2, 1, 2, 1, 2])
+
+    xdata = np.column_stack((xdata, np.ones(len(xdata))))
+    weights = 1. / sigma**2
+
+    res = WLS(ydata, xdata, weights=weights).fit()
+    assert_allclose(res.bse, [0.20659803, 0.57204404], rtol=1e-3)
+
+    res = WLS(ydata, xdata, weights=weights).fit()
+    assert_allclose(res.bse, [0.20659803, 0.57204404], rtol=1e-3)
+
+    res = WLS(ydata, xdata, weights=weights).fit(cov_type='fixed scale')
+    assert_allclose(res.bse, [0.30714756, 0.85045308], rtol=1e-3)
+
+    res = WLS(ydata, xdata, weights=weights / 9.).fit(cov_type='fixed scale')
+    assert_allclose(res.bse, [3*0.30714756, 3*0.85045308], rtol=1e-3)
+
+    res = WLS(ydata, xdata, weights=weights).fit(cov_type='fixed scale',
+                                                  cov_kwds={'scale':9})
+    assert_allclose(res.bse, [3*0.30714756, 3*0.85045308], rtol=1e-3)