上一篇《【R生态】非参数多元检验(ADONIS、ANOSIM、PERMANOVA及MRPP等多元方差分析)》讲了几种检验的基本用途,以及 adonis2() 的顺序检验与边缘检验。实际处理数据时,困难往往出现在函数能够运行之后:距离该怎样定,显著结果能不能直接解释,组内离散度要不要检查,重复测量又该怎样置换。这次不再比较三种方法,接着处理下面五个问题:
- Bray--Curtis、Hellinger--Euclidean 和 Jaccard 应该怎样选?
- PERMANOVA 显著,究竟是组质心不同,还是组内离散度不同?
- 总体检验显著后,怎样做两两 PERMANOVA 并校正多重检验?
- 配对、区组或重复测量数据,怎样限制置换范围?
- PCoA、R²、pseudo-F 和 P 值应该怎样规范解释和报告?
本文代码在干净的 R 会话中重新运行过一遍,文中的数值、表格和图片均取自这次运行结果。
一、先把分析顺序理清
下面按实际分析顺序展开。只有一行 adonis2(),后面的解释通常还缺几块:
text
明确研究问题与实验单位
↓
检查群落矩阵和元数据是否一一对应
↓
根据数据含义预先选择距离
↓
运行总体 PERMANOVA,报告 pseudo-F、R² 和 P
↓
用 PERMDISP 检查组内离散度
↓
总体显著后再做两两比较,并校正 P 值
↓
按实验设计限制置换;最后结合 PCoA 解释
这几步分别对应不同的判断,不能被一行 adonis2() 代替:
- 距离必须由研究问题和数据含义决定,不能看哪个 P 值小就选哪个;
- PERMANOVA 的 P 值不等于效应大小,R² 也不等于分类准确率;
- PERMANOVA 和 PERMDISP 检验的不是同一件事;
- PCoA 是二维投影,PERMANOVA 使用的是完整距离矩阵;
- 置换单位必须与实验单位一致,否则 P 值可能失真。
二、分析环境与案例数据
2.1 安装并加载 R 包
首次使用时安装:
r
install.packages(c(
"vegan", "permute", "ggplot2",
"dplyr", "tibble", "purrr"
))
之后加载:
r
library(vegan)
library(permute)
library(ggplot2)
library(dplyr)
library(tibble)
library(purrr)
set.seed(20260801)
permutations <- 9999
本文实跑环境为 R 4.6.0、vegan 2.7-5 和 permute 0.9-10。不同版本、随机种子或置换次数可能使 P 值最后几位略有变化,但不会改变本案例的主要结论。
2.2 为什么不用旧文中的 dune 数据?
旧文使用过 dune,这里改用 vegan 自带的土壤螨群落数据:
mite:70 个土壤样本 × 35 个螨类物种;mite.env:与 70 个样本对应的环境信息;- 本文关注
Shrub,即灌木密度的 3 个水平:None、Few和Many。
r
data("mite", package = "vegan")
data("mite.env", package = "vegan")
# 把有序因子明确转成普通分组因子
mite.env$Shrub <- factor(
mite.env$Shrub,
levels = c("None", "Few", "Many")
)
dim(mite)
# [1] 70 35
table(mite.env$Shrub)
# None Few Many
# 19 26 25
这三个组的样本量分别为 19、26 和 25,虽然不是完全平衡,但没有极端悬殊。
数据背景提醒:该数据来自一个有限空间范围内的观测性生态调查。本文用它演示统计流程,并不把灌木密度解释为因果因素。正式研究还应根据采样设计评估空间自相关、环境混杂和独立性。
三、第一道门:数据结构与样本顺序检查
PERMANOVA 最常见、也最隐蔽的错误之一,是群落矩阵与元数据行数相同,但样本顺序不同。
3.1 群落数据应该长什么样?
本文约定:
- 行是样本;
- 列是物种、ASV、OTU 或其他响应变量;
- 单元格是非负的丰度或计数;
- 元数据每一行与群落矩阵同一行的样本严格对应。
r
stopifnot(nrow(mite) == nrow(mite.env))
stopifnot(all(vapply(mite, is.numeric, logical(1))))
stopifnot(all(mite >= 0))
stopifnot(all(rowSums(mite) > 0))
最后一项是在查全零样本。Bray--Curtis 距离遇到这类样本时可能得到缺失值,或者失去明确含义。发现全零行后应回到原始数据查原因:是测序失败、过滤过度,还是确实没有记录到任何物种,而不是直接加一个很小的数把代码"救活"。
3.2 换成自己的 CSV 数据
建议把样本编号放在第一列,并同时作为行名读取:
r
community <- read.csv(
"community.csv",
row.names = 1,
check.names = FALSE
)
metadata <- read.csv(
"metadata.csv",
row.names = 1,
check.names = FALSE
)
# 不仅检查数量,还检查名称和顺序
stopifnot(identical(rownames(community), rownames(metadata)))
# 分组变量应显式转换为因子
metadata$group <- factor(metadata$group)
如果两个文件中的样本相同但顺序不同,先重排,而不是跳过检查:
r
metadata <- metadata[rownames(community), , drop = FALSE]
stopifnot(identical(rownames(community), rownames(metadata)))
四、距离怎么选:先回答"差异指什么"
PERMANOVA 使用的是样本之间的距离或相异度。选择哪一种距离,实际上也规定了"群落差异"在本次分析中的含义。
4.1 三种常见方案
| 方案 | 主要保留的信息 | 常见适用场景 | 需要注意 |
|---|---|---|---|
| Bray--Curtis | 物种丰度差异 | 物种丰度、OTU/ASV 计数或相对丰度 | 受丰度较高的物种影响,不使用共同缺失 |
| Hellinger--Euclidean | 相对组成经平方根变换后的差异 | 希望使用欧氏几何,同时降低优势种影响 | 必须先做 Hellinger 变换,不能直接对原始计数算欧氏距离 |
| Jaccard(binary) | 出现/未出现 | 只关心物种是否出现 | 丢弃全部丰度信息 |
如果是严格的组成数据分析,Aitchison 距离也是重要选项,但它涉及零值替换、CLR 变换及组成数据假设,不能只写一行 log(x) 草率处理,适合单独展开。
4.2 同时计算三种距离,用于敏感性分析
r
# 方案 1:Bray--Curtis,保留丰度信息
distance_bray <- vegdist(mite, method = "bray")
# 方案 2:先 Hellinger 变换,再计算欧氏距离
mite_hellinger <- decostand(mite, method = "hellinger")
distance_hellinger <- dist(mite_hellinger, method = "euclidean")
# 方案 3:binary Jaccard,只保留出现/未出现
distance_jaccard <- vegdist(
mite,
method = "jaccard",
binary = TRUE
)
然后使用完全相同的分组模型:
r
set.seed(20260801)
fit_bray <- adonis2(
distance_bray ~ Shrub,
data = mite.env,
permutations = 9999
)
set.seed(20260801)
fit_hellinger <- adonis2(
distance_hellinger ~ Shrub,
data = mite.env,
permutations = 9999
)
set.seed(20260801)
fit_jaccard <- adonis2(
distance_jaccard ~ Shrub,
data = mite.env,
permutations = 9999
)
实跑结果为:
| 距离 | pseudo-F | R² | P |
|---|---|---|---|
| Bray--Curtis | 9.036 | 0.212 | 0.0001 |
| Hellinger--Euclidean | 10.015 | 0.230 | 0.0001 |
| Jaccard(binary) | 9.352 | 0.218 | 0.0001 |

