RSS
菜单
全部文章快讯开发科技深度热点

第 28 章 案例五:A/B 测试结果分析

内容摘要

A/B 测试(实验)是数据驱动决策的标准工具:把用户随机分到对照组和实验组,比较指标差异。本案例用 pandas + scipy 完整实现:**实验设计检查、指标对比、显著性检验(t 检验/卡方检验)、置信区间、样本量**,并规范地给出结论。

A/B 测试(实验)是数据驱动决策的标准工具:把用户随机分到对照组和实验组,比较指标差异。本案例用 pandas + scipy 完整实现:实验设计检查、指标对比、显著性检验(t 检验/卡方检验)、置信区间、样本量,并规范地给出结论。
⚠️ 统计结论要谨慎:显著 ≠ 因果成立,还要检查随机化、多重比较等问题。

目录

1. A/B 测试基础

2. 数据模拟

3. 随机化检查

4. 核心指标计算

5. 显著性检验:转化率(卡方检验)

6. 显著性检验:均值指标(t 检验)

7. 置信区间与效应量

8. 样本量规划

9. 完整分析流水线

10. 结论规范表述

11. 扩展练习


1. A/B 测试基础

| 概念 | 说明 |

|------|------|

| 对照组(control) | 保持原样,基准 |

| 实验组(treatment) | 应用新方案(如新按钮、新算法) |

| 核心指标 | 转化率(二值)或人均值(连续) |

| P 值 | 原假设成立时看到当前差异的概率 |

| 显著水平 α | 通常 0.05 |

| 效应量 | 差异的实际大小(不只问"是否显著",还要问"多大") |

决策原则:显著 + 效应量大 → 放量;显著 + 效应量小 → 评估成本;不显著 → 不采纳。


2. 数据模拟

场景:电商 App 改版支付页(实验组新增"一键支付"按钮),对比转化率与客单价。

import pandas as pd
import numpy as np
from scipy import stats

np.random.seed(42)
n_control = 5000
n_treat = 5000

# 对照组:转化率 10%
control = pd.DataFrame({
    "组别": "对照",
    "用户ID": range(1, n_control + 1),
    "是否转化": np.random.rand(n_control) < 0.10,
    "消费金额": np.random.uniform(50, 500, n_control),
})
# 实验组:转化率 12%(真实提升 2pp)
treat = pd.DataFrame({
    "组别": "实验",
    "用户ID": range(n_control + 1, n_control + n_treat + 1),
    "是否转化": np.random.rand(n_treat) < 0.12,
    "消费金额": np.random.uniform(50, 500, n_treat),
})

df = pd.concat([control, treat], ignore_index=True)
df["转化用户"] = df["是否转化"].astype(int)
print(df.groupby("组别").agg(
    用户数=("用户ID", "count"),
    转化数=("转化用户", "sum"),
).assign(转化率=lambda d: d["转化数"] / d["用户数"]))
      用户数  转化数    转化率
组别
对照   5000   520  0.104
实验   5000   618  0.124

3. 随机化检查

实验前先确认两组用户特征没有系统性差异:

# 模拟一个协变量(如历史消费档位)
np.random.seed(7)
df["历史消费档"] = np.random.choice(["低", "中", "高"], len(df), p=[0.4, 0.4, 0.2])

# 卡方检验:两组的历史消费档分布是否一致
cont_table = pd.crosstab(df["组别"], df["历史消费档"])
print(cont_table)
chi2, p_val, dof, expected = stats.chi2_contingency(cont_table)
print(f"卡方={chi2:.3f}, p={p_val:.3f}")
# p > 0.05 -> 两组分布无显著差异,随机化 OK
历史消费档   低    中   高
组别
对照     2018  1981  1001
实验     2007  2024   969
卡方=0.642, p=0.725
p=0.725 > 0.05,无法拒绝"分布相同",随机化基本有效。

4. 核心指标计算

