def test_against_ols(ols_data): mod = AbsorbingLS( ols_data.y, ols_data.x, absorb=ols_data.absorb, interactions=ols_data.interactions, weights=ols_data.weights, ) res = mod.fit() absorb = [] has_dummy = False if ols_data.absorb is not None: absorb.append(ols_data.absorb.cont.to_numpy()) if ols_data.absorb.cat.shape[1] > 0: dummies = dummy_matrix(ols_data.absorb.cat, precondition=False)[0] assert isinstance(dummies, sp.csc_matrix) absorb.append(dummies.A) has_dummy = ols_data.absorb.cat.shape[1] > 0 if ols_data.interactions is not None: for interact in ols_data.interactions: absorb.append(interact.sparse.A) _x = ols_data.x if absorb: absorb = np.column_stack(absorb) if np.any(np.ptp(_x, 0) == 0) and has_dummy: if ols_data.weights is None: absorb = annihilate(absorb, np.ones((absorb.shape[0], 1))) else: root_w = np.sqrt(mod.weights.ndarray) wabsorb = annihilate(root_w * absorb, root_w) absorb = (1.0 / root_w) * wabsorb rank = np.linalg.matrix_rank(absorb) if rank < absorb.shape[1]: a, b = np.linalg.eig(absorb.T @ absorb) order = np.argsort(a)[::-1] a, b = a[order], b[:, order] z = absorb @ b absorb = z[:, :rank] _x = np.column_stack([_x, absorb]) ols_mod = _OLS(ols_data.y, _x, weights=ols_data.weights) ols_res = ols_mod.fit() assert_results_equal(ols_res, res)
def wooldridge_score(self): r""" Wooldridge's score test of exogeneity Returns ------- t : WaldTestStatistic Object containing test statistic, p-value, distribution and null Notes ----- Wooldridge's test examines whether there is correlation between the errors produced when the endogenous variable are treated as exogenous so that the model can be fit by OLS, and the component of the endogenous variables that cannot be explained by the instruments. The test is implemented using a regression, .. math :: 1 = \gamma_1 \hat{\epsilon}_1 \hat{v}_{1,i} + \ldots + \gamma_p \hat{\epsilon}_1 \hat{v}_{p,i} + \eta_i where :math:`\hat{v}_{j,i}` is the residual from regressing endogenous variable :math:`x_j` on the exogenous variables and instruments. The test is a :math:`n\times R^2 \sim \chi^2_{p}`. Implemented using the expression in Wooldridge (2002), Eq. 6.19 """ from linearmodels.iv.model import _OLS e = annihilate(self.model.dependent.ndarray, self.model._x) r = annihilate(self.model.endog.ndarray, self.model._z) nobs = e.shape[0] r = annihilate(r, self.model._x) res = _OLS(ones((nobs, 1)), r * e).fit(cov_type='unadjusted') stat = res.nobs - res.resid_ss df = self.model.endog.shape[1] null = 'Endogenous variables are exogenous' name = 'Wooldridge\'s score test of exogeneity' return WaldTestStatistic(stat, null, df, name=name)
def wooldridge_overid(self): r""" Wooldridge's score test of overidentification Returns ------- t : WaldTestStatistic Object containing test statistic, p-value, distribution and null Notes ----- Wooldridge's test examines whether there is correlation between the model residuals and the component of the instruments that is orthogonal to the endogenous variables. Define :math:`\tilde{z}` to be the residuals of the instruments regressed on the exogenous variables and the first-stage fitted values of the endogenous variables. The test is computed as a regression .. math :: 1 = \gamma_1 \hat{\epsilon}_i \tilde{z}_{i,1} + \ldots + \gamma_q \hat{\epsilon}_i \tilde{z}_{i,q} where :math:`q = n_{instr} - n_{endog}`. The test is a :math:`n\times R^2 \sim \chi^2_{q}`. The order of the instruments does not affect this test. """ from linearmodels.iv.model import _OLS exog, endog = self.model.exog, self.model.endog instruments = self.model.instruments nobs, nendog = endog.shape ninstr = instruments.shape[1] if ninstr - nendog == 0: import warnings warnings.warn( 'Test requires more instruments than ' 'endogenous variables', UserWarning) return WaldTestStatistic(0, 'Test is not feasible.', 1, name='Infeasible test.') endog_hat = proj(endog.ndarray, c_[exog.ndarray, instruments.ndarray]) q = instruments.ndarray[:, :(ninstr - nendog)] q_res = annihilate(q, c_[self.model.exog.ndarray, endog_hat]) test_functions = q_res * self.resids.values[:, None] res = _OLS(ones((nobs, 1)), test_functions).fit('unadjusted') stat = res.nobs * res.rsquared df = ninstr - nendog null = 'Model is not overidentified.' name = 'Wooldridge\'s score test of overidentification' return WaldTestStatistic(stat, null, df, name=name)
def sargan(self): """ Sargan test of overidentifying restrictions Returns ------- t : WaldTestStatistic Object containing test statistic, p-value, distribution and null Notes ----- Requires more instruments than endogenous variables Tests the ratio of re-projected IV regression residual variance to variance of the IV residuals. .. math :: n(1-\hat{\epsilon}^{\prime}M_{Z}\hat{\epsilon}/ \hat{\epsilon}^{\prime}\hat{\epsilon})\sim\chi_{v}^{2} where :math:`M_{z}` is the annihilator matrix where z is the set of instruments and :math:`\hat{\epsilon}` are the residuals from the IV estimator. The degree of freedom is the difference between the number of instruments and the number of endogenous regressors. .. math :: v = n_{instr} - n_{exog} """ z = self.model.instruments.ndarray nobs, ninstr = z.shape nendog = self.model.endog.shape[1] name = 'Sargan\'s test of overidentification' if ninstr - nendog == 0: return InvalidTestStatistic( 'Test requires more instruments than ' 'endogenous variables.', name=name) eps = self.resids.values[:, None] u = annihilate(eps, self.model._z) stat = nobs * (1 - (u.T @ u) / (eps.T @ eps)).squeeze() null = 'The model is not overidentified.' return WaldTestStatistic(stat, null, ninstr - nendog, name=name)
def wooldridge_regression(self): r""" Wooldridge's regression test of exogeneity Returns ------- t : WaldTestStatistic Object containing test statistic, p-value, distribution and null Notes ----- Wooldridge's test examines whether there is correlation between the components of the endogenous variables that cannot be explained by the instruments and the OLS regression residuals. The test is implemented as an OLS where .. math :: y_i = x_{1i}\beta_i + x_{2i}\beta_2 + \hat{e}_i\gamma + \epsilon_i where :math:`x_{1i}` are the exogenous regressors, :math:`x_{2i}` are the endogenous regressors and :math:`\hat{e}_{i}` are the residuals from regressing the endogenous variables on the exogenous variables and instruments. The null is :math:`\gamma=0` and is implemented using a Wald test. The covariance estimator used in the test is identical to the covariance estimator used with ``fit``. """ from linearmodels.iv.model import _OLS r = annihilate(self.model.endog.ndarray, self.model._z) augx = c_[self.model._x, r] mod = _OLS(self.model.dependent, augx) res = mod.fit(self.cov_type, **self.cov_config) norig = self.model._x.shape[1] test_params = res.params.values[norig:] test_cov = res.cov.values[norig:, norig:] stat = test_params.T @ inv(test_cov) @ test_params df = len(test_params) null = 'Endogenous variables are exogenous' name = 'Wooldridge\'s regression test of exogeneity' return WaldTestStatistic(stat, null, df, name=name)