引言:什么是差异表达分析

刚入行那会儿,我第一次接触RNA-seq项目,老板扔过来一个矩阵——基因名排成行,样本名列成列,每个格子都是个计数。他说:“跑一下差异表达,把上调下调的基因给我。”我当时以为就是做个除法、算个p值那么简单,结果一上手才发现,坑比想象中多得多。

什么是差异表达分析?说白了,就是比较两组(或多组)样本中,基因表达水平是否有显著差异。比如对照组vs处理组,或者肿瘤组织vs正常组织。我们关注的不是绝对表达量高低,而是两组之间的变化是否可靠、是否具有生物学意义。

核心问题:这个基因在A组平均表达量是10,在B组是100,看起来差10倍——但这个差异是真实的,还是由批次效应、测序深度不均、技术误差造成的?差异表达分析就是要给出统计学上的回答。

为什么I会单独拿PCA图来评估可靠性?

我见过太多新手扔出差异基因列表就交差,完全不管下游验证是否站得住脚。我个人习惯是:在做任何差异分析之前,先画一个PCA图。为什么?因为PCA能直观暴露出数据里的大问题——样本聚类是否正常、有没有离群点、批次效应是否严重。

举个例子。有一次我处理一个公共数据库的RNA-seq数据,三组样本,每组三个重复。跑完count矩阵后直接做DESeq2,结果差异基因哗啦啦出来几百个。但我不放心,先画了个PCA——好家伙,六个样本全混在一起,根本看不到分组趋势。仔细一查,是样本标签写反了。如果没有PCA这一步,我可能会推出一堆假阳性结果,那可就丢人了。

我的经验:PCA图是我每次差异分析流水线上的第一道质检关。没通过PCA的检查,后面的一切差异结果我都不敢信。

差异表达分析的常规流程

差异表达分析一般分几个阶段。我用一个表格总结下常见的步骤和工具,方便你有个整体印象。

阶段 核心操作 常用工具/包
数据预处理 质量控制、去接头、比对、定量 FastQC, Trimmomatic, STAR, Salmon
计数矩阵构建 生成基因-样本计数表 featureCounts, htseq-count
归一化 消除测序深度、转录本长度等系统偏差 TPM, FPKM, TMM (edgeR), median-of-ratios (DESeq2)
差异检验 基于统计模型计算p值、log2FC DESeq2, edgeR, limma-voom
多重假设校正 控制假阳性率(FDR) BH (Benjamini-Hochberg), q-value
结果可视化与评估 火山图、热图、PCA图、MA图 ggplot2, pheatmap, PCAtools
生物学解释 GO/KEGG富集分析、蛋白互作网络 clusterProfiler, DAVID, STRING

你会发现,PCA图被列在“结果可视化与评估”阶段。但我个人习惯把它提前到归一化之后、差异检验之前——先看看数据是否干净,再做统计推断。

PCA到底在看什么?

PCA(主成分分析)是一种无监督降维方法。它把高维表达数据(一个样本可能测到两万个基因)压缩成少数几个主成分,然后用前两个或三个主成分画散点图。每个点代表一个样本,点与点的距离反映转录组层面的相似程度。

正常情况下,生物学重复应该聚在一起;不同实验组应该自然分开。如果样本按已知分组完美聚类,那差异分析大概率会得到可靠的结果。如果分组混乱,说明数据里混杂了更大的变异来源——比如批次效应、细胞类型比例差异、或者根本就是样本搞混了。

警告:不要以为PCA图好看就万事大吉。PCA只能反映最大方差方向,小但重要的生物学差异可能在后续主成分上才显现。另外,PCA对离群点非常敏感,一个坏样本就足以扭曲整个PC1方向。所以看PCA时要配合统计指标(如各主成分方差解释率)一起判断。

一个简单的PCA案例(代码片段)

我用R语言演示一下最基础的PCA画图流程。假设你已经有一个归一化的表达矩阵 expr_matrix,行是基因,列是样本;还有一个 metadata 表包含样本分组信息。

# 加载包
library(ggplot2)
library(PCAtools)

# 转置:PCA要求样本为行,基因为列
pca_input <- t(expr_matrix)

# 计算PCA
pca_result <- prcomp(pca_input, center = TRUE, scale. = FALSE)

# 提取主成分得分
pca_df <- as.data.frame(pca_result$x)
pca_df$group <- metadata$group  # 添加分组信息

# 计算方差解释率
var_explained <- round(100 * pca_result$sdev^2 / sum(pca_result$sdev^2), 1)

# 绘制PC1 vs PC2
ggplot(pca_df, aes(x = PC1, y = PC2, color = group)) +
  geom_point(size = 3, alpha = 0.8) +
  stat_ellipse(aes(fill = group), geom = "polygon", alpha = 0.1) +
  labs(x = paste0("PC1 (", var_explained[1], "%)"),
       y = paste0("PC2 (", var_explained[2], "%)"),
       title = "PCA of RNA-seq samples") +
  theme_minimal()

运行这段代码,你会看到像下面这样的结构关系图。我来画一个概念性的SVG,把PCA的核心逻辑串起来——这样你记起来更容易。

差异表达分析中PCA的作用 输入:归一化表达矩阵 基因 × 样本,已去除批次效应 PCA降维 prcomp() 或 irlba PCA图 PC1 vs PC2 散点图 样本是否按组聚类? 验证实验设计有效性 有无离群样本? 发现数据质量或标注问题 批次效应是否残留? 决定是否需要校正 结论:PCA通过≠差异分析一定正确,PCA失败了差异分析基本不可靠 PCA是数据质量的必要条件,不是充分条件。但它是最快、最直观的初筛手段。

哪些因素会导致PCA图“翻车”?

我自己总结了几类常见问题,一旦PCA上出现这些特征,你就要小心了:

  • 所有样本混成一团——分组间差异太小,或者测序深度、文库复杂度差异太大盖过了生物学信号。解决办法:重新归一化或检查测序质量。
  • 重复样本离散度过大——说明技术重复或生物学重复之间不一致,可能是RNA质量差、建库批次效应或测序深度不均。我遇到过一次,同一个样本的两个技术重复一个在左上一个在右下,结果是反转录步骤污染了。
  • 某个分组完全与另一个分组重叠——要么是这个分组没有特有表达特征,要么是分组标签给错了。我曾经有一组处理组的样本反而和对照组聚在一起,后来发现是给药浓度搞混了。
  • PC1方差解释率超级高(>90%)——不一定全是好事,可能数据里有一个极强的主成分(比如批次效应或细胞周期差异)压过了所有生物学信号。这时候要先跑一跑limma的removeBatchEffect。

避坑指南:我建议在所有差异分析项目里,把PCA图的生成写成脚本的一部分,强制在运行差异检验之前先输出。不要手动去点UI画图——写成自动化流程,每次跑数据就会自动出图,你扫一眼就能决定要不要往下走。

本章小结:PCA是差异分析的门神

你不是在“画PCA图”这件事上花功夫,而是在给差异分析的可靠性设立第一道防线。没有这道防线,后面所有的p值、倍数变化、富集结果都可能是建立在沙上的城堡。

这套课程里,我们会一步一步把PCA图的每个细节抠清楚——从数据准备到主成分解释到用PCA筛选差异候选基因。我手上有几个真实项目翻车的案例,后面会拿出来给你看,让你一次记住所有坑。

嗯,先搞清楚概念,下节我们直接上手实操。