前置条件与学习依赖 / PREREQUISITES
① 数学 / 统计基础
面板数据结构、固定效应、线性约束下的二次规划(权重 $w_j \ge 0$、$\sum w_j=1$)、安慰剂检验的置换(permutation)思想。
② 经济学理论前置
政策评估 / 项目评估;理解"反事实"与"合成对照"的关系。先学 06 DID——SCM 可视为 DID 在"处理组只有 1 个"极端情形下的推广。
③ 软件 / 计算前置
Stata 17+(外部命令:synthsynth_runnerbasque 自带示例数据);Python 3.10+(pandasnumpypysynconsynthex)。
④ 站内前置页面
先学 06 DID 双重差分(平行趋势、事件研究、安慰剂)与 08 稳健性检验
⑤ 难度分级
中高 | 权重求解直觉简单,但 Donor pool 选择与安慰剂检验极易出错。

01 SCM 是什么:从 DID 到合成对照

DID 要求"处理组 + 对照组都有一定数量"。但现实中很多政策只在一个地区实施——比如加州 1988 年推出烟草控制计划、巴斯克地区的恐怖冲突、一个城市的自贸区——这时你找不到多个处理组,传统 DID 无从下手。合成控制法(Synthetic Control Method, SCM)的思路是:不去找一个"真实"的对照组,而是用所有未处理单位的加权平均,"拼"出一个与处理单位在政策前几乎完全一致的"合成处理单位"。政策后,真实处理单位与合成对照单位之间的差距,就是政策效应。

形式化地,假设面板有 $J+1$ 个单位,单位 1 是唯一处理对象,单位 $2,\dots,J+1$ 是潜在的 donor pool(未处理)。SCM 寻找一组权重 $w=(w_2,\dots,w_{J+1})$,满足 $w_j \ge 0$ 且 $\sum_{j=2}^{J+1} w_j=1$,使得政策前处理单位的预测变量与合成单位尽可能接近:

Eq. 1.1 — 合成控制权重的二次规划
$$ W^{*} = \arg\min_{W} \big\| \mathbf{X}_1 - \mathbf{X}_0 W \big\|_V^2 \quad \text{s.t. } w_j \ge 0,\ \textstyle\sum_j w_j = 1 $$

其中 $\mathbf{X}_1$ 是处理单位的 $K \times 1$ 维预测变量向量,$\mathbf{X}_0$ 是 donor pool 的 $K \times J$ 矩阵,$V$ 是预测变量的权重矩阵(让最重要的变量在拟合中占更大权重)。一旦找到 $W^*$,政策后的反事实结果为:

Eq. 1.2 — SCM 处理效应估计
$$ \hat{\alpha}_{1t} = Y_{1t} - \sum_{j=2}^{J+1} w_j^{*} Y_{jt}, \quad t > T_0 $$

其中 $T_0$ 是政策实施时点。$\hat{\alpha}_{1t}$ 就是"真实加州 − 合成加州"在政策后第 $t$ 年的差距。

SCM vs DID 的关键差别

DID 假设"处理组与对照组政策前趋势平行",SCM 假设"处理单位可以被 donor pool 的凸组合在政策前完美拟合"。后者更弱:只要 donor pool 里存在一组单位的加权平均能在政策前 mimic 处理单位的走势即可,不要求每个 donor 单独都与处理单位平行。

02 核心假设与权重求解

2.1 识别假设

SCM 的识别依赖三条核心假设:

  • 假设 1(无溢出 / SUTVA):处理只影响单位 1,donor pool 中任何单位都没有受到政策间接影响(no interference)。自贸区 A 省的政策不会通过贸易/人员流动影响到 B 省——这在相邻地区常常不成立。
  • 假设 2(预处理拟合良好):存在一组权重 $W^*$,使得政策前处理单位的结果变量轨迹与预测变量都被合成单位很好地拟合。拟合不好(RMSPE 很大)时,SCM 结论不可信。
  • 假设 3(外生干预):政策实施时点 $T_0$ 是外生的,且 donor pool 在 $T_0$ 前后没有遭遇其他重大冲击。

