AB实验与因果推断:原理差异与实战避坑指南

1次阅读
没有评论

共计 2930 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

概念辨析

1. 潜在结果框架与反事实假设

AB 实验(又称随机对照试验 -Randomized Controlled Trial)的核心数学表达是潜在结果框架(Potential Outcomes)。对于个体 i,其处理效应 τ 可表示为:

AB 实验与因果推断:原理差异与实战避坑指南

τ_i = Y_i(1) - Y_i(0)

其中 Y(1)和 Y(0)分别表示接受处理和不接受处理的潜在结果。由于个体无法同时处于两种状态,实践中我们估计平均处理效应(ATE):

ATE = E[Y(1) - Y(0)]

与因果推断的关键差异在于:

  • AB 实验通过随机化满足 可忽略性假设(Ignorability):(Y(1), Y(0)) ⊥ T
  • 因果推断中的观察性研究需额外控制混淆变量(Confounder)Z 来满足:(Y(1), Y(0)) ⊥ T | Z

2. 混淆变量的可视化分析

通过有向无环图(DAG-Causal Graph)展示典型场景:

T → Y
↑
Z

在 AB 实验中,随机化会切断 Z 到 T 的边,而观察性研究中这条路径会导致偏差。这就是为什么电商场景中新用户占比变化(Z)可能扭曲人工分组(T)对转化率(Y)的影响评估。

实战代码

1. 双样本 t 检验实现

import numpy as np
from scipy import stats

def ttest_ab(data_A: np.ndarray, data_B: np.ndarray, alpha: float = 0.05) -> dict:
    """
    执行双样本 t 检验
    :param data_A: 对照组数据数组
    :param data_B: 实验组数据数组
    :param alpha: 显著性水平
    :return: 包含统计量和 p 值的字典
    """
    t_stat, p_val = stats.ttest_ind(data_A, data_B, equal_var=False)
    return {
        'statistic': t_stat,
        'pvalue': p_val,
        'significant': p_val < alpha
    }

# 模拟数据示例
control = np.random.normal(5, 1, 1000)
treatment = np.random.normal(5.2, 1, 1000)
print(ttest_ab(control, treatment))

2. 样本量计算

from statsmodels.stats.power import tt_ind_solve_power

def calculate_sample_size(
    effect_size: float, 
    alpha: float = 0.05, 
    power: float = 0.8
) -> int:
    """
    计算每组所需最小样本量
    :param effect_size: 标准化效应量(Cohen's d):param alpha: 第一类错误概率
    :param power: 统计功效
    :return: 每组样本量(向上取整)"""
    n = tt_ind_solve_power(
        effect_size=effect_size,
        alpha=alpha,
        power=power,
        ratio=1.0
    )
    return int(np.ceil(n))

# 检测转化率从 10% 提升到 12% 所需样本量(效应量≈0.2)print(f"每组需要样本量:{calculate_sample_size(0.2)}")

生产建议

1. 新用户效应处理

当实验中新用户占比异常时:

  1. 使用分层分析(Stratification)分别计算新旧用户群体的处理效应
  2. 通过协变量调整(Covariate Adjustment)在模型中添加用户类型特征
  3. 延长实验周期至 2 - 4 周,待用户行为稳定后分析

2. 多重检验校正

from statsmodels.stats.multitest import multipletests

def bonferroni_correction(p_values: list, alpha: float = 0.05) -> list:
    """
    Bonferroni 校正实现
    :param p_values: 原始 p 值列表
    :param alpha: 总体显著性水平
    :return: 校正后的显著性判断数组
    """_, adj_p, _, _ = multipletests(p_values, method='bonferroni')
    return adj_p < alpha

# 示例:同时测试转化率、客单价、留存率三个指标
pvals = [0.03, 0.01, 0.08]
print(bonferroni_correction(pvals))  # [False, True, False]

3. 倾向得分匹配

当无法随机分组时,可用 PSM 模拟 RCT:

from sklearn.linear_model import LogisticRegression

def propensity_score_matching(
    X: np.ndarray,  # 特征矩阵
    y: np.ndarray,  # 处理组标记
    n_neighbors: int = 1
) -> np.ndarray:
    """
    计算倾向得分并进行最邻近匹配
    :return: 匹配后的处理组索引数组
    """
    ps_model = LogisticRegression().fit(X, y)
    ps_scores = ps_model.predict_proba(X)[:, 1]

    # 简化的最邻近匹配(实际生产建议用专门的库如 causalml)treated_idx = np.where(y == 1)[0]
    control_idx = np.where(y == 0)[0]

    matched = []
    for i in treated_idx:
        dist = np.abs(ps_scores[control_idx] - ps_scores[i])
        matched.append(control_idx[np.argmin(dist)])

    return np.array(matched)

延伸思考

1. 差分法替代方案

当无法进行随机分组时,双重差分法(DID-Difference in Differences)的估计公式:

DID = (E[Y|T=1,Post] - E[Y|T=1,Pre]) - (E[Y|T=0,Post] - E[Y|T=0,Pre])

关键假设是平行趋势(Parallel Trends),可通过预处理期数据验证。

2. 辛普森悖论练习

动手任务:用以下数据集验证群体反转现象

import pandas as pd

data = pd.DataFrame({'group': ['A','A','B','B'],
    'subgroup': [1,2,1,2],
    'conversion': [0.5, 0.8, 0.7, 0.4],
    'sample_size': [200, 100, 100, 200]
})

# 任务:分别计算总体和分组的转化率差异

参考文献

  1. Rubin, D.B. (2005). Causal Inference Using Potential Outcomes
  2. Imbens, G.W. (2015). Causal Inference for Statistics
  3. Kohavi, R. (2020). Trustworthy Online Controlled Experiments

通过本文的代码实践和理论对比,希望能帮助读者清晰区分 AB 实验与因果推断的应用边界。当面对 ” 观察数据能否替代实验 ” 的决策时,不妨先画出 DAG 图验证关键假设是否成立。

正文完
 0
评论(没有评论)