7.8 配对检验¶
暑假训练营结束,小率拿到一张体育老师发的记录表:同一批同学训练前后各跑一次 100 米。老师问:训练到底有没有让大家变快?
| 同学 | 训练前(秒) | 训练后(秒) | 前 - 后(秒) |
|---|---|---|---|
| A | 15.2 | 14.7 | 0.5 |
| B | 16.1 | 15.8 | 0.3 |
| C | 14.8 | 14.1 | 0.7 |
| D | 15.7 | 15.2 | 0.5 |
| ... | ... | ... | ... |
能不能把训练前当一组,训练后当另一组,直接做两独立样本 t 检验?
不能。训练前后来自同一个人,这两个数天然配对。
7.8.1 配对数据先看「自己和自己比」¶
配对数据(Paired Data)指两列观测之间有一一对应关系。常见例子包括:
- 同一个人训练前后;
- 同一位患者用药前后;
- 同一个用户看旧页面和新页面;
- 同一台机器调整前后。
配对检验的关键动作是先求差值:
\[
d_i = x_{i,\text{前}} - x_{i,\text{后}}
\]
然后把问题变成:这些差值的总体均值是否等于 0?
这样每个人天生快慢的差异就被减掉了。
对。配对的威力,就是把个体基线先消掉。
7.8.2 配对 t 检验就是差值上的单样本 t¶
若差值近似来自正态总体,使用配对 t 检验(Paired t-Test):
\[
H_0: \mu_d = 0,\qquad H_1: \mu_d > 0
\]
统计量是:
\[
t = \frac{\bar d - 0}{s_d/\sqrt n},\qquad df=n-1
\]
这里 \(n\) 是配对数量,不是观测值总数。12 名同学训练前后各一次,配对数仍然是 12,自由度是 11。
所以 `ttest_rel` 和“先算差值再单样本 t”应该完全一样?
完全一样。配对 t 的本体就是差值检验。
需要注意
配对检验要求「配对关系」真实存在。不能把两个班的成绩硬凑成第 1 个对第 1 个、第 2 个对第 2 个。那不是配对,是人为捏配对。
7.8.3 二分类配对用 McNemar¶
如果配对结果不是数值,而是「是/否」,例如同一批学生辅导前后是否通过测验,就不能做配对 t。此时看 McNemar 检验。
| 后:通过 | 后:未通过 | |
|---|---|---|
| 前:通过 | 一直通过 | 退步 |
| 前:未通过 | 进步 | 一直未通过 |
McNemar 的核心是只看发生改变的人:进步人数和退步人数是否明显不对称。
一直通过和一直未通过的人,不提供“变化方向”的信息。
正是如此。配对二分类看的是改变朝哪边更多。
7.8.4 Python 复现训练数据¶
完整脚本见:docs/assets/scripts/ch07_hypothesis_testing/08_paired_test/main.py。
import numpy as np
from scipy import stats
before = np.array([15.2, 16.1, 14.8, 15.7, 16.4, 15.5, 14.9, 16.0])
after = np.array([14.7, 15.8, 14.1, 15.2, 15.9, 15.0, 14.6, 15.5])
diff = before - after
t_rel, p_rel = stats.ttest_rel(before, after, alternative="greater")
t_one, p_one = stats.ttest_1samp(diff, popmean=0, alternative="greater")
print(f"平均进步 = {diff.mean():.2f} 秒")
print(f"配对 t: t = {t_rel:.2f}, p = {p_rel:.4f}")
print(f"差值单样本 t: t = {t_one:.2f}, p = {p_one:.4f}")
小率的笔记本
判断配对先问一句:两个数是不是来自同一个对象?如果是,就先算差值,再对差值做检验。配对 t 的自由度是配对数减 1。若是配对二分类,改用 McNemar。

