【R生态】PERMANOVA 进阶实战:距离选择、PERMDISP、两两比较与受限置换(保姆级教程)

上一篇《【R生态】非参数多元检验(ADONIS、ANOSIM、PERMANOVA及MRPP等多元方差分析)》讲了几种检验的基本用途,以及 adonis2() 的顺序检验与边缘检验。实际处理数据时,困难往往出现在函数能够运行之后:距离该怎样定,显著结果能不能直接解释,组内离散度要不要检查,重复测量又该怎样置换。这次不再比较三种方法,接着处理下面五个问题:

  1. Bray--Curtis、Hellinger--Euclidean 和 Jaccard 应该怎样选?
  2. PERMANOVA 显著,究竟是组质心不同,还是组内离散度不同?
  3. 总体检验显著后,怎样做两两 PERMANOVA 并校正多重检验?
  4. 配对、区组或重复测量数据,怎样限制置换范围?
  5. 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-5permute 0.9-10。不同版本、随机种子或置换次数可能使 P 值最后几位略有变化,但不会改变本案例的主要结论。

2.2 为什么不用旧文中的 dune 数据?

旧文使用过 dune,这里改用 vegan 自带的土壤螨群落数据:

  • mite:70 个土壤样本 × 35 个螨类物种;
  • mite.env:与 70 个样本对应的环境信息;
  • 本文关注 Shrub,即灌木密度的 3 个水平:NoneFewMany
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 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² 大小挑主分析。比较顺序应是:

  1. 先根据研究目的指定主要距离;
  2. 再把其他合理距离作为敏感性分析;
  3. 如不同距离得到相反结论,应解释差异来自丰度信息、出现信息还是变换方式,而不是挑选最显著的结果。

本文后续把 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 组的质心与另外两组明显分开,而 FewMany 仍有较多重叠。

图上的重叠程度不能代替 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 原始 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 两两比较还有三个限制

  1. 两两比较必须与总体分析使用相同的数据预处理和距离定义;
  2. 样本很少时,可能不存在足够多的唯一置换,P 值分辨率会受限;
  3. 若原始设计包含区组、配对或重复测量,两两比较也必须保留相同的置换限制,不能退回自由置换。

上面的通用函数适合独立样本的一因素设计。复杂设计应针对每个子集重新构造合法的 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 很小,所以效应一定很强"。

更完整的解释顺序应是:

  1. 报告距离、置换次数和置换限制;
  2. 报告总体 pseudo-F、R² 和 P;
  3. 报告 PERMDISP;
  4. 总体显著后报告校正后的两两比较;
  5. 用 PCoA 描述模式,但不让二维图代替统计检验;
  6. 最后结合采样设计、混杂因素和生态背景讨论。

十一、可直接改写到论文中的报告模板

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 校正后仍显著,其中 NoneMany 的差异最大。这几项结果放在一起,比单独报告一个总体 P 值更完整。

配对演示还说明了另一件事:置换方式不是函数末尾一个无关紧要的参数。样本能否交换、在哪个范围内交换,取决于实验单位和采样设计。距离定义、模型公式和置换范围能够对应上,PERMANOVA 的结果才有清楚的统计含义。


参考资料

  1. vegan:adonis2() 官方文档
  2. vegan:betadisper() 官方文档
  3. vegan:vegdist() 官方文档
  4. vegan:mite 数据官方说明
  5. permute:how() 置换设计官方文档
  6. Anderson, M. J. (2001). A new method for non-parametric multivariate analysis of variance. Austral Ecology, 26, 32--46.
  7. Anderson, M. J. (2006). Distance-Based Tests for Homogeneity of Multivariate Dispersions. Biometrics, 62, 245--253.
相关推荐
cxr8281 小时前
Graphify vs GitNexus vs CodeGraph — 三工具架构深度对比
开发语言·架构·知识图谱
废弃的小码农2 小时前
功能测试--Day07--Python编程基础
开发语言·python
fengci.3 小时前
Microweber CMS 未授权路径穿越漏洞(CVE-2026-65694)
android·开发语言·前端·学习·php
程序员zgh3 小时前
C++ 拷贝赋值运算符 详解
c语言·开发语言·c++
念何架构之路3 小时前
restartmanager-重启管理子系统
java·开发语言
SomeB1oody4 小时前
【RustyML入门】2.0. 经典机器学习
开发语言·后端·机器学习·rust·教程
0566465 小时前
Python高级——解释器执行原理与虚拟环境工程化实战
开发语言·python
HJX_07246 小时前
嵌入式软件C语言八股文复习笔记5——编译、链接与底层硬核机制
c语言·开发语言·笔记
An_s6 小时前
rust(pdfium)底层开发实现wasm包给vue3调用
开发语言·rust·wasm