三种距离都支持组间存在差异,但 R² 并不完全相同,因为它们强调的信息不同。
这张图用于比较不同距离回答问题时的侧重点,不用于按 R² 大小挑主分析。比较顺序应是:
- 先根据研究目的指定主要距离;
- 再把其他合理距离作为敏感性分析;
- 如不同距离得到相反结论,应解释差异来自丰度信息、出现信息还是变换方式,而不是挑选最显著的结果。
本文后续把 Bray--Curtis 作为主要分析,因为我们希望保留物种丰度差异。
五、总体 PERMANOVA:P 值、R² 和 pseudo-F 各自表示什么?
5.1 运行总体检验
r
set.seed(20260801)
fit_bray <- adonis2(
distance_bray ~ Shrub,
data = mite.env,
permutations = 9999
)
fit_bray
主要输出为:
text
Df SumOfSqs R2 F Pr(>F)
Model 2 3.1221 0.21244 9.0365 0.0001
Residual 67 11.5742 0.78756
Total 69 14.6963 1.00000
5.2 三个核心统计量如何解释?
1. pseudo-F = 9.036
它衡量模型所解释的组间变异相对于组内残差变异的大小。数值越大,通常表示组间差异相对组内差异越明显,但它不是传统参数 MANOVA 的 F 统计量,因此常写作 pseudo-F。
2. R² = 0.212
灌木密度分组解释了 Bray--Curtis 距离总变异的约 21.2%,剩余约 78.8% 由组内差异、其他环境因素、空间结构和未测量因素等共同构成。
R² 是解释率,不是:
- 预测准确率;
- 分类正确率;
- "灌木密度造成了 21.2% 的生态变化";
- 结果是否具有实际重要性的唯一标准。
3. P = 0.0001
在当前置换方案下,随机重新分配分组标签后,很少得到不小于观测 pseudo-F 的结果。因此拒绝"各组质心在所选距离空间中相同"的零假设。
进行了 9,999 次随机置换时,常见的最小可报告分辨率约为:
r
1 / (9999 + 1)
# [1] 0.0001
所以本例应报告 P = 0.0001,不要写成 P = 0。
统计显著和生态效应强弱是两回事。较大的样本量可以检出较小效应,样本较少时又可能因检验效能不足漏掉实际差异。因此结果中应同时给出 pseudo-F、R²、P、样本量和距离方法。
六、PCoA 可视化:图和检验必须使用同一距离
6.1 为什么使用 PCoA?
Bray--Curtis 是样本间相异度。PCoA 可以把高维距离关系投影到二维坐标,帮助观察组间位置、组内离散程度和异常样本。
为了处理 Bray--Curtis 可能产生的负特征值,下面使用 Lingoes 校正:
r
pcoa <- wcmdscale(
distance_bray,
eig = TRUE,
add = "lingoes",
k = 2
)
positive_eigenvalues <- pcoa$eig[pcoa$eig > 0]
axis_explained <- 100 * pcoa$eig[1:2] /
sum(positive_eigenvalues)
pcoa_data <- as_tibble(
pcoa$points[, 1:2],
rownames = "sample_id"
) |>
setNames(c("sample_id", "PCoA1", "PCoA2")) |>
bind_cols(mite.env |> select(Shrub))
计算每组质心和凸包:
r
centroids <- pcoa_data |>
group_by(Shrub) |>
summarise(
centroid_1 = mean(PCoA1),
centroid_2 = mean(PCoA2),
.groups = "drop"
)
pcoa_data <- pcoa_data |>
left_join(centroids, by = "Shrub")
hulls <- pcoa_data |>
group_by(Shrub) |>
slice(chull(PCoA1, PCoA2)) |>
ungroup()
绘图:
r
group_colors <- c(
None = "#0072B2",
Few = "#E69F00",
Many = "#009E73"
)
ggplot(pcoa_data, aes(PCoA1, PCoA2, color = Shrub)) +
geom_polygon(
data = hulls,
aes(fill = Shrub, group = Shrub),
alpha = 0.12,
color = NA,
show.legend = FALSE
) +
geom_segment(
aes(xend = centroid_1, yend = centroid_2),
alpha = 0.30,
linewidth = 0.55,
show.legend = FALSE
) +
geom_point(size = 3.2, alpha = 0.9) +
geom_point(
data = centroids,
aes(x = centroid_1, y = centroid_2, fill = Shrub),
shape = 23,
size = 5,
color = "black",
show.legend = FALSE
) +
scale_color_manual(values = group_colors) +
scale_fill_manual(values = group_colors) +
coord_equal() +
theme_minimal(base_size = 13) +
labs(
x = sprintf("PCoA1(%.1f%%)", axis_explained[1]),
y = sprintf("PCoA2(%.1f%%)", axis_explained[2]),
title = "不同灌木密度组的 Bray--Curtis PCoA"
)

