3. 批次效应的可视化技术:PCA、t-SNE、UMAP

可视化,说白了就是给你的数据“拍张照”。

高维数据你看不见摸不着,但降维投影到二维或三维空间后,批次效应就藏不住了。我个人习惯在跑任何统计检验之前,先用可视化扫一眼。这步做对了,后面省一半时间。

3.1 为什么需要降维可视化?

单细胞数据动辄上万基因,直接画散点图?不现实。我们需要把高维空间“压”到低维,同时保留关键结构。

三个目标必须同时满足:

  • 分组可见:生物条件(病变/正常)能否分开?
  • 批次暴露:来自不同批次的样本是否各自抱团?
  • 异常识别:有没有孤零零漂在外面的离群样本?

你想想看,如果第一批次样品全挤在左边,第二批次全压在右边,这肯定有问题。

批次效应可视化技术流程 原始高维表达矩阵 PCA:线性全局降维 速度最快,保留方差全局结构 t-SNE:非线性局部聚类 擅长局部结构,全局距离不可信 UMAP:平衡全局与局部 速度快于t-SNE,结构更清晰 综合判决:批次重叠 vs 生物分离 人工研判 + 定量指标验证 💡 我的顺序:先跑PCA快速筛查 → 发现问题后用UMAP精细确认 → t-SNE作为补充验证

3.2 PCA:第一步快速筛查

主成分分析(PCA)是你工具箱里最快的那把刀。它用线性变换找出方差最大的方向,投影下去一看便知。

3.2.1 什么时候该用PCA?

  • 初步数据浏览:5秒出图,立刻判断有没有明显批次分离。
  • 大样本量场景:万级细胞,t-SNE要跑半小时,PCA十秒就搞定。
  • 移除强烈批次效应前/后对比:校正前各批细胞在PC1上明显分离;校正后混在一起。

3.2.2 实战代码

在Python里用scanpy一行就够。我个人习惯先标准化再跑PCA。

import scanpy as sc
import matplotlib.pyplot as plt

# 读取数据
adata = sc.read_h5ad("batch_data.h5ad")

# 标准化 + 高变基因选择
sc.pp.normalize_total(adata, target_sum=1e4)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata, n_top_genes=2000)

# PCA
sc.pp.pca(adata, n_comps=50)

# 按批次着色
sc.pl.pca(adata, color="batch", title="PCA colored by batch")

# 按生物学条件着色
sc.pl.pca(adata, color="condition", title="PCA colored by condition")
看图的要点: 对比两幅图。如果“按批次着色”能看到明显分离,而“按条件着色”却混在一起——那批次效应大概率盖过了生物学信号。

3.2.3 PCA的局限性

说实话,PCA有时候也会骗人。它只能捕获线性结构。如果批次效应嵌套在非线性流形里,PCA可能完全看不到。

我在项目中遇到过这样的情况:PCA图上不同批次完全重叠,我差点以为没批次效应。结果用UMAP一跑,每个批次自成小岛。所以别只看PCA就下结论。

3.3 t-SNE:局部细节放大镜

t-分布随机邻近嵌入(t-SNE)是非线性的王者。它把高维相似度映射到低维,特别擅长揪出那些PCA漏掉的局部模式。

3.3.1 关键参数:perplexity

perplexity可以理解为“每个点考虑多少个邻居”。我见过很多人用默认值30就撒手不管了。

perplexity值 效果倾向 实测建议
5 ~ 15 局部细节丰富,但易产生假簇 小样本(<500细胞)时用
30 ~ 50 全局平衡较好 中等样本(500~5000细胞)首选
50 ~ 100 整体结构更平滑,局部细节丢失 大样本(>5000细胞)适用
避坑: t-SNE对随机种子敏感。同样的数据跑两次,结果可能不一样。我建议固定random_state=42,否则汇报时图变了会很尴尬。

3.3.2 批次效应解读技巧

