R语言方差分析:单因素与双因素ANOVA完整流程
"施肥处理组和对照组之间,植物多样性有差异吗?""不同生态系统对氮添加的响应一样吗?"多组间比较是生态研究里的高频问题。有人习惯做两两 t 检验,组一多就繁琐,还要担心多重比较把犯错的概率放大。这时候该请出 方差分析(ANOVA)。
一次 F 检验回答"所有组均值是否相等",显著后再靠事后比较定位差异组。我们拿"氮添加水平 × 生态系统类型对植物多样性的影响"当例子,从前提检验、单因素 ANOVA,走到双因素 ANOVA 和交互效应,把一篇论文里会用到的主流程完整走一遍。
一、方差分析在做什么
单因素 ANOVA 检验一个分类因子(如氮添加水平)对连续响应变量(如 Shannon 多样性指数)的影响,核心是把总变异拆成两部分:
- 组间变异:各组均值与总均值之差,反映处理效应
- 组内变异:各组内部个体间的差异,反映随机误差
F 统计量 = 组间均方 / 组内均方。处理效应远大于随机误差时 F 变大,p < 0.05 就拒绝"各组均值相等",说明至少有一组跟其他组不一样。
双因素 ANOVA 同时考察两个因子(如氮添加水平 × 生态系统),还能检验二者的交互作用,也就是"一个因子的效应是否依赖另一个因子的水平"。比如氮添加对草地的抑制可能比对农田更强,这种效应差异就是交互。
ANOVA 有三大前提假设,不满足时结果不可靠:
| 前提 | 检验方法 | 要求 |
|---|---|---|
| 独立性 | 实验设计保证 | 各样本相互独立 |
| 正态性 | Shapiro-Wilk 检验(对残差) | p > 0.05(不拒绝正态) |
| 方差齐性 | Levene 检验 | p > 0.05(不拒绝齐性) |
注意:前提检验的零假设是"假设成立"(残差正态 / 方差齐性),所以 p > 0.05 表示没有证据拒绝假设、前提可认为满足。若 p < 0.05,单因素方差不齐时优先用 Welch ANOVA (
oneway.test());双因素不能简单套 Kruskal-Wallis(它检验的是秩分布,不是"方差不齐的 ANOVA 替代品"),要按研究设计选稳健 ANOVA、置换检验或广义/混合效应模型。
二、数据准备
我们用的是一份仿真生态学数据,模拟"氮添加水平 × 生态系统"双因素实验对植物 Shannon 多样性的影响。实验设计是 3 × 3 = 9 组,每组 20 个样方,共 180 行。

| 因子 | 水平 |
|---|---|
| Nlevel(氮添加水平) | Control(对照) / Low_N(低氮) / High_N(高氮) |
| Ecosystem(生态系统) | Grassland(草地) / Forest(森林) / Cropland(农田) |
响应变量是 Shannon 多样性指数(连续变量),另有 Simpson、Pielou、物种数 S 备用。
r
# 读取数据
dat <- read.csv("anova_diversity.csv", header = TRUE)
dat$Ecosystem <- factor(dat$Ecosystem)
dat$Nlevel <- factor(dat$Nlevel, levels = c("Control", "Low_N", "High_N"))
head(dat)
仿真数据规律是这么设定的:氮添加降低植物多样性,且草地被设定为对氮添加最敏感,这是后面交互效应的重要伏笔。依据是生态学的经典结论:外源氮输入提高土壤氮有效性后,会筛选出氮利用效率高的优势种促其竞争性生长,适应低养分生境的物种被竞争排斥而消失,群落多样性随之下降(Bobbink et al., 2010; Stevens et al., 2004)。草地群落物种组成对养分变化敏感、周转快,通常认为比森林和农田的响应更剧烈。
三、核心操作:从前提检验到双因素 ANOVA
第一步:前提检验
正态性检验:对残差,不是原始数据!
很多初学者对原始数据 做 Shapiro-Wilk 检验,这是常见误区。ANOVA 的正态性假设针对的是模型残差,即使各组观测值本身偏离正态,只要残差近似正态,参数检验的结果依然有效:
r
library(car)
# 先拟合单因素模型,再检验残差
fit1 <- aov(Shannon ~ Nlevel, data = dat)
shapiro.test(residuals(fit1))
输出示例:
ini
Shapiro-Wilk normality test
W = 0.99373, p-value = 0.6408
p = 0.641 > 0.05,没有足够证据拒绝残差正态的假设,可认为残差近似正态。同时可以结合 Q-Q 图综合判断,下图就是残差的 Q-Q 图,散点大致沿对角线分布、两端只有轻微偏离,直观支持上面的结论。

