用Python做A/B测试:手把手教你用二项分布检验广告点击率差异(附完整代码)
当产品经理拿着两组广告点击数据问你"新版广告文案真的比旧版好吗?"时,一个严谨的数据分析师不会直接比较百分比差异就下结论。本文将带你用二项分布假设检验,科学验证广告效果差异的真实性。
1. 为什么A/B测试需要统计检验
某次广告优化中,我们观察到:
- 旧版广告:10,000次展示获得200次点击(CTR=2.00%)
- 新版广告:10,050次展示获得230次点击(CTR=2.29%)
表面看新版表现更好,但这两个数字真的具有统计学意义吗?二项分布检验能帮我们回答三个关键问题:
- 观测到的差异是否可能纯属偶然
- 需要多少样本量才能确信差异真实存在
- 差异的置信区间是多少
注意:直接比较百分比是新手常见错误,就像比较北京和上海的气温而不考虑测量误差
2. 二项分布在点击率分析中的原理
每次广告展示只有两种结果:
- 点击(概率p)
- 未点击(概率1-p)
这正是伯努利试验的典型特征。当进行n次独立展示时,点击次数k服从二项分布:
from scipy.stats import binom n = 10000 # 展示量 p = 0.02 # 真实CTR k = 200 # 点击量 # 计算恰好获得200次点击的概率 prob = binom.pmf(k, n, p) print(f"概率:{prob:.4f}") # 输出:0.0108关键性质:
- 期望值:E(k) = n×p
- 方差:Var(k) = n×p×(1-p)
- 标准差:σ = √(n×p×(1-p))
当n足够大时(通常np>5且n(1-p)>5),二项分布近似正态分布,这是假设检验的基础。
3. 完整的两比例Z检验实现
使用Python的scipy.stats进行检验:
import numpy as np from statsmodels.stats.proportion import proportions_ztest # 原始数据 impressions_A = 10000 clicks_A = 200 impressions_B = 10050 clicks_B = 230 # 执行检验 count = np.array([clicks_A, clicks_B]) nobs = np.array([impressions_A, impressions_B]) z_stat, p_value = proportions_ztest(count, nobs, alternative='two-sided') print(f"Z统计量:{z_stat:.3f}") # -2.108 print(f"P值:{p_value:.4f}") # 0.0350结果解读表格:
| 指标 | 值 | 判断标准 | 结论 |
|---|---|---|---|
| Z值 | -2.108 | 负值表示B组表现更好 | |
| P值 | 0.0350 | <0.05 | 差异显著 |
| 95%置信区间 | (-0.0056, -0.0002) | 不包含0 | 确信B组更优 |
4. 实际业务中的进阶技巧
4.1 样本量预估
在测试前计算所需样本量:
from statsmodels.stats.power import zt_ind_solve_power effect_size = 0.003 # 希望检测到的最小CTR差异 alpha = 0.05 # 显著性水平 power = 0.8 # 统计功效 sample_size = zt_ind_solve_power( effect_size=effect_size, alpha=alpha, power=power, ratio=1.0 # 两组样本量相等 ) print(f"每组需要样本量:{int(sample_size)}") # 每组约17,543次展示4.2 多重检验校正
当同时测试多个广告版本时,需要使用Bonferroni校正:
original_alpha = 0.05 num_tests = 5 # 同时测试5个广告版本 adjusted_alpha = original_alpha / num_tests print(f"校正后的显著性水平:{adjusted_alpha}") # 0.014.3 非参数检验方法
当样本量较小时,可以使用Fisher精确检验:
from scipy.stats import fisher_exact table = [[200, 10000-200], [230, 10050-230]] odds_ratio, p_value = fisher_exact(table, alternative='two-sided') print(f"P值:{p_value:.4f}") # 0.03985. 结果可视化与业务报告
用Matplotlib制作专业图表:
import matplotlib.pyplot as plt import seaborn as sns data = { 'Version': ['A', 'B'], 'CTR': [200/10000, 230/10050], 'CI_lower': [0.0173, 0.0200], 'CI_upper': [0.0227, 0.0258] } plt.figure(figsize=(8,5)) sns.pointplot(data=data, x='Version', y='CTR', capsize=0.2, markers='D', scale=0.7) plt.title('广告版本CTR对比 (95%置信区间)') plt.ylabel('点击率') plt.grid(axis='y', linestyle='--', alpha=0.7) plt.show()向非技术团队汇报时,建议采用这个话术结构:
- 业务影响:"新版广告预计能带来每月额外1500次点击"
- 统计确定性:"我们有95%的把握认为差异真实存在"
- 后续行动:"建议全量上线并持续监控效果"