引言

在观察性研究中,随机对照试验(RCT)是评估因果效应的黄金标准,因为它通过随机分配处理组和对照组来平衡潜在的混杂变量。然而,在许多实际场景中,随机分配是不可行或不道德的,例如研究吸烟对健康的影响或教育政策对收入的影响。这时,观察性数据就需要通过统计方法来模拟随机化,以减少混杂偏差。倾向评分匹配(Propensity Score Matching, PSM)和马氏匹配(Mahalanobis Matching)是两种常用的匹配方法,它们帮助研究者从观察数据中构建可比的处理组和对照组,从而更可靠地估计因果效应。

本文将详细探讨这两种方法的原理、应用步骤、优缺点,并通过具体例子说明其使用。同时,我们将解析常见误区,帮助研究者避免常见陷阱。文章基于因果推断和匹配方法的经典文献(如Rosenbaum和Rubin的1983年工作)以及最新应用实践(如2020年代的机器学习集成),确保内容的准确性和实用性。

倾向评分匹配(Propensity Score Matching, PSM)的原理

定义与核心概念

倾向评分匹配是一种基于倾向评分(Propensity Score)的匹配技术。倾向评分定义为在给定协变量(即预处理变量)条件下,一个个体接受处理的概率。形式上,对于个体i,其倾向评分e(X_i) = P(T_i = 1 | X_i),其中T_i是二元处理指示变量(1表示处理组,0表示对照组),X_i是协变量向量。

PSM的核心思想是:通过匹配具有相似倾向评分的处理组和对照组个体,来平衡协变量分布,从而近似随机化。Rosenbaum和Rubin(1983)证明,如果倾向评分已知或准确估计,那么在匹配后的子样本中,协变量在处理组和对照组间的分布是平衡的,这满足了“条件独立假设”(CIA),从而允许我们估计平均处理效应(ATE)或处理组平均处理效应(ATT)。

原理的数学基础

  • 倾向评分估计:通常使用逻辑回归(Logistic Regression)来估计倾向评分,因为处理是二元的。模型形式为 logit(e(X)) = β_0 + β_1 X_1 + … + β_p X_p。
  • 匹配过程:对于每个处理组个体,从对照组中选择一个或多个倾向评分相近的个体进行匹配。常见匹配方法包括最近邻匹配(Nearest Neighbor Matching)、卡尺匹配(Caliper Matching)和核匹配(Kernel Matching)。
  • 平衡性检验:匹配后,需检验协变量的标准化偏差(Standardized Bias)是否小于10%,或使用t检验/卡方检验确认无显著差异。

PSM的优势在于它只依赖于倾向评分这一维标量,简化了高维协变量的匹配问题。但前提是“可忽略性假设”(即所有混杂变量都已观测到)和“重叠假设”(倾向评分在0和1之间有重叠)。

详细例子:教育干预对大学入学率的影响

假设我们有观察数据集,包含1000名高中生,其中300人参加了大学辅导干预(处理组),700人未参加(对照组)。协变量包括:年龄(18-22岁)、GPA(0-4.0)、家庭收入(美元)、父母教育水平(1-5级)。目标是估计干预对大学入学率(二元结果)的影响。

步骤1:估计倾向评分 使用逻辑回归模型:

import pandas as pd
import statsmodels.api as sm
from sklearn.linear_model import LogisticRegression

# 假设数据
data = pd.DataFrame({
    'age': [19, 20, 18, 21, ...],  # 示例数据
    'gpa': [3.2, 3.8, 2.9, 3.5, ...],
    'income': [50000, 80000, 30000, 60000, ...],
    'parent_edu': [3, 4, 2, 5, ...],
    'treatment': [1, 0, 1, 0, ...]  # 1=干预组
})

X = data[['age', 'gpa', 'income', 'parent_edu']]
X = sm.add_constant(X)  # 添加截距
y = data['treatment']

# 逻辑回归
logit_model = sm.Logit(y, X).fit()
print(logit_model.summary())

# 预测倾向评分
data['propensity'] = logit_model.predict(X)

输出将给出系数,例如gpa的系数为正,表示高GPA学生更可能参加干预。倾向评分范围为0.1-0.9,确保重叠。

步骤2:匹配 使用最近邻匹配(1:1匹配,卡尺0.05):

from sklearn.neighbors import NearestNeighbors
import numpy as np

# 分离处理组和对照组
treated = data[data['treatment'] == 1]
control = data[data['treatment'] == 0]

# 匹配
nn = NearestNeighbors(n_neighbors=1, metric='euclidean')
nn.fit(control['propensity'].values.reshape(-1, 1))
distances, indices = nn.kneighbors(treated['propensity'].values.reshape(-1, 1))