summary = df.groupby("组别").agg(
    样本量=("用户ID", "count"),
    转化数=("转化用户", "sum"),
    平均消费=("消费金额", "mean"),
    消费中位数=("消费金额", "median"),
)
summary["转化率"] = summary["转化数"] / summary["样本量"]
print(summary.round(3))

# 差异
conv_c, conv_t = summary.loc["对照", "转化率"], summary.loc["实验", "转化率"]
lift = (conv_t - conv_c) / conv_c
print(f"\n转化率差异: {conv_t - conv_c:.2%}")
print(f"相对提升: {lift:.2%}")

5. 显著性检验:转化率(卡方检验)

转化率是"二值"指标,用卡方检验(或比例 z 检验):

# 2×2 列联表:组别 × 是否转化
table = pd.crosstab(df["组别"], df["转化用户"])
print(table)

chi2, p_val, dof, expected = stats.chi2_contingency(table)
print(f"\n卡方统计量: {chi2:.3f}")
print(f"P 值: {p_val:.5f}")
print(f"期望频数:\n{expected.round(0)}")

alpha = 0.05
print(f"\n结论: {'显著' if p_val < alpha else '不显著'} (α={alpha})")
转化用户     0     1
组别
对照     4480   520
实验     4382   618
卡方统计量: 9.557
P 值: 0.00199
p=0.002 < 0.05:实验组转化率显著高于对照组。

6. 显著性检验:均值指标(t 检验)

客单价是连续指标,用独立样本 t 检验:

# 全量用户客单价对比(含未转化用户 = 0 元时用消费金额)
amount_c = df[df["组别"] == "对照"]["消费金额"]
amount_t = df[df["组别"] == "实验"]["消费金额"]

t_stat, p_val_t = stats.ttest_ind(amount_t, amount_c, equal_var=False)
print(f"t 统计量: {t_stat:.3f}")
print(f"P 值: {p_val_t:.4f}")

# 只看转化用户的客单价
conv_c = df[(df["组别"] == "对照") & (df["转化用户"] == 1)]["消费金额"]
conv_t = df[(df["组别"] == "实验") & (df["转化用户"] == 1)]["消费金额"]
t2, p2 = stats.ttest_ind(conv_t, conv_c, equal_var=False)
print(f"转化用户客单价 t={t2:.3f}, p={p2:.4f}")

# 效应量(Cohen's d)
pooled_std = np.sqrt((conv_c.std()**2 + conv_t.std()**2) / 2)
cohen_d = (conv_t.mean() - conv_c.mean()) / pooled_std
print(f"客单价效应量 Cohen's d = {cohen_d:.3f}")
t 统计量: 0.395
P 值: 0.6928
转化用户客单价 t=0.188, p=0.8510
客单价效应量 Cohen's d = 0.012
客单价差异 p=0.69 > 0.05:不显著。实验只提升了转化率,未改变客单价。

7. 置信区间与效应量

# 转化率差异的置信区间(正态近似)
p_c, p_t = conv_c, conv_t
n_c, n_t = n_control, n_treat
se = np.sqrt(p_c * (1 - p_c) / n_c + p_t * (1 - p_t) / n_t)
diff = p_t - p_c
ci_low, ci_high = diff - 1.96 * se, diff + 1.96 * se
print(f"转化率差异: {diff:.2%}")
print(f"95% 置信区间: [{ci_low:.2%}, {ci_high:.2%}]")
# 区间不包含 0 -> 显著

# 相对提升的置信区间(粗略)
print(f"相对提升: {diff / p_c:.2%}")
转化率差异: 1.96%
95% 置信区间: [0.72%, 3.20%]
相对提升: 18.8%
置信区间 [0.72%, 3.20%] 完全在 0 以上:不仅显著,且最小也有 0.72% 的提升。

8. 样本量规划

实验开始前要估算所需样本量(这里给出反推示例):

