外观
豪斯曼检验
固定效应与随机效应的模型设定检验:hausman, sigmamore)。
核心代码
py
@staticmethod
def hausman_test(
df: pd.DataFrame,
y_var: str,
x_vars: List[str],
entity_col: str,
time_col: str,
decimals: int = 4,
sigmamore: bool = False
) -> Dict[str, Any]:
"""
Hausman检验:固定效应模型 vs 随机效应模型
参数:
df: 数据框
y_var: 因变量名
x_vars: 自变量列表
entity_col: 个体标识列
time_col: 时间标识列
decimals: 小数位数
sigmamore: 是否使用sigmamore选项(基于随机效应统一方差估计)
返回:
包含检验结果的字典
"""
# 准备数据
cols = [y_var] + x_vars + [entity_col, time_col]
df_clean = df[cols].dropna()
df_clean, entity_alias, time_alias = ModelTests._prepare_panel_test_df(
df_clean,
entity_col,
time_col
)
df_clean = drop_singletons_func(df_clean, entity_alias)
df_panel = df_clean.set_index([entity_alias, time_alias])
y = df_panel[y_var]
x = df_panel[x_vars]
# 1. 估计固定效应模型(添加常数项)
# 注意:Stata的hausman检验使用constant选项时会包含常数项
x_with_const_fe = add_constant(x)
fe_model = PanelOLS(y, x_with_const_fe, entity_effects=True, drop_absorbed=True)
fe_result = fe_model.fit(cov_type='unadjusted')
# 2. 估计随机效应模型(添加常数项)
x_with_const = add_constant(x)
re_model = RandomEffects(y, x_with_const)
re_result = re_model.fit(cov_type='unadjusted')
# 提取系数
beta_fe = fe_result.params
beta_re = re_result.params
# 确保两个模型的变量顺序一致
common_vars = [v for v in beta_fe.index if v in beta_re.index]
beta_fe = beta_fe[common_vars]
beta_re = beta_re[common_vars]
# 3. 应用sigmamore选项(如果启用)
if sigmamore:
# 根据Stata官方hausman.ado源代码(第157-169行):
# if "`sigmamore'" != "" {
# matrix `V1' = ((`s2_2'/`s2_1')^2) * `V1'
# }
# 其中:
# - V1 是固定效应模型的协方差矩阵
# - s2_1 是FE模型的sigma_e
# - s2_2 是RE模型的rmse
# 计算FE模型的sigma_e
s2_fe = np.sqrt(fe_result.resid_ss / fe_result.df_resid)
# 计算RE模型的rmse(使用Pooled OLS,因为sigma_u可能为0)
pooled_model = PooledOLS(y, x_with_const)
pooled_result = pooled_model.fit(cov_type='unadjusted')
s2_re = np.sqrt(pooled_result.resid_ss / pooled_result.df_resid)
# 计算缩放因子
scaling_factor = (s2_re / s2_fe) ** 2
# 应用缩放到FE的协方差矩阵
cov_fe = scaling_factor * fe_result.cov.loc[common_vars, common_vars]
# RE使用pooled结果的协方差矩阵
cov_re = pooled_result.cov.loc[common_vars, common_vars]
else:
# 不使用sigmamore,直接使用原始协方差矩阵
cov_fe = fe_result.cov.loc[common_vars, common_vars]
cov_re = re_result.cov.loc[common_vars, common_vars]
# 3. 计算Hausman统计量
# H = (beta_fe - beta_re)' * [Var(beta_fe) - Var(beta_re)]^(-1) * (beta_fe - beta_re)
beta_diff = beta_fe - beta_re
cov_diff = cov_fe - cov_re
try:
# 计算协方差矩阵差的逆(使用Moore-Penrose伪逆处理奇异矩阵)
cov_diff_inv = np.linalg.pinv(cov_diff)
# 计算Hausman统计量
h_stat = float(beta_diff.T @ cov_diff_inv @ beta_diff)
# 如果统计量为负数,说明协方差矩阵差不是正定的
if h_stat < 0:
# 计算标准误差异的标准误(用于显示)
std_err_diff = {}
for var in beta_diff.index:
var_diff = cov_diff.loc[var, var]
if var_diff > 0:
std_err_diff[var] = round(np.sqrt(var_diff), decimals)
else:
std_err_diff[var] = 'negative'
return {
'test_name': 'Hausman Test (Fixed Effects vs Random Effects)',
'warning': _('协方差矩阵差不是正定的(V_b-V_B is not positive definite)'),
'note': _('Stata也会显示此警告。这通常表明固定效应和随机效应模型的估计非常接近。'),
'chi2_statistic': round(abs(h_stat), decimals),
'df': len(beta_diff),
'p_value': 'N/A',
'significance': '',
'conclusion': _('由于协方差矩阵差不是正定的,无法进行标准的Hausman检验。建议:1) 两种模型估计结果非常接近,可能都适用;2) 检查数据质量和模型设定;3) 考虑使用其他模型选择方法。'),
'fe_coeffs': {k: round(v, decimals) for k, v in beta_fe.items()},
're_coeffs': {k: round(v, decimals) for k, v in beta_re.items()},
'coeff_diff': {k: round(beta_diff[k], decimals) for k in beta_diff.index},
'fe_std_err': {k: round(np.sqrt(cov_fe.loc[k, k]), decimals) for k in beta_fe.index},
're_std_err': {k: round(np.sqrt(cov_re.loc[k, k]), decimals) for k in beta_re.index},
'std_err_diff': std_err_diff
}
# 自由度等于参数个数
df = len(beta_fe)
# 计算p值(卡方分布)
p_value = 1 - chi2.cdf(h_stat, df)
# 判断结果
if p_value < 0.01:
conclusion = _('强烈拒绝原假设,应使用固定效应模型')
stars = "***"
elif p_value < 0.05:
conclusion = _('拒绝原假设,应使用固定效应模型')
stars = "**"
elif p_value < 0.1:
conclusion = _('弱拒绝原假设,倾向使用固定效应模型')
stars = "*"
else:
conclusion = _('不能拒绝原假设,可使用随机效应模型')
stars = ""
# 计算标准误差异的标准误(用于显示)
std_err_diff = {}
for var in beta_diff.index:
var_diff = cov_diff.loc[var, var]
if var_diff > 0:
std_err_diff[var] = round(np.sqrt(var_diff), decimals)
else:
std_err_diff[var] = 'negative'
return {
'test_name': 'Hausman Test (Fixed Effects vs Random Effects)',
'null_hypothesis': _('随机效应模型合适(个体效应与解释变量不相关)'),
'alternative_hypothesis': _('固定效应模型合适(个体效应与解释变量相关)'),
'chi2_statistic': round(h_stat, decimals),
'df': df,
'p_value': round(p_value, decimals),
'significance': stars,
'conclusion': conclusion,
'fe_coeffs': {k: round(v, decimals) for k, v in beta_fe.items()},
're_coeffs': {k: round(v, decimals) for k, v in beta_re.items()},
'coeff_diff': {k: round(beta_diff[k], decimals) for k in beta_diff.index},
'fe_std_err': {k: round(np.sqrt(cov_fe.loc[k, k]), decimals) for k in beta_fe.index},
're_std_err': {k: round(np.sqrt(cov_re.loc[k, k]), decimals) for k in beta_re.index},
'std_err_diff': std_err_diff
}
except Exception as e:
# 其他错误
return {
'test_name': 'Hausman Test (Fixed Effects vs Random Effects)',
'error': _('计算Hausman统计量时出错: %(error)s') % {'error': str(e)},
'suggestion': _('可能是因为模型设定问题或数据共线性,建议检查数据质量'),
'fe_coeffs': {k: round(v, decimals) for k, v in beta_fe.items()},
're_coeffs': {k: round(v, decimals) for k, v in beta_re.items()}
}极小样本复现
与固定效应模型同一份样本 fe_panel.csv(个体效应与
bash
python examples/run_hausman.py期望输出:
============================================================
豪斯曼检验(固定效应 vs 随机效应)
============================================================
卡方统计量 chi2(3) = 104.2409
p 值 = 0.0***
结论: 强烈拒绝原假设,应使用固定效应模型
------------------------------------------------------------
变量 FE 系数 RE 系数 系数差
const -0.6587 -1.0145 0.3557
x1 0.6309 0.8773 -0.2464
x2 -0.401 -0.3057 -0.0953