4. ComBat 方法原理与适用场景:经典批次校正方法的数学基础
ComBat 的全称是 Combining Batches,由 Johnson 等人在 2007 年提出。这东西在批次校正领域地位很高,说是「标配」一点都不夸张。
我第一次用 ComBat 是在一个多中心 RNA-seq 项目里。三个实验室的样本整合到一起,PCA 图一画,批次效应直接把生物学差异给淹没了。当时试了好几种方法,最后是 ComBat 把数据拉回来的。效果确实好,但我也踩过一些坑——比如没指定协变量,结果把真实的处理效应也校掉了。
这一节,咱们把 ComBat 的数学逻辑和适用场景拆开来讲。不堆公式,但核心思想得吃透。
4.1 从直觉到数学:ComBat 在做什么
说白了,ComBat 做的是两件事:调位置 和 调尺度。
你想想看,不同批次的数据,分布中心不一样(位置偏移),离散程度也不一样(尺度差异)。ComBat 对每个基因、每个批次,分别估计出这两个偏移量,然后把它们拉回到同一个基准上。
我个人习惯把这件事理解成「对齐 + 缩放」:
- 位置对齐:减去该批次特有的均值偏移。
- 尺度对齐:除以该批次特有的方差缩放因子。
但 ComBat 的高明之处在于——它不是硬生生地减均值、除方差。它用了 经验贝叶斯 框架,让估计更稳定,尤其是在样本量小的批次里。为什么这很重要?我后面会讲。
4.2 核心数学模型拆解
ComBat 的模型长这样(别怕,我一句句拆):
Y_{ijg} = α_g + Xβ_g + γ_{ig} + δ_{ig} ε_{ijg}
我来解释每个符号是什么意思:
| 符号 | 含义 | 说明 |
|---|---|---|
Y_{ijg} |
表达值 | 批次 i、样本 j、基因 g 的表达量 |
α_g |
基因基线均值 | 该基因在所有样本中的平均表达水平 |
Xβ_g |
生物学协变量效应 | 如处理组 vs 对照组,这部分我们要保留 |
γ_{ig} |
批次位置效应 | 批次 i 中基因 g 的均值偏移 |
δ_{ig} |
批次尺度效应 | 批次 i 中基因 g 的方差缩放 |
ε_{ijg} |
随机误差 | 服从 N(0, σ²),独立同分布 |
核心目标就是:估计出 γ_{ig} 和 δ_{ig},然后从原始表达值里剥离它们。
调整后的数据长这样:
Y*_{ijg} = (Y_{ijg} - α̂_g - Xβ̂_g - γ*_{ig}) / δ*_{ig} + α̂_g + Xβ̂_g
注意看:分子把批次效应和基线都去掉了,除以尺度因子,再加回基线和协变量效应。这样做的目的是——只去掉批次效应,保留生物学变异。
经验贝叶斯是怎么进来的?
这里就是 ComBat 的精髓了。它给 γ_{ig} 和 δ_{ig} 设了 先验分布:
γ_{ig}服从正态分布:γ_{ig} ~ N(μ_i, τ_i²)δ_{ig}²服从逆伽马分布:δ_{ig}² ~ InvGamma(λ_i, θ_i)
然后用所有基因的数据来估计这些先验参数(这就是「经验」二字的来源)。有了先验之后,再结合每个基因自身的观测数据,计算后验均值作为 γ*_{ig} 和 δ*_{ig}。
我在项目中遇到过一个小批次只有 1 个样本的情况。如果不用经验贝叶斯,那个批次的位置效应根本没法独立估计。ComBat 硬是靠着「借力」把这组数据校了回来。效果当然不是完美,但至少能用了。
4.3 适用场景:什么时候该用它
ComBat 不是万能的。我总结了下面这个对照表,帮你快速判断:
| 适用场景 | 不适用 / 需谨慎的场景 |
|---|---|
| 批次数量 ≥ 2,样本分布较均衡 | 批次与生物学分组完全混杂(如批次1全是对照,批次2全是处理) |
| 表达矩阵是连续值(log-CPM、FPKM、TPM 等) | 输入是 counts 整数矩阵(建议先做 VST 或 log 转换) |
| 批次效应是加性 + 乘性混合 | 批次效应仅表现为均值偏移(此时用 limma 的 removeBatchEffect 更轻量) |
| 你关心的是差异表达基因的准确性 | 你关心的是单个样本的绝对表达值(ComBat 会改变数据分布) |
| 有明确的生物学协变量需要保留 | 没有协变量信息,且批次与分组高度相关 |
我建议你在运行之前,先画一张 PCA 图看看批次和分组的关系。如果批次和分组完全重叠——嗯,那就不是统计方法能解决的问题了,得从实验设计层面找原因。
4.4 关键参数与调参经验
R 包 sva 里的 ComBat() 函数是最常用的。核心参数我列一下:
library(sva)
# 基本用法
corrected_expr <- ComBat(
dat = expr_matrix, # 基因 × 样本 的表达矩阵
batch = batch_info, # 批次标签向量
mod = design_matrix, # 协变量设计矩阵(建议包含处理组信息)
par.prior = TRUE, # TRUE:参数先验(默认);FALSE:非参数先验
prior.plots = FALSE # 是否绘制先验诊断图
)
| 参数 | 默认值 | 我的建议 |
|---|---|---|
par.prior |
TRUE | 常用 TRUE,速度更快。如果批次数量少(2-3个),可以先 TRUE 后 FALSE 对比一下 |
mod |
NULL | 一定要传! 把处理组、性别、年龄等协变量放进去,防止被校掉 |
prior.plots |
FALSE | 首次运行建议 TRUE,看看先验分布拟合得怎么样 |
关于 par.prior 的选择
我个人的经验是:
- 样本量充足(每批 ≥ 10):
par.prior = TRUE就够了,计算快,结果稳定。 - 小样本或批次间差异极大:试试
par.prior = FALSE,非参数先验更灵活,但计算慢一些。 - 两个结果差异很大:说明数据不够稳健,建议检查批次分配和协变量是否完整。
我曾经在一个只有 8 个样本、3 个批次的项目里,两种先验模式给出了完全不同的校正结果。后来发现是有一个样本被标错了批次。修好之后,两种模式就一致了。所以——先验诊断图真的很有用。
4.5 实战避坑指南
最后几个我在项目里踩过的坑,希望能帮你绕过去。
如果你不传
mod 参数,ComBat 会默认所有样本属于同一总体。如果处理组和对照组在不同批次里分布不均衡,它会把处理效应当作批次效应校掉。我曾经因为这个差点把一个阳性结果给校没了。
ComBat 假设数据是连续且近似正态分布的。原始 counts 是离散的、偏态分布。我建议先做 log-CPM 或 VST (variance-stabilizing transformation),再输入 ComBat。直接用 counts 的话,校正效果会打折扣。
这就是 ComBat 的数学逻辑和使用要点。你想想看,它的核心其实就两件事:用经验贝叶斯稳健地估计批次偏移,然后把偏移从数据里剥离出去。理解了这个,后面的方法学起来就顺了。
我用下来最大的感受是:ComBat 很强,但前提是你得用对。协变量要传、数据要预处理、批次和分组不能完全混杂。把这几点守住了,它就能帮你把多个批次的数据整合成一个可靠的整体。