—关注作者送A/B实验实战工具包在 A/B 实验的日常讨论中大家张口闭口都是 Z 检验 (Z-Test) 或 T 检验 (T-Test)因为它们直接关联着我们最关心的指标——转化率和人均时长。相比之下卡方检验 (Chi-Square Test,χ 2 \chi^2χ2Test)显得有些“边缘化”。很多分析师只知道它能用来算 P 值却很少深究它在实验生命周期中的两个截然不同的角色实验前的“体检医生”和实验后的“裁判员”。这就导致了一个常见困惑为什么有时候卡方检验用来查流量对不对SRM有时候又用来算实验显著不显著今天我们就在白板上把这件事推导清楚。我们将拆解卡方检验的两种形态适合度检验 (Goodness of Fit Test)和独立性检验 (Test of Independence)并解释它们在 A/B 实验中的核心逻辑。一、 卡方检验的物理意义在进入具体分类前先建立一个统一的直觉。无论哪种卡方检验其数学内核都只做一件事衡量“观察到的现实”与“理论上的预期”之间的距离。如果距离很近说明现实符合预期如果距离很远说明预期被打破了要么是运气不好要么是由于某种显著的外部力量。通用公式χ 2 ∑ ( O i − E i ) 2 E i \chi^2 \sum \frac{(O_i - E_i)^2}{E_i}χ2∑Ei(Oi−Ei)2O i O_iOi(Observed)观察频数。即你在实验数据里实际数出来的人数。E i E_iEi(Expected)预期频数。即在某种假设下理论上应该出现的人数。χ 2 \chi^2χ2卡方统计量。这个值越大说明“现实”偏离“预期”越远。二、 适合度检验实验的“体检医生”这是卡方检验在 A/B 实验中最基础、但常被忽视的应用。1. 概念与作用适合度检验 (Goodness of Fit Test)用于判断一个分类变量的实际分布是否符合预设的理论分布。在 A/B 实验中它主要解决一个致命问题样本比率偏差 (SRM)。当你设定流量分配为 50% vs 50% 时这是一个“神的指令”外部规则。适合度检验就是用来检查工程分流系统是否忠实执行了这个指令。2. 预期值 (E EE) 的来源外部规则在这里预期值不依赖数据本身而是依赖实验配置。场景实验配置 A/B 流量 1:1。总样本10,000 人。预期 (E EE)A 组 5,000 人B 组 5,000 人。这是定死的。3. 实战示例假设实验跑了一天进组数据如下A 组4,800 人B 组5,200 人代入公式χ 2 ( 4800 − 5000 ) 2 5000 ( 5200 − 5000 ) 2 5000 40000 5000 40000 5000 8 8 16 \chi^2 \frac{(4800 - 5000)^2}{5000} \frac{(5200 - 5000)^2}{5000} \frac{40000}{5000} \frac{40000}{5000} 8 8 16χ25000(4800−5000)25000(5200−5000)25000400005000400008816查表可知自由度为 1 时χ 2 16 \chi^2 16χ216对应的 P 值远小于 0.05。结论拒绝零假设。实际流量分配显著偏离 50/50。实验系统有 Bug数据不可用停止分析。代码importnumpyasnpfromscipyimportstatsdefcheck_srm(group_a_count,group_b_count,expected_ratio_a0.5): 使用卡方适合度检验 (Goodness of Fit) 检测流量分配是否异常 参数: group_a_count: A组实际样本量 group_b_count: B组实际样本量 expected_ratio_a: A组预期的流量占比 (默认 0.5 即 50%) # 1. 准备数据observednp.array([group_a_count,group_b_count])total_samplenp.sum(observed)# 2. 计算预期频数 (基于外部配置的规则如 50/50)# 注意适合度检验的预期值来自外部设定而非数据本身expected_atotal_sample*expected_ratio_a expected_btotal_sample*(1-expected_ratio_a)expectednp.array([expected_a,expected_b])# 3. 执行卡方适合度检验# scipy.stats.chisquare 专门用于比较观察分布与理论分布chi2_stat,p_valuestats.chisquare(f_obsobserved,f_expexpected)# 4. 输出结果print(f--- SRM 检测报告 ---)print(f观察分布: A{group_a_count}, B{group_b_count})print(f预期分布: A{int(expected_a)}, B{int(expected_b)})print(f卡方统计量:{chi2_stat:.4f})print(fP值:{p_value:.4e})# 科学计数法方便看极小值# 5. 判定结论 (通常以 0.05 或 0.01 为阈值)ifp_value0.05:print( 警告: 存在显著的 SRM 问题实际流量分配不符合预期请检查分流系统。)else:print( 通过: 流量分配正常差异在随机波动范围内。)# --- 模拟运行 ---# 案例数据A组 4800, B组 5200 (显著偏离)check_srm(4800,5200)三、 独立性检验实验的“裁判员”这是大家更熟悉的场景用来评估策略是否有效。1. 概念与作用独立性检验 (Test of Independence)用于判断两个分类变量如“分组”和“是否转化”之间是否存在关联。零假设 (H 0 H_0H0)分组A/B与结果转化/未转化独立。即颜色不影响购买A 和 B 是一样的。备择假设 (H 1 H_1H1)分组与结果不独立。即颜色影响购买A 和 B 存在显著差异。2. 预期值 (E EE) 的来源内部数据推导这是它与适合度检验最大的区别。在评估效果时我们没有“上帝视角”告诉我们转化率应该是多少。我们只能假设“如果 A 和 B 没区别它们应该都等于大盘平均水平”。预期频数计算公式E i j 第 i 行总数 × 第 j 列总数 总样本量 E_{ij} \frac{\text{第 } i \text{ 行总数} \times \text{第 } j \text{ 列总数}}{\text{总样本量}}Eij总样本量第i行总数×第j列总数3. 实战示例我们看一个 2x2 的实验结果分组转化 (Yes)未转化 (No)行合计A 组30 (O 11 O_{11}O11)70100B 组50 (O 21 O_{21}O21)50100列合计80120200计算 A 组转化的预期值 (E 11 E_{11}E11)假设分组和结果没关系A 组应该表现得和整体一样。整体转化率 80/200 40%。A 组有 100 人理论上应该有100 × 40 % 40 100 \times 40\% 40100×40%40人转化。或者套公式E 11 ( 100 × 80 ) / 200 40 E_{11} (100 \times 80) / 200 40E11(100×80)/20040。计算卡方观察值是 30预期值是 40。我们对表格中 4 个格子都做这个计算并求和。如果最终χ 2 \chi^2χ2很大说明“A/B 没区别”这个假设站不住脚从而证明B 组策略有效。代码importpandasaspdfromscipyimportstatsdefanalyze_ab_effect(data_table): 使用卡方独立性检验 (Test of Independence) 评估策略是否有效 参数: data_table: 包含频数的 2x2 (或 MxN) 列表/数组 格式: [[A组转化, A组未转化], [B组转化, B组未转化]] # 1. 打印列联表 (Contingency Table)dfpd.DataFrame(data_table,index[Group A,Group B],columns[Converted,Not Converted])print(--- 实验数据列联表 ---)print(df)print(-*30)# 2. 执行卡方独立性检验# scipy.stats.chi2_contingency 不需要手动算预期值它会根据边缘概率自动计算# correctionFalse 通常在大样本下建议关闭耶茨连续性修正或保持默认 Truechi2,p_value,dof,expected_freqstats.chi2_contingency(data_table,correctionFalse)# 3. 输出统计指标print(f卡方统计量 (Chi2):{chi2:.4f})print(fP值 (P-value):{p_value:.4f})# 4. 业务解读# 零假设 H0: 分组与转化独立 (即策略无效A/B没区别)alpha0.05ifp_valuealpha:print(f 结论 (P {alpha}): 拒绝零假设。)print(策略有效分组与转化率存在显著关联 (Not Independent)。)# 简单计算提升方向rate_adata_table[0][0]/sum(data_table[0])rate_bdata_table[1][0]/sum(data_table[1])print(fA组转化率:{rate_a:.2%}, B组转化率:{rate_b:.2%})else:print(f 结论 (P {alpha}): 无法拒绝零假设。)print(策略无效。当前数据不足以证明 B 组与 A 组有显著差异。)# --- 模拟运行 ---# 案例数据A组(30转化, 70未转化), B组(50转化, 50未转化)experiment_data[[30,70],[50,50]]analyze_ab_effect(experiment_data)四、 核心差异总结很多工程师在看文档时容易晕核心是因为没分清“预期值”是谁给的。请记住这张表维度适合度检验 (Goodness of Fit)独立性检验 (Independence)应用场景SRM 排查(流量分配检查)效果评估(转化率/留存率对比)变量数量1 个 (分组)2 个 (分组 x 结果)预期值来源外部规则(配置是 50%预期就是 50%)内部平均(基于边缘概率计算出的理论值)逻辑隐喻“你没按规矩办事”“你俩关系不一般”五、 进阶多维数据的陷阱在 A/B 实验中卡方独立性检验不仅能处理 2x2 表格还能处理M x N表格例如 3 个实验组对比 4 种用户活跃等级。原理完全一致只是格子变多了。但是严禁直接对三维数据做普通卡方检验。辛普森悖论 (Simpson’s Paradox)如果你想分析“分组”对“转化”的影响同时数据里还包含“设备类型”iOS/Android直接把设备维度合并掉做卡方检验是极度危险的。风险可能 B 组在 iOS 和 Android 上都比 A 组强但因为 B 组里转化率天生较低的 Android 用户更多导致合并后 B 组看起来比 A 组差。解决方案分层分析 (Segmentation)分别计算 iOS 的卡方和 Android 的卡方。CMH 检验 (Cochran-Mantel-Haenszel Test)这是一种分层卡方检验专门用于在控制第三个变量如设备的情况下检验前两个变量分组与转化的独立性。结语卡方检验在 A/B 实验中扮演着双重角色。做实验前用适合度检验确保流量系统没有“灌铅”做实验后用独立性检验判断策略是否真的改变了用户行为。理解了“预期值”来源的区别你就掌握了这把统计学钥匙。如果这篇文章帮你理清了思路不妨点个关注我会持续分享 AB 实验干货文章。