ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

转录组数据去批次效应:ComBat、ComBat-seq与removeBatchEffect实战指南

转录组数据去批次效应:ComBat、ComBat-seq与removeBatchEffect实战指南 拿到转录组数据我第一步永远是先看PCA图。这个习惯帮我避免过很多次“跑完差异分析却根本解释不了结果”的尴尬。不管你是从GEO下载多个数据集合并还是自己同一批样本分了几次建库测序批次效应都是绕不开的问题。尤其是整合公共数据做挖掘时不同实验室、不同平台、不同批次之间的技术差异往往比真实的生物学差异还要大。这次我就把转录组数据去批次最常用的三个方法——ComBat、ComBat-seq、removeBatchEffect——完整整理一遍包含原理、代码、参数细节和我在实际项目中踩过的坑。这篇文章适合正在做转录组整合分析的学生或研究人员尤其是被批次问题困扰、搞不清该用哪个函数、以及用完之后不知道怎么评估效果的人。我尽量用项目和实操的角度来讲不堆教科书内容。1. 批次效应转录组项目合并时最先遇到的那道坎先从一个最典型的场景说起。你从GEO里下载了两个数据集一个GSE编号是2018年的另一个是2021年的每个都有数十个样本你想把它们合并起来比较某个基因在不同分组里的差异。这时候你打开PCA图发现两个数据集的样本各自聚成一团而不是按照你真正关心的临床分组聚集。这就是最典型的批次效应。批次效应的来源非常杂RNA提取时间不同、建库试剂盒换了批号、测序仪器不一样、测序深度有差异、操作人不同甚至连季节都可能掺一脚。更隐蔽的是如果你中间换过数据分析流程版本比对软件参数、参考基因组版本不一样也会引入系统性偏差。这种偏差不属于生物学变异但在统计上会被当成“信号”的一部分严重影响差异基因筛选和后续建模。1.1 不要盲目去批次先用明确标准判断批次效应是否真的存在很多教程上来就教你怎么跑代码但我强烈建议先做判断。去批次不是“必须做”而是“需要时做”。判断过程我一般分三层走第一层是主观可视化。把所有样本的表达量矩阵跑一个PCA用不同颜色标注批次信息同时用不同形状标注目标分组。如果PC1或PC2上同批次样本明显聚集成团并且批次之间的距离远大于组内差异那就说明批次效应较强。也可以看样本间相关性的热图如果同一批次的样本明显呈现出比生物学分组更亮的颜色块同样说明问题。第二层是定量检验。举个例子你可以用vegan::adonis2做一个基于距离的PERMANOVA检验把样本间距离矩阵作为因变量批次作为分组因子如果P值显著且R2不算低就能从统计上支持“批次效应存在”。不过说实话测序数据样本量大时P值很容易显著所以我会更关注R2的大小一般R2超过0.1就需要认真对待。第三层是判断批次效应是否会影响你的目标分析。这需要结合具体问题如果你是做样品聚类、细胞分群批次会把相同类型的样本拆开影响巨大如果你是做某个已知基因的表达量验证可以用几个内部参考基因或已知标记基因做表达对比先看看批次间是否存在系统性偏移。1.2 去批次之前必须确认的关键信息动手之前请先列一张清单确认以下信息是否完备否则后面很容易返工每个样本的批次信息是否完整。批次因子必须是清晰的分类变量建议用因子类型保存不要用数字1、2、3直接当连续型变量传进去。你要保留的生物学分组是什么。去批次的原则是“去掉技术差异保留生物差异”所以你必须明确哪些变量是生物学目标不能一起被矫正掉。批次与目标分组是否完全混杂。这一点极其关键后面我会专门用一节来说它决定了方法的选择。表达量矩阵的格式和尺度。ComBat要求的是log转换后的表达量或标准化后的连续值ComBat-seq要求的是原始整数count矩阵removeBatchEffect的输入通常也是log表达量。矩阵格式错了结果就是废的。我之前就犯过一个低级错误把没有过滤低表达基因的原始count矩阵直接丢给ComBat结果不仅慢而且先验估计被大量无信息基因带偏最后出来的矫正后表达值整体被过度压缩差异分析一个显著基因都找不到。所以先过滤再矫正这个顺序不能乱。2. 三种去批次工具的原理分野同样是校正底层逻辑完全不同很多人把ComBat、ComBat-seq、removeBatchEffect当成可以随意替换的“三个按钮”其实它们的数学假设、输入格式、适用场景区别非常大。我把它们拆开讲一讲方便你在项目里做判断。2.1 ComBat经验贝叶斯校正的经典框架ComBat是Johnson在2007年提出的方法最初是为微阵列数据设计的。它的核心思路是对每个基因把观测到的表达值分解为“总体均值 生物学效应 批次偏移 噪声”。要估计的就是每个基因在每个批次里的均值偏移和方差变化。这里最妙的地方是“经验贝叶斯”如果单独用某一个基因的数据去估批次偏移样本量太小时估计极不稳定所以ComBat会借用所有基因的信息为每个基因的批次参数估计一个先验分布然后把单基因的估计值向全体先验收缩。简单理解就是“用全基因组的信息帮单个基因做稳健估计”。因为这个框架假设基因表达值近似服从正态分布所以对RNA-seq数据你不能拿原始count直接跑而是要先转成log2-CPM、voom校正后的值或者DESeq2的vst/rlog值。这也是老版本ComBat最被吐槽的地方它并没有真正为RNA-seq的离散计数特性设计模型。2.2 removeBatchEffect线性模型的“快刀”removeBatchEffect来自limma包它做的事情本质上是一个线性回归把表达矩阵作为因变量把你想保留的生物学变量放进设计矩阵design把批次和协变量作为需要剔除的项回归之后取残差。换句话说它的输出是“已经拿掉了批次贡献、只保留了你关心变量和残差”的表达值。这个函数最大的优点是快、简单、结果直观非常适合用来做数据可视化。但它的致命缺陷也在这里它是利用线性模型一次性修正的修正过程中会损失一部分自由度而且它没有对系数的方差做任何特殊处理。如果你把removeBatchEffect的输出直接拿去做下游的显著性检验尤其是limma的eBayes或DESeq2的统计推断p值的分布会出问题很容易产生大量假阳性这在同行评审时会被质疑。所以我的习惯是removeBatchEffect主要用来做PCA、聚类、热图之类的探索性可视化正式差异分析要么在模型里直接加批次协变量要么用ComBat-family在校正后再做。2.3 ComBat-seq面向raw count的负二项模型ComBat-seq是2020年发表在Bioinformatics上的方法专门针对RNA-seq原始count数据设计。它没有走“先log转换再正态假设”的老路而是假设基因表达服从负二项分布用广义线性模型把表达分解为批次效应和生物学效应然后同样用经验贝叶斯共享信息来估计参数最后在count尺度上输出矫正后的表达值。这样做的好处是显而易见的它更符合RNA-seq数据离散、过离散的特性对低表达基因和零膨胀问题处理得更好同时解决了ComBat在log尺度上对低表达基因强行转换导致的虚假差异。缺点是计算量明显变大尤其是基因数多、样本数多的时候跑一次可能要十几分钟内存也要留足。在实际项目中如果我的原始数据是count矩阵而且后续要用基于count的差异分析流程我一般会优先考虑ComBat-seq而不是ComBat。2.4 三种方法的核心区别速查表方法输入数据类型统计模型是否保留生物学分组差异典型使用场景能否直接用于下游检验ComBatlog2-CPM/voom/vst表达矩阵正态假设 经验贝叶斯通过mod参数保留微阵列数据、已转换的转录组表达矩阵谨慎使用校正后矩阵可直接作为后续分析输入但建议在差异分析模型中做好验证removeBatchEffectlog表达矩阵线性回归残差化通过design参数保留可视化前的数据清洗不建议用于正式差异统计ComBat-seq原始整数count矩阵负二项分布 经验贝叶斯通过group/full.model参数保留基于count的RNA-seq整合分析校正后矩阵可用于下游流程但需检查是否为整数这里要额外强调一点ComBat和ComBat-seq都支持在设计模型中设置你想保留的生物学分组这是一个“保命”参数。如果忘了设置方法会把所有样本间的差异都当成批次效应去矫正最终结果是你的分组差异也被一起抹掉。这个问题的具体表现和解决方案我在下一章的实战代码里会详细演示。3. 实战操作一个表达矩阵如何跑完三种去批次流程讲完原理我们直接上手。我以一个常见的RNA-seq整合分析场景为例你有一个count矩阵行是基因列是样本另外有一个样本信息表包括批次信息和目标分组。我们一步步把三种方法的代码都跑一遍并着重标注那些容易踩坑的参数。3.1 数据准备从count矩阵到不同方法需要的输入格式# 假设你的数据已读入 # count_matrix: 行为基因列为样本数值为整数count # meta: 数据框包含样本名、batch、group三列 library(edgeR) library(limma) library(sva) # 第一步过滤低表达基因 keep - rowSums(count_matrix 10) (ncol(count_matrix) * 0.2) count_matrix - count_matrix[keep, ] # 第二步生成log2-CPM表达矩阵供ComBat和removeBatchEffect使用 dge - DGEList(counts count_matrix) dge - calcNormFactors(dge) logcpm - cpm(dge, log TRUE, prior.count 3) # 第三步确认批次信息和分组信息的因子类型 meta$batch - as.factor(meta$batch) meta$group - as.factor(meta$group)我解释一下这里为什么用prior.count 3。原因很简单log2转换时如果遇到零值不加prior的话会得到负无穷或极不稳定的值加一个小的prior.count可以把零表达基因压缩到合理区间后续ComBat的经验贝叶斯估计才不会被极端值干扰。keep的过滤阈值需要根据实际情况调整。样本量少时我会放宽到“在至少20%的样本中count10”样本量多时可以适当提高。这一步不只是为了计算效率更重要的是排除那些几乎不表达、完全由噪声驱动的基因它们会污染先验估计。3.2 ComBat实操mod参数决定你是否保留分组差异# ComBat要求输入log表达矩阵 # 关键mod里面必须包含你真正关心的生物学变量 mod - model.matrix(~ group, data meta) combat_data - ComBat( dat logcpm, batch meta$batch, mod mod, par.prior TRUE, prior.plots FALSE )mod是ComBat里面最容易被忽略却又最关键的一个参数。如果你设mod NULLComBat会默认认为样本之间唯一的差异来源就是批次于是它会把所有生物学差异全部吸收进批次效应里。结果是矫正后的数据里“病例vs对照”这种目标差异可能荡然无存。有人可能会担心mod里放group会不会导致group的信息被强行保留即使它实际上是假的这个问题需要具体分析。mod的矩阵形式只是告诉模型“这些变量是我关心的不应该被当作批次效应去掉”模型会把它当成固定效应来估计并不会改变它的显著性检验框架。所以放group进去是安全的。如果你的实验里有其他需要保留的连续协变量比如年龄也建议加进mod。par.prior TRUE表示使用参数化的正态先验计算更快大部分时候够用。如果基因表达分布比较诡异可以尝试par.prior FALSE用非参数先验更灵活但更慢。判断方法是跑一次prior.plots TRUE看看基因的表达分布是否符合正态假设。3.3 removeBatchEffect实操搞清楚design和batch的职责# removeBatchEffect的输出适合可视化不适合正式差异检验 design - model.matrix(~ group, data meta) corrected_for_plot - removeBatchEffect( x logcpm, batch meta$batch, covariates NULL, design design )这里design里放的是“你希望保留的生物学变量”而batch放的是“你要去掉的批次变量”。如果你有连续的批次相关协变量可以放在covariates里比如RNA integrity numberRIN或测序深度。有个特别容易犯的错误把design NULL函数照样能运行但它会默认把所有样本均值也去掉只留下残差相当于把所有样本的总体表达水平都强制拉平了。这样画出来的热图可能很好看但已经不代表真实的表达水平了。所以我每次都会检查design矩阵的列名确保包含自己关心的分组变量。3.4 ComBat-seq实操慢工出细活的count尺度校正# ComBat-seq的输入是过滤后的原始count矩阵 # 注意这里不要传logcpm要传整数count combatseq_data - ComBat_seq( counts count_matrix, batch meta$batch, group meta$group, full.model TRUE )full.model TRUE表示在建模时保留group的信息返回的矩阵会尽量去除批次差异的同时保留分组差异。如果你设成FALSE相当于假设样本之间没有生物学分组把所有变异都当作批次处理结果经常是过度矫正。在ComBat-seq的模型里group参数还有一个作用它会影响负二项模型的离散度估计。假设同一分组内的样本本身就有生物学异质性模型会把这个异质性估计进离散参数里而不是错误地当成批次效应。所以即使你的目标分组在后续差异分析里不会作为主效应只要样本确实来自不同群体group里也应该填上对应的变量。跑ComBat-seq时要有点耐心。基因数2万左右、样本几百个的情况下我实测一般需要10到30分钟。我通常给它单独开一个R会话或者提前用filterByExpr把不表达的基因去掉能省下不少时间。3.5 效果评估三板斧去完批次到底有没有用跑完代码不算结束你还得确认矫正是否真的有效。我习惯用三个维度来评估第一看PCA前后对比。这几乎是必须做的一步。对比原始logcpm和矫正后矩阵的PCA图期望看到批次分组的点不再扎堆而同一种生物学分组的点开始聚拢。如果矫正后批次反而更集中或者分组完全没规律那多半是参数设置有问题。第二看样本聚类树或热图。用矫正后矩阵计算样本间欧氏距离或相关系数画聚类树把批次信息和分组信息标在树下面。如果聚类树中同一批次的样本还是牢牢粘在一起说明矫正力度不足如果聚类树只按分组聚集而不管批次说明效果较好。第三验证已知标记基因的表达模式。如果你的研究领域有一些公认的标记基因或通路看它们在矫正后的矩阵里是否还能区分目标分组。这一步很容易被人忽略但它能直接反映矫正是否“误伤好人”。我曾在一个项目里用ComBat-seq矫正完PCA看起来完美结果发现一个经典marker在两组间的差异完全消失了。后来发现是因为原始矩阵里这个marker的表达量很低过滤阈值设得太严它被提前删掉了。所以金标准永远是矫正结果必须经得起生物学验证。4. 真实项目中的选型标准与翻车经验什么情况该用哪个方法讲完原理和操作最后聊聊我在多个真实项目里总结出来的决策思路以及一些容易翻车的细节。这一节我尽量直接给出判断路径减少你试错的时间。4.1 首选思考差异分析里能不能直接放批次协变量很多人看到批次效应第一反应就是“先矫正再分析”但我想告诉你一个更稳的思路如果你的差异分析框架支持协变量建模优先把批次放进模型里而不是先做预矫正。以limma为例# 更推荐的思路design里直接包含批次 design_full - model.matrix(~ batch group, data meta) fit - lmFit(logcpm, design_full) fit - eBayes(fit)DESeq2同样支持library(DESeq2) dds - DESeqDataSetFromMatrix(countData count_matrix, colData meta, design ~ batch group) dds - DESeq(dds)这么做的好处是差异检验的统计框架没有破坏批次变量作为协变量进入模型后系统会估计它的效应并把它从检验中剔除同时保留所有样本的真实表达方差。相比之下预矫正方法多少都会改变表达值的分布性质可能引入假阳性或假阴性。那什么时候才需要预矫正我会在以下几种情况选择ComBat或ComBat-seq批次效应实在太强强到即使在模型中加入协变量PCA里同一个分组的样本还是被批次强行分开整合的数据集太多直接放进模型里自由度消耗非常严重或者你的分析目标压根不是差异检验只是做聚类、分类、伪时间等无监督分析此时无法把批次作为协变量只能预先校正。4.2 批次与分组完全混杂没有任何方法能解决这是所有去批次问题里最无解的一个场景病例组全部在第一批发出来的对照组全部在第二批。这时候批次信息和分组信息完全共线统计学上没有任何方法可以分清楚一个基因表达量的差异到底来自疾病还是来自批次。无论你用ComBat-seq还是ComBat矫正后的结果要么保留了批次效应而看不出分组差异要么把分组差异也一并去掉。如果你发现自己面对的是这种数据我的建议是不要硬去批次因为这属于依赖模型假设的结果。能做的补救只有几种去公共数据库补充其他批次的对照组样本重新设计纳入额外验证队列或者把结果定位为“发现性分析”再用独立数据集做验证。任何声称能在这类混杂设计中完美去批次的方法背后一定有很强的假设你需要保持怀疑。4.3 常见翻车点与规避策略整理一下我在实际支持和审稿中见到的高频错误把raw count直接灌给ComBat。ComBat假设正态分布而count离散且低表达基因大量为0跑出来的结果偏差很大。务必先做log转换或使用ComBat-seq。把ComBat-seq的输出直接喂给DESeq2但不检查是否为整数。ComBat-seq返回的是矫正后的表达值在实数尺度上DESeq2的rlog或vst之前需要整数count。如果不放心先round()或者在建模时仍然用原始count只是把批次放进design。对全部基因包括低表达基因直接去批次。低表达基因的噪声比例高会干扰经验贝叶斯的先验估计。先过滤再去批次这个顺序不能反。把removeBatchEffect输出当作正式差异分析的输入。前面已经强调过它的自由度损耗会扭曲统计推断审稿人如果较真p值分布很容易露馅。用矫正后的矩阵做训练集和测试集划分时引入数据泄漏。如果先用全部样本包括测试集的基因表达分布做矫正再划分训练集那就属于典型的数据泄漏。正确做法是先划分再在每个数据集内单独做批次处理或者在同一批次校正流程中严格以训练集估计参数后应用于测试集。忽略因子顺序。batch因子如果是字符型某些R函数不会把它当分类变量处理导致模型估计出荒谬的连续型斜率。务必提前as.factor()。4.4 我的选型决策路径总结心中有料落笔不慌。我给出一个自己的决策路径供你参考拿到数据后先做PCA和层次聚类确认批次效应强度。如果目标分析是差异检验优先在DESeq2/limma/edgeR的模型里直接加batch协变量。如果目标分析是无监督分析或模型加协变量后仍然有明显批次分离再考虑矫正。输入是count矩阵时首选ComBat-seq输入已经是log表达矩阵时首选ComBat如果只是为了画图直观展示用removeBatchEffect足够。矫正前后各出一套PCA和聚类图用固定种子和固定标记基因验证确保没有把生物差异一起抹掉。这个方法不一定适合所有人的所有数据但在我经历过的大多数转录组整合项目中它确实能避免在“选了方法但不知道后果”上浪费太多时间。最后说一点我个人的真实体会。做转录组整合分析这几年栽过的最大跟头不是代码不会写而是“太相信矫正算法而忽视实验设计”。批次效应不是一个纯粹的算法问题它和你的实验设计、样本量、分组结构深度绑定。去批次只是补救手段最好的办法永远是实验阶段就尽量避免批次混杂、保证每个分组在每批次里都有足够的生物学重复。对实在无法避免的数据ComBat系列提供了很好的补救框架但记住去完批次一定要用生物学知识来验收结果。一个经不起生物学验证的去批次结果就是一堆华丽但无意义的数字。
返回列表