辛普森悖论 从分组反转到标准化比较
配对 Bootstrap回答固定比较口径下,样本差值会怎样波动。在计算区间之前,还必须确认比较的到底是不是同一类人、同一种任务。若模型 A 和 B 接到的样本构成不同,总体均值的差异可能主要来自分母。
本篇用一组完全合成的查询成功率演示:A 在每个用户组中都更好,总体成功率却更低。这是可直接核验的加权平均反转;计算没有矛盾,改变的是两边使用的权重。
两张分组表怎样得到相反结论
假设“成功”已经有预先固定的二元判定规则。系统把不同流量送给两个固定模型,各收到 100 条查询。用户组在分流之前定义,这里仅作描述性比较,不假定分流是随机实验。
| 用户组 | A 成功 / 查询 | A 成功率 | B 成功 / 查询 | B 成功率 |
|---|---|---|---|---|
| 新用户 | 8 / 10 | 80% | 63 / 90 | 70% |
| 回访用户 | 36 / 90 | 40% | 3 / 10 | 30% |
| 总体 | 44 / 100 | 44% | 66 / 100 | 66% |
A 的每组成功率均比 B 高 10 个百分点,总体却低 22 个百分点。表中所有数值是教学构造,不是本站模型或真实用户的实验结果。
不要把最后一行写成两个组百分比的简单平均:A 的 90% 流量在成功率较低的回访组,而 B 的 90% 流量在成功率较高的新用户组。总体计算必须使用各自实际分母。
总体是条件成功率的加权平均
用 z 表示分组,r_Az=s_Az/n_Az 表示 A 在组 z 的成功率,w_Az=n_Az/Σ_z n_Az 表示该组在 A 中的查询占比。由计数恒等式:
1 | R_A = Σ_z s_Az / Σ_z n_Az |
同理 R_B=Σ_z w_Bz r_Bz。本例的逐项计算是:
| 模型 | 新用户权重 × 率 | 回访权重 × 率 | 总体 |
|---|---|---|---|
| A | 0.1×0.8=0.08 | 0.9×0.4=0.36 | 0.44 |
| B | 0.9×0.7=0.63 | 0.1×0.3=0.03 | 0.66 |
只有当两边使用同一组非负权重时,每一组 A 都不差于 B,才必然有加权总体 A 不差于 B,因为 Σ_z q_z(r_Az−r_Bz)≥0。反转例子用的是不同的 w_A 和 w_B,不能套用这个结论。
flowchart TD A[看到总体 A 低于 B] --> B[按事先定义的用户组展开分子分母] B --> C[检查每组成功率] B --> D[检查两模型的组占比] C --> E[组内差异] D --> F[样本构成差异] E --> G[固定共同目标权重再比较] F --> G G --> H[同时报告原总体和标准化结果]
直接标准化是在定义另一项比较
先选择目标总体的组权重 q_z,要求 q_z≥0、Σq_z=1,两边都用它:
1 | R_A(q) = Σ_z q_z r_Az |
取新用户、回访用户各 1/2,得到 A 为 0.6、B 为 0.5,差值 +0.1。原总体差值 −0.22 仍然正确,只是回答不同问题:前者比较共同构成下的组内表现,后者描述各自实际接收流量的总体结果。
| 共同目标构成 q | A 标准化成功率 | B 标准化成功率 | A−B |
|---|---|---|---|
| 新用户 1/2,回访 1/2 | 0.60 | 0.50 | +0.10 |
| 新用户 1/5,回访 4/5 | 0.48 | 0.38 | +0.10 |
| 新用户 4/5,回访 1/5 | 0.72 | 0.62 | +0.10 |
本例差值不随共同 q 变化,是因为两组差值恰好都为 0.1。一般数据不会有这个性质;当某些组 A 好、某些组 B 好时,目标权重会影响差值甚至方向。可以报告敏感性分析,但不能看完结果后挑一套最有利权重,当作预先定义的主结论。
把原总体差异精确拆成两部分
定义两模型平均组权重 w̄_z=(w_Az+w_Bz)/2,以及平均组成功率 r̄_z=(r_Az+r_Bz)/2。对任意一组都有:
1 | w_A r_A − w_B r_B |
展开右侧,交叉项相消即可证明。这给出一个对称的算术分解:
1 | R_A − R_B = 组内率差项 + 构成差异项 |
| 组 | 平均权重 | 组内率差 | 组内项贡献 | 平均率 | 权重差 | 构成项贡献 |
|---|---|---|---|---|---|---|
| 新用户 | 0.5 | +0.1 | +0.05 | 0.75 | −0.8 | −0.60 |
| 回访用户 | 0.5 | +0.1 | +0.05 | 0.35 | +0.8 | +0.28 |
| 合计 | 1 | — | +0.10 | — | 0 | −0.32 |
因此 −0.22=+0.10−0.32。这个对称恒等式不是唯一可能的分解;换一个参照权重也能得到另一种分配。这里的“组内项”“构成项”都只是算术描述,不能直接称作“模型造成的提升”和“分流造成的损失”。
七列表格使用可聚焦的横向滚动区域,手机上可以左右移动查看,不缩小正文来塞满全部列。
若分组变量受到模型或分流影响,或者分层会引入选择偏差,直接控制它可能改变要估计的效应。需要先定义时间顺序、目标问题与因果假设,再决定是否调整。不能只因分组结论更好看就宣布它更正确。
这个区别也是 Pearl 的《Understanding Simpson’s Paradox》讨论的关键:观察表格本身不足以决定应采用边际关系还是条件关系。本文的合成表与分解只验证加权计算,不承担因果识别证明。
flowchart TD
A[准备比较两个总体率] --> B{分组和分母口径一致吗}
B -->|否| C[先修复数据定义]
B -->|是| D{目标权重对应哪个总体}
D --> E[预先固定共同权重]
E --> F{目标组两边都有观测吗}
F -->|否| G[报告缺少支持 不填零]
F -->|是| H[计算标准化结果和原始结果]
H --> I[另行分析抽样不确定性与因果假设]
完整标准库实现与精确实验
计数用整数、比例用 Fraction,避免把二进制浮点舍入当成恒等式不成立。输入已按组汇总;若原始数据有同一用户的多条查询,分母仍是查询数,推断时的独立单位则可能是用户,二者不要混淆。
1 | import json |
实际输出 raw_delta=−11/50、standard_delta=1/10、within=1/10、composition=−8/25。只把 A 的查询构成变成与 B 一样的九成新用户、一成回访用户,保持两组成功率不变,原总体差值就变为 +1/10,标准化差值保持不变。
代码有意拒绝浮点权重,使用 “1/2” 或 Fraction(1,2) 明确给出精确比例;缺组、空组和错误分母也直接拒绝。即使某组目标权重是零,这份同时计算原总体和分解的实现也要求该组两边都有合法分母。真正需要合并组或删组时,应先重新定义比较口径。
设组数为 K,计算使用 O(K) 次有理数运算,存储 O(K)。Fraction 的实际成本还随整数位数增长,这不是固定机器字长下的常数时间承诺;原始 N 行数据若尚未汇总,需先进行 O(N) 的分组聚合。
数值相等不等于问题已经解决
| 情况 | 不能省略的处理 |
|---|---|
| 某模型在目标组没有样本 | 无法直接估计该组成功率;不把未知当作 0% |
| 某组样本极少 | 分层率可能波动很大;标准化不是增加样本量 |
| 分组内任务难度仍不同 | 标准化只平衡已定义组,组内剩余差异仍在 |
| 用户重复出现 | 按独立用户或更合适单位处理推断,不能直接逐行重采样 |
| 目标权重也由样本估计 | 若目标是总体权重,需要考虑它的不确定性;本例把 q 当作固定 |
| 组是分流之后才产生 | 先明确因果问题;避免把中介或选择机制随意当作控制变量 |
还应区分两类量:原始总体率适合描述各自实际流量,标准化率适合描述明确共同构成下的比较。报告时保留分子分母、分组定义、目标权重和差值方向,读者才有机会判断结论适用到哪里。
若要继续计算区间,可以在每次合法重采样后重算各组率与 Δ(q),并保持预定的独立单位和目标权重定义;不能简单把总体 Bootstrap 区间搬到标准化差值上。本篇只做精确算术与构成反例,不报告未经验证的覆盖率。
自检:每组 A 都高于 B,是否应该删除那条“总体 A 更差”的结果?
不应该。两个结果描述不同的构成。应保留实际总体结果,说明权重差异,再报告预先定义的共同目标构成下的比较;是否能作因果解释,需要额外假设。
验证程序 tests/verify-simpson-standardization-article.cjs 运行全文代码,以原始二元行展开独立对照计数,并检查随机分组恒等式、模型交换、复制不变性、权重敏感性及非法输入。可接回配对 Bootstrap或模型回归评测;也可对照主动学习,理解模型主动选择的数据为何不宜直接代表目标总体。