图中菱形表示每组质心。None 组的质心与另外两组明显分开,而 Few 和 Many 仍有较多重叠。
图上的重叠程度不能代替 PERMANOVA。本例 PCoA1 和 PCoA2 分别解释约 16.4% 和 6.0%,只展示了完整距离信息的一部分;adonis2() 使用的则是完整距离矩阵。
七、解释总体结果前,先检查 PERMDISP
7.1 PERMANOVA 为什么可能被离散度影响?
PERMANOVA 主要用于检验各组在多维距离空间中的位置是否不同。但当各组的组内离散程度明显不同时,位置差异与离散度差异可能混在一起,特别是在样本量不平衡时更需要谨慎。
两项检验关心的对象不同:
- PERMANOVA 主要问:"不同组的中心位置是否不同?"
- PERMDISP 主要问:"不同组围绕各自中心的散开程度是否不同?"
因此,PERMDISP 是对总体结果的补充检查,并不是 PERMANOVA 的替代方法。
7.2 使用 betadisper() 和 permutest()
r
dispersion_model <- betadisper(
distance_bray,
group = mite.env$Shrub,
type = "median",
bias.adjust = TRUE
)
set.seed(20260801)
dispersion_test <- permutest(
dispersion_model,
permutations = 9999
)
dispersion_test
结果为:
text
Df Sum Sq Mean Sq F N.Perm Pr(>F)
Groups 2 0.03275 0.016374 1.0013 9999 0.3793
Residuals 67 1.09571 0.016354
本例 PERMDISP 的 P = 0.3793,没有证据表明三个灌木密度组的组内离散度不同。这使总体 PERMANOVA 的显著结果更适合解释为组质心位置存在差异,而不是主要由某一组更分散造成。

