4. Seurat 降维:PCA 原理与参数归纳总结
今天我们来聊聊 PCA 这个老朋友。
说实话,在单细胞分析流程里,降维这一步是承上启下的关键。你在前面找到了高变基因,做了标准化,数据已经准备好了。接下来就是让 PCA 上场,把这些高维数据‘压缩’成几个核心维度。
为什么要这么做?说白了,是为了后续的聚类和可视化铺路。高维空间里找邻居,那是灾难级的计算量,而且噪音多。PCA 能帮你把最核心的变异信号提取出来,去伪存真。
4.1 PCA 到底在做一件什么事?
我刚开始用 Seurat 时,对 PCA 的理解停留在‘它能降维’这个层面。后来我自己跑了一些模拟数据,才真正搞清楚它在干嘛。
想象一下,你有三万多个基因的表达量。每个细胞就是一个三万维空间里的点。PK——这个空间太大了,没法直接看。
PCA 的做法很简单:它在高维空间里找一条线(或者一个方向),让所有细胞投影到这条线上后,投影点的方差尽可能大。第一条这样的线就叫 主成分 1 (PC1)。然后,再找第二条与第一条垂直的方向,方差次大,就是 PC2。以此类推。
所以,PCA 本质上是在做 坐标旋转。它不改变细胞之间的相对关系,只是把坐标系转到了一个更能解释数据变异的方向上。
在 Seurat 里,PCA 的过程封装得很好。你只需要一行代码。但理解背后的逻辑,能帮你更好地设置参数。
# Seurat 中运行 PCA 的标准做法
pbmc <- RunPCA(pbmc, features = VariableFeatures(object = pbmc), npcs = 30)
4.2 影响 PCA 结果的关键参数
别看 RunPCA 函数的参数不多,每一个都值得琢磨。我根据项目经验,把最重要的几个参数总结了一下:
| 参数 | 默认值 | 作用 | 我的经验 |
|---|---|---|---|
| npcs | 50 | 计算多少个主成分 | 我一般先设 30-50,后期再降 |
| features | VariableFeatures | 用哪些基因跑 PCA | 建议只用高变基因,别全上 |
| approx | TRUE | 是否用随机 SVD 近似 | 大样本(>10万细胞)建议打开 |
| ndims.print | 5 | 打印前几个主成分的基因 | 调试时用来观察主成分生物学意义 |
这里我重点聊聊 features 和 npcs。
4.2.1 关于 npcs
npcs 决定了你要保留的维度。你可能会想,保留越多不是越好吗?
不完全对。保留太多主成分,会把噪音带进来。后续的聚类和 UMAP 会受这些噪音的干扰。保留太少,又会丢失重要的生物学变异。
我记得有一次,同事在处理一个含有罕见亚群的数据集时,一共用了 50 个 PC,结果那个稀有细胞群在聚类时被硬生生地合并了。后来我们只用了 20 个 PC,那个群就清晰了。为什么?因为那个稀有群在后面的主成分里有信号,但前面的主成分被占大头的细胞群‘淹没’了。
4.2.2 关于 features
另一个容易忽略的是 features。你可能觉得,把所有基因都放进去才能不遗漏信息。但实际效果恰恰相反。
全基因跑 PCA,首先计算量会暴涨。其次,那些非高变基因里大多是噪音,会干扰主成分的计算。
你想想看,如果你有两个细胞亚群,它们的差异主要表现在 50 个高变基因上。而另外 20000 个基因的表达基本一致。你把那 20000 个基因放进去,PCA 第一眼看过去,发现大多数基因都没差异,于是它认为这俩细胞没什么区别。这就把关键的信号稀释了。
VariableFeatures() 筛选后,PCA 的结果才回归到真正的生物学差异上。
4.3 PCA 参数调优的实操建议
下面是我个人的一套标准流程,你可以参考:
- 先用默认参数跑一遍,比如 npcs=30。目的不是调优,而是看看数据的基本结构。这一步可以让你快速了解数据质量。
- 查看 Elbow Plot。Seurat 的
ElbowPlot(pbmc, ndims = 30)可以帮你决定保留几个主成分。注意,不要只看曲线上最陡的那个拐点。那个拐点往往只对应 PC1 到 PC3,太少了。 - 综合考虑生物学意义。我会结合
DimHeatmap来查看不同主成分的基因表达模式。如果某个主成分的基因全是细胞周期或应激基因,我可能会考虑回归这些效应。 - 对稀有细胞群,加大多主成分。如果你的研究目标是稀有细胞群,建议在 Elbow Plot 之后,往前多取 5-10 个主成分。
- 多尝试几个 npcs 值。比如分别试 20、30、40,然后对比聚类结果。选那个在生物学上解释性最好的。
这里提供一个简单实用的代码模式:
# 建议的npcs探索流程
library(Seurat)
# 第一步:原始计算
pbmc <- FindVariableFeatures(pbmc, selection.method = "vst", nfeatures = 2000)
pbmc <- ScaleData(pbmc)
pbmc <- RunPCA(pbmc, features = VariableFeatures(pbmc), npcs = 50)
# 第二步:可视化拐点
ElbowPlot(pbmc, ndims = 50)
# 比如这里看到PC30之后方差变化趋于平稳,我就初定npcs=30
# 第三步:用不同npcs跑聚类,对比结果
pbmc_20pc <- FindNeighbors(pbmc, dims = 1:20) %>% FindClusters(resolution = 0.5)
pbmc_30pc <- FindNeighbors(pbmc, dims = 1:30) %>% FindClusters(resolution = 0.5)
pbmc_40pc <- FindNeighbors(pbmc, dims = 1:40) %>% FindClusters(resolution = 0.5)
# 对比后,我可能选择30,因为它在分离主群和稀有群上表现最好
4.4 PCA 输出结果解读
跑完 PCA 后,Seurat 的 object 里会存储以下关键内容:
| 数据对象 | 存放位置 | 说明 |
|---|---|---|
| Cell Embeddings | pbmc@reductions$pca@cell.embeddings | 每个细胞在每个PC上的坐标 |
| Feature Loadings | pbmc@reductions$pca@feature.loadings | 每个基因对每个PC的权重 |
| Standard Deviation | pbmc@reductions$pca@stdev | 每个PC的标准差,即该PC解释的方差程度 |
| JackStraw p-value | pbmc@reductions$pca@jackstraw@overall.p.values | JackStraw重抽样法计算的PC显著性 |
我个人习惯查看 feature.loadings。通过 top N 的基因,往往能直接看出这个 PC 代表了什么生物学过程,比如是 T 细胞、B 细胞还是应激反应。
4.5 本章知识脉络
为了帮你更直观地掌握 PCA 在单细胞分析中的位置,我画了一张流程示意图:
这张图清晰地展示了 PCA 在 Seurat 分析流程中的输入、计算、输出,以及后续如何根据输出反馈到参数选择上。你做完这一步,后面的聚类和可视化才有意义。
- PCA 本质是坐标旋转,保留最大方差方向
- npcs 和 features 是影响结果最重要的两个参数
- 不要只看 Elbow Plot 的拐点,要结合生物学意义调整主成分数
- 全基因跑 PCA 不可取,稀释信号,引入噪音
- PCA 结果不是一成不变的,需要反复迭代优化
嗯,关于 PCA 的理论与参数,就说这么多。这些经验不是书本上能完全给你的,是我在几十个数据集上反复踩坑之后才总结出来的。你把这套逻辑拿走吧。