Skip to content

9.4 安慰剂检验(Placebo Tests)

验证 DID 稳健性的关键方法


本节目标

  • 了解安慰剂检验的思想与作用
  • 掌握常见的安慰剂方案及适用场景
  • 能够用 Python 实现常用安慰剂检验

一、为什么需要安慰剂检验

在 DID 框架下,核心识别假设是“平行趋势”。即便我们做了前趋势检验,也仍需通过“安慰剂检验”进一步验证:如果政策是“假的”(不在真实时间/不在真实组上/换个无关结果变量),那么估计出来的“政策效应”应当接近 0、且不显著。否则说明模型可能把别的因素误当成了政策效应。

常见动机:

  • 排除巧合(时点上刚好发生别的事)
  • 排除选择性(处理组与对照组差异导致的伪效应)
  • 排除模型设定问题(不恰当控制、聚类方式、趋势设定等)

二、常见安慰剂检验方法(推荐优先级)

  1. 假政策时间(Placebo Time)

    • 做法:把“政策实施时间”往前/往后平移若干期,再估计 DID。
    • 期望:系数应接近 0(不显著)。
    • 适用:时间维度较长,且没有密集干预的情形。
  2. 假对照组(Placebo Group)

    • 做法:把未受影响、且与处理组相似的一些单位,随机当作“处理组”。
    • 期望:DID 系数分布以 0 为中心。
    • 适用:横截面样本较多时。
  3. Leave-one-out 稳健性(排除单个处理单位/地区)

    • 做法:每次剔除一个处理单位,重复估计。
    • 期望:结果不被单一单位驱动。
  4. 随机化推断(Randomization Inference, 置换检验)

    • 做法:保持时间结构,随机打散处理分配,重复 B 次,形成“虚拟效应”分布。
    • 期望:真实效应位于置换分布的尾部(得到近似 p 值)。
  5. 安慰剂结果变量(Placebo Outcome)

    • 做法:选择一个“理论上不受政策影响”的结果变量运行 DID。
    • 期望:估计系数≈0。

三、Python 简易实现模板

为了便于快速上手,以下给出可复制的“安慰剂检验”最小化代码模板(与 9.3 的风格一致,UTF-8 编码)。

python
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 估计器。

本节小结

  • 安慰剂检验是对 DID 识别假设的“反事实压力测试”。
  • 常用方法:假时点、假对照组、Leave-one-out、置换检验、安慰剂结果。
  • 重点是“接近 0、且不显著”的基准期待;一旦显著,要回到识别与设定层面查因。
  • 报告时建议配合:图(分布/事件研究)、表(ATE 与 p 值)与文字解释。

上一节: 9.3 平行趋势假设 | 下一节: 9.5 经典案例和 Python 实现

基于 MIT 许可证发布。内容版权归作者所有。