ComBat 的假设条件和适用场景
ComBat 不是万能的,它有自己的一套脾气。说白了,如果你想用它来消除批次效应,就得先搞清楚它背后藏了哪些隐含假设。一旦这些假设不成立,校正结果就可能“翻车”。这一节我就带你仔细盘一盘 ComBat 的底层逻辑,以及到底哪些数据能用、哪些场景千万别碰。
核心假设:位置-尺度模型
ComBat 的核心思想,就是假设批次效应可以拆成两部分:一个是整体均值的偏移(位置效应),另一个是整个分布的拉伸或压缩(尺度效应)。用数学语言说,每个基因在不同批次下的表达式可以写成:
Y_{ijg} = α_g + β_g X_{ij} + γ_{ig} + δ_{ig} ε_{ijg}
这里 γ 负责调整均值,δ 负责调整方差。模型假设每个批次内部所有样本共享一组γ和δ,而且这些效应是加在同一个基因表达的真实信号之上的。
我个人觉得,这个模型对大多数基因表达数据是合理的。但有一个坑你必须注意:它假设批次效应只影响位置和尺度,不会改变基因之间的相关性结构。我在一个多中心 RNA-seq 项目中遇到过,如果不同批次用的测序平台不一样(比如 Illumina HiSeq vs. NovaSeq),数据里的基因共表达模式都可能变了,这时单纯用位置-尺度模型就不够用了。
关键假设小结:
- 批次效应与生物学变量(如疾病状态、处理条件)相互独立
- 批次内所有基因的效应可以用同一组γ/δ参数描述
- 批次效应在基因间是可加的,不考虑交互作用
- 每批次至少有2个样本,最好多于5个
适用数据类型:哪些数据能用
ComBat 最初是为微阵列芯片设计的,后来大家发现它对 RNA-seq 的 log-CPM 或 RPKM 格式也挺好使。但要注意,如果你拿原始 Count 矩阵直接跑,结果会很奇怪——因为计数分布不符合正态假定。
我建议,在使用 ComBat 之前,务必先把数据转换到正态或近似正态的尺度上。具体来说:
| 数据类型 | 推荐的预处理 | ComBat 表现 | 我的经验 |
|---|---|---|---|
| 微阵列芯片 (Affymetrix, Agilent) | RMA / quantile normalization 后的 log2 强度 | 极好 | 我用过多次,稳定可靠 |
| RNA-seq (counts) | log-CPM 或 vst 或 rlog (DESeq2) | 良好 | 注意全零基因要提前去除 |
| 单细胞 RNA-seq | 先归一化到 ln(CPM+1) 或 scran 校正 | 中等,需谨慎 | 若批次内细胞数太少,容易过校正 |
| 甲基化芯片 (450K, EPIC) | dasen / funnorm 后的 beta 值 | 良好 | 我处理过 TCGA 甲基化数据,效果不错 |
| 蛋白质组学 (TMT, LFQ) | log2 强度,缺失值先填充 | 中等 | 注意批次内技术重复是否足够 |
小技巧:如果你的数据是非负实数(比如质谱峰面积),可以先做 log2(x+1) 变换再跑 ComBat。我习惯用 log2(x+1),因为零值不会丢失。
什么时候不能用?避坑指南
ComBat 不是银弹。有些场景用了反而帮倒忙。我踩过不少坑,今天一并告诉你。
场景一:生物学因素与批次完全混杂。 比如,所有病例样本都在第一批处理,所有对照都在第二批。ComBat 会把病例和对照之间的真实差异当做批次效应给校正掉。我当时在一个项目里就犯了这个错,结果校正后两组样本完全重叠了,差点得出“没有差异表达”的结论。
场景二:每个批次样本太少。 ComBat 需要通过经验贝叶斯来估计每个基因的批次参数。如果某批次只有1个样本,那方差根本没法估计。我建议每批次至少5个样本,实在不行至少也要2个,而且有多个批次来共享信息。
场景三:表达矩阵有大量零值。 尤其是单细胞数据,dropout 太多。ComBat 假设数据是连续正态分布,大量的零点会破坏这个假设。我曾经试过强行用 ComBat 校正单细胞数据,结果校正后很多负值,毫无意义。
场景四:批次内包含不同亚型。 比如一个批次里既有肿瘤组织又有正常组织,而且它们在批次内的比例与生物学设计不同。这时模型可能把组织类型差异也归为批次效应,导致假阴性。
# 这是一个典型的错误用法示例(不要模仿)
library(sva)
# 假设 batch 列的组别与 treatment 完全对应
corrected <- ComBat(dat = expr, batch = batch, mod = NULL)
# mod=NULL 意味着没有包含任何生物学协变量,危险!
⚠️ 强烈建议:把已知的生物学变量(如疾病状态、性别、年龄)作为协变量传入 mod 参数,否则 ComBat 可能过度校正。
一张图总结核心逻辑
下面这张 SVG 图把 ComBat 的假设、适用类型、禁忌场景串起来了,方便你一眼看清整个知识框架。你把它当成一张检查清单就好。
关于协变量的几点补充
ComBat 允许你传入一个 mod 矩阵来表示你想保留的生物学变量。我个人强烈建议你把分组、年龄、性别这些东西都放进去。这能告诉 ComBat:“这些差异是真实的,你不能动它们”。
举个例子,如果你有一个肿瘤 vs 正常的研究,那么 mod 里必须有一列是 tumor/normal。否则 ComBat 可能会把肿瘤和正常之间的差异也当做批次效应给抹平——这在我入行第五年的时候吃过一次大亏,教训深刻。
# 正确的用法:传入协变量
library(sva)
mod <- model.matrix(~ group, data = pheno) # group 是肿瘤/正常
corrected <- ComBat(dat = expr, batch = batch, mod = mod)
还有一个容易被忽略的点:防止过拟合。如果你传入的协变量跟 batch 几乎完全相关,比如 batch1 全是男性、batch2 全是女性,那么 ComBat 就无法区分性别效应和批次效应。这时你应该考虑放弃 ComBat,或者换用 limma 的 removeBatchEffect 加上 blocking 策略。
复杂实验设计:多因素批次
有些实验不止一个批次来源,比如测序日期不同、建库人员不同、试剂批号不同。ComBat 的原始版本只能处理一维批次变量。别担心,现在有 ComBat_seq (sva 包的扩展) 或者 neuroCombat 支持多批次联合校正。我建议你把多个批次变量合并成一个新变量,或者使用 ComBat 的 mod 参数把额外的批次作为协变量放入。
到底该合并还是加协变量?我的经验是:如果你有明确的层级关系(比如“中心-日期-机位”),合并成单一批次变量效果更直接。如果多个批次彼此独立且没有层次,那么主批次用 batch 参数,其余用 mod 传入会更稳健。
# 多批次时的典型陷阱
# 错误:只用一个batch变量,忽略其他批次
corrected <- ComBat(dat = expr, batch = center)
# 较好:把日期作为协变量
mod <- model.matrix(~ date, data = pheno)
corrected <- ComBat(dat = expr, batch = center, mod = mod)
# 更好:合并成一维批次 (center_date)
pheno$new_batch <- paste0(pheno$center, "_", pheno$date)
corrected <- ComBat(dat = expr, batch = pheno$new_batch)
最后啰嗦一句:运行 ComBat 之前,一定要检查数据分布。如果看到类似“负的表达量”或者“方差为零的基因”,赶紧处理掉。我习惯在第一行就加个 expr <- expr[rowSums(expr > 0) > 0, ] 把全零行先干掉,省得后面报错。