Hypothesis tests

crabbymetrics exposes hypothesis tests in two layers:

The estimator method is the preferred path when the fitted object owns the covariance calculation.

import numpy as np
import crabbymetrics as cm

rng = np.random.default_rng(123)
x = rng.normal(size=(300, 3))
y = 1.0 + x @ np.array([0.0, 0.0, 0.8]) + rng.normal(scale=0.7, size=300)

model = cm.OLS()
model.fit(x, y)

# Joint test that the first two slope coefficients are zero.
R = np.array([
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
])
model.wald_test(R, vcov="hc1")
{'statistic': 1.1440001997700853,
 'df': 2,
 'p_value': 0.5643954617446911,
 'test': 'wald'}

For intercept-bearing estimators, the restriction matrix uses coefficient order [intercept, slopes...]. For fixed-effects summaries without an intercept, use the reported slope order.

The same test can be run manually from arrays when needed:

summary = model.summary(vcov="hc1")
coef = np.r_[summary["intercept"], summary["coef"]]
cm.wald_test(coef, summary["vcov"], R)
{'statistic': 1.1440001997700853,
 'df': 2,
 'p_value': 0.5643954617446911,
 'test': 'wald'}

For IV models with one endogenous regressor, the Anderson Rubin method tests \(H_0: \beta = \beta_0\) by checking whether the excluded instruments predict y - beta0 * d after accounting for included exogenous regressors. This remains valid under weak instruments.

n = 300
x_exog = rng.normal(size=(n, 2))
z = rng.normal(size=(n, 2))
x_endog = 0.8 * z[:, [0]] - 0.4 * z[:, [1]] + rng.normal(size=(n, 1))
y_iv = 0.5 + 1.2 * x_endog[:, 0] + x_exog @ np.array([0.3, -0.2]) + rng.normal(size=n)

iv = cm.TwoSLS()
iv.fit(x_endog, x_exog, z, y_iv)
iv.anderson_rubin_test(beta=1.2, vcov="hc1")
{'statistic': 0.492874433101036,
 'wald_statistic': 0.985748866202072,
 'df_num': 2,
 'df_denom': 295,
 'p_value': 0.611370092172306,
 'beta': 1.2,
 'vcov_type': 'hc1',
 'test': 'Anderson Rubin'}

Likelihood-ratio tests are intentionally kept as array/scalar utilities for now, because they compare two nested likelihood values rather than one fitted covariance summary.

cm.lr_test(unrestricted_loglik=-100.0, restricted_loglik=-103.0, df=2)
{'statistic': 6.0,
 'df': 2,
 'p_value': 0.04978706836786395,
 'test': 'likelihood_ratio'}