ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微生物组数据预处理:OTU/ASV过滤与相对丰度转换实战指南

微生物组数据预处理:OTU/ASV过滤与相对丰度转换实战指南 拿到一批16S/ITS测序数据之后你最该做的不是PCA不是热图也不是急着算个alpha多样性。先把OTU/ASV表和注释结果做一轮踏踏实实的过滤再做相对丰度转换——这一步做得稳不稳直接决定你后面所有可视化结果和统计检验是否站得住脚。很多初学者在这个环节掉进坑里有的不过滤直接怼到lefse里结果一堆只在单个样本出现1条的spurious OTU把差异菌硬生生顶出来有的反过来阈值一刀切得太狠把一些真实存在但丰度偏低的标志菌给误杀了后面找差异菌怎么找都找不到。我这些年处理过的微生物组数据项目里从土壤、肠道、水体到发酵食品都碰过总结下来从原始特征表到最终可用于下游分析的数据其实是一条固定路径质量控制 → 特征过滤 → 归一化 → 相对丰度转换。这篇文章就把这条路径掰开揉碎把我实际使用的流程、每个参数为什么这么定、哪些地方容易出幺蛾子全部讲清楚。1. OTU与ASV的分水岭先搞清楚你要处理的是什么数据1.1 OTU和ASV到底差在哪先说概念。OTUOperational Taxonomic Unit是操作分类单元传统的做法是把测序得到的序列按97%的相似度进行聚类聚类后一个簇代表一个OTU。它的优势是能屏蔽一部分测序误差因为同一物种的序列即使有1~3个碱基的差异也会被归到一起缺点是分辨率有限97%相似度意味着它只能大致划分到属甚至科的水平不同物种如果有高度相似的保守区会被合并成一个OTU丢失真实的微生态差异。ASVAmplicon Sequence Variant则完全不一样。它不做聚类直接通过降噪算法把测序reads校正到单碱基分辨率区分出真正的生物学序列变异。DADA2、Deblur、UNOISE3这些工具都是走ASV路线。ASV的优势是两个分辨率高到可以区分单个碱基的差异可重复性强因为不需要依赖参考数据库进行聚类不同批次、不同研究的数据可以直接合并比对不会像OTU那样因为聚类阈值漂移导致结果不可复现。这里有一个很实际的问题你手里的特征表如果是早几年用mothur或者QIIME1做的大概率是OTU表如果是近期用QIIME2的DADA2插件、或者用R的DADA2包、以及用usearch/vsearch的unoise3流程做的那就是ASV表。拿到数据第一步先确认这件事因为过滤策略的选择和阈值设定对OTU和ASV是完全两套逻辑。1.2 数据生成方式决定了后续过滤策略OTU表基于聚类簇内包含的reads数天然包含了部分误差reads低丰度OTU的“质量”比ASV更可疑。一个只包含2条reads的OTU极有可能是嵌合体或者PCR错误扩增出来的拼凑序列是“假阳性”的重灾区。ASV表的每一行都对应一个经过严格质控的单碱基精度序列丰度低不代表它一定是错的——很多真实存在但稀有度高的菌群就藏在低丰度ASV里例如一些在肠道中丰度极低但对宿主免疫有显著影响的特定菌株。因此ASV表的过滤阈值可以比OTU表更宽松重点过滤的是“在极少数样本中以极低reads数出现”的特征而不是机械地删除所有低丰度ASV。我在实际处理中遇到过一位合作者拿着QIIME2输出的ASV表还在沿用以前OTU时代的老规矩去掉所有相对丰度低于0.01%的ASV。结果把一个在健康组里稳定但低频出现的标志菌给删了后续做差异分析怎么都不显著。后来我把过滤阈值改成“至少在2个样本中检测到且总reads数≥10”这个原本被删掉的ASV稳稳地留在了数据里而且在后续的随机森林模型里排进了前20个重要特征。所以先确认数据是OTU还是ASV比你急着定阈值重要得多。1.3 嵌合体与测序错误比低丰度更隐蔽的“污染源”过滤绝不只是删掉低丰度特征这么简单。嵌合体是PCR扩增过程中两条不同模板序列拼接形成的嵌合序列在聚类成OTU时往往会被当成新物种在ASV流程中DADA2的核心算法本身会做嵌合体检测但检测掉的只是它识别出的那部分漏网的仍然存在于特征表中。我见过最典型的一个案例一批粪便样本中某个“菌属”在几乎所有样本里都有2%~5%的相对丰度看起来稳定得很。后来我把它的代表序列拿去BLAST发现它在数据库里最高匹配度只有89%而且比对上的区域恰好在两段不同的细菌16S序列上——典型嵌合体。如果我当时不去做代表序列的核查后面所有围绕这个“菌属”的分析都会是空中楼阁。所以在进入低频过滤之前先做好三件事确认上游质控流程中嵌合体检测已经执行DADA2默认做Qiime1的usearch61/uclust需要单独检查对有疑问的代表序列做一次NCBI BLAST或RDP Classifier复核如果实验设计了空白对照negative control/blank先观察空白对照中富集的特征这些是试剂或环境带来的污染特征优先级高于低丰度过滤这点我得单独强调很多微生物组项目压根没做空白对照或者做了但数据分析时完全没用上。空白对照的意义不是走个形式它可能是整个过滤流程里最科学、最不容易引起争议的污染判定依据。后面我还会展开讲怎么用空白对照来定污染特征。2. 过滤不是删数据低丰度过滤的阈值逻辑与质控标准2.1 为什么要过滤低丰度特征——以及为什么不能盲目过滤过滤低丰度特征表面上是为了让数据“变得干净”本质是解决一个统计问题低丰度特征的测序计数具有极高的不确定性。同一样本重复测两次一个真实丰度只有0.0001%的物种第一次可能测到3条reads第二次可能一条都测不到它的出现与否几乎由抽样误差决定而不是由生物学真实丰度决定。把这种特征放进差异丰度分析里会让P值分布偏保守或偏激进得到一些不可复现的“假差异”。但反过来过滤掉的不仅仅是“噪声”也可能是真实的、有生物学意义的稀有类群。稀有物种在微生物群落里扮演着“种子库”的角色——平时丰度极低环境一变就爆发。如果项目的科学问题本身就是关注稀有生物圈rare biosphere那过滤更得小心翼翼。所以标准的处理逻辑不是“删掉低于某个丰度的所有特征”而是综合三个维度过滤维度核心问题常见策略出现频率prevalence这个特征是否只在极少数样本中出现至少在N个样本中出现N通常取总样本数的5%~10%或固定2~3个丰度大小abundance总reads数是否低到不可信总counts ≥ 10或基于比例阈值如相对丰度≥0.01%相对丰度最大值max relative abundance在所有样本中的最大丰度是否极低比如max RA 0.1% 时剔除或max RA 0.01%时剔除这三个维度交叉使用才算有依据的过滤。实际我在处理不同项目时的阈值设定会在下一节展开。2.2 阈值怎么选基于观测、基于比例还是基于空白对照先聊聊我在不同情景下使用的具体参数情景A临床/人体微生物组样本量30~100例关注物种组成差异这个场景下我一般用ASV至少在2个样本中出现如果总样本数超过50可以放宽到至少在3~5个样本中出现ASV总reads数 ≥ 10不设置全局相对丰度阈值因为一些关键稀有菌可能远低于0.01%但在生物学上重要这样过滤后通常能保留几千个ASV后续用CLR转换或CSS归一化来做多元统计分析都够稳。情景B环境样本土壤、水体测序深度高、物种丰富度极高这个场景我建议稍微放宽至少在2~3个样本中出现总reads数 ≥ 5因为环境样本的物种均匀度通常较低大量特征都是低丰度的机械卡10可能误杀太多但必须结合空白对照剔除污染特征情景C关注特定标志物、打算做机器学习建模如随机森林、LEfSe这个场景建议从严至少在5%的样本中出现总reads数 ≥ 10~20剔除所有在空白对照中相对丰度 0.1% 的特征过滤后如果特征数仍大于2000~3000再根据每一类的mean abundance筛掉底部25%为什么机器学习建模要从严过滤因为随机森林等算法对无关噪声特征非常敏感低丰度噪声特征一旦偶尔出现在某个组里很容易被当成模式识别出来造成严重的过拟合。宁可牺牲部分稀有类群也要保证进入模型的特征在统计上是可靠的。情景D有明确空白对照negative control的项目这是优先级最高的做法。先看空白对照中富集了哪些属/ASV计算每个特征在空白对照中的平均reads数或平均相对丰度计算同一特征在真实样本中的平均reads数如果空白对照中的丰度 ≥ 真实样本丰度的某个比例我常用10%~20%文献中常见0.1%~10%不等就把这个特征标记为潜在污染物这个叫“decontam”方法有R包可以直接做有两个核心指标frequency: 基于DNA定量定量PCR得到的DNA浓度来判断污染特征是否与真实样本的DNA量呈负相关prevalence: 基于特征在真实样本与空白对照中的出现频率差异来判断我的经验是想省事只想人工过滤的话直接算“污染分数 空白对照平均相对丰度 / 真实样本平均相对丰度”大于0.2的一律剔除大于0.1的建议剔除。真实样本中平均相对丰度如果已经很低比如0.01%同时空白对照又频繁出现这种基本都在污染清单里。2.3 过滤顺序与质控标准先嵌合体、再污染、最后丰度很多人拿到特征表还没搞明白顺序就直接开筛这里我强调一个严格执行的流程顺序先去掉线粒体、叶绿体、真核生物等非目标序列主要是16S数据里的线粒体和叶绿体污染用注释结果过滤再做嵌合体复核检查是否有代表性序列在BLAST中匹配率低但又被保留下来的特征手动移出再做空白对照污染剔除基于decontam或人工比对剔除可能的试剂/环境来源序列再做基于丰度和出现频率的低频过滤最后才做相对丰度转换或归一化顺序为什么重要因为如果先做低频过滤再比对空白对照一些在空白对照中丰度不低的污染特征可能因为总体丰度低被删掉一部分但剩下的那些仍然留在数据里你在后续用decontam时就会漏掉它们在真实样本中的“投影”。反过来如果把污染特征先剔除再低频过滤数据损失量更小、过滤依据也更科学。在这个环节我有几个经验参数可以分享过滤线粒体/叶绿体时按注释结果过滤OTU/ASV注释到“Mitochondria”或“Chloroplast”的不管丰度高低一律删。如果用的是Greengenes数据库还要注意注释结果里“Cyanobacteria/Chloroplast”可能存在分类层级歧义用silva数据库相对安全。3. 从counts到相对丰度归一化逻辑与抽平争议3.1 为什么不能直接比较原始counts过了过滤这一关接下来就是老生常谈但必须讲清楚的话题为什么不能拿原始reads count直接做样本间的比较原因很简单不同样本的测序深度不一样。你一个样本测了8万条reads另一个样本测了2万条reads哪怕所有物种的真实比例完全相同前者的所有counts大约是后者的4倍。直接比较counts就是在拿“绝对数量”去推测“相对构成”而扩增子测序本身获得的并不是基因组DNA的绝对定量值它只是一个组成比例的数字投影。所以我们才需要所谓的标准化/归一化normalization。这里我明确区分两个概念相对丰度转换relative abundance transformation把每个特征在样本内的counts除以该样本的total counts得到每个特征的相对占比总和为1。归一化normalization通过某种算法消除样本间测序深度的技术性差异同时尽可能保留生物学变异的信息。两者不冲突但先后顺序和算法选择会显著影响下游结果。最朴素的做法就是在过滤后直接做相对丰度转换把counts除以样本总和乘100变成百分比。这种做法计算简单、可视化直观但有一个已知的局限性它没有处理“组成性”问题。因为所有特征相对丰度加起来是固定值1一个特征的变化必然导致其他特征的下降这种负相关是数学产物而非生物学信号。因此做PCA、PERMANOVA这类基于欧氏距离的多元分析时直接用原始相对丰度表会带来“伪负相关”的偏差。3.2 抽平rarefaction的争议与我的取舍抽平rarefaction是微生物组分析中最广为人知也最受争议的步骤。做法是从每个样本中随机抽取相同数量的reads比如统一抽到10000条少于这个测序深度的样本被丢弃或按最低样本深度抽取抽完后每个样本的总reads数完全一致。支持抽平的理由很朴素既然测序深度不同那就强行拉平让样本的“抽样力度”相同后面的比较就公平了。反对抽平的理由集中在两点丢弃有效reads测序深度高的样本本来信息更丰富抽平后白白丢掉大量数据统计功效下降。随机抽取带来的不确定性每次抽平都引入随机性不同随机种子得到的结果可能不同对低丰度特征尤其不稳定。那我的取舍是什么分场景来计算alpha多样性如Shannon、Observed species我坚持用抽平后的数据。因为alpha多样性对测序深度极其敏感不抽平的话测序深度高的样本总是高估物种数目无法公平比较。目前最常用的是用“最小样本测序深度”统一抽平丢掉少数深度严重不足的样本。计算beta多样性和样本间距离我倾向用不抽平、但经过其他归一化如CSS、CLR处理的数据。原因是我希望保留尽可能多的生物学信息标准化算法的引入已经解决了测序深度差异问题不需要再靠抽平“自损一千”。差异丰度分析DESeq2、edgeR、ANCOM-BC等这些工具本身内置了对测序深度的处理但不能直接喂原始counts必须按工具要求输入通常是不抽平但有专门归一化步骤的数据。有人可能会问抽平后再转相对丰度跟不抽平但用其他归一化方法结果差别大吗实测下来高丰度物种的结论基本一致低丰度物种和稀有类群差异非常大。如果你的项目关注的核心菌群是丰度前100的物种抽不抽平没那么大差别如果你在找罕见但有标志意义的细菌抽平会显著放大随机误差降低发现能力。实际例子一个水处理系统的微生物组项目里两个反应器样本的测序深度分别是1.2万和8.5万reads。抽平到1万reads后高深度样本里一个丰度约0.05%的稀有脱氮菌属消失了——它总共约42条reads随机抽1万时平均只能抽到5条左右经常抽不到。而在不抽平的数据里它在所有高深度样本中都是稳定存在的。这种稀有菌往往肩负着功能枢纽的作用脱氮、固氮、降解在生态功能分析中丢掉它们代价相当大。所以我的标准流程是alpha多样性用抽平数据beta多样性和差异分析用非抽平的标准化数据。抽平的具体实现可以用QIIME2的q2-feature-table rarefy或者R里vegan包的rrarefy函数。3.3 其他归一化方案CSS、TMM、CLR的适用场景除了抽平和相对丰度近几年主流的方法还有几种我列个表直接对比方法全称/原理适用场景代表工具/包注意事项CSSCumulative Sum Scaling累积和缩放基于数据的分布分位数估计缩放因子beta多样性分析常配合Bray-Curtis、Jaccard距离metagenomeSeq对稀疏数据稳健但不太适合差异丰度检验TMMTrimmed Mean of M-values基于加权截尾均值求归一化因子差异丰度分析配合edgeR使用edgeR假设大部分特征在不同组间没有差异有生物学冲突时不稳定CLRCentered Log-Ratio中心对数比转换先对数再中心化组成型数据分析适用于PCA、稀疏相关网络如SparCC、SPIEC-EASIcompositions、vegan的decostand(., clr)、 microbiome包要求数据不能有0值需要先做伪计数处理或乘子替代RLERelative Log Expression相对对数表达差异丰度分析DESeq2DESeq2同样假设多数基因/特征不变对极偏分布敏感推荐在特征表上用它之前先粗略筛一遍CLR是目前我处理组成型数据时最常用的一种因为它能消除“和为1”的组成性约束让数据在欧氏空间中做PCA、PCoA不会受到伪负相关影响。操作上CLR转换前可以把0值替换为特征最小非零值的1/2这是一种常见伪计数策略然后用transform(otu_table, transform clr)处理。注意CLR输出结果里有负数是正常的它只是数值变换不代表丰度是负的。而我个人处理流程一般是做Beta多样性PCoA/NMDS/PERMANOVA时用CSS标准化或抽平后的Bray-Curtis/UniFrac距离做群落结构差异描述、柱状图、热图用相对丰度百分比数据直观好讲做排序分析PCA、RDA、CCA时用CLR转换后的数据做差异丰度分析优先DESeq2有推荐输入非负整数的counts或ANCOM-BCLEfSe就用它的默认流程内部有自己处理逻辑但必须喂非负counts4. 一条能直接跑的实战流水线从phyloseq到相对丰度表4.1 数据导入与格式检查下面是我在R里跑的一套完整处理流程读者可以按步骤直接套用。假设已经有一个OTU/ASV的counts表行为特征、列为样本、注释/分类表每个特征对应的门纲目科属种、元数据表样本分组信息。# 加载核心包 library(phyloseq) library(vegan) library(microbiome) library(tidyverse) # 读入数据三种格式任选其一 # 方式1直接从biom文件导入 ps - import_biom(feature-table.biom) # 方式2从QIIME2的4个输出文件导入新版qiime2R # library(qiime2R) # ps - qza_to_phyloseq(table.qza, taxonomy.qza, tree.qza, metadata.txt) # 方式3手动读入三个表再合并 otu_mat - read.delim(otu_table.txt, row.names 1, check.names FALSE) tax_mat - read.delim(taxonomy.txt, row.names 1, check.names FALSE) meta_df - read.delim(metadata.txt, row.names 1, stringsAsFactors FALSE) OTU - otu_table(as.matrix(otu_mat), taxa_are_rows TRUE) TAX - tax_table(as.matrix(tax_mat)) META - sample_data(meta_df) ps - phyloseq(OTU, TAX, META)导入后的第一件事永远是格式体检# 数据体检 sample_sums(ps) # 每个样本的总reads数 ntaxa(ps) # 特征数量 rank_names(ps) # 注释层级是否齐全 table(tax_table(ps)[, Phylum]) # 看看各门的分布是否存在异常这一步骤没有捷径可走。我看到过太多人在这一步跳过去直接往下做结果样本ID大小写不一致、注释表行列反了的、元数据和特征表样本对不上的全都堆在最后才发现。其中最常见的是样本名匹配问题。两个表的样本名一个是“Sample_1”一个是“sample_1”phyloseq不会自动帮你合并它只会保留两个表中都出现的样本。建议在导入阶段就统一全部改成小写或统一格式sample_names(ps) - tolower(sample_names(ps))4.2 过滤与转换的R实现以下是我的标准过滤方案以ASV表、样本数40为例# 第一步过滤线粒体/叶绿体 ps_filtered - subset_taxa(ps, !Phylum %in% c(Cyanobacteria) | Class %in% c(Chloroplast) FALSE ) # 简单粗暴版直接去掉注释里有Mitochondria或Chloroplast的特征 ps_filtered - subset_taxa(ps, !grepl(Mitochondria, tax_table(ps)[, Family], ignore.case TRUE) !grepl(Chloroplast, tax_table(ps)[, Class], ignore.case TRUE) ) # 第二步按出现频率和总丰度过滤 # 保留至少在3个样本中出现且总counts 10的特征 ps_filtered - filter_taxa(ps_filtered, function(x) sum(x 0) 3 sum(x) 10, prune TRUE ) # 也可以分步做便于看每一步删了多少 prev_otu - apply(otu_table(ps_filtered), 1, function(x) sum(x 0)) abun_otu - apply(otu_table(ps_filtered), 1, sum) keep - prev_otu 3 abun_otu 10 ps_filtered - prune_taxa(keep, ps_filtered)这个过滤后可以看一眼损失情况# 过滤后样本总reads保留比例 keep_rate - sample_sums(ps_filtered) / sample_sums(ps) summary(keep_rate)我一般会要求平均保留比例不低于70%。如果过滤后单样本保留率低于50%说明过滤条件过严或者这批数据本身质量问题严重需要回头核查上游质控。紧接着做alpha多样性时使用的抽平数据# 抽平到最小样本深度 set.seed(42) # 固定随机种子保证可重复 min_depth - min(sample_sums(ps_filtered)) ps_rarefied - rarefy_even_depth(ps_filtered, sample.size min_depth, replace FALSE, # 无放回抽平 rngseed 42 )注意替换参数replace我默认选FALSE因为无放回抽平能保证每个特征最多被抽到它原始reads数的数量更接近真实抽样过程但有放回抽样能保留更多零膨胀结构在特殊需求下可使用。总体我建议无放回。再做相对丰度转换# 相对丰度转换不抽平的数据直接转成百分比 ps_comp - transform_sample_counts(ps_filtered, function(x) x / sum(x) * 100) # 提取相对丰度表 rel_abund - otu_table(ps_comp) %% as.data.frame() # 检查每列是否都等于100 colSums(rel_abund) # 应该是100如果要生成CLR转换数据# CLR转换0值替换为最小非零值的1/2 min_nonzero - min(rel_abund[rel_abund 0]) rel_abund_tmp - rel_abund rel_abund_tmp[rel_abund_tmp 0] - min_nonzero / 2 # 计算CLR逐样本进行 clr_data - apply(rel_abund_tmp, 2, function(x) log(x) - mean(log(x)))上面的CLR我在实际操作中更推荐用microbiome包的transform()函数一次到位ps_clr - microbiome::transform(ps_filtered, clr) clr_otu - as.data.frame(otu_table(ps_clr))4.3 输出前必做的数据体检转换完成不等于万事大吉。每套数据在下游分析之前我都会做这几项体检这里列出来给大家参考体检1样本测序深度的分布summary(sample_sums(ps_filtered))如果样本间深度差异超过10倍我建议谨慎处理要么提高重叠建库质量要么考虑做基于中位数深度抽平而不是最小深度抽平否则扔掉太多数据。体检2相对丰度转换后是否满足全距分布合理好的转换结果应该有特征的丰度从非常大到趋近于0都有分布如果所有特征集中在一个很窄的区间里那说明原counts表已经过于稀疏过滤力度不够或者测序深度太低。体检3差异分析前检查零值比例zero_prop - apply(rel_abund, 1, function(x) sum(x 0) / ncol(rel_abund)) summary(zero_prop)如果大部分特征的零值比例超过80%建议在差异分析环节采用专门处理稀疏数据的模型如ANCOM-BC、ZicoSeq不要硬上传统的t检验或秩和检验。体检4生物学阳性和阴性对照如果实验设计里有阳性/阴性对照样本在完成过滤和转换后先看对照样本是否和预期一样聚集。如果阳性对照跑偏了或者组内被明显区分开马上回头检查污染过滤是否彻底。这一步看似可有可无但从项目整体的可信度来看比任何统计方法都重要。5. 我在实际项目中踩过的坑过滤顺序、零值问题和可重复性5.1 先转换再过滤为什么会出问题有一段时间我在流程设计上偷过懒——先转相对丰度再套用过滤条件。当时想的是既然最终是要以相对丰度来展示数据那直接在相对丰度表上做过滤不是省事吗结果踩了个大坑。用相对丰度过滤和用原始counts过滤实际得到的保留特征集合完全不一样。我举个例子说明样本A总reads数8万样本B总reads数1万。某个特征在A中有80条reads、在B中有20条reads。按“总reads≥50”过滤它保留了但按“相对丰度≥0.1%”过滤时它在A中是0.1%刚好卡线、在B中是0.2%——看起来没问题。但如果有另一个特征在A中400条reads0.5%、B中0条reads0%按“至少在2个样本中出现”的标准它会被删但如果用“相对丰度平均值0.1%”它又会保留因为它平均下来有0.25%。这就导致同样的数据用不同单位的“表”做过滤最终特征列表可能差出500个以上。而且转换后再过滤最麻烦的是会引入0值的放大效应一个特征在大多数样本中是0只在少数样本中丰度高转换后这些少数样本中的值会被“占比放大”低深度样本因为总量小相对丰度反而虚高可能侥幸通过阈值。所以我的铁律是过滤永远在原始counts表上做转换永远在过滤后的counts表上做顺序颠倒所得到的所有结果都不值得信任。5.2 零值膨胀对下游分析的影响过滤和转换做完后特征表里仍然会有大量0值。这是扩增子数据的固有特性叫“零值膨胀”zero inflation。不同样本的微生物组成差异本来就大加上测序深度限制大量稀有特征只在少数样本中出现产生了海量0。零值膨胀会导致三个具体问题基于Bray-Curtis距离的排序对0值比例极其敏感。如果两个样本都缺少很多特征它们的“共有缺失”会被计算为相似性——这两个样本不是生物学相似而是都没测到罢了。这会让PCoA图出现“低深度样本扎堆”的假象。相关系数计算失真。例如计算两个属的Spearman相关性时大量0值的共同出现会让相关系数虚高因为0-0也算“一致”。机器学习模型的特征重要性被零值主导。某些特征在训练集中因为零存在规律性容易被视为分类依据但换一批数据就完全失效。应对策略分几层分析层面优先使用考虑了零值膨胀的统计方法如ANCOM-BC自带处理0值的机制、ZicoSeq专门针对零膨胀问题的差异检验距离选择层面用UniFrac考虑到系统发育关系的加权/非加权距离比单纯的Bray-Curtis更能抵抗零值干扰用Aitchison距离基于CLR然后算欧氏距离是我目前处理稀疏组成的首选可视化层面热图展示不要直接在原始丰度表上做Z-score先在CLR转换或“相对丰度伪计数log”后再标准化否则零值会把热图的颜色压缩得没有区分度这里多说一句不要迷信“用0值填充某种小常数”的办法来解决所有问题。伪计数的大小会影响CLR结果尤其是低丰度特征不同的伪计数策略可导致排序结果肉眼可见的漂移。所以我建议使用相对丰度表做图用CLR表做统计两者分开用不要在同一个分析里混着来。5.3 如何让流程可复现版本锁定与参数记录最后这条可能是全篇里最“枯燥”但最救命的一节。很多项目做完一两年后写论文时要补几个分析你回头想重新生成一张图结果发现跑出来的数据和当时的完全不一样——这种经历我至少碰过三次。问题基本都是这几点R包版本变了。phyloseq 1.36和phyloseq 1.42的filter_taxa行为不完全一致vegan的rrarefy在不同版本中对随机数处理有变化。随机种子没固定。抽平、机器学习模型训练都涉及随机性不固定种子就没法复现。参数记录不全。当时用的过滤阈值是0.1%还是0.01%“至少在2个样本出现”还是“5个”事后翻聊天记录都找不到。原始数据被覆盖或移动了位置中间产物丢失。我自己现在养成的习惯是每个项目从拿到原始数据的那一刻起就在项目目录下建一个00_scripts、01_data_raw、02_data_processed、03_results、04_figures的规范目录并把所有关键脚本的头部写好下面这段信息# # Project: 某某项目微生物组分析 # 分析内容: 从特征表到相对丰度表的标准流程 # 作者/日期: xxx, 2025-xx-xx # 输入文件: data/raw/feature-table.biom # 输出文件: data/processed/ps_filtered.rds # 环境要求: # R 4.2.1, phyloseq 1.42.0, vegan 2.6-4, microbiome 1.18.0 # 关键参数: # prevalence 3, total_count 10 # 抽平深度 min_depth, rngseed 42 # CLR伪计数 最小非零值/2 # 在项目结束时用sessionInfo()把所有包版本的快照也保存一份writeLines(capture.output(sessionInfo()), session_info.txt)不要嫌这些工作麻烦。等你在返修稿阶段被审稿人要求“请提供完整的参数配置和可复现流程”时就会发现当初的这段记录完全是救命稻草。还有一个实用小技巧把所有关键中间产物过滤后的phyloseq对象、抽平后的表、CLR表都用saveRDS()存成.rds文件按日期命名并保留三个版本原始版、过滤版、最终分析版。这样即使哪个环节改了参数需要重新跑你也能快速对比新旧结果差异在哪而不是从头到尾把流程再跑一遍。在可复现性这件事上我的态度是宁可脚本写得丑一点、笨一点也要保证半年后我自己能看懂、能跑出相同结果。比起追求代码优雅数据的可追溯和可复现永远排在第一位。
返回列表