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”。我当时刚学的时候,总觉得截距那列是多余的,后来才明白,没有截距,模型就少了一个基准,后面的对比矩阵写起来反而更绕。所以我的建议是:先用默认编码跑通,再考虑别的。

小技巧:在 R 里用 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 值也更可靠。

核心一句话:模型公式搭骨架,设计矩阵填数据,对比矩阵问问题,最小二乘法算参数,假设检验给结论。五步走,线性模型就没死角了。
线性模型分析核心流程 ① 实验设计 ② 设计矩阵 X ③ 模型公式 y = Xβ + ε ④ 最小二乘法估计 β̂ ⑤ 假设检验 ⑥ 生物学结论 💡 关键提醒 设计矩阵与对比矩阵 是连接实验设计与 统计推断的桥梁。 正确构建它们, 分析就成功了一半。

这张图就是整个线性模型分析的骨架。从实验设计到最后出结论,每一步都依赖上一步的正确构建。我个人觉得,最卡人的地方往往是设计矩阵和对比矩阵的衔接——只要这两个写对了,模型拟合和假设检验基本上就是自动完成的事。希望这份回顾能让你在后面接触 limma 的实际案例时,心里更有底。