4. 线性模型基础回顾
线性模型,说白了就是一条数学公式,把你要解释的变量(y)和你的实验条件(X)之间的关系给定量化。你想想看,无论是两组比较还是多组时间序列,背后都能用这套框架搞定。我个人习惯把线性模型拆成三块来看:模型公式、设计矩阵、对比矩阵。这三块搞明白了,limma 的批次校正也就没什么神秘的了。
4.1 模型公式:从抽象到具体
最基本的形式长这样:
y = Xβ + ε
每个符号的意思我掰开来说:
- y —— 你测到的数据,比如基因表达量,一个 n×1 的向量。
- X —— 设计矩阵,n×p 的矩阵,告诉模型每个样本属于什么组、受了什么处理。
- β —— 回归系数,p×1 的向量,代表每个实验条件对表达量的贡献。
- ε —— 误差项,模型没解释掉的那部分随机波动。
我在项目中遇到过有人把模型公式记成 y = a + bx,然后直接套到多组实验上,结果对比矩阵怎么都写不对。嗯,这里要注意:公式里的 X 不是只有一个变量,它可以包含多个列,对应不同的组别、批次、协变量。理解这个,后面的设计矩阵才不会写错。
4.2 设计矩阵:把实验设计翻译成数字
设计矩阵就是把你脑袋里的实验方案,翻译成一堆 0 和 1,让计算机能算。每一行是一个样本,每一列是一个模型参数。
举个例子,三组比较(A、B、C),每组两个样本,用 treatment 编码:
(Intercept) groupB groupC
1 1 0 0
2 1 0 0
3 1 1 0
4 1 1 0
5 1 0 1
6 1 0 1
你看,第一列全是 1,代表截距(通常就是 A 组的均值)。第二列是 B 组 vs A 组的指示变量,第三列是 C 组 vs A 组。这种编码方式在 R 里是默认的,叫“treatment contrast”。我当时刚学的时候,总觉得截距那列是多余的,后来才明白,没有截距,模型就少了一个基准,后面的对比矩阵写起来反而更绕。所以我的建议是:先用默认编码跑通,再考虑别的。
model.matrix(~ group, data = your_data) 就能直接看到设计矩阵长什么样。跑之前先看一眼,避免低级错误。
4.3 对比矩阵:精准提问的工具
设计矩阵搭好了,模型也拟合了,接下来你要回答具体的科学问题。比如:“B 组和 C 组到底有没有差异?” 这时候就需要对比矩阵。
对比矩阵 C 的每一行代表一个你要检验的假设。还拿上面三组(A、B、C)的例子来说,参数 β 有三个:截距(A 组均值)、B vs A、C vs A。如果你想检验“B 组和 C 组均值相等”,对应的对比向量是:
C = [0, 1, -1]
意思就是:B 组的效应减去 C 组的效应,看它是不是显著不为零。我见过有人直接用 groupB - groupC 去做 t 检验,但那样忽略了模型估计的联合方差,结果容易偏。正确做法是在 limma 里用 contrasts.fit 函数,把对比矩阵传进去,它会自动考虑所有参数的相关性。
| 对比类型 | 对比向量(参数顺序:Intercept, groupB, groupC) | 检验问题 |
|---|---|---|
| B vs A | [0, 1, 0] | B 组与 A 组是否有差异 |
| C vs A | [0, 0, 1] | C 组与 A 组是否有差异 |
| B vs C | [0, 1, -1] | B 组与 C 组是否有差异 |
| B 和 C 的均值 vs A | [0, 0.5, 0.5] | 处理组均值是否与对照组不同 |
contrasts.fit 跑一下,它会报错提醒你。
4.4 最小二乘法:怎么找到最佳拟合线
有了模型公式和设计矩阵,下一步就是估计 β。最经典的方法就是最小二乘法——让预测值和真实值的平方差之和最小。
它的闭式解大家应该都见过:
β̂ = (XᵀX)⁻¹ Xᵀ y
在 R 里,一行代码就搞定了:
fit <- lm(y ~ group, data = my_data)
coef(fit)
但我得提醒你,这个公式成立的前提是 XᵀX 可逆。我在项目中遇到过两次设计矩阵“秩亏”的情况:一次是两组样本完全一样,另一次是不小心把截距和所有组别哑变量都放进去了,造成了完全多重共线性。R 会默默地帮你扔掉一些列,但如果你没留意,后面解释系数时就乱套了。所以,跑 lm 之前,我建议先检查一下设计矩阵的秩:
qr(X)$rank
让它等于列数,才是满秩的。
4.5 假设检验:这个效应显著吗?
参数估计出来了,你还得知道它是不是真的可靠。这就轮到假设检验上场。在线性模型里,最常用的是 t 检验和 F 检验。
- t 检验 —— 检验单个回归系数是否显著不为零。比如上面的 B vs A,对应的 β₂ 是不是 ≠ 0。
- F 检验 —— 检验一组系数是否同时为零。比如整个“组别”这个因素是否显著,对应就是所有组别哑变量一起检验。
在 limma 里,t 检验对应 topTable 里的 t 列,F 检验对应 F 列。p 值大家都懂,但注意了,limma 用的是“经验贝叶斯”方法,它会借用所有基因的信息来稳定方差估计。这一点比普通的 lm 要稳健很多,特别是样本量小的时候。
我曾经见过有人把 limma 的 p 值直接用普通的 t 分布去解释,忽略了 limma 内部的方差收缩。结果筛选出来的差异基因一大堆假阳性。嗯,记住:limma 的检验统计量是“ moderated t ”,自由度通常比传统 t 检验大,p 值也更可靠。
这张图就是整个线性模型分析的骨架。从实验设计到最后出结论,每一步都依赖上一步的正确构建。我个人觉得,最卡人的地方往往是设计矩阵和对比矩阵的衔接——只要这两个写对了,模型拟合和假设检验基本上就是自动完成的事。希望这份回顾能让你在后面接触 limma 的实际案例时,心里更有底。