# 卡尺过滤(例如0.05)
matched_control = control.iloc[indices.flatten()]
mask = distances.flatten() < 0.05
matched_treated = treated[mask]
matched_control = matched_control[mask]

# 合并匹配样本
matched_data = pd.concat([matched_treated, matched_control])

这里,我们为每个处理组个体找到最近的对照组个体,仅保留距离小于0.05的匹配对。

步骤3:效应估计与平衡检验

from scipy import stats

# 平衡检验:比较匹配后协变量的均值
for var in ['age', 'gpa', 'income', 'parent_edu']:
    t_stat, p_val = stats.ttest_ind(matched_data[matched_data['treatment']==1][var],
                                   matched_data[matched_data['treatment']==0][var])
    print(f"{var}: t-stat={t_stat:.2f}, p-value={p_val:.3f}")

# 效应估计:逻辑回归结果
y_matched = matched_data['college_admission']  # 假设结果变量
X_matched = matched_data[['treatment', 'age', 'gpa', 'income', 'parent_edu']]
effect_model = sm.Logit(y_matched, sm.add_constant(X_matched)).fit()
print(effect_model.summary())

假设匹配后样本为200对(400人),t检验p>0.05表示平衡。干预效应(treatment系数)为0.5,表示干预增加入学率50%(odds ratio e^0.5≈1.65)。

这个例子展示了PSM如何从观察数据中“模拟”随机化,但需注意:如果遗漏关键协变量(如学生动机),匹配仍会偏差。

马氏匹配(Mahalanobis Matching)的原理

定义与核心概念

马氏匹配是一种基于马氏距离(Mahalanobis Distance)的多变量匹配方法,由P.C. Mahalanobis于1936年提出,用于测量多维空间中点的相似性。在因果推断中,它用于直接匹配协变量向量,而非倾向评分。马氏距离考虑了协变量的协方差结构,因此在处理相关协变量时更鲁棒。

马氏距离公式为:D_M(X_i, X_j) = sqrt((X_i - X_j)^T S^{-1} (X_i - X_j)),其中S是协变量的样本协方差矩阵的逆。这使得距离在协变量尺度和相关性上标准化,避免了欧氏距离忽略相关性的问题。

马氏匹配常用于PSM的补充,或当倾向评分模型难以指定时(如协变量高度非线性)。它更注重协变量的多维平衡,而非概率估计。

原理的数学基础

  • 距离计算:首先计算协方差矩阵S,然后求逆S^{-1}。对于每个处理组个体,计算与所有对照组个体的马氏距离,选择最小距离的个体匹配。
  • 匹配变体:类似于PSM,可使用最近邻、卡尺(基于距离阈值)或半径匹配。卡尺通常设为样本协方差的倍数,如0.2 * sqrt(p)(p为协变量维度)。
  • 平衡性:匹配后,协变量的均值和协方差应平衡。马氏距离的优势在于它自动处理协变量间的相关性,例如如果年龄和GPA相关,距离会调整权重。

马氏匹配的假设包括:协变量服从多元正态分布(近似即可),且样本协方差矩阵正定。缺点是计算密集,尤其在高维数据中。

详细例子:医疗干预对血压的影响

假设数据集有500名高血压患者,其中200人接受新药干预(处理组),300人未接受(对照组)。协变量:年龄、体重指数(BMI)、吸烟史(0/1)、基线血压(连续)。目标:估计干预对6个月后血压降低的影响。

步骤1:准备协变量与协方差矩阵

import numpy as np
import pandas as pd
from scipy.spatial.distance import cdist

# 假设数据
data = pd.DataFrame({
    'age': np.random.normal(50, 10, 500),
    'bmi': np.random.normal(28, 4, 500),
    'smoke': np.random.binomial(1, 0.3, 500),
    'baseline_bp': np.random.normal(140, 15, 500),
    'treatment': [1]*200 + [0]*300
})

# 分离组
treated = data[data['treatment'] == 1][['age', 'bmi', 'smoke', 'baseline_bp']]
control = data[data['treatment'] == 0][['age', 'bmi', 'smoke', 'baseline_bp']]

# 计算协方差矩阵(使用全样本或匹配前)
S = np.cov(data[['age', 'bmi', 'smoke', 'baseline_bp']].T)
S_inv = np.linalg.inv(S)

步骤2:计算马氏距离并匹配

# 计算距离矩阵
dist_matrix = cdist(treated, control, metric='mahalanobis', VI=S_inv)

