9.4 安慰剂检验(Placebo Tests)
验证 DID 稳健性的关键方法
本节目标
- 了解安慰剂检验的思想与作用
- 掌握常见的安慰剂方案及适用场景
- 能够用 Python 实现常用安慰剂检验
一、为什么需要安慰剂检验
在 DID 框架下,核心识别假设是“平行趋势”。即便我们做了前趋势检验,也仍需通过“安慰剂检验”进一步验证:如果政策是“假的”(不在真实时间/不在真实组上/换个无关结果变量),那么估计出来的“政策效应”应当接近 0、且不显著。否则说明模型可能把别的因素误当成了政策效应。
常见动机:
- 排除巧合(时点上刚好发生别的事)
- 排除选择性(处理组与对照组差异导致的伪效应)
- 排除模型设定问题(不恰当控制、聚类方式、趋势设定等)
二、常见安慰剂检验方法(推荐优先级)
假政策时间(Placebo Time)
- 做法:把“政策实施时间”往前/往后平移若干期,再估计 DID。
- 期望:系数应接近 0(不显著)。
- 适用:时间维度较长,且没有密集干预的情形。
假对照组(Placebo Group)
- 做法:把未受影响、且与处理组相似的一些单位,随机当作“处理组”。
- 期望:DID 系数分布以 0 为中心。
- 适用:横截面样本较多时。
Leave-one-out 稳健性(排除单个处理单位/地区)
- 做法:每次剔除一个处理单位,重复估计。
- 期望:结果不被单一单位驱动。
随机化推断(Randomization Inference, 置换检验)
- 做法:保持时间结构,随机打散处理分配,重复 B 次,形成“虚拟效应”分布。
- 期望:真实效应位于置换分布的尾部(得到近似 p 值)。
安慰剂结果变量(Placebo Outcome)
- 做法:选择一个“理论上不受政策影响”的结果变量运行 DID。
- 期望:估计系数≈0。
三、Python 简易实现模板
为了便于快速上手,以下给出可复制的“安慰剂检验”最小化代码模板(与 9.3 的风格一致,UTF-8 编码)。
import numpy as np
import pandas as pd
import statsmodels.formula.api as smf
# df: 包含列 y, treated(0/1), time(整数), post(0/1), id
def did_once(df):
"""标准 DID 回归(实体/时间固定效应的最简形式,可按需扩展)"""
model = smf.ols('y ~ treated*post + C(id) + C(time)', data=df).fit(cov_type='cluster', cov_kwds={'groups': df['id']})
return model.params.get('treated:post', np.nan)
def placebo_time(df, shift=2):
"""把政策时点平移 shift 期,重新构造 post 并估计 DID"""
df2 = df.copy()
true_cut = df2.loc[df2['post']==1, 'time'].min() # 真实时点
fake_cut = true_cut + shift
df2['post'] = (df2['time'] >= fake_cut).astype(int)
return did_once(df2)
def placebo_group(df, n_draw=200, seed=42):
"""随机把同规模的对照单位当成“处理组”,重复 n_draw 次,返回伪效应分布"""
rng = np.random.default_rng(seed)
ids = df['id'].unique()
treated_ids = df[df['treated']==1]['id'].unique()
k = len(treated_ids)
fake_ate = []
for _ in range(n_draw):
fake_treated = set(rng.choice(ids, size=k, replace=False))
df2 = df.copy()
df2['treated'] = df2['id'].isin(fake_treated).astype(int)
fake_ate.append(did_once(df2))
return pd.Series(fake_ate, name='placebo_group_ate')使用建议:先跑一次真实 DID,记录 ATE;再运行 placebo_time / placebo_group,绘制分布并标出真实 ATE 所在位置。
四、结果解读与注意事项
- Placebo Time:在一系列“假时点”附近,系数应围绕 0 波动;若显著,警惕时序性混淆。
- Placebo Group:伪效应分布应以 0 为中心,真实 ATE 应落在分布尾部。
- Leave-one-out:若剔除任一单位结果都稳定,说明不是由“某个特殊单位”驱动。
- 置换检验:可报告置换 p 值作为稳健性证据。
- 安慰剂结果:明确解释为何该结果“不应受政策影响”。
技术细节:
- 聚类标准误:面板数据常对实体聚类(或双向聚类)。
- 多期/错位处理:建议采用事件研究(见 9.3)或近年的多期 DID 估计器。
三重差分法(Triple Differences, DDD)
当标准 DID 面临"时变的组间混淆因素"时,三重差分法(DDD) 提供了一种更强的识别策略。其核心思想是:在 DID 的基础上,再引入一个组内安慰剂对照组,通过第三次差分来剔除那些可能同时影响处理组和对照组、但随时间变化的干扰因素。
DDD 的数据结构:数据按三个维度组织——组别(处理组 vs 对照组)、时间(政策前 vs 政策后)以及子群体(真正受影响的群体 vs 安慰剂群体)。
回归方程:
其中 是三重交互项的系数,即我们关心的政策效应参数。
经典案例:Gruber (1994) 的生育保险研究
Gruber 研究了美国部分州强制要求雇主提供生育保险对劳动力市场的影响。他的研究设计如下:
- 处理组州:实施了强制生育保险的州
- 对照组州:未实施该政策的州
- 受影响群体:20-40 岁已婚女性(直接受益于生育保险)
- 安慰剂群体:40 岁以上女性和单身男性(不直接受该政策影响)
识别逻辑:
- 标准 DD(仅看处理组州 vs 对照组州的已婚适龄女性)得到的工资效应为 -0.062
- 安慰剂群体的 DD 估计为 -0.008(接近零)
- DDD 估计 = 处理群体 DD - 安慰剂群体 DD = -0.062 - (-0.008) = -0.054
这意味着强制生育保险使受影响女性的时薪下降了约 5.4%——与理论预测一致(雇主将保险成本转嫁为更低的工资)。
DDD 的关键质量指标:政策效应应集中在"真正受影响"的群体中,而安慰剂群体的 DD 估计应接近零。如果安慰剂群体也出现了显著的"效应",则说明可能存在更广泛的混淆因素,DDD 的识别策略也会受到质疑。
参考 Cunningham (2021), Causal Inference: The Mixtape, Chapter 9;另见 Gruber (1994)
本节小结
- 安慰剂检验是对 DID 识别假设的"反事实压力测试"。
- 常用方法:假时点、假对照组、Leave-one-out、置换检验、安慰剂结果。
- 三重差分法(DDD)通过引入组内安慰剂群体,进一步增强了识别的可信度。
- 重点是"接近 0、且不显著"的基准期待;一旦显著,要回到识别与设定层面查因。
- 报告时建议配合:图(分布/事件研究)、表(ATE 与 p 值)与文字解释。
上一节: 9.3 平行趋势假设 | 下一节: 9.5 经典案例和 Python 实现