r
library(ggplot2)
p_qq <- ggplot(data.frame(res = residuals(fit1)), aes(sample = res)) +
stat_qq(color = "#5B8CC9", alpha = 0.7) +
stat_qq_line(color = "#1F3B73", linewidth = 0.5) +
labs(title = "Q-Q plot of ANOVA residuals", x = "Theoretical quantiles",
y = "Sample quantiles") +
theme_classic(base_size = 8, base_family = "Arial") +
theme(axis.line = element_line(linewidth = 0.4),
axis.ticks = element_line(linewidth = 0.4),
panel.grid = element_blank())
方差齐性:Levene 检验
方差齐性要求各组方差相近,推荐用 Levene 检验 。car::leveneTest() 默认以中位数为中心,属于 Brown-Forsythe 型,对非正态数据通常比经典的 Bartlett 检验更稳健:
r
leveneTest(Shannon ~ Nlevel, data = dat)
输出示例:
r
Df F value Pr(>F)
group 2 1.9516 0.1451
177
p = 0.145 > 0.05,没有足够证据拒绝方差齐性的假设,可认为各组方差相近。
第二步:单因素 ANOVA + Tukey 多重比较
前提检验通过后,用 aov() 拟合单因素模型,summary() 查看 F 检验结果:
r
summary(fit1)
输出示例:
r
Df Sum Sq Mean Sq F value Pr(>F)
Nlevel 2 26.860 13.430 125.2 < 2.2e-16 ***
Residuals 177 18.997 0.107
F(2, 177) = 125.2, p < 0.001,氮添加水平显著影响 Shannon 多样性。但整体显著不代表每组都不同,需要事后多重比较定位具体差异。
为什么不用两两 t 检验?三组两两比较要算 3 个 t 检验,每个按 α=0.05 判断时,整体犯第一类错误的概率会膨胀到约 1-(1-0.05)³ ≈ 14%。Tukey HSD(Honestly Significant Difference) 用学生化极差分布校正了这种膨胀,把整体错误率(family-wise error rate)控制在 α 以内;只有当目标是"每个处理都与对照比"而非两两互比时,才改用 Dunnett 检验。
r
TukeyHSD(fit1)
输出示例:
bash
$Nlevel
diff lwr upr p adj
Low_N-Control -0.5491233 -0.690464 -0.407783 0.000000
High_N-Control -0.9419133 -1.083254 -0.800573 0.000000
High_N-Low_N -0.3927900 -0.534130 -0.251450 0.000000
三组两两比较均显著(p adj 均 < 0.001)。diff 是均值差,lwr/upr 是 95% 置信区间。High_N 比 Control 平均低 0.94,Low_N 比 Control 低 0.55,氮添加显著抑制了多样性。
把结果画成箱线图:三组箱体用蓝系渐变(浅蓝 → 深蓝)呈现氮添加水平递增,箱体上方标注 Tukey 字母 a/b/c,字母不同表示组间差异显著。从左到右箱线图中位数逐级下降(约 2.86 → 2.23 → 1.89),跟统计结果一致(均值分别为 2.81、2.26、1.87)。

