8.4 弱工具变量与诊断检验
"Weak instruments are worse than no instruments at all.""弱工具变量比没有工具变量更糟糕。"— Douglas Staiger & James Stock, 1997
当工具变量太"弱"时会发生什么?如何诊断和应对?
📌 本节概要
在本节中,我们将深入探讨:
- 弱工具变量问题: 时的灾难性后果
- Staiger & Stock (1997) 的 F > 10 经验法则
- Stock & Yogo (2005) 的严格临界值
- 弱 IV 的偏差方向:向 OLS 偏移
- 过度识别检验:Sargan/Hansen J 检验
- 内生性检验:Hausman 检验 / Durbin-Wu-Hausman 检验
- 实用诊断清单
- 所有检验的 Python 实现
🚨 弱工具变量问题
什么是弱工具变量?
回顾 2SLS 的第一阶段:
弱工具变量指的是 非常接近于零——即 对 的影响很微弱。
相关性条件要求 ,但即使 在统计意义上不为零,如果它很小,也会带来严重的问题。
为什么弱 IV 是灾难性的?
回忆 IV 估计量:
当分母 时,我们实际上是在除以一个接近零的数。这会导致:
1. 偏差(Bias)
即使 很小(排他性近似成立),当 时,比值 可以变得很大!
大白话:微小的排他性违反,在弱 IV 下会被放大成巨大的偏差。
2. 偏向 OLS
Bound, Jaeger & Baker (1995) 和 Staiger & Stock (1997) 证明了一个重要结论:
其中 是第一阶段 F 统计量。
含义:
- 当 (强 IV):(无偏)
- 当 (弱 IV):(向 OLS 偏移!)
- 当 :2SLS 的偏差和 OLS 完全一样——做了 IV 等于白做!
3. 标准误膨胀
弱 IV 下,IV 估计量的方差急剧增大:
当 时,方差 → 。
4. 置信区间失效
- 弱 IV 下,2SLS 估计量的分布不是正态分布(即使大样本)
- 常规的 t 检验和置信区间不可信
- 需要使用 Anderson-Rubin (1949) 等弱 IV 稳健推断方法
蒙特卡洛模拟:弱 IV 的灾难
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from linearmodels.iv import IV2SLS
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style("whitegrid")
np.random.seed(42)
def simulate_iv(n, pi1, n_sims=1000):
"""模拟不同强度工具变量下的 IV 估计"""
beta_true = 3.0
ols_estimates = []
iv_estimates = []
for _ in range(n_sims):
U = np.random.normal(0, 1, n)
Z = np.random.normal(0, 1, n)
D = pi1 * Z + 0.8 * U + np.random.normal(0, 1, n)
Y = 10 + beta_true * D + 2 * U + np.random.normal(0, 1, n)
# OLS
from numpy.linalg import lstsq
X_ols = np.column_stack([np.ones(n), D])
beta_ols = lstsq(X_ols, Y, rcond=None)[0][1]
ols_estimates.append(beta_ols)
# IV (Wald-like)
cov_YZ = np.cov(Y, Z)[0, 1]
cov_DZ = np.cov(D, Z)[0, 1]
if abs(cov_DZ) > 1e-10:
iv_estimates.append(cov_YZ / cov_DZ)
return ols_estimates, iv_estimates
# 不同强度的 IV
pi1_values = [0.01, 0.1, 0.5, 2.0]
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
beta_true = 3.0
for idx, pi1 in enumerate(pi1_values):
ax = axes[idx // 2][idx % 2]
ols_est, iv_est = simulate_iv(500, pi1, n_sims=2000)
# 计算近似 F 统计量
approx_F = (pi1**2 * 500) / 1 # 简化的 F ≈ n * π₁²
ax.hist(ols_est, bins=50, alpha=0.5, color='red', label='OLS', density=True)
ax.hist(iv_est, bins=50, alpha=0.5, color='blue', label='IV/2SLS', density=True,
range=(beta_true - 5, beta_true + 5))
ax.axvline(x=beta_true, color='green', linewidth=2, linestyle='--', label=f'真实值 = {beta_true}')
ax.set_title(f'π₁ = {pi1}(F ≈ {approx_F:.0f})', fontsize=13, fontweight='bold')
ax.legend(fontsize=9)
ax.set_xlim(beta_true - 5, beta_true + 5)
plt.suptitle('弱工具变量的蒙特卡洛模拟\n(工具变量越弱,IV 估计越不可靠)',
fontsize=15, fontweight='bold')
plt.tight_layout()
plt.savefig('weak_iv_simulation.png', dpi=300, bbox_inches='tight')
plt.show()
print("观察:")
print("- π₁ = 0.01(极弱 IV):IV 的分布极其分散,几乎没有信息")
print("- π₁ = 0.1(弱 IV):IV 的分布虽然中心接近真实值,但方差很大")
print("- π₁ = 0.5(中等 IV):IV 表现开始变好")
print("- π₁ = 2.0(强 IV):IV 分布紧密集中在真实值附近")📏 第一阶段 F 统计量
Staiger & Stock (1997) 的经验法则
问题:如何判断工具变量是否足够"强"?
Staiger & Stock (1997) 提出了一个简单的经验法则:
第一阶段 F 统计量是第一阶段回归中工具变量联合显著性的 F 检验统计量:
其中:
- = 工具变量的数量
- = 样本量
- = 所有解释变量的数量
- "unrestricted" = 包含 IV 的回归
- "restricted" = 不包含 IV 的回归
直觉:F 统计量衡量的是"加入工具变量后,第一阶段回归的解释力提高了多少"。
判断标准
| F 统计量 | 判断 | 建议 |
|---|---|---|
| F > 100 | 非常强的 IV | 放心使用 |
| 10 < F < 100 | 可以接受 | 结果可信,但需注意 |
| F ≈ 10 | 临界值 | 需要额外检查 |
| F < 10 | 弱 IV | ⚠️ 结果不可信! |
| F < 5 | 极弱 IV | 🚫 不要使用 IV! |
Stock & Yogo (2005) 的严格临界值
Staiger & Stock 的 F > 10 只是一个粗略的经验法则。Stock & Yogo (2005) 提供了更严格的临界值,取决于:
- 内生变量的数量
- 工具变量的数量
- 可接受的偏差水平(相对于 OLS 偏差的比例)
Stock & Yogo 临界值表(单一内生变量,5% 显著性水平):
| IV 数量 | 10% 偏差 | 15% 偏差 | 20% 偏差 | 25% 偏差 |
|---|---|---|---|---|
| 1 | 16.38 | 8.96 | 6.66 | 5.53 |
| 2 | 19.93 | 11.59 | 8.75 | 7.25 |
| 3 | 22.30 | 12.83 | 9.54 | 7.80 |
| 5 | 26.87 | 15.09 | 10.27 | 8.84 |
| 10 | 26.80 | 15.60 | 11.49 | 9.35 |
解读:
- "10% 偏差"意味着 2SLS 偏差不超过 OLS 偏差的 10%
- 例如:1 个 IV 时,要保证偏差不超过 OLS 的 10%,需要 F > 16.38
- F > 10 大约对应 OLS 偏差的 15-20%
Python 实现:第一阶段 F 统计量
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
from linearmodels.iv import IV2SLS
np.random.seed(123)
n = 5000
# 生成数据
U = np.random.normal(0, 1, n)
Z = np.random.normal(0, 1, n)
X1 = np.random.normal(0, 1, n)
# 控制工具变量的强度
pi1 = 0.3 # 改变这个值来模拟不同强度
D = 1 + pi1 * Z + 0.5 * X1 + 0.8 * U + np.random.normal(0, 1, n)
Y = 5 + 3 * D + 0.3 * X1 + 2 * U + np.random.normal(0, 2, n)
df = pd.DataFrame({'Y': Y, 'D': D, 'Z': Z, 'X1': X1})
# --- 方法 1:手动计算第一阶段 F ---
# 第一阶段回归
first_stage_full = smf.ols('D ~ Z + X1', data=df).fit()
first_stage_restricted = smf.ols('D ~ X1', data=df).fit()
# F 统计量(Z 的贡献)
from scipy import stats
RSS_restricted = first_stage_restricted.ssr
RSS_full = first_stage_full.ssr
q = 1 # IV 的数量
n_obs = len(df)
k = first_stage_full.df_model
F_manual = ((RSS_restricted - RSS_full) / q) / (RSS_full / (n_obs - k - 1))
print("=" * 70)
print("第一阶段 F 统计量诊断")
print("=" * 70)
print(f"\n手动计算的 F 统计量: {F_manual:.2f}")
print(f"Z 的 t 统计量: {first_stage_full.tvalues['Z']:.2f}")
print(f"Z 的 t² (≈ F): {first_stage_full.tvalues['Z']**2:.2f}")
print(f"(单一 IV 时,F = t²)")
# 判断
if F_manual > 10:
print(f"\n✅ F = {F_manual:.2f} > 10,工具变量足够强")
else:
print(f"\n⚠️ F = {F_manual:.2f} < 10,存在弱工具变量问题!")
# --- 方法 2:使用 linearmodels 自动诊断 ---
iv_result = IV2SLS.from_formula('Y ~ 1 + X1 [D ~ Z]', data=df).fit(cov_type='robust')
print("\n" + "=" * 70)
print("linearmodels 自动诊断")
print("=" * 70)
print(iv_result.first_stage.diagnostics)🔍 过度识别检验:Sargan / Hansen J 检验
动机
当工具变量数量多于内生变量数量时(过度识别),我们可以检验:
"所有的工具变量是否都满足排他性约束?"
直觉:如果有 个 IV 和 1 个内生变量,我们实际上有 个"多余"的识别条件(over-identifying restrictions)。这些多余的条件可以用来检验 IV 的有效性。
Sargan / Hansen J 检验
零假设::所有 IV 都是有效的(满足排他性约束)
备择假设::至少有一个 IV 是无效的
检验步骤:
- 用 2SLS 估计模型,得到残差
- 将 对所有 IV 和外生变量回归,得到
- 检验统计量:
- = IV 数量
- = 内生变量数量
- 自由度 = (过度识别的数量)
解读:
- p 值大 → 不拒绝 → IV 可能有效 ✅
- p 值小 → 拒绝 → 至少一个 IV 可能无效 ❌
注意:
- 恰好识别时(),自由度 = 0,无法进行 J 检验
- J 检验的功效有限——不拒绝 不代表 IV 一定有效
- J 检验假设至少有一个 IV 是有效的
Python 实现
np.random.seed(456)
n = 5000
# 数据生成:多个 IV
U = np.random.normal(0, 1, n)
Z1 = np.random.normal(0, 1, n)
Z2 = np.random.normal(0, 1, n)
Z3 = np.random.normal(0, 1, n)
D = 1 + 0.5 * Z1 + 0.3 * Z2 + 0.4 * Z3 + 0.8 * U + np.random.normal(0, 1, n)
Y = 5 + 3 * D + 2 * U + np.random.normal(0, 2, n)
df_over = pd.DataFrame({
'Y': Y, 'D': D, 'Z1': Z1, 'Z2': Z2, 'Z3': Z3
})
# 过度识别的 IV2SLS
iv_over = IV2SLS.from_formula('Y ~ 1 [D ~ Z1 + Z2 + Z3]', data=df_over)
iv_over_result = iv_over.fit(cov_type='robust')
print("=" * 70)
print("过度识别检验(Sargan / Hansen J 检验)")
print("=" * 70)
print(f"\nIV2SLS 估计结果:")
print(f" D 的系数: {iv_over_result.params['D']:.4f}")
print(f" 标准误: {iv_over_result.std_errors['D']:.4f}")
# 手动 J 检验
residuals = iv_over_result.resids
Z_matrix = df_over[['Z1', 'Z2', 'Z3']].values
Z_with_const = np.column_stack([np.ones(n), Z_matrix])
from numpy.linalg import lstsq
beta_aux = lstsq(Z_with_const, residuals, rcond=None)[0]
residuals_fitted = Z_with_const @ beta_aux
R2_aux = 1 - np.sum((residuals - residuals_fitted)**2) / np.sum((residuals - residuals.mean())**2)
J_stat = n * R2_aux
df_J = 3 - 1 # 3 个 IV - 1 个内生变量
p_value_J = 1 - stats.chi2.cdf(J_stat, df_J)
print(f"\n手动 Sargan J 检验:")
print(f" J 统计量: {J_stat:.4f}")
print(f" 自由度: {df_J}")
print(f" p 值: {p_value_J:.4f}")
if p_value_J > 0.05:
print(f" ✅ p = {p_value_J:.4f} > 0.05,不拒绝 H₀,IV 可能有效")
else:
print(f" ❌ p = {p_value_J:.4f} < 0.05,拒绝 H₀,至少一个 IV 可能无效!")
# linearmodels 自动报告
print(f"\nlinearmodels 过度识别检验:")
print(iv_over_result.wooldridge_overid)🔄 内生性检验:Hausman 检验
动机
一个自然的问题是:
"我到底需不需要用 IV? 真的是内生的吗?"
如果 实际上是外生的,那么 OLS 就是一致且有效的,IV 反而引入了不必要的方差。
Hausman 检验 / Durbin-Wu-Hausman (DWH) 检验
核心思想:比较 OLS 和 IV 的估计值。
- 如果 是外生的:OLS 和 IV 都是一致的,但 OLS 更有效率
- 如果 是内生的:只有 IV 是一致的
零假设:: 是外生的(不存在内生性)
备择假设:: 是内生的
检验统计量(Hausman 形式):
等价的回归方法(DWH 检验)
更实用的是回归版本的 Hausman 检验:
步骤:
- 第一阶段回归:,得到残差
- 将 加入原回归:
- 检验
直觉: 是 中未被 解释的部分。如果 ,说明这个"脏"的部分确实与 的误差相关——即 是内生的。
Python 实现
np.random.seed(789)
n = 5000
# --- 场景 1:D 是内生的 ---
U = np.random.normal(0, 1, n)
Z = np.random.normal(0, 1, n)
D_endo = 1 + 0.5 * Z + 0.8 * U + np.random.normal(0, 1, n)
Y_endo = 5 + 3 * D_endo + 2 * U + np.random.normal(0, 2, n)
df_endo = pd.DataFrame({'Y': Y_endo, 'D': D_endo, 'Z': Z})
# --- 场景 2:D 是外生的 ---
D_exo = 1 + 0.5 * Z + np.random.normal(0, 1, n)
Y_exo = 5 + 3 * D_exo + 2 * U + np.random.normal(0, 2, n)
# 注意:U 影响 Y 但不影响 D → D 是外生的
df_exo = pd.DataFrame({'Y': Y_exo, 'D': D_exo, 'Z': Z})
def hausman_test_regression(df, formula_first='D ~ Z', formula_second='Y ~ D'):
"""回归版 Hausman 检验(DWH)"""
# 第一阶段
first_stage = smf.ols(formula_first, data=df).fit()
df_temp = df.copy()
df_temp['v_hat'] = first_stage.resid
# 扩展回归
augmented = smf.ols(f'{formula_second} + v_hat', data=df_temp).fit()
return {
'delta': augmented.params['v_hat'],
't_stat': augmented.tvalues['v_hat'],
'p_value': augmented.pvalues['v_hat'],
'reject': augmented.pvalues['v_hat'] < 0.05
}
print("=" * 70)
print("Hausman / DWH 内生性检验")
print("=" * 70)
# 场景 1:内生
print("\n--- 场景 1:D 是内生的 ---")
result1 = hausman_test_regression(df_endo)
print(f" δ̂ (v_hat 系数): {result1['delta']:.4f}")
print(f" t 统计量: {result1['t_stat']:.4f}")
print(f" p 值: {result1['p_value']:.6f}")
if result1['reject']:
print(f" ❌ 拒绝 H₀:D 是内生的,需要使用 IV!")
else:
print(f" ✅ 不拒绝 H₀:D 可能是外生的,OLS 即可")
# 场景 2:外生
print("\n--- 场景 2:D 是外生的 ---")
result2 = hausman_test_regression(df_exo)
print(f" δ̂ (v_hat 系数): {result2['delta']:.4f}")
print(f" t 统计量: {result2['t_stat']:.4f}")
print(f" p 值: {result2['p_value']:.6f}")
if result2['reject']:
print(f" ❌ 拒绝 H₀:D 是内生的,需要使用 IV!")
else:
print(f" ✅ 不拒绝 H₀:D 可能是外生的,OLS 即可")
# 使用 linearmodels 的内生性检验
print("\n" + "=" * 70)
print("linearmodels 的 Wu-Hausman 检验")
print("=" * 70)
iv_endo = IV2SLS.from_formula('Y ~ 1 [D ~ Z]', data=df_endo).fit()
iv_exo = IV2SLS.from_formula('Y ~ 1 [D ~ Z]', data=df_exo).fit()
print("\n场景 1(内生):")
print(iv_endo.wu_hausman())
print("\n场景 2(外生):")
print(iv_exo.wu_hausman())📋 IV 诊断清单
实用的 IV 分析流程
┌─────────────────────────────────────┐
│ Step 1: 论证 IV 的有效性 │
│ (经济学直觉 + 制度知识) │
│ - 排他性约束合理吗? │
│ - 独立性条件可信吗? │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Step 2: 检验相关性 │
│ 第一阶段 F > 10 ? │
│ (Stock & Yogo 临界值) │
│ - F > 10: ✅ 继续 │
│ - F < 10: ⚠️ 弱 IV 问题! │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Step 3: 内生性检验 │
│ Hausman / DWH 检验 │
│ - 拒绝 H₀: 需要 IV │
│ - 不拒绝 H₀: OLS 可能就够 │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Step 4: 过度识别检验 │
│ Sargan / Hansen J 检验 │
│ (仅当 IV 数 > 内生变量数时) │
│ - 不拒绝 H₀: IV 可能有效 │
│ - 拒绝 H₀: 至少一个 IV 有问题 │
└──────────────┬──────────────────────┘
↓
┌─────────────────────────────────────┐
│ Step 5: 报告和解释 │
│ - 2SLS 估计值和标准误 │
│ - LATE 的解释 │
│ - 稳健性检验 │
└─────────────────────────────────────┘完整的诊断 Python 函数
from linearmodels.iv import IV2SLS
from scipy import stats
def iv_diagnostics(df, dep_var, endog_var, instruments, exog_vars=None):
"""
全面的 IV 诊断工具
Parameters
----------
df : DataFrame
dep_var : str, 因变量名
endog_var : str, 内生变量名
instruments : list of str, 工具变量名
exog_vars : list of str, 外生控制变量名(可选)
"""
print("=" * 70)
print("工具变量(IV/2SLS)全面诊断报告")
print("=" * 70)
# 构建公式
if exog_vars:
exog_str = ' + '.join(exog_vars)
iv_str = ' + '.join(instruments)
formula = f'{dep_var} ~ 1 + {exog_str} [{endog_var} ~ {iv_str}]'
ols_formula = f'{dep_var} ~ {endog_var} + {exog_str}'
fs_formula = f'{endog_var} ~ {iv_str} + {exog_str}'
else:
iv_str = ' + '.join(instruments)
formula = f'{dep_var} ~ 1 [{endog_var} ~ {iv_str}]'
ols_formula = f'{dep_var} ~ {endog_var}'
fs_formula = f'{endog_var} ~ {iv_str}'
# --- 1. OLS 估计 ---
print("\n📊 1. OLS 估计(参考)")
print("-" * 50)
ols_result = smf.ols(ols_formula, data=df).fit()
print(f" OLS 系数 ({endog_var}): {ols_result.params[endog_var]:.4f}")
print(f" 标准误: {ols_result.bse[endog_var]:.4f}")
print(f" p 值: {ols_result.pvalues[endog_var]:.6f}")
# --- 2. 第一阶段 ---
print(f"\n🔧 2. 第一阶段回归:{endog_var} ~ instruments")
print("-" * 50)
fs_result = smf.ols(fs_formula, data=df).fit()
for iv in instruments:
print(f" {iv}: 系数={fs_result.params[iv]:.4f}, t={fs_result.tvalues[iv]:.2f}, p={fs_result.pvalues[iv]:.6f}")
# F 统计量
if len(instruments) == 1:
F_stat = fs_result.tvalues[instruments[0]] ** 2
else:
F_stat = fs_result.fvalue
print(f"\n 第一阶段 F 统计量: {F_stat:.2f}")
if F_stat > 10:
print(f" ✅ F = {F_stat:.2f} > 10 → 工具变量足够强")
else:
print(f" ⚠️ F = {F_stat:.2f} < 10 → 弱工具变量警告!")
# --- 3. IV/2SLS 估计 ---
print(f"\n📈 3. IV/2SLS 估计")
print("-" * 50)
iv_result = IV2SLS.from_formula(formula, data=df).fit(cov_type='robust')
print(f" IV 系数 ({endog_var}): {iv_result.params[endog_var]:.4f}")
print(f" 标准误: {iv_result.std_errors[endog_var]:.4f}")
print(f" p 值: {iv_result.pvalues[endog_var]:.6f}")
ci = iv_result.conf_int()
print(f" 95% CI: [{ci.loc[endog_var, 'lower']:.4f}, {ci.loc[endog_var, 'upper']:.4f}]")
# --- 4. 内生性检验 ---
print(f"\n🔍 4. 内生性检验 (Wu-Hausman)")
print("-" * 50)
try:
wh = iv_result.wu_hausman()
print(f" Wu-Hausman 统计量: {wh.stat:.4f}")
print(f" p 值: {wh.pval:.6f}")
if wh.pval < 0.05:
print(f" ❌ 拒绝 H₀ (p < 0.05):D 是内生的,需要 IV")
else:
print(f" ✅ 不拒绝 H₀ (p > 0.05):D 可能是外生的")
except Exception as e:
print(f" 无法计算: {e}")
# --- 5. 过度识别检验 ---
if len(instruments) > 1:
print(f"\n📏 5. 过度识别检验 (Sargan/Hansen J)")
print("-" * 50)
try:
overid = iv_result.wooldridge_overid
print(f" J 统计量: {overid.stat:.4f}")
print(f" p 值: {overid.pval:.6f}")
print(f" 自由度: {len(instruments) - 1}")
if overid.pval > 0.05:
print(f" ✅ 不拒绝 H₀ (p > 0.05):IV 可能有效")
else:
print(f" ❌ 拒绝 H₀ (p < 0.05):至少一个 IV 可能无效!")
except Exception as e:
print(f" 无法计算: {e}")
else:
print(f"\n📏 5. 过度识别检验")
print("-" * 50)
print(f" (恰好识别,无法进行 J 检验)")
# --- 6. 汇总 ---
print(f"\n📋 6. 汇总比较")
print("-" * 50)
print(f" {'方法':<15} {'系数':<12} {'标准误':<12}")
print(f" {'OLS':<15} {ols_result.params[endog_var]:<12.4f} {ols_result.bse[endog_var]:<12.4f}")
print(f" {'IV/2SLS':<15} {iv_result.params[endog_var]:<12.4f} {iv_result.std_errors[endog_var]:<12.4f}")
print(f" {'差异':<15} {iv_result.params[endog_var] - ols_result.params[endog_var]:<12.4f}")
return iv_result, ols_result
# 使用示例
np.random.seed(2024)
n = 5000
U = np.random.normal(0, 1, n)
Z1 = np.random.normal(0, 1, n)
Z2 = np.random.normal(0, 1, n)
D = 1 + 0.5 * Z1 + 0.3 * Z2 + 0.8 * U + np.random.normal(0, 1, n)
Y = 5 + 3 * D + 2 * U + np.random.normal(0, 2, n)
df_diag = pd.DataFrame({'Y': Y, 'D': D, 'Z1': Z1, 'Z2': Z2})
iv_result, ols_result = iv_diagnostics(
df_diag,
dep_var='Y',
endog_var='D',
instruments=['Z1', 'Z2']
)⚠️ 弱 IV 的应对策略
策略 1:寻找更强的工具变量
这是最根本的解决方案。一个好的 IV 应该:
- 与处理变量有强烈的相关性
- 背后有清晰的理论机制
策略 2:Anderson-Rubin (AR) 检验
AR 检验在弱 IV 下仍然有效:
其中 是 的投影矩阵。
# Anderson-Rubin 置信区间
# linearmodels 提供了 AR 检验
print("\nAnderson-Rubin 检验(弱 IV 稳健):")
try:
ar_test = iv_result.anderson_rubin()
print(f" AR 统计量: {ar_test.stat:.4f}")
print(f" p 值: {ar_test.pval:.6f}")
except Exception as e:
print(f" {e}")策略 3:LIML 估计量
**有限信息最大似然估计(LIML)**在弱 IV 下比 2SLS 有更小的偏差:
from linearmodels.iv import IVLIML
liml_result = IVLIML.from_formula('Y ~ 1 [D ~ Z1 + Z2]', data=df_diag).fit()
print("\nLIML 估计:")
print(f" D 的系数: {liml_result.params['D']:.4f}")
print(f" 标准误: {liml_result.std_errors['D']:.4f}")策略 4:多个弱 IV 合并
如果有多个单独很弱但联合起来足够强的 IV,可以合并使用。但要注意 Many Instruments 偏差。
📝 本节要点
- 弱工具变量()会导致 2SLS 偏向 OLS、标准误膨胀、置信区间失效
- Staiger & Stock 法则:第一阶段 ;更严格用 Stock & Yogo 临界值
- Sargan/Hansen J 检验:检验过度识别约束(多 IV 时可用)
- Hausman/DWH 检验:检验是否真的需要 IV
- 弱 IV 应对:AR 检验(稳健推断)、LIML(更小偏差)、寻找更强 IV
- 始终报告完整的诊断结果——这是 IV 论文可信度的基石
下一节:8.5 经典案例与 Python 实现 — 用 Python 复现 Angrist & Krueger (1991) 和 Card (1995) 的经典研究。