# 最近邻匹配(1:1)
matched_indices = np.argmin(dist_matrix, axis=1)
matched_control = control.iloc[matched_indices]

# 卡尺过滤(假设阈值为2.0)
threshold = 2.0
mask = dist_matrix[np.arange(len(treated)), matched_indices] < threshold
matched_treated = treated[mask]
matched_control = matched_control[mask]

# 合并
matched_data = pd.concat([
    matched_treated.assign(treatment=1),
    matched_control.assign(treatment=0)
])

这里,马氏距离考虑了年龄与BMI的相关性(例如,年龄越大BMI越高),确保匹配更精确。

步骤3:效应估计与平衡检验

from scipy.stats import ttest_ind

# 平衡检验:协变量均值差异
for var in ['age', 'bmi', 'smoke', 'baseline_bp']:
    group1 = matched_data[matched_data['treatment']==1][var]
    group2 = matched_data[matched_data['treatment']==0][var]
    t_stat, p_val = ttest_ind(group1, group2)
    print(f"{var}: Mean Diff={group1.mean()-group2.mean():.2f}, p={p_val:.3f}")

# 效应估计:线性回归(假设结果为血压降低)
matched_data['bp_change'] = np.random.normal(-10, 5, len(matched_data))  # 模拟结果
import statsmodels.api as sm
X = matched_data[['treatment', 'age', 'bmi', 'smoke', 'baseline_bp']]
y = matched_data['bp_change']
model = sm.OLS(y, sm.add_constant(X)).fit()
print(model.summary())

假设匹配后样本为150对,平衡检验p>0.05。干预系数为-5,表示药物降低血压5单位。

马氏匹配在医疗研究中特别有用,因为它直接处理连续协变量,而无需概率模型。

应用详解

PSM的应用场景

PSM广泛应用于社会科学、经济学和流行病学。例如,在评估最低工资政策对就业的影响时,使用PSM匹配不同州的经济协变量(如GDP、失业率)。最新应用结合机器学习(如随机森林估计倾向评分)提高准确性(见2022年Causal Inference in Python书籍)。

马氏匹配的应用场景

马氏匹配常用于生态学或工程匹配,如在环境政策评估中匹配地理协变量(温度、湿度)。在医学中,用于匹配临床试验的观察子集。结合PSM使用时,可先用PSM粗匹配,再用马氏距离细化。

两者结合的应用

在高维数据中,可先用PSM减少样本,再用马氏匹配优化平衡。例如,在COVID-19疫苗效果研究中,PSM匹配年龄/健康状况,马氏匹配处理相关性高的协变量如BMI和慢性病史。

常见误区解析

误区1:忽略倾向评分模型的正确性

许多人错误地认为PSM只需运行代码即可,但倾向评分模型若遗漏关键协变量(如未观测的动机),匹配仍偏差。解析:始终进行敏感性分析,例如E值(VanderWeele, 2017)来量化未观测混杂所需强度。例子:在教育干预中,若遗漏“学生自我效能”,ATT估计可能高估20%。

误区2:匹配后不检验平衡性

匹配后直接估计效应,而不检查协变量平衡。解析:必须使用标准化偏差或Love图可视化。误区后果:虚假显著性。例子:马氏匹配中,若协变量非正态,距离计算偏差,导致p值<0.05但实际不平衡。

误区3:过度依赖匹配忽略其他方法

认为PSM/马氏匹配是万能的,而忽略逆概率加权(IPW)或双重差分(DID)。解析:匹配适合构建子样本,但若样本小或重叠差,IPW更高效。最新趋势:结合倾向评分与机器学习(如XGBoost)避免线性假设误区。

误区4:高维数据下的计算陷阱

在协变量>20时,马氏距离矩阵计算O(n^2)复杂度导致内存溢出。解析:使用近似算法或降维(如PCA)。例子:在大数据中,使用R的MatchIt包或Python的causalml库优化。

误区5:混淆ATT与ATE

PSM常估计ATT(处理组效应),但用户误以为是总体ATE。解析:明确研究问题,若需ATE,使用完整匹配或加权。误区:在政策评估中,ATT仅适用于已干预群体,忽略未干预者。

结论

倾向评分匹配和马氏匹配是观察性研究中强大的工具,前者通过概率简化匹配,后者通过多维距离确保精确平衡。通过上述例子,我们看到它们在教育和医疗中的实际效用。然而,成功应用依赖于正确模型、平衡检验和避免误区。建议使用软件如R的MatchIt或Python的PropensityScoreMatching库,并结合最新文献(如2023年Journal of Causal Inference)更新方法。最终,匹配不是因果推断的终点,而是起点,应辅以敏感性分析以增强结论可靠性。