第2章:批次效应概念——你数据里藏着的“隐形干扰”
做质谱数据分析时间长了,你可能会遇到这种情况:明明是对照组和实验组,但聚类的时候,样品不是按分组聚在一起,而是按“哪天上机”分的堆。嗯,这大概率就是批次效应在捣乱。
记得我刚入行那会儿,处理一批血浆样本的蛋白组学数据,差异蛋白列表一出来,足足300多个。我当时挺兴奋,觉得发现了新大陆。结果仔细一看,大部分差异都跟实验条件没关系,而是因为第一批样品和第二批样品换了一根色谱柱。那次教训太深刻了,从此我对批次效应就格外敏感。
批次效应到底是什么
说白了,批次效应就是不同批次实验带来的系统性偏差。它不是随机噪声,而是一种有规律的偏移。
你想想看,同一台质谱仪,今天开机和明天开机,仪器状态能完全一样吗?校准液配方、离子源的清洁程度、真空度,这些参数都会有细微差别。这些差别反映在数据上,就是某个批次的样品整体信号偏高,或者某个质荷比范围有偏移。
我习惯用一张图来理解它:
批次效应从哪来——三个主要根源
我在实际项目中总结过,批次效应的来源可以归成三类,我管它们叫“三座大山”。
| 根源 | 具体表现 | 我遇到过的例子 |
|---|---|---|
| 仪器状态 | 离子源污染程度、质量分析器校准偏差、检测器老化、真空度波动 | 有一次QE质谱的离子传输管堵了,后面两批数据信号直接掉了30% |
| 试剂批次 | 流动相纯度不一、内标浓度偏差、衍生化试剂活性差异 | 同一品牌的乙腈,不同批次批号,背景噪声谱完全不一样 |
| 操作人员 | 样品前处理手法、进样技巧、数据采集参数设置 | 两个实习生操作,一个喜欢涡旋30秒,一个涡旋1分钟,蛋白质酶解效率差别明显 |
批次效应怎么干扰你的分析结果
你可能会想,不就是有点偏差吗,影响能有多大?我告诉你,这可不是小问题。
第一,假阳性激增。 如果你的实验设计是批次和分组完全混杂——比如第一批全是对照组,第二批全是处理组——那么统计检验根本分不清差异是来源于处理效应还是批次效应。结果就是,你会把仪器漂移导致的信号变化,当成蛋白质表达量变化。
第二,定量严重偏差。 我做过一个验证实验:同一份血清样本,分三批上机。结果同一个蛋白在第一批的定量值是第二批的2.6倍。你想想,如果没有校正,这种误差能把正常值都变成“显著差异”。
第三,聚类和分类完全乱掉。 主成分分析(PCA)图上,按道理样品应该按生物学分组聚集。但批次效应会让样品按批次聚集,你根本看不到真实的生物学结构。我曾经盯着一张PCA图发呆了两个小时,就是找不出为什么对照组和处理组会分成两堆——后来才发现其实是第一批和第二批的差异。
核心公式:
观测值 = 真实生物学信号 + 批次效应 + 随机噪声
↓ ↓ ↓
你想找的 必须消除的 可以容忍的
说白了,批次效应和随机噪声不一样。噪声是多测几次能平均掉的,批次效应是系统的、一致的偏移,平均一百次也没用。
如何判断你的数据有没有批次效应
经验告诉我,不用急着搬什么高大上的算法,先做两步简单的检查:
- 肉眼观察:画个PCA图,按批次染色。如果同批次样品聚在一起,不同批次明显分开——嗯,批次效应已经很明显了。
- 统计检验:对每个蛋白/代谢物做批次间差异分析,看看显著差异的比例是不是远高于理论值。
我常用的检查代码长这样:
# R语言示例:快速检查批次效应
library(ggplot2)
# dat: 表达矩阵,行为样品,列为蛋白
# batch: 批次标签向量
pca_result <- prcomp(dat, scale. = TRUE)
pca_df <- data.frame(PC1 = pca_result$x[,1],
PC2 = pca_result$x[,2],
Batch = batch)
ggplot(pca_df, aes(x = PC1, y = PC2, color = Batch)) +
geom_point(size = 3) +
theme_minimal()
小技巧: 如果PCA图上前两个主成分解释的方差超过40%,而且批次分开,说明批次效应很强。这时候别想了,必须校正。
一个真实的案例拆解
去年我帮一个合作者处理临床血清样本的数据。120个样本,分6批采集,每批20个。合作者说“操作流程很严格,应该没什么问题”。
我拿过来一看,先跑了个PCA。结果你猜怎么着?6批样品在PC1上排得整整齐齐,就好像按采集时间排了个队。我跟他说:“你这数据里,第一批和第二批的差异,比你处理组的效应还要大两倍。”
他不信。我算了个数字给他看:
# 批次效应 vs 处理效应 粗略比较
# 计算批次间的平均距离
batch_center <- aggregate(pca_df[,1:2], by=list(Batch=batch), FUN=mean)
# 计算处理组间的平均距离
trt_center <- aggregate(pca_df[,1:2], by=list(Treatment=treatment), FUN=mean)
# 结果:批次间距离 5.8,处理间距离 2.3
# 批次效应强度的确大于处理效应
后来我们用ComBat做了校正,校正后再跑PCA,6批样品就彻底混合在一起了,处理组和对照组的差异这才真正显现出来。原来只有12个差异蛋白,校正后找到了37个——里面好几个是已知的疾病标志物。
避坑指南: 我曾经见过有人用“对每个蛋白做ANCOVA把批次作为协变量”的方法来处理批次效应。这个方法在某些情况下能用,但如果批次和分组完全混杂(比如第一批全是对照组),统计模型是分不开的,结果会非常糟糕。ComBat在这类场景下鲁棒性要好得多。