2.2 权重求解:两层优化

SCM 的权重求解是一个两层优化

外层:选择 V(预测变量权重)
V 是对角矩阵,决定哪些预测变量在拟合中更重要。Abadie-Diamond-Hainmueller 的做法是:选择 V 使得政策前处理单位的结果变量轨迹被合成单位拟合得最好(即用 pre-treatment outcome 本身作为"拟合目标"反推 V)。这一步在 Stata synth 中由 optmethod() 自动完成。
内层:给定 V,求 W
给定 V,权重 W 是一个带线性约束的二次规划:$w_j \ge 0$、$\sum w_j = 1$。Stata synth 用二次规划求解器自动完成。
⚠ 权重稀疏性

SCM 解出的权重通常是稀疏的——donor pool 里几十个单位中,往往只有 3–5 个权重显著大于 0。这是 feature 不是 bug:它强迫你解释"合成加州 = 0.4×内华达 + 0.3×犹他 + 0.2×新墨西哥 + 0.1×阿拉斯加"这种经济学含义。审稿人一定会问"为什么是这几个州?"

03 Donor pool 选择与预处理拟合

3.1 Donor pool 怎么选

Donor pool 是 SCM 最容易出错的环节。三条经验法则:

  • 排除受政策影响的单位:donor 里不能有任何单位在 $T_0$ 后也被处理(否则反事实被污染)。如果全国所有省份最终都推行了同一政策,你只能把"政策前已被处理"的单位剔除,或改用合成 DID(合成控制 + DID 的组合)。
  • 排除遭遇特殊冲击的单位:donor 里如果某州在政策同期经历了大地震、金融危机、政权更迭,它会把合成对照的轨迹带偏。
  • 地域 / 经济结构相似:donor pool 应限定在与处理单位在经济结构、发展阶段上相似的单位。研究加州烟草控制时,donor 应是其他州而非其他国家。

3.2 预处理拟合诊断

跑完 SCM 后,第一件事是看政策前拟合图(pre-treatment fit plot):横轴是时间,纵轴是结果变量,画两条线——真实处理单位、合成对照单位。理想情况下,政策前两条线应该几乎重合(RMSPE 很小),政策后才开始分叉。

诊断指标含义经验阈值
Pre-treatment RMSPE政策前真实 vs 合成结果变量的均方根预测误差应小于政策后 RMSPE 的 1/5 ~ 1/10
预测变量均值差距处理单位 vs 合成单位在每个预测变量上的均值差每个变量差距 < 0.1 个标准差
权重稀疏度权重 > 0.01 的 donor 个数通常 3–8 个;超过 15 个说明 donor pool 太杂
负权重是否违反 $w_j \ge 0$SCM 不允许;若出现说明约束设置错了

04 安慰剂检验与统计推断

SCM 没有传统意义上的标准误——因为处理对象只有 1 个,渐近理论无从谈起。Abadie-Diamond-Hainmueller 的解决方案是置换检验(permutation test):把"处理"假装分配给 donor pool 里的每个单位,对每个"伪处理单位"都跑一遍 SCM,得到一组"伪效应"。然后看真实处理单位的政策后效应,在这组伪效应分布里有多极端。