r
library(multcompView) # Tukey 字母计算
# 计算 Tukey 字母(a/b/c)
tk <- TukeyHSD(fit1)$Nlevel
lv <- levels(dat$Nlevel)
pmat <- matrix(1, 3, 3, dimnames = list(lv, lv))
for (pr in rownames(tk)) {
# 注意:按 "-" 拆分组名对假设组名不含连字符成立;
# 若你的因子水平名含 "-"(如 "Low-N"),改用 multcompLetters4() 等更稳健的接口
v <- strsplit(pr, "-")[[1]]
pmat[v[2], v[1]] <- pmat[v[1], v[2]] <- tk[pr, "p adj"]
}
let1 <- multcompLetters(pmat)$Letters
lab1 <- data.frame(Nlevel = names(let1), letters = let1,
y = tapply(dat$Shannon, dat$Nlevel, max) + 0.1)
p_oneway <- ggplot(dat, aes(x = Nlevel, y = Shannon, fill = Nlevel)) +
geom_boxplot(alpha = 0.85, outlier.shape = NA, linewidth = 0.4) +
geom_jitter(width = 0.12, size = 0.6, alpha = 0.35, color = "grey35") +
geom_text(data = lab1, aes(x = Nlevel, y = y, label = letters),
size = 3, fontface = "bold") +
scale_fill_manual(values = c(Control = "#D6E4F0", Low_N = "#5B8CC9",
High_N = "#1F3B73")) +
labs(x = "Nitrogen addition level", y = "Shannon diversity index") +
theme_classic(base_size = 8, base_family = "Arial") +
theme(legend.position = "none",
axis.line = element_line(linewidth = 0.4),
axis.ticks = element_line(linewidth = 0.4),
panel.grid = element_blank())
几个关键参数:
| 参数 | 作用 | 本图设置 |
|---|---|---|
geom_boxplot(alpha = 0.85) |
箱线图,半透明 | 透明度 0.85 |
outlier.shape = NA |
隐藏异常值散点 | 避免与 jitter 混淆 |
geom_jitter(width = 0.12) |
叠加原始数据点 | 灰色小点展示分布 |
geom_text(data = lab1, ...) |
箱体上方标注 Tukey 字母 | 加粗 a/b/c |
scale_fill_manual() |
自定义箱体配色 | 蓝系渐变(浅→深) |
theme_classic(base_size = 8) |
极简科研主题 | Arial、紧凑字号、无网格 |
第三步:双因素 ANOVA ------ 氮添加 × 生态系统
同时考虑两个因子。先拟合主效应模型(加法模型,不含交互):
r
# 主效应模型:只考虑两个因子的各自影响
fit2a <- aov(Shannon ~ Nlevel + Ecosystem, data = dat)
summary(fit2a)
输出示例:
r
Df F value Pr(>F)
Nlevel 2 154.465 < 2.2e-16 ***
Ecosystem 2 21.693 3.83e-09 ***
Residuals 175
两个因子都极显著。接着拟合含交互模型,看氮添加效应是否随生态系统变化:
r
# 含交互模型:考虑氮添加与生态系统的交互作用
fit2b <- aov(Shannon ~ Nlevel * Ecosystem, data = dat)
summary(fit2b)
输出示例:
r
Df F value Pr(>F)
Nlevel 2 164.3332 < 2.2e-16 ***
Ecosystem 2 23.0791 1.34e-09 ***
Nlevel:Ecosystem 4 3.7951 0.005537 **
Residuals 171
交互项 Nlevel:Ecosystem 显著(F = 3.80, p = 0.0055),说明氮添加对多样性的效应依赖生态系统类型,不同生态系统的响应强度不同。
前提检验要对最终使用的模型做。 前面验的是单因素模型 fit1 的残差,但真正要解读的是双因素交互模型 fit2b。正确做法是对最终模型 fit2b 的残差再检验一次前提(正态性 + 方差齐性),而不是默认单因素前提成立就推广到双因素:
r
# 对最终双因素模型做前提检验
shapiro.test(residuals(fit2b))
leveneTest(residuals(fit2b) ~ interaction(dat$Nlevel, dat$Ecosystem))
输出示例:
ini
Shapiro-Wilk normality test
W = 0.99601, p-value = 0.919
Levene's Test for Homogeneity of Variance (center = median)
Df F value Pr(>F)
group 8 0.6552 0.7302
171
fit2b 残差正态 p = 0.919、方差齐性 p = 0.730,双因素模型的前提也得到满足。无论跑单因素还是双因素,都要对最终使用的模型做残差诊断,这是科研分析的习惯,而不是"检验一次就一劳永逸"。
公式写法速记:
A + B是主效应模型;A * B自动展开为A + B + A:B(含交互)。交互显著时应保留交互项解读;交互不显著时不能仅凭 p 值机械删掉交互项,要结合研究设计、预先设定的假设和模型简化原则判断。
分面箱线图把交互画出来:三个面板(Cropland / Forest / Grassland)中,氮添加均使多样性下降,每面板内标注 Tukey 字母。Grassland 面板对照组最高(约 3.2)且下降最陡,直观展示交互的来源。

