2.4 平均处理效应(ATE/ATT/LATE)
"The central role of the randomized experiment in statistical inference is beyond dispute.""随机实验在统计推断中的核心地位是无可争议的。"— Guido Imbens, 2021 Nobel Laureate in Economics (2021年诺贝尔经济学奖得主)
理解不同因果效应的含义与估计
本节目标
- 区分 ATE、ATT、ATU、LATE 等不同效应
- 理解意向性分析(ITT)
- 掌握处理效应异质性(CATE)
- 学习不依从问题的处理方法
平均处理效应的类型
核心问题
为什么需要区分不同的平均效应?
因为在现实中:
- 并非所有人都接受分配的处理(不依从,Non-compliance)
- 处理效应可能因人而异(异质性,Heterogeneity)
- 我们关心的总体可能不同(全样本 vs 处理组 vs 依从者)
1️⃣ ATE:平均处理效应
定义
Average Treatment Effect(ATE):全样本的平均因果效应
含义:如果随机抽取一个人接受处理,平均会有多大效应
案例:教育培训
# 假设有"上帝视角"数据
data = pd.DataFrame({
'id': range(5),
'Y0': [5000, 6000, 5500, 7000, 6500], # 不培训的收入
'Y1': [6500, 7200, 6800, 8000, 7800], # 培训后的收入
})
data['tau'] = data['Y1'] - data['Y0'] # 个体因果效应
ATE = data['tau'].mean()
print(f"ATE = {ATE:.0f} 元") # ATE = 1400 元适用场景
- 政策普遍推广:如果要在全国推广某政策,我们关心 ATE
- 随机抽样实验:RCT 中随机分配,自然估计 ATE
2️⃣ ATT:处理组的平均效应
定义
Average Treatment Effect on the Treated(ATT):接受处理者的平均因果效应
含义:对于实际接受处理的人,平均效应是多少
与 ATE 的区别
继续上面的例子:
# 假设只有前 3 人接受了处理
data['D'] = [1, 1, 1, 0, 0]
# ATT:只计算处理组的平均效应
ATT = data[data['D'] == 1]['tau'].mean()
print(f"ATT = {ATT:.0f} 元") # ATT = 1433 元
# ATE:全样本的平均效应
ATE = data['tau'].mean()
print(f"ATE = {ATE:.0f} 元") # ATE = 1400 元何时 ATT ≠ ATE?
条件:处理效应存在异质性
案例:职业培训
- 主动参加培训的人可能更有动力
- 他们的培训效应可能更大
- 因此 ATT > ATE
适用场景
- 项目评估:评估已经参与项目的人获得了多少收益
- 志愿者项目:参与者自选择,我们关心他们的收益
3️⃣ ATU:未处理组的平均效应
定义
Average Treatment Effect on the Untreated(ATU):未接受处理者的平均反事实效应
含义:如果让没接受处理的人接受处理,平均效应是多少
案例
# ATU:只计算对照组的平均效应(反事实)
ATU = data[data['D'] == 0]['tau'].mean()
print(f"ATU = {ATU:.0f} 元") # ATU = 1350 元政策含义
- 扩展项目:如果要扩大项目覆盖面,新参与者的效应可能是 ATU
- 边际效应:项目从当前参与者扩展到非参与者,边际收益是 ATU
4️⃣ LATE:局部平均效应
问题:不依从(Non-compliance)
完美 RCT 的假设:
- 分配到处理组 → 100% 接受处理
- 分配到对照组 → 0% 接受处理
现实:
- 单边不依从(One-sided non-compliance)
- 分配到处理组,部分人不接受
- 分配到对照组,无人接受
- 双边不依从(Two-sided non-compliance)
- 分配到处理组,部分人不接受
- 分配到对照组,部分人自己获得处理
案例:药物临床试验
# RCT 数据
rct_data = pd.DataFrame({
'id': range(10),
'Z': [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], # Z = 随机分配
'D': [1, 1, 0, 1, 1, 0, 1, 0, 0, 0], # D = 实际服药
'Y': [120, 115, 95, 125, 118, 90, 105, 88, 92, 85] # 血压
})
print(rct_data)观察:
- ID 2:分配到处理组(Z=1),但未服药(D=0)→ 不依从者
- ID 6:分配到对照组(Z=0),但服药了(D=1)→ 双边不依从
四种人群类型
根据潜在处理状态 和 ,人群可分为:
| 类型 | 描述 | 英文 | ||
|---|---|---|---|---|
| 依从者 | 1 | 0 | 分配到什么就接受什么 | Compliers |
| 总是接受 | 1 | 1 | 无论如何都接受处理 | Always-takers |
| 总不接受 | 0 | 0 | 无论如何都不接受 | Never-takers |
| 反叛者 | 0 | 1 | 做与分配相反的事 | Defiers |
假设:单调性(Monotonicity)— 无反叛者
LATE 的定义
Local Average Treatment Effect(LATE):依从者的平均因果效应
含义:对于会响应随机分配的人,平均效应是多少
LATE 的估计:工具变量(IV)
关键洞察:随机分配 可以作为实际处理 的工具变量
IV 估计量:
分子:意向性效应(ITT) 分母:依从率
# 计算 LATE
ITT_Y = (rct_data[rct_data['Z'] == 1]['Y'].mean() -
rct_data[rct_data['Z'] == 0]['Y'].mean())
ITT_D = (rct_data[rct_data['Z'] == 1]['D'].mean() -
rct_data[rct_data['Z'] == 0]['D'].mean())
LATE = ITT_Y / ITT_D
print(f"ITT (Y): {ITT_Y:.2f}")
print(f"ITT (D): {ITT_D:.2f}")
print(f"LATE: {LATE:.2f}")Python 实现:2SLS(两阶段最小二乘)
import statsmodels.api as sm
from statsmodels.sandbox.regression.gmm import IV2SLS
# 第一阶段:D ~ Z
X1 = sm.add_constant(rct_data['Z'])
first_stage = sm.OLS(rct_data['D'], X1).fit()
print("第一阶段 F 统计量:", first_stage.fvalue)
# 第二阶段:Y ~ D_hat (using Z as instrument)
iv_model = IV2SLS(
endog=rct_data['Y'],
exog=sm.add_constant(np.ones(len(rct_data))),
instrument=sm.add_constant(rct_data['Z'])
).fit()
print(iv_model.summary())5️⃣ ITT:意向性分析
定义
Intention-to-Treat(ITT):按随机分配(而非实际处理)分组的效应
含义:被分配到处理组(无论是否实际接受)vs 对照组的平均差异
ITT 的重要性
| 优点 | 说明 |
|---|---|
| 保留随机化 | 分析单位是随机分配 ,无选择偏误 |
| 政策相关 | 反映了"提供机会"的效应(而非"强制接受") |
| 保守估计 | ITT < ATE(稀释偏误,Dilution bias) |
ITT vs LATE
推导:
案例:药物试验
# 依从率 = 80%
compliance_rate = 0.8
# 真实 LATE = 25 (对依从者的效应)
true_LATE = 25
# ITT = LATE × 依从率
ITT = true_LATE * compliance_rate
print(f"ITT: {ITT:.2f}") # ITT = 20
# 解读:提供药物(无论是否服用)平均降低血压 20 点
# 但对实际服药的人,效应是 25 点处理效应异质性(CATE)
定义
Conditional Average Treatment Effect(CATE):给定协变量 的条件平均效应
含义:不同子群的处理效应可能不同
案例:教育培训的异质性
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 生成数据
np.random.seed(42)
n = 1000
data = pd.DataFrame({
'baseline_skill': np.random.normal(50, 15, n),
'treatment': np.random.binomial(1, 0.5, n)
})
# 异质性效应:基础好的人效应更大
data['tau'] = 5 + 0.3 * data['baseline_skill'] # 个体效应
data['Y0'] = 50 + 0.8 * data['baseline_skill'] + np.random.normal(0, 10, n)
data['Y1'] = data['Y0'] + data['tau']
data['Y_obs'] = np.where(data['treatment'] == 1, data['Y1'], data['Y0'])
# CATE 估计:分组回归
# 将基础技能分为三组
data['skill_group'] = pd.cut(data['baseline_skill'], bins=3,
labels=['低', '中', '高'])
cate_results = []
for group in ['低', '中', '高']:
group_data = data[data['skill_group'] == group]
ATE_group = (group_data[group_data['treatment'] == 1]['Y_obs'].mean() -
group_data[group_data['treatment'] == 0]['Y_obs'].mean())
cate_results.append({'组别': group, 'CATE': ATE_group})
cate_df = pd.DataFrame(cate_results)
print(cate_df)
# 可视化
plt.figure(figsize=(8, 6))
plt.bar(cate_df['组别'], cate_df['CATE'], color=['#3498db', '#2ecc71', '#e74c3c'])
plt.xlabel('基础技能组别')
plt.ylabel('CATE(处理效应)')
plt.title('处理效应异质性:基础越好,培训效应越大')
plt.grid(axis='y', alpha=0.3)
plt.show()机器学习估计 CATE
Causal Forest(因果森林):
# 使用 EconML 库(微软开发)
from econml.dml import CausalForestDML
# 训练因果森林
causal_forest = CausalForestDML(
model_y=RandomForestRegressor(),
model_t=RandomForestClassifier(),
n_estimators=1000
)
X = data[['baseline_skill']]
causal_forest.fit(Y=data['Y_obs'], T=data['treatment'], X=X)
# 预测每个人的 CATE
data['cate_pred'] = causal_forest.effect(X)
# 可视化
plt.figure(figsize=(10, 6))
plt.scatter(data['baseline_skill'], data['cate_pred'], alpha=0.3, s=10)
plt.xlabel('基础技能')
plt.ylabel('预测的 CATE')
plt.title('因果森林估计的个体处理效应')
plt.show()不同效应的关系
数学关系
可视化
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
# 效应值
effects = {
'ATE': 10,
'ATT': 12,
'ATU': 8,
'LATE': 15,
'ITT': 9
}
colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6']
bars = ax.barh(list(effects.keys()), list(effects.values()), color=colors)
# 添加数值标签
for i, (k, v) in enumerate(effects.items()):
ax.text(v + 0.5, i, f'{v}', va='center', fontweight='bold')
ax.set_xlabel('处理效应大小')
ax.set_title('不同平均效应的对比')
ax.set_xlim(0, 17)
ax.grid(axis='x', alpha=0.3)
# 添加注释
ax.annotate('LATE > ATT:依从者效应更大',
xy=(15, 3), xytext=(13, 4.5),
arrowprops=dict(arrowstyle='->', color='gray'))
ax.annotate('ITT < ATE:不依从导致稀释',
xy=(9, 4), xytext=(11, 3.5),
arrowprops=dict(arrowstyle='->', color='gray'))
plt.tight_layout()
plt.show()Python 实战:完整案例
案例:在线课程 RCT(含不依从)
import pandas as pd
import numpy as np
from scipy import stats
import statsmodels.api as sm
from linearmodels.iv import IV2SLS
# ==== 1. 生成数据 ====
np.random.seed(42)
n = 1000
# 个体特征
data = pd.DataFrame({
'id': range(n),
'motivation': np.random.normal(50, 15, n), # 学习动机
'baseline_score': np.random.normal(60, 10, n) # 基线成绩
})
# 随机分配(RCT)
data['Z'] = np.random.binomial(1, 0.5, n)
# 实际参与(不依从)
# 高动机的人更可能依从
prob_comply = 1 / (1 + np.exp(-(data['motivation'] - 50) / 10))
data['comply'] = np.random.binomial(1, prob_comply)
# 实际接受处理
data['D'] = data['Z'] * data['comply']
# 潜在结果
# 效应异质性:动机越高,效应越大
data['tau'] = 5 + 0.2 * data['motivation']
data['Y0'] = 60 + 0.3 * data['motivation'] + 0.5 * data['baseline_score']
data['Y1'] = data['Y0'] + data['tau'] + np.random.normal(0, 5, n)
data['Y_obs'] = np.where(data['D'] == 1, data['Y1'], data['Y0'])
# ==== 2. 估计不同效应 ====
print("=" * 60)
print("不同平均效应的估计")
print("=" * 60)
# (1) 真实 ATE(上帝视角)
true_ATE = data['tau'].mean()
print(f"\n真实 ATE: {true_ATE:.2f}")
# (2) 简单对比(有偏!因为有自选择进入处理)
naive = (data[data['D'] == 1]['Y_obs'].mean() -
data[data['D'] == 0]['Y_obs'].mean())
print(f"简单对比: {naive:.2f} (有偏!)")
# (3) ITT:按随机分配比较
ITT_Y = (data[data['Z'] == 1]['Y_obs'].mean() -
data[data['Z'] == 0]['Y_obs'].mean())
print(f"ITT: {ITT_Y:.2f}")
# (4) 依从率
compliance_rate = (data[data['Z'] == 1]['D'].mean() -
data[data['Z'] == 0]['D'].mean())
print(f"依从率: {compliance_rate:.2%}")
# (5) LATE(IV 估计)
LATE = ITT_Y / compliance_rate
print(f"LATE: {LATE:.2f}")
# ==== 3. 回归估计 ====
print("\n" + "=" * 60)
print("回归估计")
print("=" * 60)
# ITT 回归
X_itt = sm.add_constant(data['Z'])
itt_model = sm.OLS(data['Y_obs'], X_itt).fit(cov_type='HC3')
print("\nITT 回归:")
print(f" 系数: {itt_model.params['Z']:.2f}")
print(f" SE: {itt_model.bse['Z']:.2f}")
print(f" p-value: {itt_model.pvalues['Z']:.4f}")
# 2SLS(LATE)
iv_model = IV2SLS(
dependent=data['Y_obs'],
exog=sm.add_constant(np.ones(n)),
endog=data[['D']],
instruments=data[['Z']]
).fit(cov_type='robust')
print("\n2SLS (LATE):")
print(f" 系数: {iv_model.params['D']:.2f}")
print(f" SE: {iv_model.std_errors['D']:.2f}")
print(f" F-stat (第一阶段): {iv_model.f_statistic.stat:.2f}")
# ==== 4. CATE 估计 ====
print("\n" + "=" * 60)
print("异质性分析(CATE)")
print("=" * 60)
# 按动机分组
data['motivation_group'] = pd.qcut(data['motivation'], q=3,
labels=['低动机', '中动机', '高动机'])
for group in ['低动机', '中动机', '高动机']:
group_data = data[data['motivation_group'] == group]
# ITT by group
itt_group = (group_data[group_data['Z'] == 1]['Y_obs'].mean() -
group_data[group_data['Z'] == 0]['Y_obs'].mean())
# 依从率 by group
comp_group = (group_data[group_data['Z'] == 1]['D'].mean() -
group_data[group_data['Z'] == 0]['D'].mean())
# LATE by group
late_group = itt_group / comp_group if comp_group > 0 else np.nan
print(f"\n{group}:")
print(f" ITT: {itt_group:.2f}")
print(f" 依从率: {comp_group:.2%}")
print(f" LATE: {late_group:.2f}")
# ==== 5. 可视化 ====
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 子图 1:不依从情况
comply_counts = data.groupby(['Z', 'D']).size().unstack(fill_value=0)
comply_counts.plot(kind='bar', ax=axes[0, 0], color=['skyblue', 'salmon'])
axes[0, 0].set_xlabel('随机分配(Z)')
axes[0, 0].set_ylabel('人数')
axes[0, 0].set_title('不依从情况')
axes[0, 0].legend(['未接受处理', '接受处理'])
axes[0, 0].set_xticklabels(['对照组', '处理组'], rotation=0)
# 子图 2:ITT vs LATE
effects = ['ITT', 'LATE', '真实ATE']
values = [ITT_Y, LATE, true_ATE]
axes[0, 1].bar(effects, values, color=['#3498db', '#e74c3c', '#2ecc71'])
axes[0, 1].set_ylabel('效应大小')
axes[0, 1].set_title('不同效应估计对比')
axes[0, 1].grid(axis='y', alpha=0.3)
# 子图 3:异质性(CATE)
cate_data = []
for group in ['低动机', '中动机', '高动机']:
group_data = data[data['motivation_group'] == group]
itt = (group_data[group_data['Z'] == 1]['Y_obs'].mean() -
group_data[group_data['Z'] == 0]['Y_obs'].mean())
cate_data.append(itt)
axes[1, 0].bar(['低动机', '中动机', '高动机'], cate_data,
color=['#3498db', '#2ecc71', '#e74c3c'])
axes[1, 0].set_ylabel('ITT')
axes[1, 0].set_title('处理效应异质性')
axes[1, 0].grid(axis='y', alpha=0.3)
# 子图 4:散点图(动机 vs 效应)
treated = data[data['Z'] == 1]
control = data[data['Z'] == 0]
axes[1, 1].scatter(treated['motivation'], treated['Y_obs'],
alpha=0.3, s=10, label='处理组', color='salmon')
axes[1, 1].scatter(control['motivation'], control['Y_obs'],
alpha=0.3, s=10, label='对照组', color='skyblue')
axes[1, 1].set_xlabel('学习动机')
axes[1, 1].set_ylabel('最终成绩')
axes[1, 1].set_title('动机 vs 成绩(按分配组)')
axes[1, 1].legend()
plt.tight_layout()
plt.savefig('ate_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
print("\n 分析完成!")小结
核心概念对比
| 效应 | 定义 | 估计量 | 适用场景 |
|---|---|---|---|
| ATE | 全样本平均效应 | 简单差分(RCT) | 政策普遍推广 |
| ATT | 处理组平均效应 | 匹配、DID | 项目评估 |
| ATU | 对照组平均效应 | 匹配 | 项目扩展 |
| LATE | 依从者平均效应 | IV/2SLS | 有不依从的 RCT |
| ITT | 意向性效应 | 按分配分组 | 政策效应(保守) |
| CATE | 条件平均效应 | 分组/机器学习 | 异质性分析 |
关键洞察
不依从问题
- ITT 保留随机化,但低估真实效应
- LATE 通过 IV 估计依从者效应
- 依从率 = ITT / LATE
异质性分析
- CATE 揭示不同子群的差异效应
- 有助于精准施策、优化资源配置
效应选择
- 政策制定者关心 ITT(提供机会的效应)
- 研究者关心 LATE(真实机制)
- 实践者关心 CATE(个性化干预)
思考题
理解题:为什么说 ITT 是"保守估计"?在什么情况下 ITT 可能更有政策意义?
计算题:某教育 RCT 发现:
- ITT = 0.2 个标准差
- 依从率 = 60%
问:LATE 是多少?如果强制所有人参与(100% 依从),预期效应是多少?
设计题:你在研究"健身 APP 对减重的因果效应"。
- (a) 定义 ATE、ATT、LATE
- (b) 如何估计 CATE(年龄、性别、基线 BMI)
- (c) 预期哪个效应最大?为什么?
点击查看答案提示
问题 1:
- ITT "保守"是因为包含了不依从者(稀释效应)
- 政策意义:反映"提供项目机会"的实际效应(不能强迫所有人参与)
问题 2:
- LATE = ITT / 依从率 = 0.2 / 0.6 = 0.33 标准差
- 100% 依从的预期效应 ≈ LATE = 0.33(假设 LATE = ATE)
问题 3:
- (c) 预期 ATT > ATE > ATU(自选择效应:主动使用 APP 的人效果更好)
下一步
下一节我们将学习 识别策略与有效性,深入理解因果推断的核心假设(SUTVA、独立性等)。
继续!
参考文献:
- Angrist, J. D., Imbens, G. W., & Rubin, D. B. (1996). "Identification of causal effects using instrumental variables". JASA.
- Imbens, G. W., & Angrist, J. D. (1994). "Identification and estimation of local average treatment effects". Econometrica.
- Athey, S., & Imbens, G. (2016). "Recursive partitioning for heterogeneous causal effects". PNAS.