def test_glm(constraints=None): # More comprehensive tests against GLM estimates (this is sort of redundant # given `test_ols`, but this is mostly to complement the tests in # `test_glm_constrained`) endog = dta.infl exog = add_constant(dta[['unemp', 'm1']]) mod = RecursiveLS(endog, exog, constraints=constraints) res = mod.fit() mod_glm = GLM(endog, exog) if constraints is None: res_glm = mod_glm.fit() else: res_glm = mod_glm.fit_constrained(constraints=constraints) # Regression coefficients, standard errors, and estimated scale assert_allclose(res.params, res_glm.params) assert_allclose(res.bse, res_glm.bse, atol=1e-6) # Note: scale here is computed according to Harvey, 1989, 4.2.5, and is # the called the ML estimator and sometimes (e.g. later in section 5) # denoted \tilde \sigma_*^2 assert_allclose(res.filter_results.obs_cov[0, 0], res_glm.scale) # DoF # Note: GLM does not include intercept in DoF, so modify by -1 assert_equal(res.df_model - 1, res_glm.df_model) # OLS residuals are equivalent to smoothed forecast errors # (the latter are defined as e_t|T by Harvey, 1989, 5.4.5) # (this follows since the smoothed state simply contains the # full-information estimates of the regression coefficients) actual = (mod.endog[:, 0] - np.sum(mod['design', 0, :, :] * res.smoothed_state, axis=0)) assert_allclose(actual, res_glm.resid_response, atol=1e-7) # Given the estimate of scale as `sum(v_t^2 / f_t) / (T - d)` (see # Harvey, 1989, 4.2.5 on p. 183), then llf_recursive is equivalent to the # full OLS loglikelihood (i.e. without the scale concentrated out). desired = mod_glm.loglike(res_glm.params, scale=res_glm.scale) assert_allclose(res.llf_recursive, desired) # Alternatively, we can construct the concentrated OLS loglikelihood # by computing the scale term with `nobs` in the denominator rather than # `nobs - d`. scale_alternative = np.sum( (res.standardized_forecasts_error[0, 1:] * res.filter_results.obs_cov[0, 0]**0.5)**2) / mod.nobs llf_alternative = np.log( norm.pdf(res.resid_recursive, loc=0, scale=scale_alternative**0.5)).sum() assert_allclose(llf_alternative, res_glm.llf) # Prediction # TODO: prediction in this case is not working. if constraints is None: design = np.ones((1, 3, 10)) actual = res.forecast(10, design=design) assert_allclose(actual, res_glm.predict(np.ones((10, 3)))) else: design = np.ones((2, 3, 10)) assert_raises(NotImplementedError, res.forecast, 10, design=design) # Hypothesis tests actual = res.t_test('m1 = 0') desired = res_glm.t_test('m1 = 0') assert_allclose(actual.statistic, desired.statistic) assert_allclose(actual.pvalue, desired.pvalue, atol=1e-15) actual = res.f_test('m1 = 0') desired = res_glm.f_test('m1 = 0') assert_allclose(actual.statistic, desired.statistic) assert_allclose(actual.pvalue, desired.pvalue) # Information criteria # Note: the llf and llf_obs given in the results are based on the Kalman # filter and so the ic given in results will not be identical to the # OLS versions. Additionally, llf_recursive is comparable to the # non-concentrated llf, and not the concentrated llf that is by default # used in OLS. Compute new ic based on llf_alternative to compare. actual_aic = aic(llf_alternative, res.nobs_effective, res.df_model) assert_allclose(actual_aic, res_glm.aic)
def test_glm(constraints=None): # More comprehensive tests against GLM estimates (this is sort of redundant # given `test_ols`, but this is mostly to complement the tests in # `test_glm_constrained`) endog = dta.infl exog = add_constant(dta[['unemp', 'm1']]) mod = RecursiveLS(endog, exog, constraints=constraints) res = mod.fit() mod_glm = GLM(endog, exog) if constraints is None: res_glm = mod_glm.fit() else: res_glm = mod_glm.fit_constrained(constraints=constraints) # Regression coefficients, standard errors, and estimated scale assert_allclose(res.params, res_glm.params) assert_allclose(res.bse, res_glm.bse, atol=1e-6) # Note: scale here is computed according to Harvey, 1989, 4.2.5, and is # the called the ML estimator and sometimes (e.g. later in section 5) # denoted \tilde \sigma_*^2 assert_allclose(res.filter_results.obs_cov[0, 0], res_glm.scale) # DoF # Note: GLM does not include intercept in DoF, so modify by -1 assert_equal(res.df_model - 1, res_glm.df_model) # OLS residuals are equivalent to smoothed forecast errors # (the latter are defined as e_t|T by Harvey, 1989, 5.4.5) # (this follows since the smoothed state simply contains the # full-information estimates of the regression coefficients) actual = (mod.endog[:, 0] - np.sum(mod['design', 0, :, :] * res.smoothed_state, axis=0)) assert_allclose(actual, res_glm.resid_response, atol=1e-7) # Given the estimate of scale as `sum(v_t^2 / f_t) / (T - d)` (see # Harvey, 1989, 4.2.5 on p. 183), then llf_recursive is equivalent to the # full OLS loglikelihood (i.e. without the scale concentrated out). desired = mod_glm.loglike(res_glm.params, scale=res_glm.scale) assert_allclose(res.llf_recursive, desired) # Alternatively, we can construct the concentrated OLS loglikelihood # by computing the scale term with `nobs` in the denominator rather than # `nobs - d`. scale_alternative = np.sum(( res.standardized_forecasts_error[0, 1:] * res.filter_results.obs_cov[0, 0]**0.5)**2) / mod.nobs llf_alternative = np.log(norm.pdf(res.resid_recursive, loc=0, scale=scale_alternative**0.5)).sum() assert_allclose(llf_alternative, res_glm.llf) # Prediction # TODO: prediction in this case is not working. if constraints is None: design = np.ones((1, 3, 10)) actual = res.forecast(10, design=design) assert_allclose(actual, res_glm.predict(np.ones((10, 3)))) else: design = np.ones((2, 3, 10)) assert_raises(NotImplementedError, res.forecast, 10, design=design) # Hypothesis tests actual = res.t_test('m1 = 0') desired = res_glm.t_test('m1 = 0') assert_allclose(actual.statistic, desired.statistic) assert_allclose(actual.pvalue, desired.pvalue, atol=1e-15) actual = res.f_test('m1 = 0') desired = res_glm.f_test('m1 = 0') assert_allclose(actual.statistic, desired.statistic) assert_allclose(actual.pvalue, desired.pvalue) # Information criteria # Note: the llf and llf_obs given in the results are based on the Kalman # filter and so the ic given in results will not be identical to the # OLS versions. Additionally, llf_recursive is comparable to the # non-concentrated llf, and not the concentrated llf that is by default # used in OLS. Compute new ic based on llf_alternative to compare. actual_aic = aic(llf_alternative, res.nobs_effective, res.df_model) assert_allclose(actual_aic, res_glm.aic)