from scipy.stats import norm

def sample_size(p_base, p_target, alpha=0.05, power=0.8):
    """最小样本量(每组)"""
    z_alpha = norm.ppf(1 - alpha / 2)
    z_beta = norm.ppf(power)
    pooled_p = (p_base + p_target) / 2
    se = pooled_p * (1 - pooled_p)
    n = (z_alpha * np.sqrt(2 * se) + z_beta * np.sqrt(se)) ** 2 / (p_target - p_base) ** 2
    return int(np.ceil(n))

n_needed = sample_size(0.10, 0.12)
print(f"检测 10%→12% 转化率变化,每组至少需 {n_needed:,} 人")
# 每组约 3883 人,本次 5000 人满足要求

9. 完整分析流水线

把以上整合成一个可复用的函数:

def ab_test_report(df, group_col, conv_col, amount_col=None, alpha=0.05):
    """A/B 测试结果分析报告"""
    print("=" * 46)
    groups = df[group_col].unique()
    assert len(groups) == 2, "只支持两组"

    # 1. 基本统计
    summ = df.groupby(group_col).agg(
        n=(conv_col, "count"),
        conv_num=(conv_col, "sum"),
    )
    summ["conv_rate"] = summ["conv_num"] / summ["n"]
    print(summ)

    # 2. 转化率卡方检验
    table = pd.crosstab(df[group_col], df[conv_col])
    chi2, p_conv, _, _ = stats.chi2_contingency(table)
    print(f"\n[转化率] 卡方={chi2:.3f}, p={p_conv:.4f}",
          "→ 显著" if p_conv < alpha else "→ 不显著")

    # 3. 金额 t 检验
    if amount_col:
        g1, g2 = groups
        a1 = df[df[group_col] == g1][amount_col]
        a2 = df[df[group_col] == g2][amount_col]
        t, p_amt = stats.ttest_ind(a1, a2, equal_var=False)
        print(f"[金额] t={t:.3f}, p={p_amt:.4f}",
              "→ 显著" if p_amt < alpha else "→ 不显著")

    # 4. 结论
    rates = summ["conv_rate"]
    lift = (rates.iloc[1] - rates.iloc[0]) / rates.iloc[0]
    print(f"\n相对提升: {lift:.2%}")
    return {"p_conv": p_conv, "lift": lift}

result = ab_test_report(df, "组别", "转化用户", "消费金额")

10. 结论规范表述

=== 实验结论 ===
1. 实验组转化率 12.4% vs 对照组 10.4%,差异 +2.0pp(相对提升约 19%),
   卡方检验 p=0.002 < 0.05,统计显著;
2. 差异的 95% 置信区间 [0.72%, 3.20%],不含 0,效应稳健;
3. 客单价无显著变化(t 检验 p=0.69),说明改版只带来"更多用户转化",
   并未改变单客价值;
4. 随机化检查通过(历史消费档分布无差异)。

=== 决策建议 ===
- 可以放量:支付页"一键支付"改版对转化率有正向影响;
- 关注长期指标:上线后监控复购率、退货率是否变化;
- 若叠加优惠活动,需重新实验(活动会干扰因果推断)。
注意:真实实验中还要考虑:AA 测试校准、多重比较、新奇效应、长期影响、样本量不足时的序贯检验等。

11. 扩展练习

1. 用比例 z 检验(proportions_ztest)复核卡方检验结果。

2. 把显著水平换成 0.01 再下结论,观察决策变化。

3. 模拟"两组样本量不等"(5000 vs 20000)的情况,重新检验。

4. 对金额指标做 Mann-Whitney U 检验(非参数,稳健)。

5. 计算最小可检测效应(MDE)并说明样本量设计依据。


下一篇预告:第 29 章 案例六:商品评论文本分析 —— 分词、词频、情感与主题,文本分析综合实战。
— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 28 章 案例五:A/B 测试结果分析》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。