1. 先搞清楚:PCA在转录组分析里到底扮演什么角色
1.1 PCA算的到底是什么
做转录组分析的同学,十有八九都画过那张经典的PCA图——样本在二维平面上一颗一颗散开,组间分开了就长舒一口气,分不开就开始焦虑,甚至怀疑自己整个实验出了问题。PCA(主成分分析)几乎是转录组数据下机后第一个真正“看到”全貌的步骤,也是质控报告里最常被截图发到群里讨论的图。但我这几年经手的数据里,十个PCA图至少有四五个存在分析流程上的硬伤,问题不在数据本身,而在处理方式。
先澄清一个基础概念:PCA全称主成分分析,核心逻辑是把高维数据投影到方差最大的几个方向上。转录组数据动辄两万多个基因,每个样本就是一个两万多维的点,人眼根本没法直接看。PCA做的就是把这堆高维坐标压缩成二维或三维,同时尽可能保留样本之间的相对距离,也就是样本间的差异结构。PC1是方差最大的方向,PC2是与PC1正交且方差次大的方向,以此类推。
但这里有个容易被忽略的点:PCA不关心“分组标签”。它不知道你的样本是处理组还是对照组,它只根据表达矩阵的方差结构找主方向。所以PCA图上的分组信息,完全是数据自己“长”出来的。这也是为什么PCA特别适合做质控:如果样本处理、建库、测序过程有问题,或者批次效应严重,样本就会按照这些技术因素聚在一起,而不是按生物学分组聚在一起。可以说,PCA图是转录组数据的第一面“照妖镜”。
1.2 三个误区为什么频发
问题在于,PCA虽然是一个经典的多元统计方法,但在转录组这个高维、高噪声、强批次效应的场景里,它的行为模式和教科书上的经典案例差别很大。我见过太多人直接拿默认参数的prcomp()函数怼表达矩阵,甚至有人拿raw count就跑,然后对着屏幕上那团乱麻的PCA图发呆,来来回回换颜色、换形状,就是找不到原因。
转录组PCA分析最常见的三个误区——数据标准化、离群样本处理、批次效应处理——其实是整个分析流程里三个连续的“关卡”,每一关处理不当,结果都会失真。而且它们互相影响:标准化没做好,离群样本就容易被放大;离群样本没处理,批次效应就容易被掩盖。所以这篇文章我按分析顺序来讲,每一关都给出可落地的判断标准和操作方案。不管你是刚接触转录组的新手,还是已经被数据折磨过几轮的“老油条”,照着这个思路排查,基本能把PCA图上的绝大多数“灵异现象”解释清楚。
2. 误区一:数据标准化没做好,PC1会被高表达基因“绑架”
2.1 不标准化的后果:高表达基因主导主成分方向
先看一个最常见的翻车现场:把原始count矩阵或者raw CPM矩阵直接喂给prcomp()。不夸张地说,这一步做错,后面看PCA图基本上是在看“假象”。
转录组数据的根本特点是动态范围极大。一个样本里,表达量最高的基因(比如某些核糖体蛋白基因、管家基因)的count可能达到几万甚至几十万,而大多数基因的count只有几十到几百。如果不做任何处理,PCA的方差计算会被这些高表达基因主导——PC1的方向几乎完全由少数高表达基因决定,而真正体现样本差异的中低表达基因的贡献被淹没了。
我用一个生活化的类比来解释这个问题。假设你要分析一个班级同学的“特点差异”,给每个同学记录了身高、体重、鞋码,也记录了课外阅读量、社团活动参与度。如果直接把这些数值放一起做PCA,“身高体重鞋码”这种动态范围大的指标会主导结果,最后你会发现PC1基本等于“体型轴”,而真正把同学区分开的“兴趣爱好”差异根本体现不出来。转录组里的高表达基因就相当于身高体重,低表达但差异显著的基因相当于兴趣特长——如果不做标准化,你看到的永远是“体型”,而不是“性格”。
2.2 定量单位的坑:raw count、CPM、TPM、FPKM别混用
这是我在帮人看数据和审稿时遇到最多的问题之一。不同来源的转录组数据,定量单位可能不一样,很多人没注意就直接合并开跑:
- raw count:测序得到的原始read数,受测序深度影响大,同一个基因在不同样本里的count不能直接比。
- CPM/CPM:counts per million,按总read数归一化,简单粗暴,但没考虑基因长度和组成偏好。
- TPM:transcripts per million,同时考虑了总read数和转录本长度,跨样本可比性比CPM好。
- FPKM/RPKM:早期使用的长度归一化单位,现在做差异分析基本不推荐了。
- log2-CPM、vst、rlog:转换后的数值,适合下游统计建模和可视化。
如果用不同单位的数据混在一起跑PCA,比如一个公共数据集的样本是TPM,另一个数据集是raw count,那PCA图上样本的分布会先按“数据来源单位”分开,而不是按生物学分组分开。这个现象特别坑,因为看起来分组很清楚,但分的是“错误的分组”——你以为是生物学差异,其实是定量方式的差异。
实际处理时我的建议是:如果要做PCA,最好回到count矩阵层面,用DESeq2的vst()或rlog()做方差稳定化转换,再跑PCA。这两种转换专门为count数据设计,能把低表达基因的噪声压下来,同时保持高表达基因的贡献合理。如果只能拿到TPM或CPM,那就先取log2,再加一个小的伪计数(比如log2(TPM + 1)),避免0值取对数报错,也别用原始值直接上。
2.3 要不要scale:一个被忽略的重要参数
接下来是prcomp()里那个经常被忽略的scale参数。这个参数我几乎每次讲课都会重点提,因为它对转录组数据的影响实在太大了。
默认情况下很多教程的代码是prcomp(t(log_data), scale. = FALSE),但这个选择对转录组数据影响非常大。scale. = TRUE的意思是对每个基因(变量)做z-score标准化,即减均值除以标准差,让所有基因的方差变成1。如果scale. = FALSE,则保留基因原有的方差结构——这时候高表达、高方差的基因仍然会主导主成分。
那么转录组PCA到底该不该scale?我的经验是:在做了vst/rlog转换之后,一般不需要再scale,因为vst已经做了方差稳定化,基因间的方差已经处于可比尺度;但如果你的数据是log2-CPM这种比较粗糙的转换,建议考虑scale,否则结果容易被个别极端高变异的基因带偏。
另外一个更根本的判断标准是:如果你的目的是看样本的“绝对表达水平”差异——比如某个样本整体表达量低,或者某组样本普遍低表达——就不要scale,让“总表达量”这个信息保留在主成分里;如果你的目的是看样本的相对表达模式,scale更稳妥。做整合分析、跨平台比较时,通常建议scale。
这里必须强调一个隐蔽的错误:scale是对基因维度做,不是对样本维度做。有些人写代码时忘了转置,把样本当变量做了scale,结果PCA图完全是错的。这个错法很隐蔽,因为图也能画出来,但每个点的含义完全不同——相当于你把每个样本的“整体表达谱形状”硬掰成了均值0方差1,然后拿形状差异当成了差异来源。我见过不止一个项目因为这个细节导致PCA分组结果完全失真。
2.4 推荐流程:一套可以直接用的标准化代码
下面这套流程是我在实际项目里反复用的标准操作,可以直接抄作业:
library(DESeq2) # 假设 count_matrix 是行为基因、列为样本的整数计数矩阵 # colData 包含样本分组信息和批次信息 dds <- DESeqDataSetFromMatrix(countData = count_matrix, colData = colData, design = ~ batch + condition) # 方差稳定化转换,blind=TRUE 表示只基于所有样本估计离散度 vsd <- vst(dds, blind = TRUE) # 提取转换后的表达矩阵 expr_mat <- assay(vsd) # PCA pca_res <- prcomp(t(expr_mat), scale. = FALSE) # 查看主成分解释率 summary(pca_res) # 提取PC坐标并绘图 pca_df <- as.data.frame(pca_res$x) pca_df$sample <- rownames(pca_df) pca_df$group <- colData$condition[match(pca_df$sample, rownames(colData))] library(ggplot2) ggplot(pca_df, aes(x = PC1, y = PC2, color = group, label = sample)) + geom_point(size = 3) + ggrepel::geom_text_repel(size = 3) + theme_classic() + labs(x = paste0("PC1 (", round(summary(pca_res)$importance[2, 1] * 100, 1), "%)"), y = paste0("PC2 (", round(summary(pca_res)$importance[2, 2] * 100, 1), "%)"))这里有几个细节要提醒。blind = TRUE的意思是计算离散度时不考虑分组信息,这样得到的vst矩阵是“无偏”的,适合做PCA和样本聚类;如果blind = FALSE,会把分组信息“泄露”进转换过程,PCA的分组效果会被人为放大,看起来很好看但不真实,后续下游分析也会受影响。
另外我在图上加了ggrepel的标签,方便直接看到每个点的样本名。建议画PCA图时永远加上样本标签,排查问题会方便得多。如果样本数很多(几百个),标签会重叠,可以只标离群样本,或者用plotly做交互式绘图。最后,PC轴标签上的解释率一定要保留,很多审稿人会看这个数字判断批次效应和分组分离的合理性。
3. 误区二:看到PCA离群样本就删,你很可能扔掉了重要信息
3.1 先判断离群性质:生物学差异还是技术伪影
PCA图上最常见的“惊悚画面”是:大多数样本聚成一团,角落里孤零零飘着一个点。很多人的第一反应是“这个样本有问题,删掉重跑”。这个反应可以理解,但太草率了——因为离群样本有两种截然不同的来源,处理方式完全不同。
第一种是技术性离群。样本RNA质量差(RIN值低)、建库失败、测序深度异常低、样本污染——这些技术问题会导致该样本的表达谱和同组其他样本差异巨大。这种离群样本应该处理,甚至剔除。
第二种是生物学离群。样本本身没质量问题,但它确实跟其他样本不一样。比如研究某种疾病时,一个病人恰好有特殊的并发症状;或者细胞实验中,某个培养皿发生了自发分化。这种离群点不是“坏数据”,而可能是一个重要的生物学发现线索。
这两个情况在PCA图上看不出来区别——都是孤零零一个点。所以我的原则是:看到离群样本,先不要动,先做证据收集。在动手删之前,至少要把下面这些信息拉出来看一眼。
3.2 离群样本的量化识别方法
光靠肉眼在PC1-PC2平面上判断,容易漏掉或误判。因为某些离群效应可能只在PC3、PC4上体现,而PC1-PC2图上看起来一切正常。我的做法是综合以下几个指标来识别:
- 样本间的欧氏距离或相关性:计算所有样本两两之间的Pearson相关性,如果某个样本与同组其他样本的平均相关性显著低于整体水平,就标记出来。
- Cook's distance:DESeq2的
results()输出里就带这个指标。它衡量的是“如果把某个样本剔除,整体拟合结果变化多大”。一般建议Cook's distance大于0.5的样本需要警惕。 - 表达谱层面的质控指标:比对率、基因检出数、rRNA比例、ERCC spike-in回收率。这些是建库测序质量的客观反映。
- 多个主成分的投影:别只看PC1-PC2,还要看PC3-PC4、PC5-PC6的图。有些样本在PC1-PC2上看不出离群,但在后面的主成分上会暴露出来。
我写过一个简单的小脚本,用来批量检查样本相关性:
# 基于vst后的表达矩阵 expr_mat <- assay(vsd) # 计算样本间相关性 cor_mat <- cor(expr_mat, method = "pearson") # 每个样本与其他样本的平均相关性 avg_cor <- colMeans(cor_mat) # 同组内的平均相关性 group_cor <- sapply(unique(colData$condition), function(g) { idx <- which(colData$condition == g) mean(cor_mat[idx, idx][upper.tri(cor_mat[idx, idx])]) })如果发现某个样本的平均相关性明显低于同组其他样本,我就会把它列为“候选离群样本”,进入下一步排查。这个脚本很好用,但要注意:相关性矩阵对样本量敏感,样本太少时不要过度解读数值差异。
3.3 保留、修正还是剔除:一条可执行的决策路径
做完证据收集后,我的决策路径一般是这样:
- 如果技术指标异常(RNA质量差、比对率低、测序深度严重不足),剔除或重测。写文章时要报告剔除理由。
- 如果技术指标正常,但有明确的外部证据表明样本搞混了或者被污染,剔除并在方法部分说明。
- 如果技术指标正常,也没有污染证据,但样本就是离群——不急着删。先看看去掉这个样本后,差异表达分析的关键结论是否改变。如果没有改变,说明它对结论影响不大,可以保留(在图里用不同形状标注出来);如果改变了,那就深挖背后的生物学原因。
- 如果同组里好几个样本都“离群”,那可能不是单个样本的问题,而是这批样本的“组标签”本身就有问题——比如收集样本时把不同亚型混在一起了。这时候该做的是重新审查样本的临床信息或处理条件,而不是逐个删点。
实操中还要注意一个反向的坑:不要因为“删掉某个样本后PCA图变得更好看”就删。PCA图好看不是保留样本的理由,样本取舍的唯一依据是样本质量是否可靠、是否符合研究目的。我见过有人为了把组间分开,把分得不好的样本全删了,最后剩下一组“漂亮”的样本,但这本质上是数据造假。审稿人一旦发现,整个文章的可靠性都会打折扣。真的要删,必须有客观依据,而且要在文章里如实报告。
3.4 实操案例:一个离群样本的完整排查过程
说一个真实项目。当时做一批肿瘤样本和正常样本的转录组测序,PCA图上正常组12个样本聚得很好,肿瘤组14个样本里有1个飘到了正常组附近。第一反应是样本标签是不是搞反了。
排查过程分三步走。第一步,查质控报告。这个样本的RIN值是9.2,比对率92%,基因检出数和其他肿瘤样本在一个水平——技术质量没问题。第二步,查临床信息。翻原始记录发现,这个样本对应的病人肿瘤纯度特别低,病理报告写着“肿瘤细胞占比约15%,大量炎症细胞浸润”。第三步,做个简单验证。把肿瘤组的样本按肿瘤纯度排序,再和这个离群样本的表达相关性做对比,结果发现PCA图上离群样本的位置和“低纯度肿瘤样本”这个特征高度吻合。
最后结论:这个样本不是坏样本,它揭示了一个生物学事实——肿瘤纯度低导致转录组特征偏向微环境,甚至接近正常组织。我们没有删它,而是在文章里做了亚组分析,把高纯度和低纯度的肿瘤样本分开比较。这个处理方式比简单粗暴地删除有价值得多。所以每次有人问我“离群样本能不能删”,我都会反问一句:它为什么离群?你先查清楚这个“为什么”,再决定删不删。
4. 误区三:批次效应处理太晚,PCA图分不清组其实不是组的问题
4.1 批次效应为什么在PCA里“显形”
第三个误区是批次效应处理时机不对。PCA本身不产生批次效应,但它是暴露批次效应最直观的工具。如果样本的建库批次、测序flow cell、上机日期不同,即使同一批生物学样本,也会因为技术差异在表达谱上产生系统性的偏移。这种偏移在PCA图上表现为:样本没有按生物学分组聚在一起,而是按批次聚在一起。
为什么批次效应这么“扎眼”?因为技术批次往往影响多条通路的大量基因——比如建库时间不同导致的试剂批号差异,可能让几百个基因的表达量同时发生小幅度偏移。单个基因的偏移可能不显著,但这几百个基因的“协同偏移”累积起来,方差非常大,PCA的前几个主成分很容易被这种技术性方差主导。
一个典型的现象:PCA图上每个批次都聚成一团,而生物学分组反而被打散了。很多新手看到这种图还在纠结“怎么分组分不开”,其实问题根本不是分组,而是批次效应盖过了生物学信号。遇到这种情况,先别急着调颜色、调形状,先按批次上色看看。如果按批次分得清清楚楚,那就基本实锤了。
4.2 检测批次效应的正确姿势
批次效应的检测不能只看PC1-PC2。虽然大多数时候批次效应对PC1的贡献最大,但也可能出现批次效应“藏”在PC3、PC4里的情况。我自己常用的检测方法是:
- 把PCA图的颜色改成按批次上色,看看样本是否按批次聚集。
- 用PC1-PC10的坐标做线性判别分析(LDA)或者随机森林,看能否仅凭前几个主成分就准确预测出“批次标签”。如果准确率很高,说明批次信息在表达数据里很强。
- 计算每个批次内的样本方差和批次间的样本方差,做一个简单的方差分解,量化批次因素对整体方差的贡献比例。
有人会问,是不是所有项目都要做批次效应检测?我的意见是:只要样本来自不同的建库批次、不同的测序lane或flow cell、不同时间提取的RNA,就默认存在潜在批次效应,必须检测。尤其是公共数据集的整合分析,更是重灾区。很多人下载TCGA、GEO的数据合并分析,不检查批次就直接跑差异,结果找出来的“差异基因”一大半是批次差异,这在生信圈已经是老生常谈的翻车现场了。
检测的代码很简单:
# 用vst后的表达矩阵,检查批次和分组对前几个PC的解释程度 pca_df <- as.data.frame(pca_res$x[, 1:10]) # 计算批次对前5个PC的解释程度,用ANOVA for (pc in 1:5) { fit <- lm(pca_df[, pc] ~ colData$batch) print(paste0("PC", pc, " batch R2: ", round(summary(fit)$r.squared, 3))) }如果某个PC的batch R2很高,比如超过0.3,说明批次信息在数据里占主导,必须处理。
4.3 去除批次效应的常用方法对比
确认存在批次效应之后,选择哪种方法去除?我在实际项目里常用四个方案,各有适用场景。下面这个表格是我自己整理对比的:
| 方法 | 适用数据 | 优点 | 缺点 |
|---|---|---|---|
| limma::removeBatchEffect() | 归一化后的表达矩阵 | 快、简单、参数少 | 线性假设,会损失部分真实信号 |
| ComBat(sva包) | 芯片数据、bulk RNA-seq | 经验贝叶斯稳健,小样本表现好 | 对非线性批次效应效果一般 |
| ComBat-seq(sva包) | RNA-seq count数据 | 保留count离散结构,适合后续差异分析 | 计算量大,内存占用高 |
| RUVSeq | 各类数据 | 不依赖批次标签,可处理未知批次 | 负对照基因选择很关键,选不好引入偏差 |
选择方法的一条核心原则:不要把批次效应校正当成一个“必选动作”。如果批次效应和生物学分组完全混淆(比如处理组都是第一批次,对照组都是第二批次),任何方法都无法真正分开“技术差异”和“生物学差异”。这种情况要从实验设计上解决,不是事后用算法能补救的。我帮人看数据时,遇到这种混淆设计,只能建议补样本或者重新设计实验。
ComBat-seq的代码也很简单:
library(sva) # 输入为count矩阵,batch为批次向量,group为分组向量 adjusted_counts <- ComBat_seq(counts = count_matrix, batch = batch_info, group = condition)运行完ComBat-seq之后,建议把校正后的count矩阵重新用vst转换,再跑一次PCA确认效果。如果批次效应被有效去除,PCA图上样本应该按生物学分组聚类,而不是按批次聚类。
4.4 批次效应处理的“黄金时间”:必须在PCA之后、下游分析之前
一个我在实际中反复强调的点:批次效应处理要在PCA“暴露问题”之后尽快做,在差异分析之前必须完成。为什么这么说?因为PCA图本身就是用来检查批次效应的质控工具。如果先做PCA、发现问题、然后校正、再跑一次PCA验证,这是标准流程;但如果PCA已经确认了批次效应,却不做任何校正,直接拿原始表达矩阵去做差异分析,那结果就是被批次效应污染的。
正确的流程是:
- 拿到count矩阵,先做基础QC:过滤低表达基因、检查样本质控指标。
- 用vst或rlog做转换,跑第一轮PCA——这时候PCA的目的是“暴露问题”,包括离群样本和批次效应。
- 根据第一轮PCA的结果,处理离群样本和批次效应。
- 处理完后再跑一次PCA,确认问题是否消除、样本是否按生物学分组聚类。
- 用校正后的矩阵做差异表达分析、聚类、富集分析等下游分析。
很多教程会把PCA放在“差异分析之后”讲,好像它只是一个可视化工具,画个图展示样品相似性就完了。但实际上PCA是转录组分析的“第一道质检工序”,它的定位是发现问题,而不是展示结果。如果顺序反了,后面所有分析都是在“带病”的数据上做,再花哨的富集分析结果都是空中楼阁。
5. 常见问题速查与排查技巧
5.1 PCA分不开组怎么排查
PCA图上两组样本完全混在一起,这是最让人焦虑的情况之一。我的排查思路是按顺序排除,而不是只盯着一个原因:
- 先确认标准化是否做了。如果直接用raw count跑PCA,分不开很正常——高表达基因主导方差,真正的组间差异被淹没了。
- 再看是否有批次效应。做个按批次上色的PCA图,如果按批次分开了,优先处理批次。
- 然后看是否有离群样本在“拉偏”。个别离群样本会极大地影响PC方向,导致其他样本被压缩在很小范围内,看起来就分不开。
- 最后才考虑生物学原因:样本量太小、组内异质性太强、两组本身的转录组差异就不大——这种情况下分不开是“真实”的,不要强行美化。
还有一个实用技巧:有时候PC1分不开,但分组在PC2和PC3的组合上很清楚。我习惯把PC1-PC2、PC2-PC3、PC1-PC3都画一遍,有时分组在小方差的主成分上反而很明显。如果所有组合都分不开,再回到上面四条逐一排查。
5.2 主成分解释率过低或过高怎么看
PCA图坐标轴上的百分数代表该主成分解释的方差比例。转录组数据里,PC1往往只占10%-30%的方差,这很正常——因为基因数量太多,总方差被摊薄了。如果PC1能解释40%以上的方差,反而要提高警惕:这通常说明有一个特别强的主导因素,可能是批次效应,也可能是某个样本的极端离群,或者是少数几个基因的异常高表达在“拉动”主方向。
如果PC1和PC2加起来还不到10%,说明数据里没有明显的“主导方差来源”,这时候样本分布会比较分散,不容易看出聚类。这不一定有问题,但意味着数据里的生物学信号可能不强,后续分析要重点关注差异表达的统计效力。
5.3 批次效应与生物学差异混淆的陷阱
最后一个最隐蔽的坑。有时候批次效应和生物学分组是“部分混淆”的——比如每个批次里两组都有,但比例不平衡。这时候用ComBat还得小心,因为ComBat在估计批次效应时默认分组信息是可靠的。如果分组标签本身不准,或者组内还有隐藏的亚群,ComBat可能会把生物学差异的一部分当成批次效应给削掉。
我的做法是把批次信息和分组信息、以及所有已知的协变量(性别、年龄、RIN值、测序深度)一起放进设计矩阵里。这样在处理批次效应时,模型能区分哪些差异来自批次、哪些来自分组、哪些来自协变量。不要只丢一个batch参数进去就完事。尤其是在做临床样本分析时,性别年龄对转录组的影响非常明显,不纳入协变量去批次,等于把真实生物学信号和噪音混在一起处理。
6. 关于转录组PCA的几点操作体会
写到这里,把我这几年在转录组PCA分析上踩过的坑和积累下来的经验再浓缩一遍,当是给后来者的一份备忘录。
第一,PCA不是炫技工具,它是质控工具。别急着把PCA图做得漂亮,先问自己:这张图告诉我数据里有什么问题?标准化做了没有?离群样本排查了没有?批次效应处理了没有?这三关都过了,PCA图才真正反映生物学。
第二,每一个“删除”操作都要有记录。不管是删样本还是删基因,要写进方法部分。审稿人最反感的是“为了结果好看而删数据”。我在自己的分析流程里会保留所有QC步骤的截图和日志,包括每次PCA图的版本记录,这样写文章时可以直接引用,别人问起来也能说得清楚。
第三,PCA是起点不是终点。它告诉我们样本的总体结构,但具体哪些基因驱动了样本分离,要靠后续的差异表达分析和富集分析回答。PCA图上分不开的组,不一定没有差异基因;PCA图上分得很开的两群,也不一定就是你要找的生物学差异。多问一个“为什么分开”,比多跑一个漂亮的图更有价值。
最后,如果你刚开始接触转录组分析,我的建议是把每个项目的PCA图都存下来,建成一个“QC档案”。每次分析新数据,先翻一遍PCA图,比看一百页质控报告都直观。我自己的习惯是每跑完一版分析,就把PCA图、相关性热图、质控指标表存到一个文件夹里,按时间命名。这个习惯帮我省了无数次返工的时间,也让我在跟合作者讨论数据时更有底气——毕竟,一张清晰的PCA图和它背后完整的处理逻辑,比任何口头解释都更有说服力。