潜在结果框架与因果图 Potential Outcomes & Causal DAG
从 Rubin 的潜在结果记号 $Y_i(1),Y_i(0)$ 到 Pearl 的 do-calculus 与后门准则。本节讲清楚因果推断的两大语言体系:为什么存在"因果推断的根本问题"、什么是可忽略性假设、DAG 上的混杂因子/对撞因子/中介变量如何识别、以及如何用一张因果图判断"控制哪些变量才对"。
01 Rubin 潜在结果框架与根本问题
因果推断的第一语言是 Rubin 潜在结果框架(Rubin Causal Model, RCM)。对每一个个体 $i$,设想两种"平行宇宙":一种宇宙里 $i$ 接受处理(treatment,记 $D_i=1$),另一种里 $i$ 不接受处理($D_i=0$)。两个宇宙下的结果变量分别记为潜在结果:
个体 $i$ 的个体处理效应(individual treatment effect)被定义为两个潜在结果之差:
问题来了:现实中 $i$ 要么接受处理、要么不接受,只能观测到其中一个潜在结果。我们实际观测到的结果 $Y_i$ 是一个"拼接"出来的量:
这就是 Holland (1986) 所说的 "因果推断的根本问题(fundamental problem of causal inference)":$\tau_i$ 本身永远不可观测,因为我们无法对同一个人同时做实验和不做实验。反事实(counterfactual)——即"如果 $i$ 接受了相反的处理,他的结果会是什么"——在原则上不可见。于是因果推断的全部艺术,就是用其他个体的观测结果作为 $i$ 的反事实替身。这也解释了为什么"处理组 vs 对照组"的设计如此重要:我们只能假设"在给定协变量后,对照组的平均结果可以近似处理组的反事实平均结果"。
如果你想知道"上大学对收入的影响",你不能拿"上过大学的你"和"没上过大学的你"比——因为后者根本不存在。你只能拿"另一个和你很像但没上大学的人"来替你想象那个平行宇宙。因果推断就是把这个"很像"用数学严格化。
02 ATE / ATT / ATU 的定义与关系
既然个体效应不可观测,统计上只能估计某种平均效应。最常用的三个量是:
- ATE(Average Treatment Effect):在整个人群上的平均效应。回答"随机抽一个人,处理平均影响多大?"
- ATT(Average Treatment effect on the Treated):在实际接受处理人群中的平均效应。回答"已经上大学的这批人,上大学对他们的收入影响多大?"——这是政策评估中最关心的量,因为政策的受益者已经或即将是这批人。
- ATU(Average Treatment effect on the Untreated):在未接受处理人群中的平均效应。回答"如果把政策扩大到目前没被覆盖的人群,他们的效应会是多大?"
三者关系:设 $P(D=1)=p$,则 $p\cdot\text{ATT}+(1-p)\cdot\text{ATU}=\text{ATE}$。当处理效应存在异质性(即 $\tau_i$ 随 $X$ 变化)且处理分配 $D$ 与异质性相关时,$\text{ATT}\neq\text{ATU}\neq\text{ATE}$。例如:上大学的回报在"本来就该上大学"的人身上更高,则 ATT > ATE > ATU,这就是著名的自选择偏差。
OLS 在可忽略性下估的是 ATE;匹配/PSM 若在处理组内加权,估的是 ATT;LATE(IV)估的是 complier 上的 LATE,既不是 ATE 也不是 ATT。写论文时必须明确报告估的是哪一个,并说明政策含义对应哪一个。
03 可忽略性 / 无混杂假设
要让"用对照组结果当反事实"在统计上成立,必须施加一个核心假设——可忽略性(ignorability)/ 无混杂(unconfoundedness):
读作:在给定观测协变量 $X$ 后,潜在结果与是否接受处理 $D$ 相互独立。直觉是:一旦我们控制了 $X$,"谁接受处理"就近似于随机分配——处理组和对照组在 $X$ 相同的子集上,$Y(0),Y(1)$ 的分布是相同的。此时对照组在 $X=x$ 处的平均结果 $E[Y\mid D=0,X=x]$ 就可以无偏地当处理组在 $x$ 处的反事实 $E[Y(0)\mid X=x]$。
这个假设等价于:所有同时影响 $D$ 和 $Y$ 的混淆变量都已被 $X$ 完整观测并控制。它本质上是一个"信仰声明"——我们永远无法用数据直接证明它成立,只能靠经济理论、制度背景、平衡检验(covariate balance)和稳健性检验来间接说服读者。
它要求没有未观测混淆变量(no unobserved confounder)。如果存在一个 $U$ 同时影响 $D$ 和 $Y$ 且 $U\not\perp D\mid X$,那么即使你控制了所有观测协变量,估计仍有偏。这正是内生性问题的本质,也是 IV / RDD / DID 这些"识别策略"存在的理由——它们用不同的方式绕开"无观测混淆"这个强假设。
04 因果图 DAG:节点、箭头与三类变量
有向无环图(Directed Acyclic Graph, DAG)是 Pearl 的因果语言:节点代表变量,箭头 $A\to B$ 代表"$A$ 对 $B$ 有直接因果影响"。DAG 把"可忽略性假设是否成立"翻译成一张可以肉眼检查的图。在 DAG 上有三类关键变量:
- 混杂因子(confounder)$Z$:同时指向处理 $D$ 和结果 $Y$,即 $Z\to D$ 且 $Z\to Y$。它是造成"$D$ 与 $Y$ 相关但非因果"的元凶。控制它可以切断伪相关。
- 对撞因子(collider)$C$:同时被 $D$ 和 $Y$ 指向,即 $D\to C$ 且 $Y\to C$。对撞因子的特点是:不控制它时,它不造成偏误;但一旦控制它(或基于它做条件),反而会在原本独立的 $D,Y$ 之间制造伪相关。这就是 Berkson 悖论。
- 中介变量(mediator)$M$:$D\to M\to Y$。它是处理影响结果的传导通道。控制 $M$ 会"切断"$D$ 经由 $M$ 对 $Y$ 的因果效应,把总效应分解为直接效应和间接效应。估计 ATE 时不要控制中介;估计"直接效应"时才需要控制。
一条从 $D$ 到 $Y$ 的路径(path)就是顺着箭头走出来的一串节点序列。路径分两种:
- 因果路径(causal path):所有箭头方向都与 $D\to Y$ 同向,代表真正的因果通道。
- 后门路径(backdoor path):从 $D$ 出发、沿某个箭头反向回到 $D$、再正向走到 $Y$ 的路径——即 $D \leftarrow \cdots \to Y$ 的形式。后门路径上承载的都是非因果的相关,必须全部阻断。
混杂因子要控制;对撞因子要绕开(控制了反而出问题);中介变量在估总效应时要绕开、估直接效应时才控制。
05 后门准则 backdoor criterion
Pearl 提出的 后门准则(backdoor criterion)给出了一个可机械检查的规则:给定处理变量 $D$、结果变量 $Y$,一组变量 $Z$ 是否足够"控制混杂"?规则有两条:
(i) $Z$ 中不包含任何 $D$ 的后代节点(descendant);
(ii) $Z$ 阻断(block)所有从 $D$ 到 $Y$ 的后门路径(即所有含有指向 $D$ 的箭头的路径)。
"阻断"一条路径的含义是:路径上存在一个非对撞的中介/混杂变量被 $Z$ 控制,或存在一个对撞因子不被 $Z$ 控制(对撞因子本身就阻断路径,无需控制)。
若 $Z$ 满足后门准则,则在 $Z$ 上做条件化后,$D$ 与 $Y$ 之间的关联 $P(Y\mid D,Z)$ 就等于因果效应 $P(Y\mid \text{do}(D))$,可以从观测数据非参数地识别。这正是"可忽略性 $Y(0),Y(1)\perp D\mid Z$"的图论版本。
后门准则告诉你:不是变量越多越好。多控制一个对撞因子或后代节点,可能反而引入偏误;少控制一个混杂因子,则残留偏误。写实证之前,先画一张 DAG,列出候选后门集,是避免"控制错变量"的最便宜方法。
06 do-calculus 入门
do 算子是 Pearl 区分"观测到 $D=d$"与"人为把 $D$ 设为 $d$"的关键记号:
前者是观测条件分布:"我们看到的人里,$D=d$ 的人 $Y$ 分布如何";后者是干预后分布:"我们手术式地把所有人的 $D$ 都设为 $d$(切断所有指向 $D$ 的箭头),$Y$ 的分布如何"。因果效应就是后者的函数。do-calculus 给出三条规则,允许我们在 DAG 上把含 do 的表达式逐步改写为不含 do 的、可从观测数据计算的表达式:
- 规则1(删除观测):若在 $G_{\overline{D}}$(删掉所有指向 $D$ 的箭头后的图)上有 $Y\perp D\mid Z$,则 $P(Y\mid \text{do}(D),Z)=P(Y\mid Z)$。
- 规则2(观测/干预交换):若在 $G_{\underline{D},\overline{Z}}$ 上有 $Y\perp D\mid Z$,则 $P(Y\mid \text{do}(D),\text{do}(Z),X)=P(Y\mid \text{do}(D),Z,X)$——把对 $Z$ 的"干预"降级为"观测"。
- 规则3(删除干预):若在 $G_{\overline{DZ}}$(删掉所有指向 $D$ 和 $Z$ 的箭头)上 $Y$ 与 $\text{do}(Z)$ 无路径,则 $P(Y\mid \text{do}(D),\text{do}(Z),X)=P(Y\mid \text{do}(D),X)$。
对入门读者,最实用的结论是:如果后门准则满足,规则2直接给出 $P(Y\mid \text{do}(D))=\sum_z P(Y\mid D,Z=z)P(Z=z)$,这就是标准的调整公式(adjustment formula)。当后门准则不满足时(例如存在未观测混淆但有工具变量),do-calculus 的三条规则是判断"这个因果效应到底能不能从观测数据识别"的形式化工具。
07 选择偏误 vs 混杂偏误
这两个概念经常被混用,但在 DAG 语言里它们有本质区别:
| 维度 | 混杂偏误 confounding | 选择偏误 selection bias |
|---|---|---|
| 结构 | $Z\to D$ 且 $Z\to Y$(后门路径未阻断) | 在对撞因子 $C$ 上做条件,或基于 $D,Y$ 的共同后代抽样 |
| 来源 | 遗漏变量(omitted variable) | 对撞偏倚 / 样本选择(Berkson 悖论、Heckman 选择) |
| 何时出现 | 没控制 $Z$ | 控制了不该控制的 $C$,或抽样框只覆盖 $C=1$ |
| 修正 | 把 $Z$ 加入回归 / 匹配 / 加权 | 不要在 $C$ 上做条件;用 Heckman 两阶段 / IPW 修正抽样 |
典型例子:大学毕业生的工资差异。若你只在"进入大学"的人里研究"专业选择→工资",你可能控制了"是否上大学"这个对撞因子——因为"上大学"同时由能力(影响工资)和专业偏好(影响工资)决定,控制它反而在专业与工资之间制造伪相关。另一个经典例子是 Heckman (1979):研究工资方程时,样本只包含已工作的人,这等价于在"工作参与"这个对撞因子上做条件,导致 OLS 有偏。
08 完整示例:教育对收入的影响
用一个经典研究问题——上大学($D$)对年收入($Y$)的影响——演示完整的 DAG 分析流程。候选变量:
Ability(能力,未观测):$Ability\to D$(能力强的人更可能上大学)且 $Ability\to Y$(能力直接影响收入)。FatherEdu(父亲教育):$FatherEdu\to Ability$、$FatherEdu\to D$、$FatherEdu\to Y$。Age(年龄):$Age\to Y$(经验效应)。CollegeQuality(大学质量):$D\to CollegeQuality\to Y$,是中介。
画出 DAG 后,从 $D$ 到 $Y$ 的后门路径有:$D\leftarrow FatherEdu\rightarrow Y$、$D\leftarrow Ability\rightarrow Y$、$D\leftarrow FatherEdu\rightarrow Ability\rightarrow Y$。
后门集识别:
- 控制 $\{FatherEdu, Age\}$:阻断了 $D\leftarrow FatherEdu\rightarrow Y$,但 $D\leftarrow Ability\rightarrow Y$ 仍在——因为 Ability 未观测。不满足后门准则。
- 若 Ability 可观测(如用 IQ 分数代理),控制 $\{FatherEdu, Ability, Age\}$:所有后门路径被阻断,且这三个变量都不是 $D$ 的后代。满足后门准则,可识别 ATE。
- 若 Ability 不可观测,则后门准则失败,观测数据无法识别因果效应——必须借助 IV(如 Card (1995) 的"出生地距大学距离")、RDD(如 Clark & Royer)或 DID。
- 不要控制
CollegeQuality,因为它是 $D$ 的后代(中介);控制它会切断 $D\to CollegeQuality\to Y$ 这条因果路径,估计的是"直接效应"而非总效应。
"我们以 DAG 梳理识别策略(图1)。控制父亲教育、年龄后,剩余后门路径 $D\leftarrow Ability\rightarrow Y$ 无法被观测变量阻断。因此基准 OLS 有自选择偏误,我们以距大学距离作为 IV ……(见 05 内生性与IV)。"
09 代码:DAG 可视化与混杂模拟
下面两段 Python 代码:第一段用 networkx 画一张教育-收入的 DAG;第二段模拟数据,演示"不控制混杂"时 OLS 估计的偏误,以及"控制后门集"后偏误消失。
# ============================================================
# 09.1 用 networkx 画"教育 -> 收入"的因果 DAG
# pip install networkx matplotlib
# ============================================================
import networkx as nx
import matplotlib.pyplot as plt
G = nx.DiGraph()
# 节点:D=上大学, Y=收入, A=能力(未观测), F=父亲教育, Age=年龄, Q=大学质量(中介)
nodes = ["D", "Y", "A", "F", "Age", "Q"]
G.add_nodes_from(nodes)
# 因果箭头:从左到右
edges = [
("A", "D"), # 能力影响是否上大学
("A", "Y"), # 能力直接影响收入
("F", "A"), # 父亲教育 -> 能力
("F", "D"), # 父亲教育 -> 上大学
("F", "Y"), # 父亲教育 -> 收入(网络)
("Age", "Y"), # 年龄 -> 收入
("D", "Q"), # 上大学 -> 大学质量
("Q", "Y"), # 大学质量 -> 收入
]
G.add_edges_from(edges)
# 手工布局:让 D 在左、Y 在右
pos = {
"F": (-2, 1),
"A": (-1, 0),
"Age": (-1, -1),
"D": ( 0, 0.5),
"Q": ( 1, 1.2),
"Y": ( 2, 0),
}
labels = {
"D": "D (上大学)",
"Y": "Y (年收入)",
"A": "A (能力,未观测)",
"F": "F (父亲教育)",
"Age": "Age (年龄)",
"Q": "Q (大学质量,中介)",
}
plt.figure(figsize=(9, 5))
nx.draw_networkx(G, pos, labels=labels, with_labels=True,
node_size=2200, node_color="#e8eef5",
font_size=10, font_family="Noto Sans CJK SC",
edge_color="#888", arrows=True, arrowsize=15)
plt.title("因果图:教育 -> 收入(A 未观测,后门路径未阻断)")
plt.axis("off")
plt.tight_layout()
plt.savefig("dag_edu_income.png", dpi=150)
# 读图可知:D<-A->Y 是无法用观测变量阻断的后门路径
# ============================================================
# 09.2 模拟数据:演示"控制混杂前后 OLS 系数的变化"
# 真实 DGP:A -> D, A -> Y, D -> Y, 且 A 不可观测
# 真实因果效应 tau0 = 1.0
# ============================================================
import numpy as np
import statsmodels.api as sm
np.random.seed(42)
n = 5000
# 混淆变量 A(未观测)
A = np.random.normal(0, 1, n)
# 处理 D 由 A 决定:A 高的人更可能上大学
D = (A + np.random.normal(0, 1, n) > 0).astype(float)
# 结果 Y:真实 tau=1.0,A 对 Y 也有直接效应 0.8
Y = 1.0 * D + 0.8 * A + np.random.normal(0, 1, n)
# ---- 模型1:错误地不控制 A ----
X_bad = sm.add_constant(D)
m1 = sm.OLS(Y, X_bad).fit()
print("不控制混杂:beta_D =", round(m1.params[1], 3),
"(真实=1.0,被 A 抬高)")
# ---- 模型2:假设我们观测到 A(理想世界)----
X_good = sm.add_constant(np.column_stack([D, A]))
m2 = sm.OLS(Y, X_good).fit()
print("控制混杂 A:beta_D =", round(m2.params[1], 3),
"(接近真实 1.0,无偏)")
# ---- 模型3:用一个代理变量 A_hat 控制(部分缓解)----
A_hat = A + np.random.normal(0, 0.3, n) # 带噪代理
X_proxy = sm.add_constant(np.column_stack([D, A_hat]))
m3 = sm.OLS(Y, X_proxy).fit()
print("用噪声代理 A_hat:beta_D =", round(m3.params[1], 3),
"(偏误缩小但未消失)")
# install.packages("dagitty")
library(dagitty)
# 用 DAGitty 语法定义图:D 是处理,Y 是结果
dag <- dagitty('dag {
A [unobserved]
F -> A
F -> D
F -> Y
A -> D
A -> Y
Age -> Y
D -> Q
Q -> Y
D [exposure]
Y [outcome]
}')
# 自动计算满足后门准则的最小调整集
adjustmentSets(dag, exposure = "D", outcome = "Y",
type = "backdoor")
# 输出会给出:{ F, Age } 等可观测的调整集;
# 但因为 A 未观测,含 A 的集合并不可行,
# 说明:仅靠观测变量无法识别 ATE —— 必须找 IV。
# 画出 DAG
plot(graphLayout(dag))
10 逐步流程
11 论文案例与常见错误
常见错误
控制对撞因子或 $D$ 的后代(中介)会引入偏误或切断因果通道。写论文前先画 DAG,明确每一个控制变量为什么要控制。
OLS 在可忽略性下估 ATE,PSM 估 ATT,IV 估 LATE。三者不可互换。报告时必须写清楚估的是哪个量,并讨论政策含义对应哪个。
带噪代理变量(如用 IQ 代理能力)只能部分缓解偏误。残留的测量误差本身会造成衰减偏误。要做敏感性分析(如 Oster 2019 的 $\delta$ 法)。
"上大学的人收入高"可能完全来自能力混淆。不画 DAG、不识别后门集,直接把 OLS 系数解释为因果效应,是中文论文最常见的硬伤。
例如只研究"在职人群"的工资方程、只研究"上线产品"的销售差异、只在"通过筛选的样本"里做回归——这些都是在对撞因子上做条件,会引入 Berkson 偏误。
进阶资料
- Pearl, Glymour & Jewell (2016), Causal Inference in Statistics: A Primer —— 入门最友好。
- Hernán & Robins, Causal Inference: What If(免费在线书)—— 流行病学家视角,强调 DAG 与 g-formula。
- R 包
dagitty/ Stata 包dagitty:自动找后门集、画 DAG。 - Python 包
networkx、graphviz:画自定义 DAG。