共计 2828 个字符,预计需要花费 8 分钟才能阅读完成。
为什么相关性不等于因果性?
在广告点击率分析中,我们经常观察到「高收入用户点击奢侈品广告更多」的现象。传统统计方法可能得出『收入水平影响广告点击』的结论,但实际上存在多种可能性:

- 真实因果:高收入导致奢侈品点击($income \to click$)
- 反向因果:频繁点击奢侈品广告的用户被系统标记为高收入($click \to income$)
- 混杂因素:大城市用户既收入高又对奢侈品感兴趣($city \to income$,$city \to click$)
因果推断 vs 传统统计
传统统计方法(如回归分析)只能识别变量间的 关联性,而因果推断需要回答反事实问题:
如果对同一用户同时展示 / 不展示广告(实际不可能),点击行为会如何变化?
do-calculus 数学基础
Judea Pearl 提出的 do- 算子形式化定义了干预(intervention):
$$
P(Y|do(X=x)) \neq P(Y|X=x)
$$
关键差异在于:
- $P(Y|X=x)$ 反映观察到的关联
- $P(Y|do(X=x))$ 表示强制设定 $X=x$ 后的结果分布
核心实现:倾向得分匹配(PSM)
Python 实现代码
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
# 生成模拟数据(广告曝光 T, 用户特征 X, 点击 Y)data = pd.DataFrame({'age': np.random.normal(35, 5, 1000),
'income': np.random.lognormal(10, 0.3, 1000),
'T': np.random.binomial(1, p=0.4, size=1000), # 是否曝光广告
'Y': np.zeros(1000)
})
# 标准化特征
scaler = StandardScaler()
X = scaler.fit_transform(data[['age', 'income']])
# 计算倾向得分(propensity score)ps_model = LogisticRegression()
ps_model.fit(X, data['T'])
data['ps'] = ps_model.predict_proba(X)[:, 1]
# 最近邻匹配(1:1)from sklearn.neighbors import NearestNeighbors
treated = data[data['T']==1]
control = data[data['T']==0]
nbrs = NearestNeighbors(n_neighbors=1).fit(control[['ps']])
_, indices = nbrs.kneighbors(treated[['ps']])
matched_control = control.iloc[indices.flatten()]
# 平衡性检查(标准化均值差 SMD < 0.1)def smd(a, b):
return (a.mean() - b.mean()) / np.sqrt((a.var() + b.var())/2)
print(f"Age SMD: {smd(treated['age'], matched_control['age']):.3f}")
print(f"Income SMD: {smd(treated['income'], matched_control['income']):.3f}")
因果图构建与验证
使用 DAG(有向无环图)明确变量间的因果关系假设:
import networkx as nx
import matplotlib.pyplot as plt
dag = nx.DiGraph()
dag.add_edges_from([('city', 'income'),
('city', 'click'),
('income', 'click'),
('ad_exposure', 'click')
])
# 可视化
pos = nx.spring_layout(dag)
nx.draw(dag, pos, with_labels=True, node_size=2000, font_weight='bold')
plt.show()
# 识别混杂因子(需要控制的变量集)print("Backdoor paths:",
nx.d_separated(dag, {'ad_exposure'}, {'click'}, {'income'}))
性能优化技巧
高维特征处理
当特征维度 $p$ 很大时,双重稳健估计(Doubly Robust)结合了倾向得分和结果模型优势:
$$
\hat{\tau}_{DR} = \frac{1}{n}\sum \left[
\frac{T_i(Y_i – \hat{\mu}_1(X_i))}{\hat{e}(X_i)} + \hat{\mu}_1(X_i) \right] – \left[
\frac{(1-T_i)(Y_i – \hat{\mu}_0(X_i))}{1-\hat{e}(X_i)} + \hat{\mu}_0(X_i) \right]
$$
隐变量敏感性分析
使用 R 包 sensemakr 或以下公式评估未观测变量 $U$ 的影响:
$$
\Gamma = \frac{P(U=1|T=1)/P(U=1|T=0)}{P(U=0|T=1)/P(U=0|T=0)}
$$
生产环境挑战
样本重叠问题
当同一个用户出现在实验组和对照组时,采用交叉验证或聚类标准误:
from statsmodels.stats.weightstats import DescrStatsW
# 聚类在用户 ID
res = DescrStatsW(outcome, weights=ipw, cluster=data['user_id'])
print(res.ttest_mean())
缺失数据处理
对于非随机缺失(MNAR),使用多重插补或模式混合模型:
from statsmodels.imputation.mice import MICEData
imp = MICEData(data[['age', 'income', 'T', 'Y']])
imp.update_all() # 迭代 5 次
completed_data = imp.data
扩展学习路线
- 基础理论:
- 《Causal Inference: The Mixtape》- Scott Cunningham
-
Pearl 的因果图模型(DAG)
-
算法实现:
- PC 算法(Peter-Clark)用于因果发现
-
微软的 DoWhy 库
-
前沿方向:
- 时变因果(Dynamic Treatment Regimes)
- 异质性因果效应(CATE)
实践心得
在电商场景应用因果推断时,我们发现:
- 单纯 PSM 在特征高度重叠时效果较好,但对极端倾向得分敏感
- 双重差分(DID)更适合面板数据,但需要平行趋势假设
- 工具变量(IV)的寻找往往依赖业务知识而非纯数据驱动
建议从 AB 测试的扩展场景开始实践,逐步过渡到观测数据分析。
