Z检验需先验证正态近似条件,否则直接调用scipy或statsmodels计算P值可能完全不可信;其本质是中心极限定理的近似,不适用于小样本或极低转化率场景。

Z检验前必须确认数据满足正态近似条件
直接套用scipy.stats.norm.ppf或statsmodels.stats.weightstats.ztest算P值,但结果可能完全不可信——因为Z检验本身是中心极限定理的近似应用,不是万能开关。样本量小、转化率极低(比如
实操建议:
- 每组样本量
n≥ 30 是底线,但更稳妥的是检查n * p和n * (1 - p)是否都 ≥ 5(p为预估转化率),否则改用scipy.stats.binom_test - 若A组转化率 2.1%,B组 2.5%,A组流量 1000,B组 1200,则A组期望成功数 =
1000 * 0.021 = 21,满足;但若A组只有 200 流量,200 * 0.021 = 4.2,不满足,别硬上Z检验 - 用
proportion_confint(来自statsmodels.stats.proportion)先看两组95%置信区间是否重叠:重叠 ≠ 不显著,但不重叠基本可判断显著
手动计算Z值比调库更易排查逻辑错误
很多人用ztest函数返回两个数就完事,但一旦P值看起来“反直觉”(比如提升0.8个百分点却P=0.4),根本没法定位问题出在假设、方差估计还是数据清洗上。
推荐手写核心计算步骤,控制每个环节:
立即学习“Python免费学习笔记(深入)”;
- 转化率差值:
delta = p_b - p_a - 合并比例(pooled proportion):
p_pool = (success_a + success_b) / (n_a + n_b)—— 这步错得最多,有人误用各自转化率平均 - 标准误:
se = sqrt(p_pool * (1 - p_pool) * (1/n_a + 1/n_b)) - Z值:
z = delta / se,再查标准正态表或用scipy.stats.norm.cdf(abs(z))算双侧P值
示例:A组 1000人中21转化,B组 1200人中30转化 → p_a=0.021, p_b=0.025, p_pool=(21+30)/(1000+1200)=0.02318,漏掉这个合并步骤直接套公式,Z值会偏高约12%。
双侧检验P值不能直接当“提升概率”解读
输出P = 0.032,不代表“B组比A组好有96.8%把握”。P值只是假设检验框架下的条件概率:在“两组真实转化率相等”这个零假设成立的前提下,观察到当前差异(或更大)的概率。
常见误用场景:
- 业务方问“B组到底有多大概率更好”,你答“P=0.03,所以有97%胜率”——这是贝叶斯解释,Z检验不提供该信息
- 设置α=0.05后拒绝零假设,但没报告效应量(如
delta)、置信区间宽度,导致无法判断实际业务价值(0.004提升在千万级流量下才值得推) - 多次检验未校正:同一组数据反复按渠道、设备、新老用户切片做Z检验,
P 出现概率远超5%,需用Bonferroni或FDR校正
Python里Z检验结果要和原始数据对齐,尤其注意NaN和类型转换
从数据库或CSV读进来的success列如果含字符串"null"、空格或None,astype(int)会变成NaN,而ztest默认跳过NaN但不报错,实际参与计算的样本量变少,且不提示——这会导致SE低估、Z值虚高。
关键检查点:
- 运行前加断言:
assert not df[['n', 'success']].isna().any().any() -
success列必须是整数,不能是float(哪怕值是21.0),否则ztest内部可能触发隐式类型转换异常 - 若用
statsmodels的ztest,输入必须是1D数组,传入DataFrame列要显式取.values,否则报ValueError: too many dimensions - 测试脚本开头固定随机种子(如
np.random.seed(42))仅对模拟数据有意义;真实AB测试无需也不应设seed
最常被忽略的一点:线上分流ID和实验日志中的用户ID匹配不上,导致n_a和success_a统计口径不一致——Z检验再准,输进去的是错数据,结果就是废纸。


















