单细胞数据整合概述:为什么需要整合,常见整合方法
做过单细胞项目的朋友都知道,整合(Integration)几乎是每次数据分析逃不掉的一步。说白了一句话:一个样本不够,多个样本凑。但凑完麻烦就来了——你可能拿到十几个样本,每批次测序深度、细胞捕获效率都不一样,甚至有的是10x v2,有的是v3。如果不整合,聚类出来同一个细胞类型却分成了几团,你根本分不清是生物学差异还是技术噪声。
核心目标:消除批次效应,保留真实生物学变异。
为什么需要整合?
我刚开始做单细胞时特别天真,觉得把多个样本的count矩阵直接合并,跑个PCA就能出结果。结果画UMAP一看,嗯,样本1的T细胞和样本2的T细胞隔了十万八千里。这就是典型的批次效应,不是生物学差异。
具体来说,需要整合的场景包括:
- 同一个实验的不同批次:比如上午做一批,下午做一批,试剂配比可能有点漂移。
- 不同测序平台:10x vs Smart-seq2 vs Drop-seq,捕获效率差很多。
- 不同组织或疾病状态:正常vs患病,需要找到共有的细胞类型。
- 多模态数据:CITE-seq同时有RNA和蛋白数据,你要把两个模态对齐。
如果不整合,直接合并聚类,你可能看到的是:
UMAP上同一cluster被批次标签完全分开
不同批次间marker基因表达不一致
差异分析结果被批次效应污染
所以整合不是什么高级选项,而是多样本分析的前置必要条件。我曾经在一个大项目里,跳过整合直接分析,结果发现「差异基因」全是批次特异性基因,被审稿人追问了好几个月,教训够深刻。
个人经验:拿到数据先画个UMAP按样本染色,如果不同样本的细胞混在一起,说明批次效应小;如果分成明显几坨,别犹豫,直接开始整合。
常见整合方法概览
现在常用的整合方法,有三大经典派系:基于锚点的(Seurat)、基于校正的(Harmony)、基于深度学习的(scVI)。每一种都有它的脾气,选哪个取决于你的数据规模、计算机资源,以及你有多想手动调参。
| 方法 | 核心思路 | 适用场景 | 我使用频率 |
|---|---|---|---|
| Seurat(CCA+MNN) | 找锚点(anchor),再通过相互最近邻变换 | 中等规模数据(几万细胞),单机即可 | ⭐⭐⭐⭐⭐ |
| Harmony | 在PCA空间做迭代软聚类校正 | 大规模数据(几十万细胞),速度快 | ⭐⭐⭐⭐ |
| scVI(scVI-tools) | 变分自编码器,学习隐含的批次无关表征 | 超大型数据,多批次,多模态 | ⭐⭐⭐ |
Seurat 整合
Seurat是目前R语言单细胞分析的扛把子。它的整合思路是:先找不同数据集之间的「锚点」细胞对,然后用这些锚点来校正表达矩阵。说白了,就是找到跨批次最相似的细胞对,用它们做桥梁。
基本步骤:
- 对每个样本分别做标准化、选高变基因。
- 使用
FindIntegrationAnchors找锚点。 - 用
IntegrateData生成校正后的表达矩阵。
代码示例(R):
library(Seurat)
# 假设有一个 list,包含多个 Seurat 对象
anchors <- FindIntegrationAnchors(object.list = seurat_list, dims = 1:30)
integrated <- IntegrateData(anchorset = anchors, dims = 1:30)
# 后续正常做标准化、UMAP等
integrated <- ScaleData(integrated)
integrated <- RunPCA(integrated)
integrated <- RunUMAP(integrated, dims = 1:30)
注意:Seurat整合会返回一个新的Assay,叫"integrated"。原来的RNA Assay还在,但作图时默认用integrated的降维结果。
我遇到过新手把Seurat整合后的矩阵直接拿去做差异分析,结果发现所有基因表达都被平滑了,差异分析几乎没结果——这是错误的用法,差异分析要回到原始RNA Assay。
Harmony 整合
Harmony是Python和R双语言的整合利器。它的做法是在PCA空间里,通过一种叫「迭代模糊聚类」的方法,把相似的细胞往一起拉,同时保留批次内部的变异。
我自己的感觉:Harmony速度很快,适合几十万细胞的超大数据集。而且它的参数少,基本就是调个 theta 和 lambda,省心。
Python示例(scanpy环境):
import scanpy as sc
import harmonypy
# 假设 adata 已经做过 PCA
ho = harmonypy.run_harmony(
adata.obsm['X_pca'],
adata.obs,
vars_use='batch',
max_iter_harmony=10,
do_pca=False
)
adata.obsm['X_pca_harmony'] = ho.Z_corr.T
# 用校正后的pca做umap
sc.pp.neighbors(adata, use_rep='X_pca_harmony')
sc.tl.umap(adata)
R版本就更简单了,直接集成在Seurat的管道里:
library(Seurat)
library(harmony)
# 先跑标准PCA
pbmc <- RunPCA(pbmc)
# 用harmony校正
pbmc <- RunHarmony(pbmc, group.by.vars = "batch")
# 后续用harmony降维结果
pbmc <- RunUMAP(pbmc, reduction = "harmony")
避坑指南:Harmony默认假设你已经有好的PCA。如果PCA本身被批次扭曲得太严重,Harmony效果会打折。我一般会先检查一下PCA的前几个主成分是否被批次主导,如果PC1按样本分开,那Harmony正好派上用场。
scVI 整合
scVI是深度学习派代表,基于变分自编码器(VAE)。它会学到一个低维的隐变量,这个隐变量不受批次影响,但保留了生物学变异。
说人话就是:你把原始count矩阵和批次标签喂给它,它自己学出一个「干净」的细胞表征。而且scVI还能处理不同测序深度、零膨胀等问题。
不过代价是:需要GPU,训练时间较长,而且对新手来说调参可能需要一点经验。
Python安装与使用示例:
pip install scvi-tools
import scvi
import scanpy as sc
# 加载数据
adata = sc.read("my_data.h5ad")
# 确保 batch 信息在 adata.obs 中
scvi.model.SCVI.setup_anndata(adata, batch_key="batch")
# 创建模型
model = scvi.model.SCVI(adata)
# 训练
model.train()
# 获取校正后的降维结果
latent = model.get_latent_representation()
adata.obsm['X_scVI'] = latent
# 后续邻居和UMAP
sc.pp.neighbors(adata, use_rep='X_scVI')
sc.tl.umap(adata)
选择建议:
- 数据量 < 5万细胞,用Seurat(R用户首选)或Harmony(Python/R通用)。
- 数据量 5-50万,Harmony 最快最稳。
- 数据量 > 50万,或者多模态数据(比如CITE-seq),上scVI。
- 如果你不会调深度学习参数,先试Harmony,再考虑scVI。
三种方法优劣势对比
我整理了一个表,方便你按项目需求快速决策。
| 维度 | Seurat | Harmony | scVI |
|---|---|---|---|
| 语言环境 | R | R + Python | Python |
| 是否依赖原始count | 是 | 否(PCA后) | 是 |
| 计算资源 | 内存相对高 | CPU/内存均衡 | GPU必需 |
| 保留全局结构 | 偏保守,容易过度校正 | 适中 | 灵活性最大 |
| 可解释性 | 高(锚点可解释) | 中 | 低(黑盒) |
| 我踩过的坑 | 整合后差异分析用错Assay | PCA质量差时校正无效 | 模型不收敛,需要调learning rate |
知识体系框架
我把本章的核心逻辑画成了一张图,方便你理解整合在整个单细胞流程中的位置,以及方法之间的关系。
这张图展示了整合的基本流程:从原始数据出发,识别批次效应,选择合适方法(Seurat/Harmony/scVI),得到校正后的表征,然后做下游分析。而咱们这门课的核心——kBET和LISI——正是用来校验整合效果的工具。别只看整合跑完就算完,没有校验的整合等于盲打。
我的选择建议
作为过来人,我建议你按这个思路来:
- 先跑Seurat(如果你用R),或者先跑个简单的Harmony(Python),快速看到结果。
- 用kBET/LISI检查一次整合质量(我们后面章节会详细讲)。如果分数不行,再换方法。
- 数据量超大时,直接上scVI,但记得提前准备GPU。
别纠结哪个方法「最好」。我做过对比实验,同一个数据集,Seurat、Harmony、scVI三个跑出来的UMAP看起来差异不大。真正有区别的是你对下游分析的把握,以及整合后校验是否到位。