资讯详情

资讯详情

R语言生态数据分析实战:从群落矩阵到多样性排序绘图全流程

做生态学野外调查的人都知道从样方里数完物种、称完生物量、记录完环境因子的那一刻真正头疼的工作才刚刚开始一摞物种丰度表怎么变成能写进论文的统计结果和发表级图件。R语言在这条链路上几乎是绕不开的选择vegan、ggplot2、iNEXT这些包组合起来能覆盖从α多样性到β多样性、从排序分析到聚类绘图的全流程。这篇内容不是我教科书式的功能介绍而是把我处理生物群落数据时踩过的坑、验证过的流程、觉得真正好用的方法整理出来给刚接触生态数据统计的人一条能直接上手的路径。先说清楚这篇分享适合谁。如果你手头已经有一套物种调查数据可能是植物样方记录、底栖动物鉴定表、土壤微生物OTU表想算多样性、看群落差异、画排序图但又不知道从哪一步开始那这篇文章正好对路。如果你只是刚装好R语言入门准备学生态分析也能从中拿到一份从数据清洗到绘图输出的完整路线图。我不打算堆术语每个环节都尽量解释为什么要这么做而不是直接甩一段代码让你复制。1. 生物群落数据分析为什么R语言是首选工具1.1 先搞清你的数据结构样方-物种矩阵很多新手拿到数据后第一件事就是急着跑分析结果不是报错就是结果跟自己想的不一样。生态统计的第一步永远是整理数据结构。生物群落数据的核心是“样方-物种矩阵”术语叫community data matrix行是样方列是物种单元格是物种在样方里的多度、盖度、生物量或者有无记录。可以理解为一张Excel宽表第1列是样方编号后面每一列是一个物种每一行是一个调查样方。vegan包内部几乎所有函数——diversity、vegdist、metaMDS、rda——都默认接受这种宽格式矩阵。所以拿到数据后第一件事就是把你的原始记录整理成这种格式。我自己在项目里见过太多反例有人把样方编号放在了行名里有人物种名带着特殊符号有人把不同年份的数据纵向堆在一起就开跑这些都会在后续分析里变成各种莫名其妙的错误。整理数据时有一个铁律样方名必须唯一、无缺失物种名最好统一为字母加数字的组合不要出现中文标点和空格。举个例子。假设我调查了12个样方记录了20个物种的多度整理后的矩阵大概长这样# 模拟一套物种丰度数据方便演示后续所有步骤 set.seed(42) otu - matrix(round(runif(12 * 20, 0, 60)), nrow 12, ncol 20) rownames(otu) - paste0(Site, 1:12) colnames(otu) - paste0(Sp, 1:20) group - factor(rep(c(Control, Treatment), each 6))这种数据量不大但流程跟处理真实的宏基因组OTU表、植物样方调查表完全一致。我建议所有刚入门的人先把数据结构理成这样再用str()和head()确认行列名、数据框类型再开始分析。1.2 R包选型vegan、ggplot2与配套工具R语言能处理生态数据很大程度上归功于vegan这个包。它是芬兰生态学家Jari Oksanen主导开发的社区生态学分析工具集承担了多样性计算、距离矩阵、排序分析、置换检验等一整套功能。与其自己写算法不如在vegan框架下做组合这也是整个生态分析生态的主流做法。除了vegan我常用的还有这么几个ggplot2出版级图表的绘制基础箱线图、散点图、热图都靠它iNEXT做物种累积曲线和外推多样性时的首选尤其是扩增子测序数据pheatmap热图绘制展示物种丰度格局时比手写ggplot2省力tidyverse数据处理全家桶dplyr、tidyr在清洗数据时基本离不开ggpubr把显著性检验结果直接标到图上的小工具省去手动加星号的麻烦。这些包可以按需安装但不要一次性全装。装包本身也有一些讲究后续我会单独讲。1.3 环境准备与R语言安装的常见坑如果你还没装R先去CRAN官网下载对应系统的安装包Windows装base版本就行macOS注意芯片类型选对安装包Apple Silicon机器不要下x86_64版本否则后面装包容易出兼容性报错。装完R之后再装RStudio它能让你同时看到脚本、环境变量、绘图窗口和文件目录对调试代码帮助很大。包安装上新手最容易遇到的问题是默认的CRAN镜像连接不稳定尤其在国内环境下。建议安装时指定一个速度可靠的镜像站点# 设置镜像例如中科大或清华的CRAN镜像 options(repos c(CRAN https://mirrors.ustc.edu.cn/CRAN/)) install.packages(vegan) install.packages(ggplot2)如果你用的是Bioconductor的包比如 microbiome 生态分析里一些菌群包要先用BiocManager::install()。装包报错时先看错误信息尾部绝大多数是缺系统依赖库Windows下一般缺RtoolsmacOS下缺Xcode Command Line Tools补上就能继续。这一步看似琐碎但70%的生态R分析卡壳都发生在环境配置上。2. 数据清洗与转换跑出可靠结果的第一道门槛2.1 物种丰度表的规范化整理要点实际调查数据很少能直接进入分析。物种鉴定表可能有多份采样记录里可能有GPS坐标、日期、深度等环境信息混在一起还有重复行、空行、NA值等问题。我的建议是建立一套固定的清洗流程读入数据→检查缺失→删重→转换类型→确认行列名。每一步都用代码留痕方便以后回看。清洗时有一个容易忽略的问题样方与物种矩阵里的行名和后续分组向量的长度是否一致。比如你有12个样方分组向量也必须是12个值顺序必须和矩阵行顺序完全对应。很多人在这里犯迷糊矩阵一行是Site1到Site12分组向量写成Control、Treatment一组6个最后用rownames(otu) %in%这种方式匹配结果顺序错乱也不知道。我通常的写法是直接用data.frame合并再做子集这样顺序永远对得上# 构造一个包含样方、分组和丰度矩阵的综合数据框 library(tidyverse) metadata - data.frame(Site rownames(otu), Group group) dat_clean - metadata %% left_join(as.data.frame(otu), by c(Site row.names))这种长流程的清洗核心思想是让数据在进入vegan之前就已经是干净、顺序和结构完全可控的状态。2.2 零值占比高时怎么处理生态群落数据最典型的问题就是零值过多。植物样方里可能只有少数几种常见种微生物测序数据里绝大多数OTU都是稀疏的零值占比轻松超过80%。如果不处理后续的Bray-Curtis距离、PCA分析都会被这些零值主导本质是“零膨胀”问题。先说结论零值本身不能随便删因为零值也是有意义的生态信息表示这个样方里没有这个物种。但也不建议直接拿原始多度做所有分析。常用的处理思路有两类一类是过滤稀有物种另一类是转换降权。过滤稀有物种要看研究目标如果你关注的是优势种格局可以把在超过80%样方里都不出现的物种删掉如果你关注稀有物种那就要保留。关键是过滤标准必须写清楚论文里可以复现。我处理微生物OTU表时的常用做法是先看每个OTU的出现率# 统计每个物种在多少个样方中出现 occ - apply(otu, 2, function(x) sum(x 0)) # 保留在至少3个样方中出现的物种 otu_filt - otu[, occ 3]这个阈值可以根据你的采样强度调整目的就是去掉那些“只出现过一次”的偶然记录降低噪音。注意这只是预处理的一种策略不代表所有分析都必须这么做RDA或CCA里通常还会再结合环境变量做变量筛选。2.3 Hellinger转换vs相对丰度怎么选标准化转换是生态数据里最容易被忽略、又最影响结果的一步。很多新人上来就计算Bray-Curtis距离完全不考虑物种丰度量纲不同比如一个物种多度50、另一个多度5000后者直接主导了距离计算。合适的做法是先做转换再算距离。Hellinger转换是我的首选它对数据先按样方总和做相对化再开平方作用是对丰度差异大的物种进行降权同时保留物种组成差异的生态信息特别适合后续接PCA、RDA这类线性排序方法。代码很简单library(vegan) otu_hel - decostand(otu_filt, method hellinger)相对丰度转换则是把每个样方的物种多度除以该样方总多度得到0-1之间的比例常用于微生物组成分析。两者的区别在于相对丰度保留了原始比例关系但稀有种和优势种的贡献仍然差异很大Hellinger转换进一步压缩了这种差异更适合多元分析。如果数据本身是0/1有无数据那就可以不转换直接算距离但没有专门说明的话默认先做Hellinger转换通常不会错。3. α多样性与β多样性一步步算出生态学核心指标3.1 α多样性指数计算全流程α多样性指一个样方内部的物种多样性最常用的是Shannon指数、Simpson指数、物种丰富度即Chao1和ACE。vegan包里用diversity()函数可以一次性算出Shannon和Simpsonspecnumber()算物种数estimateR()可以算Chao1和ACE。实际操作中我建议把所有α多样性指数放在同一个数据框里方便后续跟分组信息合并画图# 计算α多样性指数 alpha_div - data.frame( Site rownames(otu_filt), Richness specnumber(otu_filt), Shannon diversity(otu_filt, index shannon), Simpson diversity(otu_filt, index simpson), Chao1 estimateR(otu_filt)[2, ] # estimateR返回多行第二行是Chao1 )这里需要提醒一点estimateR()返回的是一个矩阵第一行是物种数第二行是Chao1第三行是ACE估计提取时千万别取错。另外不同的α多样性指数反映的信息侧重点不同Shannon对常见种敏感Simpson对优势种敏感Chao1和ACE则侧重估计未观测到的物种数。写论文时最好结合两到三个指数一起展示而不是只放一个。如果你做的是扩增子测序数据仅基于OTU表算出的α多样性并不完善因为测序深度不同会直接造成物种数差异。这时候用iNEXT包做稀疏曲线和外推是很稳妥的做法。它允许你基于当前采样深度外推物种总数再比较不同组的多样性差异这也是现在主流期刊比较认可的做法。3.2 组间多样性差异检验与可视化衔接算出α多样性后下一步往往是比较不同处理组或不同环境条件下的多样性差异。最基础的是做t检验但生态数据经常不满足正态性和方差齐性我建议默认使用Wilcoxon秩和检验两组或Kruskal-Wallis检验多组它们对分布假设非常宽松在生态学论文里也更容易通过审稿人那关。# 两组比较 wilcox.test(alpha_div$Shannon ~ group) # 多组比较比如3个处理水平 kruskal.test(alpha_div$Shannon ~ group)这里的p值只能告诉你“有没有差异”不能告诉你是哪两组之间有差异。多组比较时要做多重比较校正比如pgirmess::kruskalmc()或者agricolae::kruskal()否则容易出现假阳性。我自己以前写论文时只跑了个Kruskal-Wallis觉得显著就完事了结果审稿人直接要求做两两比较补了一次才发现原来只有A组和C组之间有差异B组是夹在中间的模糊状态。所以不要吝啬这一步后面画箱线图时把检验结果标上去信息量立刻不一样。3.3 β多样性距离矩阵计算与合理选择β多样性描述的是样方之间的物种组成差异它的核心是距离矩阵。vegan里的vegdist()是主力函数支持Bray-Curtis、Jaccard、欧氏距离等多种方法。我用的最多的是Bray-Curtis距离它基于丰度数据对零值相对稳健而且生态含义直观数值越大物种组成差异越大。# 基于Hellinger转换后的丰度矩阵计算Bray-Curtis距离 otu_bray - vegdist(otu_hel, method bray)如果你的数据是0/1有无数据Jaccard距离更合适因为Jaccard本身就是为二元数据设计的。这里我想强调一个新手非常容易犯的错误计算距离矩阵之前到底应不应该做转换。我在2.3节已经讲过了Bray-Curtis本身自带“先相对化再求和取最小”的算法但并不代表原始多度直接算出来的结果就合理。我通常会对比一下原始多度、Hellinger转换后分别算出的距离矩阵在排序图上的差别你会发现差别非常明显——尤其当某些物种多度特别高的时候原始矩阵几乎完全被高多度物种主导。这个对比可以作为你数据汇报的一部分非常能体现数据分析的严谨性。4. 排序分析与聚类把群落格局变成可解释的图4.1 PCA还是NMDS看数据说话排序分析的目标是把高维的物种组成数据压缩到低维空间让我们能用眼睛直观看到样方之间的格局。最常用的线性排序PCA以RDA以及非度量排序NMDS。如果物种丰度沿环境梯度的变化接近线性PCA就够用它的数学基础是特征值分解结果稳定解释起来也简单主坐标轴就是方差最大的方向。但如果群落数据存在明显的非线性响应、大量零值、生态梯度比较复杂PCA的解释能力就会大打折扣。这时候NMDS是更好的选择。NMDS不要求数据满足线性关系的假设它基于距离矩阵的秩次进行迭代目标是让低维空间中样方间的距离排序与原始距离矩阵的排序尽可能一致。代价是计算更耗时而且结果还会受到随机起始点的影响所以一定要设置随机种子保证结果可复现。我的判断习惯是当样方数量超过30个、环境梯度跨度大、数据零值占比高时默认先跑NMDS看看如果stress值比较低小于0.2基本就可以用来解释群落格局。如果数据线性关系明显或者还要跟环境因子做约束排序RDA那就选PCA/RDA路线。4.2 NMDS的stress值怎么看NMDS结果里最要紧的一个指标是stress它表示低维空间中样方距离排列与原始距离排列的差异程度。vegan的metaMDS()会在运行结束后直接告诉你stress值。我以前见过很多人拿stress0.29的NMDS图硬着头皮解释群落差异这是很危险的事。经验基准大概是stress小于0.05表示拟合极好0.05到0.1表示良好0.1到0.2表示尚可但需要谨慎解读超过0.2基本就不要拿去做严格解释了。遇到高stress最简单的尝试是增加维度比如把k从2改成3虽然3维图不容易在纸面上表达但至少能判断数据是否为强非线性的另外一个思路是改用其他距离矩阵有时候Bray-Curtis换成Jaccard后stress会明显下降。set.seed(123) nmds_result - metaMDS(otu_bray, k 2, trymax 100) nmds_result$stress # 查看stress值4.3 聚类分析与ANOSIM/PERMANOVA联合使用排序图能显示样方间的距离大小但要说“这几个组之间差异是否显著”还需要结合统计检验。最常用的是ANOSIM相似性分析和PERMANOVA置换多元方差分析两个都属于置换检验不依赖正态假设非常适合生态群落数据。ANOSIM的思路是把样方间距离的秩次用于比较组内和组间差异输出R统计量R越接近1说明组间差异越大同时给一个p值。PERMANOVA则直接基于距离矩阵做方差分解可以处理多因素设计。两个分析在vegan里都非常简单# ANOSIM set.seed(123) anosim(otu_bray, group) # PERMANOVA set.seed(123) adonis2(otu_bray ~ group, permutations 999)实操上我一般两个检验都跑如果结果一致说明结论比较稳健如果出现不一致就要检查是不是组内离散度差异过大。PERMANOVA对组间离散度的差异很敏感也就是说如果一组内部样方差异特别大也可能导致假显著这时候可以配合betadisper()做组间多度均匀性检验。这是审稿人很爱问的一个点提前主动做掉能省很多麻烦。聚类分析可以和排序图互补常用的方法是在Bray-Curtis距离矩阵上做层级聚类再用hclust()绘制聚类树可以直观看到样方如何聚集成群是否与预设的分组一致。注意hclust()的默认方法是complete linkage有时候可以用Ward方法对比看聚类结构的稳定性。5. 生态学绘图实战从默认图到出版级图表5.1 多样性箱线图与显著性标注ggplot2的核心语法是先映射数据再叠加图形元素。画α多样性的箱线图是非常标准的操作我直接给一套模板library(ggplot2) library(ggpubr) alpha_plot - alpha_div %% left_join(metadata, by Site) %% ggplot(aes(x Group, y Shannon, fill Group)) geom_boxplot(width 0.6, outlier.shape NA) geom_jitter(width 0.15, size 2, alpha 0.7) stat_compare_means(method wilcox.test, label p.signif, comparisons list(c(Control, Treatment))) theme_classic() labs(y Shannon Index, x NULL)这里有几个细节值得注意。outlier.shapeNA是不显示离群点因为我已经用geom_jitter把原始数据点画上去了箱线图只保留箱体和须线能避免离群点重复显示。stat_compare_means来自ggpubr包它能自动计算检验p值并添加显著性标记*表示p0.05**表示p0.01这样图里就带上了统计结果读图的人一眼能看到差异程度。如果你不想引入ggpubr也可以用annotate()手动加星号但工作量大不少。5.2 NMDS排序图的美化细节排序图是生态学论文里的重头戏也是新手最容易画得丑的地方。用ggplot2绘制NMDS图需要先从nmds_result对象里提取样方坐标再结合分组信息画点、画置信椭圆、画连线。# 提取NMDS样方坐标 nmds_points - as.data.frame(scores(nmds_result, display sites)) nmds_points$Site - rownames(nmds_points) nmds_points - merge(nmds_points, metadata, by Site) # 提取物种坐标用于箭头显示 nmds_species - as.data.frame(scores(nmds_result, display species)) nmds_species$Species - rownames(nmds_species) ggplot(nmds_points, aes(x NMDS1, y NMDS2)) geom_point(aes(color Group, shape Group), size 3) stat_ellipse(aes(color Group), level 0.95, linetype dashed) geom_text(data nmds_species, aes(x NMDS1, y NMDS2, label Species), size 2.5, alpha 0.7) theme_classic() labs(x NMDS1, y NMDS2)置信椭圆用stat_ellipse画的是95%置信区间能直观看出组间分离程度。物种点的坐标表示该物种在排序空间中的位置箭头越长、越靠近某个样方组说明该物种在这个组的样方里贡献越大。图上如果物种名太多会非常杂乱我通常只标注对排序贡献最大的几个物种或者干脆不标注物种名只保留样方点和组椭圆图面会更加干净。ggplot2的主题系统值得花一点时间调。出版级别的图尽量去掉灰色背景theme_bw()或theme_classic()都适合生态学期刊的审美。坐标轴字体大小、图例位置这些细节建议在投稿前统一用theme()配合ggsave()输出PDF或tiff。5.3 群落结构热图与聚类树组合展示热图特别适合展示物种丰度矩阵的整体格局尤其是样本数量多、物种数量多的时候。pheatmap包用起来最省心它可以把聚类树画在热图边缘还能自动标准化颜色。标准流程是先对丰度矩阵做标准化再用pheatmap展示library(pheatmap) # 对物种丰度做z-score标准化避免量纲影响 otu_scale - t(scale(t(otu_filt))) pheatmap(otu_scale, cluster_rows TRUE, cluster_cols TRUE, annotation_row data.frame(Group group, row.names rownames(otu_filt)), show_rownames TRUE, show_colnames FALSE)这里annotation_row是给每个样方加一个分组标签条热图旁边会多出一列颜色条直观展示分组与聚类结果的关系。用scale(t(...))做的是按物种标准化即每个物种在所有样方中的丰度均值为0、标准差为1这样高丰度和低丰度的物种都出现在同一张图里否则丰度高的物种会占满整个色阶低丰度物种的颜色信息几乎看不出来。热图颜色默认是从蓝到红但生态学数据我更喜欢用从浅黄到深红的渐变或者直接用RColorBrewer里的RdYlBu配色。颜色选择不是审美问题而是可读性问题越冷的颜色代表丰度越低越暖的颜色代表丰度越高这样看图的人能一眼判断出优势物种集中出现在哪些样方。6. 报错排查与实操心得6.1 高频报错速查表用得多了你会发现生态R分析里的报错其实很集中。我记录了几类最常见的以及对应的解决办法整理成了一张速查表。报错信息原因解决方式row names contain missing values数据框行名中有NA通常是合并数据时出现的用rownames(data) - 1:nrow(data)重新赋值或清洗掉NA行could not find function rda没有加载vegan包用library(vegan)加载注意rda在vegan里不是vegan以外species names were not matched绘图时物种名与矩阵列名不完全一致检查拼写、大小写、前后空格用make.names()统一stress 0.2NMDS拟合效果差增加k维度尝试不同距离矩阵或检查异常样方invalid times argument某列数据被识别成字符型不是数值型用as.numeric()转换数据列object not found变量名拼写错误或没在环境中用ls()查环境检查变量名cannot allocate vector of size矩阵太大内存不足用稀疏矩阵表示、简化数据、分批运算missing values in object丰度矩阵里有NAna.omit()或对NA值填充0根据实际情况决定这张表不是一次性生成的是我在项目里一次次碰壁后攒下来的。建议你在跑分析时把报错信息也记录下来形成自己的错误笔记因为同样的错误大概率会再次遇到。6.2 几个用报错换来的经验第一永远不要在原始数据上直接改。我刚开始做生态项目时为了省事直接在Excel里把某个样方的物种数据改了结果后续分析全部基于错误数据返工了整整两天。现在我会在R里用代码完成所有清洗原始文件只读不改每一步都有记录可查。这在数据密集型研究里是最基本的防呆习惯。第二固定随机种子保证结果可重复。NMDS、ANOSIM、PERMANOVA、随机森林这类算法都有随机性如果不设置set.seed()每次运行结果都会有细微差别审稿人如果要求重跑结果对不上是很尴尬的。我现在所有涉及随机置换的分析都在开头加上set.seed(123)这个习惯值得推广。第三先小样本跑通再全部数据分析。我经常先用10个样方、20个物种跑一遍流程确认所有代码无误、图形正常再换全量数据运行。这个方法能省下大量调试时间否则每次全量跑完才发现一个bug会非常痛苦。第四画图前考虑清楚要表达什么信息。很多人把NMDS图画得花里胡哨却说不清楚到底要表达什么。我现在的习惯是先明确图表要回答什么问题——比如“处理组与对照组是否明显分离”——然后再挑选图形元素。多余的点、线条、变量统统去掉。第五不要迷信p值。PERMANOVA的p值小于0.05不一定说明群落差异有实际生态意义还要看效应量比如adonis2结果里的R²再结合排序图的分离程度综合判断。一个统计显著但排序图重叠严重的结论写进论文里容易被审稿人质疑。说到底R语言生态数据分析的核心从来不是代码本身而是你对自己数据的理解程度数据结构是什么距离矩阵选什么排序方法适不适合图要传达什么。代码只是一层外衣。我的建议是花点时间把你自己的数据从原始记录到最终图件的完整流程跑通一遍再回头看这些技巧你会发现自己已经有了独立处理生态数据的能力。后续如果你想做更进阶的分析比如RDA约束排序、变差分解、零膨胀模型可以在这些基础流程之上继续搭路径就顺多了。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →