方差分析与事后多重比较
跟完这篇,您能对三组及以上的数据做单因素与双因素方差分析(ANOVA),读懂交互作用,在 Tukey 与 Dunnett 之间选对事后比较,并在前提不满足时换用 Kruskal-Wallis——每一步都有可照着敲的 R 代码。
比较两组差异用 t 检验;一旦到了三组及以上,就该用方差分析。有人图省事,把多组拆成好几次两两 t 检验——这会放大假阳性(原因见 多重检验校正)。正确顺序是两步:先用 ANOVA 做一次整体检验,回答「这几组之间到底有没有差异」;整体显著后,再用事后比较定位「具体是哪几组不同」,并对多重比较做校正。这篇就带您把这条链完整走一遍。
什么时候用方差分析
方差分析回答的是一个整体问题:若干组的均值是不是全都相等? 原假设是「各组均值相等」,p 小则提示「至少有一组和别人不一样」——但它不会告诉您是哪一组,这要留给事后比较。
按您手头的设计,对应不同版本:
| 场景 | 方法 | 例子 |
|---|---|---|
| 一个分组因素、三组及以上 | 单因素 ANOVA | 比较对照、低剂量、高剂量三组的表达量 |
| 两个分组因素 | 双因素 ANOVA | 同时看「饮食」和「性别」对体重的影响,以及两者是否相互作用 |
| 不满足正态、或有难处理的离群值 | Kruskal-Wallis(非参数) | 小样本、明显偏态时替代单因素 ANOVA |
开工前先把数据摆成长表,这是 R 里做 ANOVA 最顺手的格式:
- 每行一个样本(观测);
- 一列放要比较的数值(因变量,连续型);
- 一列或多列放分组(自变量),且必须是因子(factor)类型。
str(df)
# 分组列若还是字符或数字,先转成因子,R 才会当成「类别」而非数值处理
df$group <- as.factor(df$group)
分组列忘了转因子,是新手最常见的坑:R 会把 1/2/3 当连续变量去拟合,结果完全跑偏。
单因素 ANOVA:一步步做
以「一个分组因素、三组」为例,照着走:
-
拟合模型。公式写成
数值 ~ 分组:fit <- aov(value ~ group, data = df) -
看整体检验结果:
summary(fit)输出是一张方差分析表,重点看
group这一行的最后一列Pr(>F):列 含义 Df自由度 Sum Sq/Mean Sq离差平方和 / 均方 F valueF 统计量:组间变异相对组内变异有多大 Pr(>F)对应的 p值——这就是整体检验的结论 -
判断:
group行的Pr(>F) < 0.05,说明各组均值不全相等,可以进入下一步的事后比较;若不显著,通常就停在这里,不必再两两比。
p < 0.05判显著是常用惯例、不是铁律,请结合效应量、样本量和研究场景一起判断。
只报一个 p 值往往不够。按惯例还应给出效应量,说明差异有多大而不只是「显著与否」,方差分析里常用 η²(eta-squared):
# 需要 effectsize 包;报告效应量是良好习惯,别只交代 p 值
library(effectsize)
eta_squared(fit)
检查前提假设
ANOVA 和 t 检验一样有三条前提:观测独立、残差近似正态、各组方差齐。前两条里「独立」由实验设计决定、数据补不回来;正态和方差齐则有一定容忍度,样本量大且各组均衡时更稳健。拟合完模型后,按下面顺序核对:
-
残差正态性。注意看的是模型残差,不是原始数据:
qqnorm(residuals(fit)); qqline(residuals(fit)) # 点大致沿直线即可 shapiro.test(residuals(fit)) # 辅助检验,别只看它shapiro.test的p别当开关用:小样本检验力弱、大样本又会对无关紧要的微小偏离过度敏感,配合 Q-Q 图一起看更靠谱。 -
方差齐性。常用 Levene 检验,它比 Bartlett 对正态偏离更稳健:
library(car) leveneTest(value ~ group, data = df) # p < 0.05 提示方差不齐 plot(fit, 1) # 残差 vs 拟合值,看散布是否随组变化 -
方差不齐怎么办。改用不要求方差齐的 Welch 方差分析:
oneway.test(value ~ group, data = df, var.equal = FALSE) # Welch ANOVA这一步和 t 检验里「方差不齐就用 Welch t」是一个道理。方差不齐时,事后比较也要换成对应的稳健方法(见下一节的 Games-Howell)。
事后多重比较:Tukey 还是 Dunnett
ANOVA 整体显著后,事后比较负责回答「具体哪几组不同」。它们都会内建多重比较校正,这正是不能用裸奔的两两 t 检验的原因。选哪种,取决于您真正关心哪些对比:
| 场景 | 事后方法 | R 写法 |
|---|---|---|
| 想两两全比(每组对每组) | Tukey HSD | TukeyHSD(fit) |
| 各处理组只跟一个对照比 | Dunnett | multcomp::glht(...) |
| 方差不齐 | Games-Howell | rstatix::games_howell_test() |
Tukey HSD:给出所有两两组合的差值、置信区间和校正后 p 值,适合「没有预设方向,想全面看看谁和谁不同」:
TukeyHSD(fit)
plot(TukeyHSD(fit)) # 置信区间不跨 0 的那对,即为显著
Dunnett:只把每个处理组跟指定的对照组比(如「各给药组 vs 安慰剂」)。因为对比数量更少,检验力通常高于 Tukey——只要您的问题确实是「和对照差不差」,就优先它:
library(multcomp)
# 确保对照是因子的第一个水平;relevel 可指定谁当参照
df$group <- relevel(df$group, ref = "control")
fit <- aov(value ~ group, data = df)
dunnett <- glht(fit, linfct = mcp(group = "Dunnett"))
summary(dunnett) # 每个处理组与对照的比较,已做多重校正
一句话选法:想两两全比,用 Tukey;只关心「各组 vs 一个对照」,用 Dunnett(更省、更有力);方差明显不齐,用 Games-Howell。
双因素 ANOVA 与交互作用
当有两个分组因素时(如「饮食」×「性别」),双因素 ANOVA 能一次回答三个问题:饮食有没有主效应、性别有没有主效应、以及两者是否存在交互作用。
-
拟合模型。用
*连接两个因素,它会自动展开成「两个主效应 + 交互项」:fit2 <- aov(value ~ diet * sex, data = df) # 等价于 value ~ diet + sex + diet:sex summary(fit2) -
先看交互项(
diet:sex这一行)。交互作用的含义是:一个因素的效应,会随另一个因素的水平而改变。- 交互项显著 → 别再孤立地解读主效应,因为「饮食的作用」在不同性别下并不一样,笼统说「饮食有没有效」会误导。此时应看简单效应(固定一个因素,在其每个水平内分别比较另一个因素)。
- 交互项不显著 → 可以放心分别解读两个主效应。
-
画交互作用图帮助判断:
interaction.plot(df$diet, df$sex, df$value)两条线大致平行,提示没有交互作用;线明显交叉或发散,提示有交互作用。
-
交互显著时,拆简单效应。
emmeans包很适合做这件事:library(emmeans) # 在每个性别内,分别比较不同饮食(并自动做多重校正) emmeans(fit2, pairwise ~ diet | sex)
一个容易被忽略的细节:当各组样本量不相等(不平衡设计)时,
aov()默认的 I 型平方和会受因素书写顺序影响。此时按惯例常改用 II 型或 III 型平方和,例如car::Anova(fit2, type = 2)。平衡设计下三者结果一致,无需纠结。
非参数替代:Kruskal-Wallis
如果是小样本又明显偏离正态、或有难以处理的离群值,就把单因素 ANOVA 换成它的非参数对应——Kruskal-Wallis 检验。它比较的是各组的秩分布,不要求正态:
kruskal.test(value ~ group, data = df) # p 小提示各组分布不全相同
整体显著后,同样需要事后两两比较。两种常规做法:
# 做法一:Dunn 检验,KW 之后最常配套的事后方法(需 FSA 包)
library(FSA)
dunnTest(value ~ group, data = df, method = "bh") # bh:Benjamini-Hochberg 校正
# 做法二:成对 Wilcoxon 秩和检验,并对多重比较做校正
pairwise.wilcox.test(df$value, df$group, p.adjust.method = "BH")
用非参数方法时,有两点边界要心里有数:
- Kruskal-Wallis 只处理一个分组因素,没有直接对应「双因素 + 交互」的标准方法。两因素又严重非正态时,常见做法是先尝试变量变换(如对数),或改用更专门的稳健模型;
- 当数据其实近似正态时,非参数方法的效能通常略低于 ANOVA。所以不是「拿不准就一律非参数」,而是「ANOVA 前提明显不满足时才换」。
到这里,一条完整的分析链就走通了:分清几个因素 → 拟合 ANOVA、看整体是否显著 → 核对残差正态与方差齐(不满足就 Welch 或 Kruskal-Wallis)→ 按「两两全比 or 对照比」选 Tukey 或 Dunnett 做事后比较 → 双因素别忘了先看交互作用。
在平台上跑一遍
上面这套流程您不必手动敲代码。在百沐一下的【智能统计】里,上传一张整洁的长表(每行一个样本、含分组列),用一句话说清目的,例如「比较三组表达量是否有显著差异,并两两比较」,它会自动帮您核对正态与方差齐性、选好 ANOVA 或 Kruskal-Wallis、跑出整体检验与事后比较,并附上可复现的 R 代码供您核对。用法见 智能统计使用指南,或直接登录 app.baimuyixia.com 试一遍。