r
# 每个 Ecosystem 面板内的 Tukey 字母
let2 <- by(dat, dat$Ecosystem, function(d) {
m <- aov(Shannon ~ Nlevel, data = d)
tk <- TukeyHSD(m)$Nlevel
lv <- levels(d$Nlevel)
pmat <- matrix(1, 3, 3, dimnames = list(lv, lv))
for (pr in rownames(tk)) {
v <- strsplit(pr, "-")[[1]]
pmat[v[2], v[1]] <- pmat[v[1], v[2]] <- tk[pr, "p adj"]
}
multcompLetters(pmat)$Letters
})
lab2 <- do.call(rbind, lapply(names(let2), function(ec) {
data.frame(Ecosystem = ec, Nlevel = names(let2[[ec]]), letters = let2[[ec]],
y = tapply(dat$Shannon[dat$Ecosystem == ec],
dat$Nlevel[dat$Ecosystem == ec], max) + 0.12)
}))
p_twoway <- ggplot(dat, aes(x = Nlevel, y = Shannon, fill = Nlevel)) +
geom_boxplot(alpha = 0.85, outlier.shape = NA, linewidth = 0.4) +
geom_jitter(width = 0.12, size = 0.6, alpha = 0.35, color = "grey35") +
geom_text(data = lab2, aes(x = Nlevel, y = y, label = letters),
size = 3, fontface = "bold") +
facet_wrap(~ Ecosystem, ncol = 3) +
scale_fill_manual(values = c(Control = "#D6E4F0", Low_N = "#5B8CC9",
High_N = "#1F3B73")) +
labs(x = "Nitrogen addition level", y = "Shannon diversity index") +
theme_classic(base_size = 8, base_family = "Arial") +
theme(legend.position = "none",
strip.text = element_text(face = "bold"),
axis.line = element_line(linewidth = 0.4),
axis.ticks = element_line(linewidth = 0.4),
panel.grid = element_blank())
第四步:交互效应可视化
交互作用用文字描述不够直观,交互效应图(interaction plot)是很好的辅助呈现方式,把各因子组合的均值连成线。注意:线条不平行通常提示可能存在交互,但交互是否存在应以 ANOVA 模型中的交互项 F 检验为准,交互图是直观展示而非正式检验:
r
# 计算分组均值
mu <- aggregate(Shannon ~ Nlevel + Ecosystem, data = dat, FUN = mean)
# 交互效应图:连线是否平行反映交互是否显著
p_interact <- ggplot(mu, aes(x = Nlevel, y = Shannon,
group = Ecosystem, color = Ecosystem)) +
geom_point(size = 2.2) +
geom_line(linewidth = 0.7) +
scale_color_manual(values = c(Grassland = "#2C7A57", Forest = "#7A4D8E",
Cropland = "#B85C4A")) +
labs(x = "Nitrogen addition level", y = "Mean Shannon diversity index") +
theme_classic(base_size = 8, base_family = "Arial") +
theme(axis.line = element_line(linewidth = 0.4),
axis.ticks = element_line(linewidth = 0.4),
panel.grid = element_blank())
三条线都下降但不平行:Grassland 线(绿色)最陡,从约 3.10 降至约 1.87;Forest(紫色)与 Cropland(红棕)两线近乎平行、下降较缓(分别约 2.80→2.01 与 2.53→1.72)。草地降幅最大,正是本仿真数据"草地最敏感"的设定,所以交互项显著。