7.3 PERMANOVA 与 PERMDISP 的组合解释
| PERMANOVA | PERMDISP | 建议解释 |
|---|---|---|
| 显著 | 不显著 | 支持组质心存在差异,离散度混淆证据较弱 |
| 显著 | 显著 | 位置和离散度都可能不同,不能把 PERMANOVA 简单写成"组中心显著分离" |
| 不显著 | 显著 | 未检测到质心差异,但组内异质性可能不同 |
| 不显著 | 不显著 | 当前数据没有提供足够证据支持位置或离散度差异 |
如果 PERMDISP 总体检验显著,再考虑查看成对离散度差异:
r
if (dispersion_test$tab[1, "Pr(>F)"] < 0.05) {
TukeyHSD(dispersion_model)
}
本例总体检验不显著,因此没有必要根据所有 Tukey 两两结果继续"找显著"。
P > 0.05不等于已经证明各组离散度完全相同,它只表示当前样本没有提供足够证据拒绝离散度齐性的零假设。小样本时尤其不能把"不显著"写成"完全一致"。
八、总体显著以后:两两 PERMANOVA 与多重校正
8.1 为什么不能直接比较所有原始 P 值?
三组会产生 3 次两两比较,四组会产生 6 次,六组则有 15 次。比较次数越多,至少出现一次假阳性的概率越高。因此两两 PERMANOVA 必须同时报告校正后的 P 值。
这里使用 Benjamini--Hochberg(BH)方法控制错误发现率。若研究计划要求更严格地控制家族错误率,也可选择 Holm 方法,但应在分析前确定。
8.2 编写一个可复用函数
下面的函数不依赖额外的 GitHub 程序包,直接调用官方 vegan::adonis2():
r
pairwise_permanova <- function(
community,
group,
method = "bray",
permutations = 9999,
p_adjust_method = "BH",
seed = 20260801) {
stopifnot(is.matrix(community) || is.data.frame(community))
if (nrow(community) != length(group)) {
stop("community 的行数必须与 group 的长度一致。")
}
group <- droplevels(factor(group))
group_pairs <- combn(levels(group), 2, simplify = FALSE)
set.seed(seed)
result <- purrr::map_dfr(group_pairs, function(current_pair) {
keep <- group %in% current_pair
sub_community <- community[keep, , drop = FALSE]
sub_group <- droplevels(group[keep])
fit <- adonis2(
sub_community ~ sub_group,
method = method,
permutations = permutations
)
fit_table <- as.data.frame(fit)
tibble(
group_1 = current_pair[1],
group_2 = current_pair[2],
n_1 = sum(sub_group == current_pair[1]),
n_2 = sum(sub_group == current_pair[2]),
pseudo_F = fit_table$F[1],
R2 = fit_table$R2[1],
p_value = fit_table[["Pr(>F)"]][1]
)
})
result |>
mutate(
p_adjust = p.adjust(
p_value,
method = p_adjust_method
),
comparison = paste(group_1, group_2, sep = " vs ")
) |>
arrange(p_adjust, desc(R2))
}
运行:
r
pairwise_results <- pairwise_permanova(
community = mite,
group = mite.env$Shrub,
method = "bray",
permutations = 9999,
p_adjust_method = "BH",
seed = 20260801
)
pairwise_results
实跑结果:
| 比较 | n1 | n2 | pseudo-F | R² | 原始 P | BH 校正 P |
|---|---|---|---|---|---|---|
| None vs Many | 19 | 25 | 16.350 | 0.280 | 0.0001 | 0.0003 |
| None vs Few | 19 | 26 | 6.040 | 0.123 | 0.0002 | 0.0003 |
| Few vs Many | 26 | 25 | 5.751 | 0.105 | 0.0006 | 0.0006 |