Eq. 4.1 — SCM 安慰剂 p 值
$$ p_{\text{placebo}} = \frac{1 + \#\{j: |\hat{\alpha}_{j,\text{post}}| \ge |\hat{\alpha}_{1,\text{post}}|\}}{J+1} $$

实务上画一张 "gap plot"(差距图):对每个单位(真实处理 + 所有伪处理),画出"真实 − 合成"随时间的轨迹。真实处理单位的轨迹应该在政策后明显跳出所有伪轨迹的信封(envelope)之外。

⚠ 只保留 pre-treatment RMSPE 小的单位

如果某个 donor 单位政策前根本拟合不上(pre-treatment RMSPE 巨大),它的"伪效应"也会天然很大——把它留在图里会让你的真实效应看起来不极端。规范做法是:剔除所有 pre-treatment RMSPE 大于真实处理单位 pre-treatment RMSPE 2 倍的伪处理单位,再画 gap plot。这一步在 synth_runner 中由 rmsexclude() 选项自动完成。

05 完整 Stata 代码(synth / synth_runner)

下面这段代码完整演示:(1) 用模拟数据构造 1 个处理地区 + 20 个 donor;(2) 跑 synth;(3) 画出处理效应图;(4) 跑 synth_runner 做安慰剂检验。所有命令在 Stata 17+ 上可运行。

stata · scm_demo.do
* ============================================================
* 合成控制法 SCM 完整演示
* 场景:某省(id=1)在 2010 年实施自贸区政策,其余 20 省未实施
* 目标:估计自贸区对 lnGDP 的因果效应
* ============================================================
clear all
set seed 20240101
set obs 2100                              // 21 地区 × 100 年
gen id = ceil(_n/100)                      // 地区编号 1..21
bysort id: gen year = 1950 + _n            // 年份 1950..2049

* --- 1. 模拟结果变量:地区固定效应 + 共同趋势 + 政策效应 ---
gen u_i = rnormal(0, 0.5) in 1/21          // 地区固定效应(每地区抽一次)
bysort id: replace u_i = u_i[1]
gen t = year - 1950
gen y = 2 + 0.05*t + u_i + rnormal(0, 0.2) // 无政策时的 lnGDP

* 处理单位 id=1 在 2010 年起获得 +0.3 的政策效应
replace y = y + 0.3 if id==1 & year>=2010

* --- 2. 预测变量(政策前 2000-2009 年的均值)---
* 为简化,这里用几个常数型变量 + 政策前结果变量均值
gen x1 = rnormal(0, 1) in 1/21
bysort id: replace x1 = x1[1]
gen x2 = rnormal(0, 1) in 1/21
bysort id: replace x2 = x2[1]

* --- 3. 安装外部命令 ---
* ssc install synth, replace
* ssc install synth_runner, replace

* --- 4. 跑 synth(经典版)---
* synth 语法:synth 结果变量 预测变量(英文变量列表), trunit(处理单位) trperiod(政策期)
* 这里我们用 x1 x2 + 政策前若干年的结果变量作为预测变量
preserve
    keep if year <= 2009                  // 只用政策前数据估计权重
    * 构造政策前结果变量的若干年份均值作为预测变量
    * synth 要求数据是 wide 形式:每地区一行,列是各年 y
    collapse (mean) y, by(id year)
    reshape wide y, i(id) j(year)
    * 用 2000-2009 年的 y 作为预测变量(典型 SCM 做法)
    synth y2009 y2008 y2007 y2006 y2005 x1 x2, ///
        trunit(1) trperiod(2010) keep(scm_result)
restore

* --- 5. 用 synth_runner 跑完整流程 + 安慰剂检验 ---
* synth_runner 是更现代的封装,自动画 gap plot、算 RMSPE ratio
preserve
    keep if year <= 2019                  // 政策后 10 年窗口
    reshape wide y x1 x2, i(id) j(year)
    * synth_runner 语法(需先 install)
    * synth_runner y, trunit(1) trperiod(2010) ///
    *     pre(1980/2009) post(2010/2019) ///
    *    covariates(x1 x2) ///
    *    mspeperiod(1980/2009) ///
    *    figure_name(gap_plot)
    * 输出:
    *   - _data.dta     : 处理 vs 合成的逐年差距
    *   - placebo_results: 所有单位的伪效应
    *   - gap plot      : 真实效应是否跳出信封
restore

* --- 6. 手动解读关键输出 ---
* synth 输出表 1:权重 W*
*   关注:哪几个 donor 权重 > 0.01?加起来是否 = 1?
* synth 输出表 2:预测变量均值对比
*   关注:处理单位 vs 合成单位在每个预测变量上的差距是否 < 0.1 SD
* synth 输出表 3:RMSPE
*   pre-RMSPE 应 < post-RMSPE / 5
Stata basque 示例数据

Abadie-Diamond-Hainmueller (2010) 的 Stata 包自带 basque 数据集(巴斯克地区 GDP),输入 use basque 后直接 synth 即可复现原文图 1–图 2。初学者强烈建议先跑一遍这个示例再换自己的数据。

06 Python 实现(pysyncon)

Python 生态中 pysynconpip install pysyncon)是 Abadie 等原文方法的最忠实复现。下面代码与上一节 Stata 等价:

python · scm_demo.py
# ============================================================
# 合成控制法 Python 实现(pysyncon)
# ============================================================
import numpy as np
import pandas as pd
from pysyncon import Dataprep, Synth

rng = np.random.default_rng(20240101)

# --- 1. 构造面板:21 地区 × 70 年 ---
n_units, n_years = 21, 70
years = np.arange(1980, 2050)
ids = np.arange(1, n_units + 1)

data = []
for i in ids:
    u_i = rng.normal(0, 0.5)
    x1 = rng.normal(0, 1)
    x2 = rng.normal(0, 1)
    for t, yr in enumerate(years):
        y = 2 + 0.05 * t + u_i + rng.normal(0, 0.2)
        if i == 1 and yr >= 2010:
            y += 0.3                       # 政策效应
        data.append({"id": i, "year": yr, "y": y, "x1": x1, "x2": x2})
df = pd.DataFrame(data)

# --- 2. Dataprep:指定处理单位、政策期、预测变量 ---
dataprep = Dataprep(
    foo=df,
    predictors=["x1", "x2"],
    predictors_op="mean",                  # 预测变量在 pre 期取均值
    time_predictors_prior=np.arange(2000, 2010),
    dependent="y",
    unit_variable="id",
    time_variable="year",
    treatment_identifier=1,                # 处理单位 id=1
    controls_identifier=list(range(2, 22)),  # donor pool
    time_optimize_ssr=np.arange(1990, 2010),  # 用 1990-2009 拟合
)

# --- 3. 跑 SCM ---
synth = Synth()
synth.fit(dataprep, optim_method="Nelder-Mead")
print(synth.weights())                    # 各 donor 的权重 W*
print(synth.summary())                    # 预测变量均值对比 + RMSPE

# --- 4. 画处理 vs 合成轨迹图 ---
synth.plot_treatment_outcome()

# --- 5. 安慰剂检验(placebo in-space)---
from pysyncon import Placebo
placebo = Placebo(dataprep, synth)
placebo.plot_placebo(figsize=(10, 6))     # gap plot
print(placebo.p_value(period_post=np.arange(2010, 2020)))

07 论文案例与常见错误

论文案例

