跳转至

9.1   单因素方差分析

暑假社团准备做一次学习方法分享。小率手里有一张活动记录:同学们被随机分到三种复习方式,最后都做了同一份小测。

图 9.1.0 小率带来三组复习成绩

复习方式 小测成绩 人数 平均分
刷题组 78, 82, 80, 79, 81 5 80.0
讲解组 83, 85, 84, 86, 82 5 84.0
混合组 88, 90, 89, 91, 87 5 89.0
三组平均分看起来不一样。那我是不是把三组两两做 t 检验就行?
两两比较当然能做,但问题是:组越多,比较次数越多,误报也越容易累积。ANOVA 先问一个更稳的问题:这些平均差异整体上像不像随机波动造成的?

9.1.1   先别急着两两比较

三组一共有 3 对比较:刷题 vs 讲解、刷题 vs 混合、讲解 vs 混合。若有 5 组,就有 10 对;若有 8 组,就有 28 对。每次都按 0.05 显著性水平判断,整体犯一次错的概率会明显升高。

单因素方差分析(One-Way ANOVA)先做一个总检验:

ANOVA 的问题

如果所有复习方式的真实平均效果都一样,我们现在看到的组间差异是否仍然合理?

它的零假设不是“某两组相等”,而是:

\[ H_0:\mu_1=\mu_2=\cdots=\mu_k \]

备择假设也很克制:

\[ H_1:\text{至少有一组均值不同} \]

所以 ANOVA 显著时,它只告诉我们“至少有一组不一样”,还没有告诉我们“到底哪两组不一样”。这个问题要留给 9.3 的事后检验。

9.1.2   F 值看的是组间差异和组内波动的比例

ANOVA 的核心图像很简单:如果三组的平均分差得很开,而每组内部同学成绩又很集中,那么“复习方式确实有影响”的证据就强。反过来,如果三组平均分差不多,或者每组内部本来就乱成一团,证据就弱。

图 9.1.1 单因素 ANOVA 把总变异拆成组间和组内

ANOVA 把总波动拆成两部分:

\[ \text{总变异}=\text{组间变异}+\text{组内变异} \]

对应到平方和:

\[ SST=SSB+SSW \]
  • 组间变异 SSB:每组平均分离总平均有多远。
  • 组内变异 SSW:同一组内部的同学分数有多分散。

F 统计量就是把两种变异都换成“均方”以后相除:

\[ F=\frac{MSB}{MSW} \]

如果 F 很大,说明组间差异相对于组内波动太大,不太像纯随机噪声。

9.1.3   从一张 ANOVA 表读出结论

ANOVA 表最常见的几列是:

来源 平方和 自由度 均方 F p
组间 SSB k - 1 MSB MSB / MSW p 值
组内 SSW N - k MSW
总计 SST N - 1

读表顺序

先看 p 值是否小于设定的显著性水平,再看效应量。显著不等于差异很大,样本量很大时很小的差异也可能显著。

常用效应量是 \(\eta^2\)

\[ \eta^2=\frac{SSB}{SST} \]

它表示“总波动中有多少比例可以被组别解释”。比如 \(\eta^2=0.62\),可以粗略理解为:成绩总差异中约 62% 和复习方式分组有关。

9.1.4   什么时候要小心使用 ANOVA

单因素 ANOVA 常见前提有三个:

  • 每个观察值相互独立。
  • 各组残差大致接近正态。
  • 各组方差大致相近。

这些前提不是用来吓人的,而是提醒我们别把错误的数据结构硬塞进模型。

需要换工具的情况

如果方差明显不齐,可以考虑 Welch ANOVA;如果分布严重偏斜、样本量又很小,可以考虑 Kruskal-Wallis 检验;如果同一个人被重复测量,不能当成普通独立样本 ANOVA。

9.1.5   用 Python 做一次单因素 ANOVA

配套脚本放在:

docs/assets/scripts/ch09_anova/01_one_way_anova/main.py

核心代码如下:

import pandas as pd
from scipy import stats

scores = {
    "刷题组": [78, 82, 80, 79, 81],
    "讲解组": [83, 85, 84, 86, 82],
    "混合组": [88, 90, 89, 91, 87],
}

f_stat, p_value = stats.f_oneway(*scores.values())
print(f"F = {f_stat:.3f}, p = {p_value:.4f}")

df = pd.DataFrame(
    [(group, score) for group, values in scores.items() for score in values],
    columns=["复习方式", "成绩"],
)
print(df.groupby("复习方式")["成绩"].agg(["count", "mean", "std"]))
所以 ANOVA 不是直接比较哪两组,而是先判断“组别这个因素整体有没有解释力”。
对。先做总检验,再追问具体差异,顺序很重要。

小率的笔记本

单因素 ANOVA 用来比较三组及以上均值。它把总波动拆成组间和组内两部分,用 \(F=MSB/MSW\) 判断组间差异是否大到不像随机波动。显著以后,还要用事后检验回答“哪几组不同”。