4. 批次效应的可视化方法:PCA、t-SNE、UMAP、热图

咱们做文献数据抽取,拿到一批多组学样本后,最怕什么?怕数据里有隐藏的批次效应。你跑完差异分析,发现一堆显著基因,结果半天发现是不同实验室的批次差异——这锅我背过。所以第一步,先把数据扔到低维空间里看分布,肉眼识别有没有明显的群组分离。

这一章我带你过四种最常用的可视化手段:PCA、t-SNE、UMAP、热图。每一种都有它的脾气,用对了事半功倍,用错了就是画饼。咱们一个一个聊。

4.1 PCA:线性的降维第一枪

主成分分析(PCA)是最基础的线性降维方法。它找的是数据方差最大的方向,把高维表达矩阵投影到几个主成分上。说白了,就是“把最乱的方向先压平”。

我在项目中会这样用:拿到归一化后的表达矩阵(基因×样本),先跑PCA看看前两个主成分能不能把不同批次分开。如果分开得很明显——嗯,批次效应已经写在脸上了。

# R 示例:用 prcomp 做 PCA
expr_matrix <- read.csv("normalized_expression.csv", row.names = 1)
pca_result <- prcomp(t(expr_matrix), scale. = TRUE)  # 转置:样本在行
summary(pca_result)  # 看各主成分方差占比
plot(pca_result$x[,1], pca_result$x[,2], 
     col = as.factor(batch_info), pch = 19,
     xlab = "PC1", ylab = "PC2")

避坑: 我曾经在 PCA 图上看到两个批次完全混在一起,以为没批次效应,结果用 t-SNE 一看分开得很清楚。所以 PCA 只能作为快速筛查,对非线性结构不敏感。

4.2 t-SNE:非线性地摊儿

t-SNE 全称 t-distributed Stochastic Neighbor Embedding,它不关心全局结构,只在乎局部邻居关系。它把高维空间里的相似性映射到二维上,让相近的点聚得更紧,远的点推得更开。

t-SNE 的两个关键参数:perplexity 和 learning rate。perplexity 建议在 5-50 之间调,太小会局部噪音太多,太大会忽略细节。我一般先设 30,看结果再微调。

# R 示例:用 Rtsne 包
library(Rtsne)
set.seed(42)
tsne_out <- Rtsne(t(expr_matrix), perplexity = 30, theta = 0.5)
plot(tsne_out$Y, col = as.factor(batch_info), pch = 19,
     xlab = "t-SNE 1", ylab = "t-SNE 2")

注意: t-SNE 每次运行因为初始化随机,结果可能不同。务必固定随机种子(set.seed),并且不要拿 t-SNE 的坐标做定量分析——它只适合“看”。

我在一个多中心转录组项目中,PCA 没看出什么,t-SNE 一画,三个中心各据一角,清清楚楚。那之后我就把 t-SNE 作为标配了。

4.3 UMAP:又快又稳的“后浪”

UMAP(Uniform Manifold Approximation and Projection)是 2018 年火起来的。它同样是非线性降维,但比 t-SNE 快很多,而且能更好地保留全局结构。它的数学基础是黎曼几何和模糊单纯集,但你不需要完全懂——会调参就行。

关键参数:n_neighbors(邻居数,默认15)和 min_dist(最小距离,默认0.1)。n_neighbors 越大,越关心全局;min_dist 越小,点越紧凑。我习惯先保持默认,如果聚类太碎就调大 n_neighbors 到 30。

# Python 示例:用 umap-learn 库
import umap
import matplotlib.pyplot as plt

reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, random_state=42)
embedding = reducer.fit_transform(expr_matrix.T)  # 样本在行
plt.scatter(embedding[:,0], embedding[:,1], c=batch_info, cmap='Set1')
plt.xlabel("UMAP 1"); plt.ylabel("UMAP 2")
plt.show()

对比 t-SNE 和 UMAP:t-SNE 对局部结构敏感,UMAP 在保持全局关系上更强。如果批次效应是线性的,PCA 就够;如果是非线性且样本多(比如单细胞数据),我优先选 UMAP。

4.4 热图:把矩阵画成彩虹

热图(heatmap)展示的是表达矩阵的数值分布。行是基因(或特征),列是样本,颜色深浅代表表达量高低。配合列注释(比如批次、分组),一眼就能看出列间是否出现分块结构——批次效应往往表现为同一批次的列颜色模式一致。

我个人会用热图做“批次效应预检”:取 top 高变基因,画一个带批次注释的热图。如果同一批次样本在列上聚成了明显的一簇,那就要小心了。

# R 示例:pheatmap 包
library(pheatmap)
# 选取 top 500 高变基因
top_genes <- order(apply(expr_matrix, 1, var), decreasing=TRUE)[1:500]
pheatmap(expr_matrix[top_genes, ],
         scale = "row",
         annotation_col = data.frame(batch = batch_info, row.names = colnames(expr_matrix)),
         show_rownames = FALSE)

小技巧: 热图的聚类分支(dendrogram)本身也是一种层次聚类。如果列聚类树中同一批次样本首先聚成一团,那批次效应就非常明显了。我曾经用这个办法在别人的数据里发现了一个没标注的批次问题。

4.5 四种方法对比:怎么选?

我习惯用下面这个表格来决策:

方法类型速度全局结构局部结构典型场景
PCA线性极快好差初步筛查,线性批次
t-SNE非线性较慢差极好复杂非线性,小样本
UMAP非线性快好好大样本,单细胞,推荐首选
热图非降维快––直接看模式,辅助验证

实际中我一般这样流水线:先 PCA 快速扫一眼;如果看出明显分群,再用热图进一步确认;如果看不出来,上 UMAP;遇到特别复杂的非线性结构(比如流式细胞、单细胞),再上 t-SNE 杀鸡用牛刀。

4.6 知识图谱:可视化方法全景

下面这张图总结了本章四种方法的定位,以及它们在批次效应识别流程中的关系。

批次效应可视化方法知识体系 批次效应可视化方法 PCA 线性降维 t-SNE 非线性·局部优先 UMAP 非线性·全局兼顾 热图 矩阵可视化 使用策略(推荐流程) 1. PCA 快速筛查 → 如果有明显分群 → 热图确认 2. PCA 看不清 → UMAP(大样本优先)或 t-SNE(小样本) 3. 热图辅助验证列聚类树,输出最终判断 可视化报告 + 批次效应决策

可视化不是终点,而是起点。拿到图之后,结合统计学检验(比如 PERMANOVA、批次方差占比)才能量化。不过,先让眼睛说了算——我做了这么多年,目测结果往往和统计结论一致。