t-SNE图上看到批次分离后,别急着下结论。先检查:分离是否沿着t-SNE1方向?如果是,那大概率是批次效应。如果分离方向与已知生物标记物对齐,那可能是真实差异。

# scanpy的t-SNE运行
sc.tl.tsne(adata, n_pcs=30, perplexity=30, random_state=42)
sc.pl.tsne(adata, color=["batch", "cell_type"], 
            title=["t-SNE by batch", "t-SNE by cell type"])

3.4 UMAP:你的日常工作马

UMAP(统一流形逼近与投影)是我现在用得最多的工具。为什么?因为它既保留了t-SNE的局部聚类分辨率,又比t-SNE快一个数量级。

3.4.1 UMAP vs PCA vs t-SNE

特性 PCA t-SNE UMAP
变换类型 线性 非线性 非线性(基于拓扑)
全局结构 保留较好 不保留 部分保留
局部聚类 一般 优秀 优秀
计算速度 极快 慢(O(n²)) 快(近似O(n¹.¹⁴))
可重复性 确定 随机种子依赖 可设置种子,相对稳定

3.4.2 UMAP的批次效应诊断流程

我个人的标准流程是这样:

  1. 先跑UMAP:用n_neighbors=15, min_dist=0.3的默认参数。
  2. 看三张图:按批次着色、按实验条件着色、按已知细胞类型着色。
  3. 对比分离度:如果批次标签能完美预测UMAP聚类,而细胞类型标签却混淆在一起,那批次效应需要校正。
  4. 调节参数验证:把n_neighbors从5调到50,看批次分离图案是否稳定。稳定的话,基本就是真·批次效应了。
# UMAP + 多视图对比
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=30)
sc.tl.umap(adata, min_dist=0.3, random_state=42)

# 同一张画布对比批次 vs 细胞类型
sc.pl.umap(adata, color=["batch", "cell_type", "condition"],
            ncols=3, wspace=0.3,
            title=["UMAP: Batch", "UMAP: Cell Type", "UMAP: Condition"])

3.5 三图联判法

光看一张图就做决定?不太稳妥。我推荐“三图联判”:

  • PCA图:快速评估全局趋势。如果PC1上各批完全对齐,大概率强批次效应。
  • UMAP图:精细确认局部结构。各批次是否形成独立的小岛?
  • t-SNE图:作为UMAP的交叉验证。如果UMAP和t-SNE都显示同样的批次分离模式,那基本上板上钉钉了。
结论规则:
如果三张图中,批次标签都能把数据完美分成独立的簇 → 批次效应严重,必须校正。
如果三张图中,批次标签只是轻微重叠在真实细胞的子结构上 → 批次效应较弱,可接受或轻度校正。
如果三张图中,只有一张图显示批次分离 → 再核实一下参数设置,可能只是可视化假象。

3.6 避坑经验

讲了这么多,最后说说我踩过的坑。

曾经有一次,我处理一个4个批次的数据。PCA看起来完美重叠——我高兴坏了,以为没有批次效应。结果UMAP一跑,4个批次排成了4条平行线。后来才发现,这4个批次里各包含不同的细胞亚型比例差异。PCA因为方差大被“细胞类型”主导了,而UMAP把“批次”的细微差异给放大了。

所以我的建议是:可视化只是线索,不是证据。看到分离后,一定要结合定量指标(如批次差异评分、PCA贡献率)做双重确认。

另外,别忘了颜色标度。有时批次只有两个值,染色很容易。当批次有10个以上时,颜色容易混淆。我会用ColorBrewer的调色板来保证区分度。

# 多批次时建议用显式调色板
batch_colors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8',
                '#f58231', '#911eb4', '#42d4f4', '#f032e6']
sc.pl.pca(adata, color="batch", palette=batch_colors)

嗯,到这里,可视化的基本套路就到位了。你手上有数据的话,建议现在就打开Jupyter跑一遍——肉眼看到的,往往比p-value更直观。