课程导论

要讲批次校正,得先聊聊 limma 这个包。limma 的全称是 Linear Models for Microarray Data,虽然名字带着“微阵列”,但它早就被用到 RNA-seq、蛋白质组、甚至单细胞数据里了。我最早接触 limma 大概是十年前,当时还在做芯片数据,用 limma 做差异表达分析,后来发现它处理批次效应的能力更强——说白了,它能把实验设计里的各种干扰项(比如操作人员、测序批次)当成协变量,用一个线性模型直接拟合掉。

limma 简介

limma 的核心思路是“借力”。它把你的基因表达数据放进去,为每个基因拟合一个线性模型。模型里有你感兴趣的变量(比如处理组 vs 对照组),也有你讨厌的变量(比如批次)。我有时候跟学生开玩笑:这就好比做菜,盐和糖是你要研究的,锅和火候是你要控制的干扰项——limma 就是那个帮你把干扰项的影响剥离出来的大厨。

limma 最厉害的地方在于经验贝叶斯(empirical Bayes)方法。样本量小的时候,基因的方差估计很不稳,limma 会借用所有基因的信息来“收缩”单个基因的方差,从而得到更稳健的统计检验。我在一个只有三个重复的项目里吃过亏,普通 t 检验几乎找不到差异基因,换成 limma 后一下子筛出两百多个——嗯,从那以后我对经验贝叶斯就特别信任。

安装 limma 非常简单:

if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("limma")

装完之后加载,你就可以用 lmFit() 和 eBayes() 两个函数完成整套分析。我建议你先别急着敲代码,先理解批次效应这回事。

批次效应概念

批次效应,说白了就是“不同时间、不同人、不同试剂做出来的实验,结果有系统差异”。它不是你关心的生物学差异,而是技术上的噪声。举个例子:你今天跑的一批样本亮得不行,明天换了一管试剂后跑出来整体偏暗——这就是批次效应。

你想想看,如果你把今天和明天的一起分析,那些因试剂导致的整体偏移,很容易被误认为处理组的差异。我见过一个案例:某课题组比较两株细胞系的基因表达,结果差异基因里全是核糖体蛋白——后来发现是因为他们第一批用了旧的裂解液,第二批换了新的,核糖体 RNA 被降解的程度不同。这就是批次效应“伪装”成生物学信号的经典场景。

表格里总结了批次效应的常见来源:

来源具体举例影响特点
实验时间不同天、周、月全局偏移
操作人员不同技术员部分样本偏移
试剂批次不同货号、不同保存条件系统漂移
仪器/芯片不同扫描仪、不同芯片批次强度非线性变化

这些效应如果不管,后续的差异分析、聚类、甚至机器学习模型都会跑偏。我个人的习惯是:拿到数据后第一件事不是看差异,而是先做 PCA 看看前两个主成分里有没有明显的批次聚集——如果有,那批次校正就必须安排上了。

为什么需要批次校正

原因很简单:不校正的话,你的分析结果可能是错的。而且错得很隐蔽——你甚至可能找不到原因。

我经历过一次痛心的教训。那时我帮一个合作者处理一组 miRNA 测序数据,两个批次各做了三对样本。用 limma 跑完差异分析,挑出了几十个显著差异的 miRNA,看起来生物学很合理。结果一位审稿人指出:“你们这里的批次和分组完全共线性,差异基因很可能是批次造成的。” 我回去一查,果然:第一批全部是对照组,第二批全部是处理组。这就是“不可分辨的”批次效应——你没办法用一个线性模型把批次和分组分开。

避坑指南: 我曾经因为没检查实验设计是否平衡,导致整个分析重做。记住:批次和分组不能完全混杂。理想情况下每个批次里都应该有处理组和对照组。

批次校正能帮你做到三件事:

  • 去除技术噪声:把批次带来的的系统偏移从表达量中拿掉。
  • 保留生物学信号:只去除批次相关的变化,不碰你的真实差异。
  • 提升可重复性:不同批次的数据可以放在一起分析,减少批次间的偏差。

你可能会问:那 limma 怎么知道哪些是批次、哪些是信号?答案在于你的实验设计矩阵。你只要在模型公式里把批次变量作为一个因子放进去,limma 就会自动估计批次效应,并把它的影响当作“截距”的一部分给移除掉。下一章我们会具体演示这个矩阵怎么构造。

课程目标与前置知识

这门课的目标就是让你学会用 limma 做批次校正。具体来说:

  • 理解 limma 的线性模型框架,知道设计矩阵和对比矩阵怎么写。
  • 能用 limma 拟合带批次因子的模型,并提取校正后的表达量。
  • 学会评估校正效果(比如 PCA 前后对比、批次相关性分析)。
  • 遇到共线性、批次混杂等实际问题时,知道怎么诊断和解决。

前置知识方面,我假设你已经:

  1. 会用 R 的基本操作(数据框、因子、向量)。
  2. 接触过表达矩阵和差异分析的基本概念。
  3. 能理解线性回归的思想——其实就是“Y = b0 + b1*X1 + b2*X2 + error”。

你不用是线性代数高手,但知道矩阵乘法会很有帮助。如果对矩阵不熟,也不影响——我会在课程里把每个步骤拆解开,用很小的例子来说明。

为了让本章的概念更直观,我画了一张流程图,展示从原始数据到批次校正完成的核心逻辑:

批次校正核心逻辑流程图 批次校正核心流程 原始表达矩阵 含批次系统性偏移 设计矩阵 分组 + 批次因子 指定模型公式 limma 线性模型 lmFit → eBayes 表达矩阵 校正后表达量 去除批次效应 removeBatchEffect 差异分析 / 聚类 更可靠的生物学发现 💡 核心思想 用设计矩阵区分“分组效应”和“批次效应”,线性模型自动剥离批次影响。

这张图里最关键的其实是“设计矩阵”这一步——你给 limma 提供了什么样的公式,它就按什么样的方式去拆解方差。如果批次因子放对了,校正后的数据就能真实反映生物学差异。如果放错了(比如忘记包含批次变量),那 limma 就会把批次效应误当成分组效应来估计——结果反而更差。

嗯,说到这个,我想起另一个经验:有一次我帮朋友处理一个公共数据库的数据,原始论文声称找到了某种疾病的标志基因。我用同样的方法重分析,并加上了批次校正,结果那些“标志基因”几乎全部消失。后来发现,原来公共数据里病例组和对照组来自两个不同的测序中心,完全混杂——没有校正就会得出虚假的阳性结果。所以,批次校正不是锦上添花,而是数据分析里的一步“必须做”的清洗。

前置知识里我还想强调一点:你最好先理解“线性模型”是什么。不深奥,就是一条直线:y = a + b*x。当你有多个变量,就变成多维的:y = a + b1*x1 + b2*x2 + ... 这里的 x1、x2 可以是分组、批次、甚至连续协变量。limma 做的就是这件事,只不过是为每个基因单独拟合一条线,然后用经验贝叶斯把基因之间的方差信息共享,让结果更稳定。