跳到正文
百沐一下百沐一下
本节目录

PCA 得分图怎么画怎么读

跟完这篇,您能把一张表达矩阵压成一张主成分得分图,在坐标轴上标清每个主成分解释了多少方差,并且学会从这张图上读出三件事:样本按分组分没分开、有没有离群样本、是不是被批次效应带偏了。

PCA 得分图(PCA score plot)是拿到表达矩阵后最先该画的一张图。它把成千上万个基因浓缩成两个坐标,一眼就能看出「样本之间整体像不像、按什么分开」。它不需要您先设定分组,因此谁和谁聚在一起是数据自己说的话——这正是它做质控、找问题最有用的地方。

先弄明白:PCA 在做什么

每个样本原本住在一个「几万维」的空间里——一个基因就是一维,坐标就是这个基因的表达量。这没法画,也没法看。PCA(主成分分析)做的事,是换一套新坐标轴:新轴是一批基因的线性组合,并且按「能解释多少差异」从大到小排好序。

  • 第一主成分(PC1):所有样本在这个方向上散得最开,抓住了数据里最大的一份变异。
  • 第二主成分(PC2):在与 PC1 垂直的前提下,抓住剩下变异里最大的一份。
  • 后面的 PC3、PC4……依此类推,越往后解释的方差越少。

得分图,就是把每个样本投影到 PC1、PC2 这两根新轴上画成散点。每个点是一个样本(注意:不是基因,这点和火山图、热图正好相反)。

术语是什么
主成分(PC)一批基因的线性组合,构成新坐标轴
得分(score)某个样本在某个主成分上的坐标,即图上的点
载荷(loading)每个基因对某个主成分的贡献权重
方差解释率该主成分抓住了整体变异的百分之几

一句话记住:PC1、PC2 是数据自己找出的「差异最大的两个方向」,得分图就是样本在这两个方向上的位置。

画之前:准备一张合适的矩阵

同一批数据,喂进去的值不对,画出来的图能完全误导人。三个前提:

  1. 用归一化、转换后的表达值,别用原始 counts。 直接拿 raw counts 画,PC1 往往只反映「文库大小」——测得深的样本天然被推到一边,压根不是生物学差异。转录组的标准做法是先做方差稳定转换:DESeq2 的 vst()rlog(),或退一步用 log2(TPM+1)。为什么必须转换、各单位怎么选,见 count / TPM / FPKM / CPM 该用哪个
  2. 常只取变化最大的一批基因。 几万个基因里,大多数在样本间几乎不变,只是噪声。习惯上先挑方差最大的前若干个(如 top 500~2000)再做 PCA,让信号更干净。这是常用惯例,数目按数据调,不是铁律——DESeq2 内置的 plotPCA() 默认就取 top 500。
  3. 要不要 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 就是各样本在每个主成分上的得分,列名正是 PC1PC2……直接拿来当坐标即可。到这一步图已经出来了,但还差最关键的一步——把坐标轴的分量标上去。

方差解释率:一定要标在坐标轴上

一张不标方差解释率的 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()

如果样本是按批次而不是按分组聚在一起——同一批的挨着、不管是处理还是对照——那就是典型的批次效应:技术上的批次差异盖过了您关心的生物学差异。怎么办:

  1. 建模时把批次放进设计公式,让统计模型替您扣掉它,比如 DESeq2 里写 design = ~ batch + condition
  2. 或用 limma::removeBatchEffect()sva::ComBat() 先校正,再画一次 PCA 确认批次结构是否被压下去。

一个设计阶段就要守住的红线:别让批次和分组完全混杂。 如果所有处理样本都在批次一、所有对照都在批次二,那批次差异和处理差异就彻底缠死了,事后任何方法都分不开。分组时把每个批次里的处理和对照打散安排,才留得住补救的余地。

顺带一提,PCA 不只用于转录组的批量样本。单细胞流程里,降维聚类之前也要先跑一次 PCA(Seurat 的 RunPCA),再据此选主成分做 UMAP 和聚类,思路一脉相承,见 单细胞分析全流程

常见误区

误区后果怎么避免
拿原始 counts 直接画PC1 基本反映文库大小,不是生物学先做 vst / rloglog2(x+1)
忘了转置矩阵把基因当样本、图完全读不对prcompt(),让样本在行
不标方差解释率读者不知道这点分开重不重要轴标签写上 (xx%)
只看 PC1 vs PC2目标差异可能藏在 PC3也看看 PC3,或画碎石图
把分不开当「实验失败」误删本可用的数据差异细腻时交给差异分析找
离群点碍眼就删等于挑数据,站不住脚先查 QC 找到原因再决定
批次与分组完全混杂批次与处理再也分不开设计阶段把批次打散

在平台上跑一遍

不想写 R 代码也能得到同样的图:在 百沐一下 上传您的表达矩阵和分组表,用一句话说清「画 PCA 得分图,按分组上色,坐标轴标出方差解释率,再按批次换个颜色看看有没有批次效应」,智能绘图就会替您选好高变基因、跑好 PCA 并出一张出版级图,连同可复现的 R 代码一起交付,配色和标注都能对话式再调。出图流程见 智能绘图使用指南

相关