4. 样本量计算与功效分析:如何确定最小样本量、功效函数、效应量计算、Python实现
在A/B测试中,样本量不足会导致检验功效过低,无法检测出真实存在的差异;样本量过大则浪费流量与时间。本章系统讲解样本量计算的理论基础、功效函数、效应量概念,并给出可直接复用的Python实现。
4.1 核心概念速览
| 概念 | 符号 | 定义 | 典型取值/说明 |
|---|---|---|---|
| 显著性水平 | α | 犯第一类错误(假阳性)的概率 | 0.05(双尾) |
| 检验功效 | 1-β | 正确拒绝原假设的概率 | 0.80 或 0.90 |
| 效应量 | d / δ | 标准化后的组间差异大小 | 小=0.2,中=0.5,大=0.8 |
| 最小样本量 | n | 每组所需的最小观测数 | 由α、1-β、d共同决定 |
经验法则:在α=0.05、功效=0.80的条件下,检测小效应量(d=0.2)需要每组约393个样本;检测中等效应量(d=0.5)需要每组约64个样本。
4.2 样本量计算公式推导
对于两独立样本均值检验(双尾),最小样本量公式为:
n = 2 * ((Z_α/2 + Z_β) / d)²
其中:
Z_α/2 = 标准正态分布在 α/2 处的临界值(α=0.05 时 ≈ 1.96)
Z_β = 标准正态分布在 β 处的临界值(功效=0.80 时 β=0.20,Z_β ≈ 0.84)
d = Cohen's d = (μ₁ - μ₂) / σ_pooled
对于比例检验(如转化率),使用基于正态近似的公式:
n = ( (Z_α/2 + Z_β)² * (p₁(1-p₁) + p₂(1-p₂)) ) / (p₁ - p₂)²
其中:
p₁ = 对照组预期转化率
p₂ = 实验组预期转化率
4.3 功效函数与效应量计算
功效函数描述了在给定样本量和效应量下,检验能够正确拒绝原假设的概率。功效随样本量增加而增加,随效应量减小而减小。
效应量计算(Cohen's d):
d = (mean_A - mean_B) / pooled_std
pooled_std = sqrt( ((n_A-1)*var_A + (n_B-1)*var_B) / (n_A + n_B - 2) )
实践要点:效应量是样本量计算的输入参数,通常基于历史数据、行业基准或最小可检测差异(MDE)来设定。不要使用实验后的观测效应量来反推样本量——这会导致循环论证。
4.4 Python实现:样本量计算与功效分析
以下代码使用 statsmodels 和 scipy 实现完整的样本量计算与功效分析流程。
import numpy as np
from scipy import stats
import statsmodels.stats.power as smp
# ========== 1. 均值检验样本量计算 ==========
def sample_size_means(d, alpha=0.05, power=0.80, alternative='two-sided'):
"""
计算两独立样本均值检验所需的最小样本量(每组)
参数:
d: Cohen's d 效应量
alpha: 显著性水平
power: 检验功效
alternative: 'two-sided' 或 'one-sided'
返回:
n: 每组所需样本量
"""
power_analysis = smp.TTestIndPower()
n = power_analysis.solve_power(
effect_size=d,
alpha=alpha,
power=power,
alternative=alternative
)
return int(np.ceil(n))
# 示例:检测中等效应量 d=0.5
n_means = sample_size_means(d=0.5)
print(f"均值检验所需样本量(每组): {n_means}")
# ========== 2. 比例检验样本量计算 ==========
def sample_size_proportions(p1, p2, alpha=0.05, power=0.80):
"""
计算两样本比例检验所需的最小样本量(每组)
参数:
p1: 对照组比例
p2: 实验组比例
alpha: 显著性水平
power: 检验功效
返回:
n: 每组所需样本量
"""
z_alpha = stats.norm.ppf(1 - alpha/2)
z_beta = stats.norm.ppf(power)
p_bar = (p1 + p2) / 2
numerator = (z_alpha * np.sqrt(2 * p_bar * (1 - p_bar)) +
z_beta * np.sqrt(p1*(1-p1) + p2*(1-p2)))**2
denominator = (p1 - p2)**2
n = numerator / denominator
return int(np.ceil(n))
# 示例:转化率从 10% 提升到 12%
n_prop = sample_size_proportions(p1=0.10, p2=0.12)
print(f"比例检验所需样本量(每组): {n_prop}")
# ========== 3. 功效曲线绘制 ==========
def power_curve(effect_sizes, n_range, alpha=0.05):
"""
生成不同效应量下的功效曲线数据
参数:
effect_sizes: 效应量列表
n_range: 样本量范围
alpha: 显著性水平
返回:
dict: 每个效应量对应的功效值列表
"""
power_analysis = smp.TTestIndPower()
results = {}
for d in effect_sizes:
powers = [power_analysis.solve_power(
effect_size=d, nobs1=n, alpha=alpha, ratio=1.0
) for n in n_range]
results[f"d={d}"] = powers
return results
# 生成数据
n_values = np.arange(20, 500, 10)
effect_sizes = [0.2, 0.5, 0.8]
power_data = power_curve(effect_sizes, n_values)
# 输出部分结果供检查
for d in effect_sizes:
idx = np.where(n_values == 100)[0][0]
print(f"d={d}, n=100 时功效: {power_data[f'd={d}'][idx]:.3f}")
常见陷阱:
- 忽略多重比较校正:同时测试多个指标时,需对α进行Bonferroni或其他方法校正。
- 使用单尾检验时未确认方向:单尾检验样本量更小,但必须事先明确效应方向。
- 未考虑样本损耗:实际投放中用户流失、数据清洗会导致有效样本减少,建议在计算结果上增加10%-20%的冗余。
4.5 样本量、效应量与功效的关系
4.6 实际案例:电商转化率A/B测试样本量规划
场景:某电商平台计划测试新版结算页面,预期转化率从当前的 8% 提升到 9%。设定 α=0.05(双尾),功效=0.80。
# 电商转化率测试样本量计算
p_control = 0.08 # 对照组转化率
p_treatment = 0.09 # 实验组预期转化率
n_ecommerce = sample_size_proportions(p1=p_control, p2=p_treatment)
print(f"电商A/B测试所需样本量(每组): {n_ecommerce}")
# 考虑10%的样本损耗
n_adjusted = int(np.ceil(n_ecommerce / 0.9))
print(f"考虑10%损耗后的样本量(每组): {n_adjusted}")
# 计算可检测的最小效应量(给定样本量)
def min_detectable_effect(n, alpha=0.05, power=0.80):
"""给定样本量,计算可检测的最小效应量"""
power_analysis = smp.TTestIndPower()
d = power_analysis.solve_power(
nobs1=n, alpha=alpha, power=power, ratio=1.0
)
return d
# 假设只能获取每组5000个样本
n_available = 5000
mde = min_detectable_effect(n_available)
print(f"n={n_available} 时可检测的最小效应量: {mde:.3f}")
# 转化为可检测的转化率差异
# 假设 pooled_std ≈ sqrt(p*(1-p)) ≈ sqrt(0.085*0.915) ≈ 0.279
pooled_std = np.sqrt(0.085 * 0.915)
detectable_diff = mde * pooled_std
print(f"可检测的最小转化率绝对差异: {detectable_diff:.4f} ({detectable_diff*100:.2f}%)")
输出解读:
- 每组需要约 5,029 个样本(未调整损耗)
- 调整10%损耗后,每组需要约 5,588 个样本
- 若只能获取每组5,000个样本,可检测的最小效应量 d≈0.056,对应转化率绝对差异约 1.56 个百分点——这意味着从8%提升到9.56%才能被可靠检测
4.7 样本量计算决策流程
4.8 常见问题与避坑指南
| 问题 | 错误做法 | 正确做法 |
|---|---|---|
| 效应量来源 | 使用实验后的观测效应量反推样本量 | 基于历史数据、文献或最小可检测差异(MDE)预先设定 |
| 多重比较 | 对每个指标单独计算样本量,未做校正 | 使用Bonferroni校正:α_adj = α / k(k为指标数) |
| 样本损耗 | 按理论样本量直接启动实验 | 增加10%-20%的冗余样本量 |
| 组间比例 | 默认1:1分流 | 若使用非均衡设计(如2:1),需调整公式中的ratio参数 |
| 连续监测 | 达到样本量后立即停止并检验 | 使用序贯检验或提前停止边界,避免功效膨胀 |
推荐工具:除了Python的
statsmodels.stats.power模块,还可以使用R语言的pwr包、G*Power桌面软件进行交叉验证。对于复杂设计(如聚类随机试验),建议使用模拟法(Monte Carlo simulation)估算样本量。