三个两两比较在 BH 校正后均显著,其中 None vs Many 的差异最大,R² 为 0.280;Few vs Many 的 R² 最小,为 0.105。
总体检验只能说明至少有部分组不同。具体差异落在哪些组之间,要看校正后的两两比较;差异大小则结合各组比较的 R² 判断。
8.3 两两比较还有三个限制
- 两两比较必须与总体分析使用相同的数据预处理和距离定义;
- 样本很少时,可能不存在足够多的唯一置换,P 值分辨率会受限;
- 若原始设计包含区组、配对或重复测量,两两比较也必须保留相同的置换限制,不能退回自由置换。
上面的通用函数适合独立样本的一因素设计。复杂设计应针对每个子集重新构造合法的 permute 置换方案。
九、受限置换:配对和重复测量不能随意打乱
9.1 为什么自由置换会错?
假设 12 个样地在处理前后各测一次。真正独立的实验单位是样地,而不是 24 个彼此独立的样本。
如果把 24 个标签完全打乱,就允许样地 S01 的 Before 与 S08 的 After 任意交换,破坏了配对结构。正确做法是:
- 把
site纳入模型,吸收样地之间的稳定差异; - 只允许在同一个
site内交换Before/After标签。
9.2 构造一个可复现的配对演示数据
以下数据是模拟的,只用于说明置换设计,不冒充真实实验结果:
r
set.seed(20260801)
n_site <- 12
n_taxa <- 18
site_baseline <- matrix(
rgamma(n_site * n_taxa, shape = 2.2, rate = 0.12),
nrow = n_site,
ncol = n_taxa
)
treatment_multiplier <- c(
rep(1.8, 4),
rep(0.62, 4),
rep(1, n_taxa - 8)
)
paired_community <- do.call(
rbind,
lapply(seq_len(n_site), function(i) {
before <- rpois(n_taxa, site_baseline[i, ])
after <- rpois(
n_taxa,
site_baseline[i, ] * treatment_multiplier
)
rbind(before, after)
})
)
paired_meta <- tibble(
site = factor(
rep(sprintf("S%02d", 1:n_site), each = 2)
),
treatment = factor(
rep(c("Before", "After"), n_site),
levels = c("Before", "After")
)
)
9.3 先看错误示范:忽略配对
r
set.seed(20260801)
naive_fit <- adonis2(
paired_community ~ treatment,
data = paired_meta,
method = "bray",
permutations = 9999
)
忽略配对时:
text
treatment: pseudo-F = 1.592, R² = 0.067, P = 0.1069
大量样地间差异被留在残差中,处理效应没有被有效识别。
9.4 正确示范:site 入模,并在 site 内置换
r
restricted_design <- how(nperm = 9999)
setBlocks(restricted_design) <- paired_meta$site
set.seed(20260801)
restricted_fit <- adonis2(
paired_community ~ site + treatment,
data = paired_meta,
method = "bray",
permutations = restricted_design,
by = "terms"
)
restricted_fit
正确保留设计结构后:
text
treatment: pseudo-F = 11.787, R² = 0.067, P = 0.000488

