PCA 得分图怎么画怎么读
跟完这篇,您能把一张表达矩阵压成一张主成分得分图,在坐标轴上标清每个主成分解释了多少方差,并且学会从这张图上读出三件事:样本按分组分没分开、有没有离群样本、是不是被批次效应带偏了。
PCA 得分图(PCA score plot)是拿到表达矩阵后最先该画的一张图。它把成千上万个基因浓缩成两个坐标,一眼就能看出「样本之间整体像不像、按什么分开」。它不需要您先设定分组,因此谁和谁聚在一起是数据自己说的话——这正是它做质控、找问题最有用的地方。
先弄明白:PCA 在做什么
每个样本原本住在一个「几万维」的空间里——一个基因就是一维,坐标就是这个基因的表达量。这没法画,也没法看。PCA(主成分分析)做的事,是换一套新坐标轴:新轴是一批基因的线性组合,并且按「能解释多少差异」从大到小排好序。
- 第一主成分(PC1):所有样本在这个方向上散得最开,抓住了数据里最大的一份变异。
- 第二主成分(PC2):在与 PC1 垂直的前提下,抓住剩下变异里最大的一份。
- 后面的 PC3、PC4……依此类推,越往后解释的方差越少。
得分图,就是把每个样本投影到 PC1、PC2 这两根新轴上画成散点。每个点是一个样本(注意:不是基因,这点和火山图、热图正好相反)。
| 术语 | 是什么 |
|---|---|
| 主成分(PC) | 一批基因的线性组合,构成新坐标轴 |
| 得分(score) | 某个样本在某个主成分上的坐标,即图上的点 |
| 载荷(loading) | 每个基因对某个主成分的贡献权重 |
| 方差解释率 | 该主成分抓住了整体变异的百分之几 |
一句话记住:PC1、PC2 是数据自己找出的「差异最大的两个方向」,得分图就是样本在这两个方向上的位置。
画之前:准备一张合适的矩阵
同一批数据,喂进去的值不对,画出来的图能完全误导人。三个前提:
- 用归一化、转换后的表达值,别用原始 counts。 直接拿 raw counts 画,PC1 往往只反映「文库大小」——测得深的样本天然被推到一边,压根不是生物学差异。转录组的标准做法是先做方差稳定转换:DESeq2 的
vst()或rlog(),或退一步用log2(TPM+1)。为什么必须转换、各单位怎么选,见 count / TPM / FPKM / CPM 该用哪个。 - 常只取变化最大的一批基因。 几万个基因里,大多数在样本间几乎不变,只是噪声。习惯上先挑方差最大的前若干个(如 top 500~2000)再做 PCA,让信号更干净。这是常用惯例,数目按数据调,不是铁律——DESeq2 内置的
plotPCA()默认就取 top 500。 - 要不要 scale(把每个基因标准化到单位方差)? 居中(center)几乎总要做,
prcomp默认就做了。是否再 scale 看情况:转录组沿用 DESeq2 的惯例通常不额外 scale,让高变基因多出点力;若您的变量量纲差别很大(比如把不同类型的指标混在一起),才需要 scale 让它们等权。
prcomp要求「样本在行、变量(基因)在列」,和表达矩阵「基因在行、样本在列」的常规摆法正好相反。所以下面代码里会先t()转置,这一步最容易漏,漏了图就全反了。
三步画出得分图
选高变基因 → 跑 prcomp → 取前两个主成分画散点。
library(ggplot2)
# mat:行 = 基因,列 = 样本,值是 vst / rlog 或 log2(TPM+1) 等转换后的表达量
# group:与 mat 各列一一对应的分组向量(如 c("Ctrl","Ctrl","Treat","Treat"))
# 一、只保留变化最大的 500 个基因(常用惯例,数目按数据调)
vars <- apply(mat, 1, var)
top <- names(sort(vars, decreasing = TRUE))[1:500]
mat_top <- mat[top, ]
# 二、跑 PCA:prcomp 要求「样本在行、变量在列」,所以先转置
pca <- prcomp(t(mat_top), center = TRUE, scale. = FALSE)
# 三、取前两个主成分的得分,拼上分组,画散点
scores <- data.frame(pca$x[, 1:2], group = group)
ggplot(scores, aes(PC1, PC2, color = group)) +
geom_point(size = 3) +
theme_bw()
pca$x 就是各样本在每个主成分上的得分,列名正是 PC1、PC2……直接拿来当坐标即可。到这一步图已经出来了,但还差最关键的一步——把坐标轴的分量标上去。
方差解释率:一定要标在坐标轴上
一张不标方差解释率的 PCA 图是残缺的。同样是「两组沿 PC1 分开」,PC1 解释 60% 和 PC1 只解释 3%,意义天差地别:前者说明分组是数据里最大的差异,后者说明这点分开其实微不足道。
方差解释率 = 该主成分的方差 ÷ 所有主成分方差之和:
# 每个主成分解释的方差比例(百分数)
percent <- pca$sdev^2 / sum(pca$sdev^2) * 100
# 拼进坐标轴标签,保留一位小数
xlab <- sprintf("PC1 (%.1f%%)", percent[1])
ylab <- sprintf("PC2 (%.1f%%)", percent[2])
ggplot(scores, aes(PC1, PC2, color = group)) +
geom_point(size = 3) +
labs(x = xlab, y = ylab, color = NULL) +
theme_bw()
读这两个百分比:
- 越高,这根轴越「重要」。 PC1 通常远大于 PC2,所以横向的分开比纵向更值得看重。
- 两根轴加起来若只占很小一部分(比如 PC1+PC2 才 20%),说明主要结构没落在前两维,光看这张图会漏掉东西,可以再看看 PC3,或画一张碎石图(scree plot,把各 PC 的方差解释率从大到小排出来)判断该看几个主成分。
如果您用 DESeq2,
plotPCA(vsd, intgroup = "condition")一行就能出图,它默认取 top 500 高变基因、并把方差解释率写进坐标轴。想自己调样式,加returnData = TRUE拿到坐标数据,再用 ggplot 画。
怎么读:样本分组分没分开
这是 PCA 得分图最主要的用途。理想的图长这样:同组样本抱成一团,不同组沿着 PC1(或 PC2)明显分开。 这说明您关心的分组差异,正是整个数据里最大的变异来源——后续做差异分析心里就有底了。
因为 PCA 是无监督的(它画图时并不知道谁是处理、谁是对照),所以这种分开是「数据自己认的」,比先挑差异基因再聚类那种循环论证要可信得多。
| 得分图长什么样 | 通常意味着 |
|---|---|
| 同组聚拢、组间沿 PC1 分开 | 生物学差异是最大变异来源,最理想 |
| 要到 PC2 / PC3 才分开 | 有更大的非目标变异(常是批次)压在前面 |
| 各组完全混在一起 | 组间信号弱,或被噪声、批次淹没 |
| 一两个点孤零零飞在外圈 | 离群样本,先查它的 QC |
要留个心眼:分不开不等于实验失败。 有时候真实差异确实细腻、只涉及少数基因,在「抓最大变异」的 PCA 上就不显眼。这时别急着下结论,交给差异分析去逐个基因地找,见 DESeq2 差异表达分析怎么做对。反过来,也别只盯着 PC1、PC2——目标差异偶尔藏在 PC3 上。
揪出离群样本与批次效应
得分图的另一半价值,在于把「藏在数据里的毛病」摆到明面上。
离群样本
一个点远离所有样本、尤其是远离自己那一组,就值得怀疑。常见原因:文库质量差、样本弄混贴错标签、污染、建库或测序失败。
处理的分寸很重要:先查、后删,绝不能因为它「碍眼」就删。 回到这个样本的 QC 指标看——比对率、文库大小、检出基因数是不是异常;确认确实是技术问题,再决定剔除,并在文中如实说明。凭直觉删离群点,本质上是在挑数据。
批次效应
把同一张图按批次上色、按分组用形状重画一遍,两个因素一起看:
# batch:与样本一一对应的批次信息(测序批次 / 日期 / 试剂批号 / 操作者等)
ggplot(scores, aes(PC1, PC2, color = batch, shape = group)) +
geom_point(size = 3) +
theme_bw()
如果样本是按批次而不是按分组聚在一起——同一批的挨着、不管是处理还是对照——那就是典型的批次效应:技术上的批次差异盖过了您关心的生物学差异。怎么办:
- 建模时把批次放进设计公式,让统计模型替您扣掉它,比如 DESeq2 里写
design = ~ batch + condition。 - 或用
limma::removeBatchEffect()、sva::ComBat()先校正,再画一次 PCA 确认批次结构是否被压下去。
一个设计阶段就要守住的红线:别让批次和分组完全混杂。 如果所有处理样本都在批次一、所有对照都在批次二,那批次差异和处理差异就彻底缠死了,事后任何方法都分不开。分组时把每个批次里的处理和对照打散安排,才留得住补救的余地。
顺带一提,PCA 不只用于转录组的批量样本。单细胞流程里,降维聚类之前也要先跑一次 PCA(Seurat 的 RunPCA),再据此选主成分做 UMAP 和聚类,思路一脉相承,见 单细胞分析全流程。
常见误区
| 误区 | 后果 | 怎么避免 |
|---|---|---|
| 拿原始 counts 直接画 | PC1 基本反映文库大小,不是生物学 | 先做 vst / rlog 或 log2(x+1) |
| 忘了转置矩阵 | 把基因当样本、图完全读不对 | prcomp 前 t(),让样本在行 |
| 不标方差解释率 | 读者不知道这点分开重不重要 | 轴标签写上 (xx%) |
| 只看 PC1 vs PC2 | 目标差异可能藏在 PC3 | 也看看 PC3,或画碎石图 |
| 把分不开当「实验失败」 | 误删本可用的数据 | 差异细腻时交给差异分析找 |
| 离群点碍眼就删 | 等于挑数据,站不住脚 | 先查 QC 找到原因再决定 |
| 批次与分组完全混杂 | 批次与处理再也分不开 | 设计阶段把批次打散 |
在平台上跑一遍
不想写 R 代码也能得到同样的图:在 百沐一下 上传您的表达矩阵和分组表,用一句话说清「画 PCA 得分图,按分组上色,坐标轴标出方差解释率,再按批次换个颜色看看有没有批次效应」,智能绘图就会替您选好高变基因、跑好 PCA 并出一张出版级图,连同可复现的 R 代码一起交付,配色和标注都能对话式再调。出图流程见 智能绘图使用指南。
相关
- 表达矩阵与分组表整理规范 —— PCA 的输入从哪来、分组表怎么和矩阵列名对齐
- 结果怎么读:PCA / 火山 / 热图 / 富集图 —— 拿到 PCA 图后一眼判断「图对不对」
- 热图聚类:注释条与配色全解 —— 另一张常配着 PCA 一起看的探索图
- 智能绘图使用指南 —— 在平台上不写代码画同一张图