English · JASA
Synthetic Control Methods for Comparative Case Studies: Estimating the Effect of California's Tobacco Control Program
Alberto Abadie, Alexis Diamond & Jens Hainmueller · Journal of the American Statistical Association, 2010, 105(490): 493–505
SCM 的方法论奠基之作。用 1970–2000 年美国 39 个州数据,估计加州 1988 年烟草控制计划对人均香烟消费的影响。合成加州 = 0.45×明尼苏达 + 0.17×康涅狄格 + ... 政策后加州人均香烟消费比合成加州低约 20 pack-year。本页方法、Stata synth 命令、basque / smoking 示例数据均来自此文。
English · AER
The Economic Costs of Conflict: A Case Study of the Basque Country
Alberto Abadie & Javier Gardeazabal · American Economic Review, 2003, 93(1): 113–132
SCM 的开山应用论文。巴斯克地区 1975 年起遭遇恐怖冲突,用西班牙其他地区的凸组合构造"合成巴斯克",发现冲突使人均 GDP 比合成地区低约 10%。证明了在"处理对象只有 1 个"的极端场景下 SCM 是可行的。
English · JEL
Using Synthetic Controls: Feasibility, Data Requirements, and Methodological Aspects
Alberto Abadie · Journal of Economic Literature, 2021, 59(2): 391–425
SCM 最新权威综述。系统讨论 Donor pool 选择、预处理拟合、安慰剂检验、SCM 与 DID 的关系、SCM 推断(in-space vs in-time placebo)、以及合成 DID(synthetic DID, Ben-Michael et al. 2021)等扩展。写 SCM 论文前必读。
English · JoE
The Comparative Politics of Administrative Campaigns: A Synthetic Control Approach(示例)
Bobo, Corrable, Pearlman, Schaub & Gipp · 多篇 SCM 应用范式 · APSR / JoE 2015–2020
SCM 在政治学/公共政策中的典型应用范式:单一国家/城市遭遇制度冲击,用其他国家/城市做 donor。论文结构固定为:图 1 处理 vs 合成轨迹;图 2 gap plot 含所有 placebo;表 1 权重表;表 2 预测变量均值对比。
中文 · 《经济研究》/《管理世界》
自贸区设立的经济效应:合成控制法应用范式
聂飞 (2016)《财贸研究》;梁双陆等 (2020)《当代经济管理》;多篇《经济研究》《管理世界》2015–2022 自贸区/高新区/智慧城市评估文章
中国自贸区(上海 2013、天津/广东/福建 2015)是典型的"单处理对象 + 渐进推广"场景,SCM 是中文顶刊评估自贸区政策效应的标准工具。典型做法:以上海为处理对象,其余省份为 donor;合成上海 vs 真实上海在 GDP、FDI、产业结构升级指数上的差距;安慰剂检验剔除 pre-RMSPE 过大的省份。

常见错误

错误 1:Donor pool 里混入了同样受政策影响的单位

研究上海自贸区效应时,把 2015 年也设了自贸区的天津/广东/福建留在 donor pool 里——这三个 donor 本身被政策污染,合成上海会被"拉向"政策后轨迹,效应被低估。规范做法:每评估一个自贸区,把其他已设自贸区的省份全部从 donor 剔除。

错误 2:预处理拟合很差还硬出结论

政策前真实处理单位和合成单位的轨迹根本不重合(pre-RMSPE 巨大),但政策后出现差距就宣称"政策有效"。如果政策前都拟合不上,政策后的差距完全可能是 donor 组合本身的偏差,不是政策效应。必须报告 pre-RMSPE,并在拟合差时改用合成 DID 或其他方法。

错误 3:安慰剂检验不剔除 pre-RMSPE 大的单位

把 donor pool 里 pre-RMSPE 巨大的单位也画进 gap plot,会让你的真实效应看起来不极端(因为这些单位的伪效应天然很大)。必须按 pre-RMSPE 排序,剔除超过真实处理单位 pre-RMSPE 2 倍的伪处理单位,再报告 placebo p 值。

错误 4:预测变量选得太随意

把所有可观测变量都扔进 predictors(),包括与结果变量无关的噪声变量。SCM 的预测变量应该是"经济学理论上预测结果变量的重要变量"(如经济结构、人口结构、政策前结果变量的若干年均值),而不是数据里有什么就放什么。Abadie (2021) 建议预测变量控制在 5–10 个。

错误 5:把 SCM 当成"唯一因果证据"

SCM 在单处理对象场景下很优雅,但它本质上是 case study,外部效度有限。顶刊做法是 SCM + DID + 事件研究三角验证,而不是只跑 SCM 就下结论。中文论文常见"跑了 SCM 就万事大吉"的倾向,审稿人现在会追问"为什么不用 csdid?为什么不 stacked DID?"

进阶资料

  • Ben-Michael, Feller & Rothstein (2021) —— Synthetic DID:SCM 与 DID 的结合,用 DID 残差调整 SCM 拟合误差。Stata 命令 sdid
  • Carcio, Kosec & Li (2019) —— Generalized Synthetic Control(gsc),允许多个处理单位 + 交互式固定效应。Stata 命令 gsynth
  • Abadie (2021 JEL) —— 入门 SCM 的最佳路线图,免费下载。