批次效应的可视化探索:用PCA、热图、UMAP做初步诊断
拿到表达矩阵后的第一件事是什么?
我个人习惯——不急着跑校正,先做可视化。
为什么?
因为数据有没有问题,眼睛一看就知道。批次效应再隐蔽,在降维图上也藏不住。你想想看,如果PCA或者UMAP图上一眼就看出来样本按批次分成几坨,那说明批次效应已经大到不能忽视了。
这一章,咱们就聊聊怎么用PCA、热图、UMAP这三种工具,给你的RNA-seq数据做一次"体检"。
1. PCA:最快的全局扫描仪
PCA(主成分分析)是我最常用的第一眼工具。它把高维表达数据压缩到前几个主成分里,然后按批次上色,一看便知。
说白了,PCA就是问一个问题:样本间最大的变异来源是什么?如果答案是"批次",那你得小心了。
核心判断标准:
- PC1 或 PC2 明显按批次分组 → 批次效应强烈
- 同一生物学条件的样本跨批次混在一起 → 批次效应可控
- PC1 是生物学分组,PC2 是批次 → 批次效应存在但可能可校正
来段代码看看吧。以下是用 R 做的 PCA 可视化:
# 表达矩阵:基因×样本,已做标准化
expr_mat <- log2(expr_mat + 1)
# PCA计算
pca_res <- prcomp(t(expr_mat), scale. = TRUE, center = TRUE)
pca_df <- data.frame(
PC1 = pca_res$x[, 1],
PC2 = pca_res$x[, 2],
batch = batch_info,
condition = condition_info
)
# 计算解释方差比例
var_exp <- round(100 * summary(pca_res)$importance[2, 1:2], 1)
# 绘图
library(ggplot2)
ggplot(pca_df, aes(PC1, PC2, color = batch, shape = condition)) +
geom_point(size = 3.5, alpha = 0.8) +
xlab(paste0("PC1 (", var_exp[1], "%)")) +
ylab(paste0("PC2 (", var_exp[2], "%)")) +
theme_bw(base_size = 14) +
ggtitle("PCA 按批次着色")
我记得有一次做TCGA数据整合,PC1直接分成了两个批次,PC2才是真实的肿瘤vs正常差异。那会儿我就意识到——批次效应不查不行。
一个小技巧:
如果样本量较大,可以用 geom_point(size = 2, alpha = 0.6) 避免点重叠。我还会额外画一个 95% 置信椭圆,帮眼睛聚焦——按批次画椭圆,看它们叠不重叠。
另外,别只看PC1和PC2。有时候批次效应会藏在PC3或PC4里。我建议至少看到PC4:
# 快速扫前4个主成分
pca_df_pc34 <- data.frame(
PC3 = pca_res$x[, 3],
PC4 = pca_res$x[, 4],
batch = batch_info
)
ggplot(pca_df_pc34, aes(PC3, PC4, color = batch)) +
geom_point(size = 3) +
theme_bw()
2. 热图:样本间相似性的"红外照片"
PCA看的是全局变异方向,而热图看的是样本间的成对相似性。说白了,它回答的是:哪些样本"长得像"?
我一般用样本间表达相关性来做热图,再配上批次注释条,非常直观。
library(pheatmap)
library(RColorBrewer)
# 计算样本间Pearson相关
cor_mat <- cor(expr_mat, method = "pearson")
# 准备注释信息
anno_df <- data.frame(
Batch = batch_info,
Condition = condition_info,
row.names = colnames(expr_mat)
)
# 自定义颜色
anno_colors <- list(
Batch = c("Batch1" = "#E41A1C", "Batch2" = "#377EB8", "Batch3" = "#4DAF4A"),
Condition = c("Control" = "#FF7F00", "Treatment" = "#984EA3")
)
# 画热图
pheatmap(cor_mat,
annotation_col = anno_df,
annotation_colors = anno_colors,
clustering_method = "ward.D2",
show_rownames = FALSE,
show_colnames = TRUE,
main = "样本间相关性热图",
color = colorRampPalette(rev(brewer.pal(9, "RdBu")))(100),
fontsize_row = 8)
看热图的时候,我关注两个点:
- 树状图的分簇结构——如果样本先按批次分成几大簇,再按生物学条件分,那批次效应很明显。
- 颜色块的模式——批次内部的颜色是否明显比批次间更"红"(相关性更高)。
避坑指南:
我曾经因为样本注释搞混了,热图画出来全是乱的。后来排查了半天,才发现是批次标签贴错了。所以——画热图之前,一定先确认注释列的准确性。一个错标,能带偏整个分析。
3. UMAP:局部结构的放大镜
PCA擅长抓全局,但它有个弱点——批次效应如果只影响局部基因模块,PCA可能看不出来。这时候就该UMAP上场了。
UMAP的核心思路是保留样本间的"近邻关系",它对局部结构更敏感。说白了,PCA看"大方向",UMAP看"小细节"。
library(umap)
# UMAP降维
umap_config <- umap.defaults
umap_config$n_neighbors <- 15 # 控制局部vs全局的平衡
umap_config$min_dist <- 0.1 # 控制点的聚集程度
umap_res <- umap(t(expr_mat), config = umap_config)
umap_df <- data.frame(
UMAP1 = umap_res$layout[, 1],
UMAP2 = umap_res$layout[, 2],
batch = batch_info,
condition = condition_info
)
# 绘图
ggplot(umap_df, aes(UMAP1, UMAP2, color = batch, shape = condition)) +
geom_point(size = 3, alpha = 0.8) +
theme_bw(base_size = 14) +
ggtitle("UMAP 按批次着色")
UMAP 参数调优经验:
n_neighbors默认15,样本多可以调大到30-50,更关注全局min_dist默认0.1,调小到0.01会让点更聚集,便于发现离群- 我个人习惯:先用PCA扫一遍,再用UMAP做double-check
我曾经有一个项目,PCA做出来看起来还行——PC1是生物学分组,PC2有点乱但不太明显。结果UMAP一跑,同一条件下的样本被批次分成了两堆。那叫一个原形毕露。
为什么PCA没看出来?
因为批次效应影响的基因数量少,在PCA的全局方差里占比小,被前几个主成分"淹没了"。而UMAP保留了局部近邻结构,批次导致的局部差异反而被放大了。
4. 三种方法怎么选?
来,我帮你总结一下。下面这个表格,我每次做项目都会对照着看一遍:
| 方法 | 核心目的 | 最适合的场景 | 关键警示信号 |
|---|---|---|---|
| PCA | 检测全局最大变异方向 | 初步快速筛查,尤其样本量大时 | PC1/PC2 按批次分离,解释方差 > 20% |
| 热图 | 检查样本间整体相似性 | 中等样本量,需要直观展示聚类结构 | 树状图先按批次分簇,而非按条件 |
| UMAP | 保留局部结构的精细降维 | PCA结果不明确,或批次效应可能较微弱 | 同条件样本被批次重新分堆 |
一个常见的误区:
很多人只看一种图就下结论。我在项目中踩过这个坑——PCA没发现问题,就以为数据是干净的。后来加了UMAP才发现完全不是那么回事。三种方法交叉验证,才能做判断。
5. 综合诊断:你的数据到底有没有批次效应?
做完三种可视化,该怎么汇总判断?我个人习惯按下面几步走:
- 先看PCA——前4个主成分有没有按批次分离?有的话,批次效应大概率存在。
- 再看热图——树状图的第一层分支是按批次还是按条件?如果是批次,说明批次效应很强。
- 最后看UMAP——有没有PCA没发现的局部批次结构?特别关注同一条件下的样本是否跨批次混在一起。
- 交叉对比——至少两种方法指向同一结论,才算确认。
如果三种方法都指向"存在明显批次效应",那就别犹豫了,接着看下一章怎么校正。如果只有一种方法有信号,其他两种正常,那批次效应可能比较弱,可以再深入检查一下。
记住一句话:
可视化不是目的,是手段。它帮你决定"要不要校正"以及"校正方向对不对"。不要跳过这一步,也不要只依赖一种方法。