图中的灰线连接同一 site 的处理前后样本。虽然处理的 R² 仍为 0.067,但在控制样地差异后,检验能够利用"同一样地前后变化"的信息。
运行时可能看到:
text
'nperm' >= set of all permutations: complete enumeration.
这不是报错。每个样地只有两个可交换状态,12 个样地共有有限种合法标签组合;当要求的置换次数超过全部合法排列时,permute 会枚举全部排列。
不要机械地把任意变量传给
Blocks。区组变量必须来自实验设计,表示标签只能在哪个独立实验单位内部交换。分组变量本身、事后挑选的批次或与处理完全混杂的变量,都不能随意当作区组。
十、一个容易混淆的问题:位置差异、离散度差异和可视化
可以把三类结果分开理解:
| 分析 | 使用的信息 | 主要回答的问题 |
|---|---|---|
| PERMANOVA | 完整距离矩阵 | 组质心位置是否存在差异 |
| PERMDISP | 样本到组中心的距离 | 各组的组内离散程度是否不同 |
| PCoA | 距离矩阵的低维投影 | 差异大致呈现在哪些方向,有无异常样本 |
因此,下面几种说法都不严谨:
- "PCoA 图分开,所以组间显著";
- "PERMANOVA 显著,所以所有组都不同";
- "PERMDISP 不显著,所以方差完全相等";
- "R² 为 0.21,所以模型准确率为 21%";
- "P 很小,所以效应一定很强"。
更完整的解释顺序应是:
- 报告距离、置换次数和置换限制;
- 报告总体 pseudo-F、R² 和 P;
- 报告 PERMDISP;
- 总体显著后报告校正后的两两比较;
- 用 PCoA 描述模式,但不让二维图代替统计检验;
- 最后结合采样设计、混杂因素和生态背景讨论。
十一、可直接改写到论文中的报告模板
11.1 方法部分模板
基于 Bray--Curtis 相异度,使用 R 包 vegan 的
adonis2()进行置换多元方差分析(PERMANOVA),以检验不同灌木密度组的群落组成差异。显著性通过 9,999 次置换评估。使用betadisper()基于样本到组空间中位数的距离检验多元离散度齐性,并通过 9,999 次置换获得 P 值。总体 PERMANOVA 显著后,进行两两 PERMANOVA,并使用 Benjamini--Hochberg 方法校正多重检验。基于同一 Bray--Curtis 距离矩阵进行 PCoA 可视化。
如果是配对或区组设计,还应补充:
根据实验设计将置换限制在同一实验单位内部,并将实验单位作为模型项纳入分析。
11.2 本案例结果部分模板
三个灌木密度组的土壤螨群落组成存在显著差异(PERMANOVA:pseudo-F = 9.036,R² = 0.212,P = 0.0001)。组间多元离散度差异不显著(PERMDISP:F = 1.001,P = 0.3793),提示总体 PERMANOVA 的显著性不太可能主要由组内离散度差异驱动。BH 校正后的两两 PERMANOVA 均显著,其中 None 与 Many 组的差异最大(R² = 0.280,校正 P = 0.0003),其次为 None 与 Few(R² = 0.123,校正 P = 0.0003)以及 Few 与 Many(R² = 0.105,校正 P = 0.0006)。
11.3 结果表建议至少包含
| 分析 | 必报内容 |
|---|---|
| 总体 PERMANOVA | 距离、公式、pseudo-F、R²、P、置换次数、置换限制 |
| PERMDISP | 中心类型、F、P、置换次数 |
| 两两 PERMANOVA | 两组样本量、pseudo-F、R²、原始 P、校正方法、校正 P |
| PCoA | 距离、校正方法、轴解释率、分组图例 |
十二、几处容易出错的地方
下表适合在分析结束后逐项核对。前两项尤其隐蔽,因为样本顺序错了,代码通常仍能正常运行。
| 问题 | 会造成什么影响 | 核对或处理方法 |
|---|---|---|
| 群落矩阵方向颠倒 | vegdist() 把物种当成样本 |
确认行是样本、列是物种,并核对 nrow(community) == nrow(metadata) |
| 群落矩阵与元数据顺序不同 | 分组标签对应到错误样本 | 使用 identical(rownames(community), rownames(metadata)),不能只比较行数 |
| 原始高维计数直接计算欧氏距离 | 总丰度较高的样本可能主导结果 | 根据问题使用 Bray--Curtis;需要欧氏几何时先考虑 Hellinger 变换 |
| 根据 P 值选择距离 | 把分析选择变成事后筛选 | 预先指定主要距离,其他合理距离作为敏感性分析完整报告 |
| 跳过 PERMDISP | 质心差异与组内离散度差异难以区分 | 对同一距离矩阵运行 betadisper() 和 permutest() |
| 总体显著后直接写"所有组都不同" | 超出了总体检验能够支持的结论 | 进行计划内的两两 PERMANOVA,并校正多重检验 |
| 两两比较只给原始 P | 比较越多,假阳性风险越高 | 同时报告预先确定的 BH、Holm 等校正方法及校正后 P |
| 重复测量仍自由置换 | 同一实验单位的观测被当成独立样本 | 依据实验设计设置 Blocks,并把相应区组项纳入模型 |
| 根据 PCoA 图形重叠判断显著性 | 用二维投影替代了完整距离检验 | PCoA 用于展示,显著性由完整距离矩阵上的 PERMANOVA 判断 |
| 把观察性关联写成因果关系 | 结论忽略混杂和空间结构 | 写成"分组与群落组成差异相关",并讨论研究设计的限制 |
十三、换成自己数据时的最简完整模板
下面给出一个独立样本、一因素分组的最简模板。复制后只需替换文件名和 group 列名:
r
library(vegan)
# 1. 读取数据
community <- read.csv(
"community.csv",
row.names = 1,
check.names = FALSE
)
metadata <- read.csv(
"metadata.csv",
row.names = 1,
check.names = FALSE
)
# 2. 对齐样本
metadata <- metadata[rownames(community), , drop = FALSE]
stopifnot(identical(rownames(community), rownames(metadata)))
stopifnot(all(rowSums(community) > 0))
metadata$group <- factor(metadata$group)
# 3. 计算主要距离
distance_bray <- vegdist(
community,
method = "bray"
)
# 4. 总体 PERMANOVA
set.seed(20260801)
permanova_result <- adonis2(
distance_bray ~ group,
data = metadata,
permutations = 9999
)
print(permanova_result)
# 5. PERMDISP
dispersion_model <- betadisper(
distance_bray,
group = metadata$group,
type = "median",
bias.adjust = TRUE
)
set.seed(20260801)
dispersion_result <- permutest(
dispersion_model,
permutations = 9999
)
print(dispersion_result)
# 6. 如果总体显著,再运行本文的 pairwise_permanova()
复杂设计不能只在这个模板上不断加变量。涉及重复测量、嵌套、裂区、时间序列或空间限制时,应先画清楚"哪些样本可以互换",再写模型和置换方案。
十四、发表前检查清单
在点击"运行"或准备投稿前,可以逐项检查:
- 行是样本、列是物种;
- 群落矩阵和元数据的样本名称及顺序完全一致;
- 没有未经解释的全零样本;
- 距离由科学问题预先确定;
- 总体结果同时报告 pseudo-F、R² 和 P;
- 使用与 PERMANOVA 相同的距离检查 PERMDISP;
- 总体显著后才进行计划内的两两比较;
- 两两 P 值已经校正;
- 配对、区组或重复测量使用合法的受限置换;
- PCoA 只作为可视化,不替代统计检验;
- 没有把观察性关联写成因果结论;
- 保存随机种子、包版本、分析脚本和原始结果表。
十五、结语
本例采用 Bray--Curtis 距离后,灌木密度解释了 21.2% 的群落距离变异;PERMDISP 没有发现明显的组内离散度差异,三个两两比较经 BH 校正后仍显著,其中 None 与 Many 的差异最大。这几项结果放在一起,比单独报告一个总体 P 值更完整。
配对演示还说明了另一件事:置换方式不是函数末尾一个无关紧要的参数。样本能否交换、在哪个范围内交换,取决于实验单位和采样设计。距离定义、模型公式和置换范围能够对应上,PERMANOVA 的结果才有清楚的统计含义。
参考资料
- vegan:
adonis2()官方文档 - vegan:
betadisper()官方文档 - vegan:
vegdist()官方文档 - vegan:
mite数据官方说明 - permute:
how()置换设计官方文档 - Anderson, M. J. (2001). A new method for non-parametric multivariate analysis of variance. Austral Ecology, 26, 32--46.
- Anderson, M. J. (2006). Distance-Based Tests for Homogeneity of Multivariate Dispersions. Biometrics, 62, 245--253.