实验是每个产品公司的基石。
无论是Spotify的AI歌单生成器、Meta的个性化Threads功能,还是谷歌最新的搜索升级,这些功能的推出,都不是某个产品团队成员一觉醒来偶然想到的灵感。他们只有在经过大量测试后才会正式发布。
掌握数据驱动决策的关键技能
产品功能通过持续不断的实验来发布和升级,这些公司的目标在于保持用户注意力,并留住用户在平台上。
一组数据科学家专门负责这些实验——他们采用的方法被称为A/B测试。作为一名数据科学家,我几乎每天都会进行并分析A/B测试,而且在我参加的每次面试中都会被深入提问A/B测试相关知识。
在本文中,我会向你介绍如何使用Python进行A/B测试。完成本教程后,你将清楚地理解什么是A/B测试,何时使用它们,以及启动和分析实验所需的统计学知识。
什么是A/B测试?
A/B测试允许你比较同一事物的两个不同版本。比如,你拥有一个网站,想要检查用户点击红色结账按钮的购买次数是否多于蓝色按钮,就可以进行A/B测试。
具体操作上,你可以让一半用户看到蓝色按钮,另一半用户看到红色按钮,运行一个月后,选用点击量更高的按钮版本发布网站。
听起来似乎很简单,对吧?
但实际上,运行A/B测试时需要考虑一些细节。
例如:
如果红色按钮获得100次点击,蓝色按钮获得99次点击,这种差异会不会只是随机现象?会不会不是按钮的颜色造成点击差异,而是用户行为或一天中不同时间段的影响?
如何决定谁看到红色按钮,谁看到蓝色按钮?
在决定哪个按钮更好之前,你需要多少点击量?每组10次?100次?还是1000次?
如果A/B测试未正确设置,你的结果将不够准确,甚至可能导致你(或公司)损失几十万美元的销售额。
本教程中,我们将探讨执行A/B测试时需要遵循的一些最佳实践。我会给你提供一个A/B测试框架——创建成功A/B测试的逐步指南,并附上每一步所需的Python示例代码。
你可以参考本指南,并在需要创建自己的A/B测试时复用其中代码。同时你也可以使用本教程中的框架,准备数据科学或数据分析岗位面试中的A/B测试相关问题。
如何在Python中运行A/B测试?
我们以一个电商网站为例:
网站所有者Jean希望将她的落地页颜色从白色改为粉色,她认为这会增加页面点击次数。
为了决定是否要改变落地页颜色,Jean决定进行A/B测试,其中包括以下步骤:
步骤一:创建假设
假设是一个明确的陈述,定义你要测试的内容及期望的结果。
Jean的假设如下:
“将落地页颜色从白色改为粉色不会影响点击量。”
这被称为零假设(H0),假定控制组(白色页面)与实验组(粉色页面)之间不存在显著差异。
测试完成后,我们可能会:
-
拒绝H0,意味着控制组和实验组之间存在显著差异。
-
无法拒绝H0,意味着未检测到明显差异。
如果我们拒绝零假设,说明页面从白色改为粉色后出现显著差异。如果差异为正(点击量增加),我们就可以更改落地页颜色。
步骤二:定义成功指标
创建假设后,需要定义实验成功的指标,决定是否拒绝零假设。
Jean的落地页案例中,我们可以选择以下其中一个指标作为主要成功指标:
-
点击率(CTR)
-
每用户点击数
-
每次网站访问点击数
为简化起见,我们选择点击率(CTR)作为主要成功指标。
因此,若粉色页面(实验组)的点击率显著高于白色页面(控制组),我们将拒绝零假设。
步骤三:计算样本量与实验时长
定义假设和成功指标后,我们需要确定实验所需的样本量和持续时间。
假设Jean的网站每月有10万访客。
那么在10%的用户上进行测试是否足够?50%?或是所有用户?
这里涉及统计功效(statistical power)和最小可检测效应(MDE)的概念。
简单来说,MDE是我们希望检测到的最小变化幅度。例如,如果Jean发现CTR提升0.1%,这个差异对业务是否有意义?还是她至少需要看到5%的改进才值得付出开发成本?
MDE帮助确定所需的样本量。如果Jean想高置信度地检测0.0001%的CTR变化,她可能需要对100万用户进行实验。但她每月只有10万访客,这意味着她需对全部用户运行实验10个月。
实际中,这么小的MDE并不现实,因为业务决策需要快速推动。因此,统计严谨性和速度必须有所权衡:
-
较低MDE = 更大样本量
-
较高MDE = 较小样本量
实验时间越长,越可能发现细微差异。但值得为微小差异而开展长达一年的实验吗?
以下是用于计算样本量和实验时长的Python代码:
步骤1:定义样本量和时长函数
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import pandas as pd
def calculate_sample_size(baseline_conversion, mde, power=0.8, significance_level=0.05):
expected_conversion = baseline_conversion * (1 + mde)
z_alpha = stats.norm.ppf(1 - significance_level/2)
z_beta = stats.norm.ppf(power)
sd1 = np.sqrt(baseline_conversion * (1 - baseline_conversion))
sd2 = np.sqrt(expected_conversion * (1 - expected_conversion))
numerator = (z_alpha * np.sqrt(2 * sd1**2) + z_beta * np.sqrt(sd1**2 + sd2**2))**2
denominator = (expected_conversion - baseline_conversion)**2
sample_size_per_variant = np.ceil(numerator / denominator)
return int(sample_size_per_variant)
def calculate_experiment_duration(sample_size_per_variant, daily_visitors, traffic_allocation=0.5):
visitors_per_variant_per_day = daily_visitors * traffic_allocation / 2
days_required = np.ceil(sample_size_per_variant / visitors_per_variant_per_day)
return int(days_required)
步骤二:计算不同MDE(最小可检测效应)对应的样本量
现在,我们可以创建一个数据表,给出不同MDE下所需的样本量和时长:
# Jean网站的示例:不同MDE与样本量的关系
daily_visitors = 100000 / 30 # 将每月访客量换算成每日访客量
baseline_conversion = 0.05 # Jean当前落地页点击率(假设为5%)
# 不同MDE值对应的样本量表
mde_values = [0.01, 0.02, 0.03, 0.05, 0.10, 0.15] # 从1%到15%变化幅度
traffic_allocations = [0.1, 0.5, 1.0] # 使用网站流量的10%、50%、100%
results = []
for mde in mde_values:
sample_size = calculate_sample_size(baseline_conversion, mde)
for allocation in traffic_allocations:
duration = calculate_experiment_duration(sample_size, daily_visitors, allocation)
results.append({
'最小可检测效应(MDE)': f"{mde*100:.1f}%",
'流量占比': f"{allocation*100:.0f}%",
'每组所需样本量': f"{sample_size:,}",
'实验时长(天)': duration
})
# 创建数据表并输出显示结果
df_results = pd.DataFrame(results)
print("不同MDE对应的样本量与实验时长:")
print(df_results)
如果你想复用上述代码,只需要修改以下参数即可:
-
每日用户数量
-
基准转化率(baseline_conversion)——根据你观察的指标修改,比如“App打开率”或“用户取消率”
-
MDE数值——上述示例中我们列出了1%-15%的范围,具体取决于你的业务场景。例如,如果你在一家拥有数百万月活用户的大型科技公司进行A/B测试,你可能需要关注0.01%-0.05%的更小MDE值
-
流量占比——根据你愿意进行实验的人群比例而定
步骤三:可视化MDE与样本量之间的关系
为了使结果更直观,我们可以绘制图表,展示MDE与样本量之间的关系:
# 可视化MDE与所需样本量的关系
plt.figure(figsize=(10, 6))
mde_range = np.arange(0.01, 0.2, 0.01)
sample_sizes = [calculate_sample_size(baseline_conversion, mde) for mde in mde_range]
plt.plot(mde_range * 100, sample_sizes)
plt.xlabel('最小可检测效应(MDE)%')
plt.ylabel('每组所需样本量')
plt.title('所需样本量与MDE的关系')
plt.grid(True)
plt.yscale('log')
plt.tight_layout()
plt.savefig('sample_size_vs_mde.png')
plt.show()
这样的图表能帮助业务相关人员更直观地理解样本量与效果之间的权衡关系,便于决策团队快速确定可接受的MDE与样本量之间的平衡点。
步骤四:分析A/B测试的实验结果
确定了样本量和实验时长后,你可以正式运行A/B测试,收集数据并做出业务决策。
在分析A/B测试结果时,我们需要回答以下问题:
-
实验组与控制组之间在指标表现上是否存在差异?
在Jean的例子中,这个问题可以表述为:“粉色页面与白色页面之间的点击率是否存在差异?”
-
这种差异是否具有统计显著性?
分析时需要关注以下要素:
-
统计显著性(Statistical significance)——控制组与实验组之间观察到的差异是否具有统计学意义?
-
置信区间(Confidence interval)——实际效应值可能处于的范围。如果置信区间包含0,意味着控制与实验组之间没有显著差异。
-
效应大小(Effect size)——控制与实验组之间差异的实际幅度。
以下是用于上述计算的Python代码:
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import pandas as pd
def analyze_ab_test_results(control_visitors, control_conversions,
treatment_visitors, treatment_conversions,
significance_level=0.05):
# 计算转化率
control_rate = control_conversions / control_visitors
treatment_rate = treatment_conversions / treatment_visitors
# 计算绝对差异和相对差异
absolute_diff = treatment_rate - control_rate
relative_diff = absolute_diff / control_rate
# 计算标准误差
control_se = np.sqrt(control_rate * (1 - control_rate) / control_visitors)
treatment_se = np.sqrt(treatment_rate * (1 - treatment_rate) / treatment_visitors)
# 计算z分数
pooled_se = np.sqrt(control_se**2 + treatment_se**2)
z_score = absolute_diff / pooled_se
# 计算p值(双尾检验)
p_value = 2 * (1 - stats.norm.cdf(abs(z_score)))
# 计算置信区间
z_critical = stats.norm.ppf(1 - significance_level/2)
margin_of_error = z_critical * pooled_se
ci_lower = absolute_diff - margin_of_error
ci_upper = absolute_diff + margin_of_error
# 判断差异是否显著
is_significant = p_value < significance_level
return {
'控制组转化率': control_rate,
'实验组转化率': treatment_rate,
'绝对差异': absolute_diff,
'相对差异(%)': relative_diff * 100, # 转换为百分比
'z分数': z_score,
'p值': p_value,
'置信区间下限': ci_lower,
'置信区间上限': ci_upper,
'是否显著': is_significant
}
你只需输入用户数量和转化数量,即可获得类似以下的结果汇总表:
控制组转化率 | 实验组转化率 | 绝对差异 | 相对差异(%) | z分数 | p值 | 置信区间下限 | 置信区间上限 | 是否显著 |
---|---|---|---|---|---|---|---|---|
5% | 5.5% | 0.5% | 10% | 3.2 | 0.001 | 0.3% | 0.7% | 是 |
回到Jean的落地页示例,这个结果表明粉色页面显著提高了10%的CTR,因此她可以决定将落地页颜色从白色更改为粉色。
总结与建议:
如果你阅读到这里,恭喜你!
你已经掌握了A/B测试的基础知识,了解了如何实施测试,以及背后的统计学原理。
你还可以复用本文代码,运行和分析其他A/B测试的结果。
此外,如果你对文中的某些概念感到困惑,不要担心!A/B测试并非总是简单易懂,尤其对于统计学初学者而言。
作为下一步建议,你可以免费学习由Google数据科学家教授的Udacity的A/B测试课程,以获得更全面的教程。
之后,你可以在Kaggle上找到A/B测试数据集,通过分析数据来进一步实践你的技能,并生成实际的业务建议。