第3章 PCA原理:从协方差矩阵到降维
各位同学,欢迎来到第三章。上一章我们聊了聊为什么要做PCA,说白了就是想在成千上万个基因里,找到最能解释差异的那几个方向。这一章,我们来点硬核的——PCA到底是怎么把高维数据「压扁」的?
你可能会问:降维听起来很玄乎,数学原理到底难不难?我个人觉得,理解了协方差矩阵,PCA就搞懂了一半。剩下的就是特征值分解,这个你大学线性代数肯定学过,咱们回忆一下就好。
3.1 为什么要从「协方差」说起?
先想想看,我们手头的数据是什么样子的?一个基因表达矩阵,行为基因,列为样本。每个样本就是一个点在N维空间里(N是基因数)。我们想找的是:哪些方向的数据波动最大?
波动,在统计上就是方差。但光看每个基因自己的方差还不够——基因之间还有相关性。A基因表达高了,B基因是不是也跟着高?这种协同变化的量,就是协方差。
我个人习惯这样记:
- 方差:一个变量自己跟自己「闹腾」的程度
- 协方差:两个变量一起「闹腾」的程度
如果所有基因都是独立的,那PCA就很简单了——我们只需要把方差大的基因挑出来就行。但现实中的基因表达数据,相关性很复杂。所以才需要协方差矩阵这个工具,把所有的成对关系打包在一起。
核心理解:协方差矩阵是一个对称矩阵,对角线上是每个基因的方差,非对角线上是基因两两之间的协方差。它在PCA中扮演了「信息地图」的角色。
3.2 协方差矩阵的数学形式
假设我们有m个样本,n个基因。数据矩阵X是m×n的,每一列是中心化后的一个基因(减去均值)。那么协方差矩阵C就是:
C = (1/(m-1)) * X^T * X
C是一个n×n的对称矩阵
第i行第j列 = cov(gene_i, gene_j)
对角线元素 = var(gene_i)
为什么是n×n?因为我们要考察所有基因两两之间的关系。如果你有2万个基因,这个矩阵就是2万×2万的——非常大,但对计算机来说不是问题。
我记得有次项目里,有人直接拿原始表达矩阵算协方差,忘记做中心化了。结果跑出来的PCA前几个主成分全是均值偏移,根本没法用。嗯,这里要注意——中心化是必要前提。
避坑指南:计算协方差矩阵前,一定先对每个基因(或变量)做中心化处理,即减去均值。不做中心化,第一主成分会被平均数「带偏」。
3.3 特征值和特征向量:PCA的灵魂
现在我们有了协方差矩阵C。下一步是什么?特征值分解。
你可能会问:为什么偏偏是特征值和特征向量?
这其实很直观。特征向量代表了数据变化的方向,特征值代表了在这个方向上变化的「强度」。PCA做的事情,就是从协方差矩阵中提取出这些方向和强度,然后按强度排序。
| 概念 | 在PCA中的含义 |
|---|---|
| 特征向量 | 主成分的方向(即新坐标轴) |
| 特征值 | 该方向上的方差(即信息量) |
| 特征值越大 | 该方向越重要,数据差异越明显 |
具体数学上:
C * v = λ * v
其中:
C — 协方差矩阵
v — 特征向量(列向量)
λ — 对应的特征值
小技巧:在Python中用numpy做特征值分解很容易。我习惯用 np.linalg.eigh 而不是 eig,因为协方差矩阵是对称的,用 eigh 更稳定、更快。
3.4 从特征值到降维:保留多少成分?
好,特征值分解做完了。我们得到了n个特征向量(方向)和n个特征值(方差大小)。那么问题来了——我们要保留几个主成分?
做法很简单:把所有特征值从大到小排序,然后计算累积解释方差比例。
解释方差比例 = λ_i / sum(λ)
累积解释方差比例 = (λ_1 + λ_2 + ... + λ_k) / sum(λ)
通常,我们会选前k个主成分,使得累积解释方差比例超过一个阈值,比如80%或90%。
我曾经遇到一个情况:某个实验数据的前两个主成分就解释了95%以上的方差。这说明什么?说明这个数据其实「低维结构」很明显,基因表达差异被少数几个关键因子主导。反过来,有些数据前十主成分才勉强达到70%,说明噪声很大,或者群体结构不清晰。
所以我说,PCA不仅是一种降维方法,更是一种诊断工具。通过看解释方差的变化曲线(Scree Plot),你能判断数据的内在复杂性。
实用经验:在差异表达分析中,如果前两个主成分解释的方差不到30%,你得小心了——数据可能没有明显的分组结构,或者批次效应太强。这是我踩过的坑,分享给大家。
3.5 降维的几何意义:投影
现在我们已经选好了前k个特征向量(主成分方向)。下一步就是把原始数据「投影」到这些新轴上。
投影的数学就是矩阵乘法:
Y = X * W
其中:
X — 中心化后的原始数据矩阵 (m×n)
W — 前k个特征向量组成的矩阵 (n×k)
Y — 降维后的数据矩阵 (m×k)
这么一乘,每个样本就从n维空间,降到了k维空间。而且,这k维空间里,样本的分布保留了原始数据中最大的方差结构。
换句话说,我们做了一件很聪明的事情:扔掉的信息,都是「不重要」的信息。
注意:投影后,各主成分之间是正交的(不相关),这也是为什么PCA能消除多重共线性的原因。但这个正交性只保证在全局方差上最优,不代表每个主成分都有生物学含义——它们只是数学构造。
3.6 PCA的核心知识结构
这张SVG图我花了不少心思整理,把PCA从数据准备到降维输出的全过程串了起来。你看一眼就能理解整个流程。
3.7 实际操作中的几个关键抉择
PCA的原理说完了,但很多人在实际操作中会犹豫。我列几个最常见的问题和我的建议:
- 要不要做标准化(scaling)?——如果基因的表达量范围差异太大(比如有的基因几万,有的几百),建议先标准化到均值为0、标准差为1。否则,PCA会被高表达量的基因「绑架」。
- 特征值分解 vs SVD?——其实是一回事。PCA可以用SVD(奇异值分解)实现,数值稳定性更好。我个人在Python里常用
sklearn.decomposition.PCA,它底层用的就是SVD。 - 怎么判断k值?——看Scree Plot的「拐点」是一个经典方法。但我更建议结合任务来看:如果目的是去除批次效应,可能前10-20个主成分就够了;如果是为了可视化,直接取前2个或3个。
实用建议:在做差异表达分析前,我总会先从PCA图看一遍数据。如果对照和实验组在PC1上有明显分离,说明差异信号足够强,后续差异基因筛选会轻松很多。如果两组混在一起,那你得想想——是不是样品出了问题?或者差异本来就微弱?