四、进阶:交互显著后用 emmeans 做简单效应
前面分面箱线图那种按 Ecosystem 分别做 ANOVA + Tukey 的做法,教学上直观,但严格说把数据拆成 3 个子集分别分析,每个子集用各自的残差自由度,并未完全利用完整模型的信息,且跨三个生态系统的整体推断存在多重比较问题。更规范的做法是用 emmeans 包在完整模型上直接计算 simple effects(简单效应,即"在某个 Ecosystem 内比较 Nlevel 各组"):
r
library(emmeans)
fit2b <- aov(Shannon ~ Nlevel * Ecosystem, data = dat)
emm <- emmeans(fit2b, pairwise ~ Nlevel | Ecosystem, adjust = "tukey")
emm$contrasts
输出示例(部分):
ini
Ecosystem = Grassland:
contrast estimate SE df t.ratio p.value
Control - Low_N 0.732 0.0904 171 8.097 <0.0001
Control - High_N 1.225 0.0904 171 13.548 <0.0001
Low_N - High_N 0.493 0.0904 171 5.450 <0.0001
emmeans 在完整模型上使用统一的残差方差和自由度(df = 171) ,三个生态系统用同一套误差估计,比拆分子集更准确、更严谨。本例中其结论与分面箱线图的字母标注一致(每个生态系统内三组两两显著),但两者方法不同:拆子集是"三套独立分析",emmeans 是"一个模型内的简单效应比较"。科研论文中推荐用 emmeans 类方法,按面板分析更适合教学演示。
五、结果解读
从分析和图中可以得出三层结论:
1. 氮添加普遍抑制多样性。 单因素 ANOVA(F = 125.2, p < 0.001)显示氮添加水平显著影响 Shannon 多样性。Tukey 多重比较显示三组两两显著,箱线图上标注的字母 a/b/c 各不相同,High_N 组均值比 Control 低 0.94。该趋势方向与氮沉降抑制植物多样性的经典生态学结论一致(本数据为仿真生成,仅呈现方法流程,不构成对真实生态规律的验证)。
2. 生态系统间存在本质差异。 无论氮添加水平如何,Grassland 的多样性整体最高,Forest 居中,Cropland 最低,Ecosystem 主效应极显著(F = 21.7, p < 0.001)。该梯度方向与农业集约化管理降低植物多样性的普遍认识相符,长期耕作、除草和施肥会抑制农田中非目标物种的定殖,使群落物种组成趋于单一。这里的生态系统间差异同样来自仿真数据的设定,仅用于演示双因素模型对第二因子的检验,不对真实农田与自然生态系统多样性作因果断言。
3. 交互作用揭示响应差异。 交互项显著(F = 3.80, p = 0.0055)表明氮添加的影响不是均匀的:从交互效应图可以看到,Grassland 线下降最陡,Forest 与 Cropland 两线近乎平行、下降较缓,交互主要来自草地的更陡响应。这一模式源于仿真数据的设定(草地最敏感),并非从真实观测中归纳得出。
Tukey 字母速读法: 箱线图顶部的字母来自事后检验的分组标记,同一字母的组差异不显著,不同字母的组差异显著。三组均标为 a、b、c(互不相同),意味着任意两组间差异都达到了显著水平。
六、完整可运行代码
下面是一份自包含的 R 脚本,复制到 RStudio 即可运行(数据文件随文章提供,可直接下载使用)。它比正文代码多了统一的 theme_pub() 极简主题和 tukey_letters() 字母计算函数,方便直接复用:
r
# ============================================================
# 单因素 & 双因素 ANOVA 完整流程
# ============================================================
# 首次运行请先安装包:
# install.packages(c("car", "ggplot2", "multcompView"))
library(car)
library(ggplot2)
library(multcompView) # Tukey 字母
# --- 读取数据 ---
dat <- read.csv("anova_diversity.csv", header = TRUE)
dat$Ecosystem <- factor(dat$Ecosystem)
dat$Nlevel <- factor(dat$Nlevel, levels = c("Control", "Low_N", "High_N"))
head(dat)
# --- 统一主题(Nature 风格极简主题) ---
theme_pub <- function(base_size = 8, base_family = "Arial") {
theme_classic(base_size = base_size, base_family = base_family) +
theme(
axis.line = element_line(linewidth = 0.4, colour = "black"),
axis.ticks = element_line(linewidth = 0.4, colour = "black"),
axis.title = element_text(size = base_size + 1),
axis.text = element_text(size = base_size - 0.5),
legend.title = element_text(size = base_size),
legend.text = element_text(size = base_size - 1),
strip.text = element_text(size = base_size, face = "bold"),
legend.key.size = unit(3, "mm"),
panel.grid = element_blank()
)
}
# 色板
cols_nlevel <- c(Control = "#D6E4F0", Low_N = "#5B8CC9", High_N = "#1F3B73")
cols_ecosystem <- c(Grassland = "#2C7A57", Forest = "#7A4D8E", Cropland = "#B85C4A")
# ============================================================
# 第一步:前提检验
# ============================================================
fit1 <- aov(Shannon ~ Nlevel, data = dat)
shapiro.test(residuals(fit1)) # 正态性(对残差)
leveneTest(Shannon ~ Nlevel, data = dat) # 方差齐性
# ============================================================
# 第二步:单因素 ANOVA + Tukey 多重比较
# ============================================================
summary(fit1)
TukeyHSD(fit1)
# ============================================================
# 第三步:双因素 ANOVA(主效应 vs 交互模型)
# ============================================================
fit2a <- aov(Shannon ~ Nlevel + Ecosystem, data = dat)
summary(fit2a) # 主效应模型
fit2b <- aov(Shannon ~ Nlevel * Ecosystem, data = dat)
summary(fit2b) # 含交互模型
# 注意:交互显著时,不要直接对 Nlevel 做总体 Tukey 比较------
# 更合适的是分别看每个 Ecosystem 内的 simple effects(见进阶部分 emmeans)
# ============================================================
# 第四步:可视化
# ============================================================
# --- Tukey 字母计算函数 ---
tukey_letters <- function(model, fac, data) {
tk <- TukeyHSD(model)[[fac]]
lv <- levels(data[[fac]])
pmat <- matrix(1, length(lv), length(lv), dimnames = list(lv, lv))
for (pr in rownames(tk)) {
v <- strsplit(pr, "-")[[1]]
pmat[v[2], v[1]] <- pmat[v[1], v[2]] <- tk[pr, "p adj"]
}
multcompLetters(pmat)$Letters
}
# 单因素箱线图(带 Tukey 字母)
let1 <- tukey_letters(fit1, "Nlevel", dat)
lab1 <- data.frame(Nlevel = names(let1), letters = let1,
y = tapply(dat$Shannon, dat$Nlevel, max) + 0.1)
p_oneway <- ggplot(dat, aes(x = Nlevel, y = Shannon, fill = Nlevel)) +
geom_boxplot(alpha = 0.85, outlier.shape = NA, linewidth = 0.4) +
geom_jitter(width = 0.12, size = 0.6, alpha = 0.35, color = "grey35") +
geom_text(data = lab1, aes(x = Nlevel, y = y, label = letters),
size = 3, fontface = "bold") +
scale_fill_manual(values = cols_nlevel) +
labs(x = "Nitrogen addition level", y = "Shannon diversity index") +
theme_pub() +
theme(legend.position = "none")
# 双因素分面箱线图(每面板 Tukey 字母)
let2 <- by(dat, dat$Ecosystem, function(d) {
m <- aov(Shannon ~ Nlevel, data = d)
tukey_letters(m, "Nlevel", d)
})
lab2 <- do.call(rbind, lapply(names(let2), function(ec) {
data.frame(Ecosystem = ec, Nlevel = names(let2[[ec]]), letters = let2[[ec]],
y = tapply(dat$Shannon[dat$Ecosystem == ec],
dat$Nlevel[dat$Ecosystem == ec], max) + 0.12)
}))
p_twoway <- ggplot(dat, aes(x = Nlevel, y = Shannon, fill = Nlevel)) +
geom_boxplot(alpha = 0.85, outlier.shape = NA, linewidth = 0.4) +
geom_jitter(width = 0.12, size = 0.6, alpha = 0.35, color = "grey35") +
geom_text(data = lab2, aes(x = Nlevel, y = y, label = letters),
size = 3, fontface = "bold") +
facet_wrap(~ Ecosystem, ncol = 3) +
scale_fill_manual(values = cols_nlevel) +
labs(x = "Nitrogen addition level", y = "Shannon diversity index") +
theme_pub() +
theme(legend.position = "none")
# 交互效应图
mu <- aggregate(Shannon ~ Nlevel + Ecosystem, data = dat, FUN = mean)
p_interact <- ggplot(mu, aes(x = Nlevel, y = Shannon,
group = Ecosystem, color = Ecosystem)) +
geom_point(size = 2.2) +
geom_line(linewidth = 0.7) +
scale_color_manual(values = cols_ecosystem) +
labs(x = "Nitrogen addition level", y = "Mean Shannon diversity index") +
theme_pub()
# 出图
ggsave("boxplot_oneway.png", p_oneway, width = 110, height = 100, units = "mm", dpi = 300)
ggsave("boxplot_twoway.png", p_twoway, width = 183, height = 90, units = "mm", dpi = 300)
ggsave("interaction_plot.png", p_interact, width = 120, height = 100, units = "mm", dpi = 300)
替换成自己的数据:
- 准备 CSV 文件:前面的列是因子列(分类变量),最后一列是连续响应变量
- 将因子列转为 factor,如
dat$Group <- factor(dat$Group) - 替换
read.csv()中的文件名和公式中的变量名,如aov(value ~ Group) - 若单因素方差齐性不满足,改用
oneway.test(Shannon ~ Nlevel, data = dat)(Welch ANOVA);双因素设计方差不齐时需按研究设计选择稳健方法
七、实用技巧总结
aov 公式写法速查:
| 公式 | 含义 |
|---|---|
aov(y ~ A) |
单因素 ANOVA |
aov(y ~ A + B) |
双因素主效应模型 |
aov(y ~ A * B) |
双因素含交互模型(等价于 A + B + A:B) |
常见注意事项:
- 前提检验要验残差,不是原始数据。 对原始数据做 Shapiro-Wilk 常误报非正态,ANOVA 的正态性假设针对的是模型残差而非观测值本身
- 前提检验要对最终模型做。 单因素和双因素是不同的模型,每个模型都应做残差诊断,不能"检验一次就一劳永逸"
- 方差齐性用 Levene 而非 Bartlett。
car::leveneTest()默认以中位数为中心(Brown-Forsythe 型),对非正态数据通常比 Bartlett 更稳健 - "p > 0.05"是"没有证据拒绝",不是"证明成立"。 前提检验的零假设是"假设成立",p>0.05 只是不拒绝;样本量很大时 Shapiro 会对微小偏离敏感,应结合 Q-Q 图综合判断
- 显著后的定位依赖事后比较。 ANOVA 显著只说明"有差异",具体哪两组不同必须看 TukeyHSD 等事后检验
- Tukey 字母是常见标注方式之一。 用
multcompView::multcompLetters()把事后检验结果转成 a/b/c 字母标注在箱线图上,字母不同即差异显著,是生态/农业论文中常见做法 - 剂量梯度用渐变配色。 处理水平有递增语义时(如氮添加 0/低/高),用同一色系由浅到深比用不同色相更能传递"剂量递增"的含义
- 交互显著时慎谈主效应。 交互显著意味着一个因子的效应依赖另一因子,此时主效应需在交互背景下解读;交互显著后的组间比较宜用 emmeans 做 simple effects
- 注意伪重复(pseudoreplication)。 若多个样方来自同一个样地/样点(嵌套或重复测量结构),它们不是完全独立的重复,普通 ANOVA 会高估样本量。此时需改用混合效应模型,如
lmer(Shannon ~ Nlevel * Ecosystem + (1 | Site), data = dat) - 交互不显著不等于必须删交互项。 是否简化加性模型应结合研究设计与预设假设判断,不能仅凭 p 值机械删项
- 方差不齐要按设计处理。 单因素用 Welch ANOVA(
oneway.test());双因素不能简单套 Kruskal-Wallis,需按设计选稳健/置换/广义/混合模型
如果对你有帮助,欢迎 点赞 + 收藏。欢迎评论区交流 👋