ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

单细胞测序数据整合实战:Seurat与Harmony流程详解

单细胞测序数据整合实战:Seurat与Harmony流程详解 单细胞测序这几年是真的火但只要你手里样本一多超过三个、五个甚至十几个摆在你面前的第一道坎儿就是数据整合。我见过太多人把所有样本简单merge到一起之后跑个PCA结果UMAP图上每个样本抱成一团细胞类型被批次效应盖得严严实实。说实话单细胞测序数据整合这件事不是把矩阵拼起来就完事儿它直接决定后续找细胞类型、找marker、找差异基因这几步靠不靠谱。这篇博文就把我这几年的整合经验一次性讲透先讲明白为什么必须整合再把我常用的Seurat整合流程和Harmony流程完整跑一遍最后把那些“整过头”“整不匀”的坑挨个给你摆出来。不管是刚入门还在对着Seurat文档发懵的新手还是已经被批次效应折磨到怀疑人生的老手这篇文章都能给你一套直接拿去用的方案。1. 单细胞数据整合的整体设计与思路拆解1.1 为什么多批次样本不能直接合并跑聚类先说一个基础问题单细胞测序数据里的批次效应到底从哪儿来的。你以为同一套流程出来的数据就应该一致实际上完全不是。不同时间上机、不同建库批次、不同操作员、不同试剂盒lot号、不同测序深度都会让同一类细胞在表达谱上产生系统性偏移。更别提采样时间、组织处理方式、解离步骤的微小差别这些因素叠加在一起造成的结果就是同样一群T细胞来自样本A的跟来自样本B的在PCA空间里可能隔得老远。这种技术性差异和真实的生物学差异纠缠在一起你直接合并跑聚类算法会优先按技术差异把细胞分开而不是按生物学相似性聚在一起。比如同一个患者在治疗前后的两个样本如果批次效应处理不好治疗前的细胞和治疗后的细胞先各占一个山头真正的共享细胞亚群反而被拆散后续分析全乱套。我做过一个六个样本的整合项目临床样本占大半实验员不同、上机日期不同批次效应强到第一版UMAP出来样本来源的颜色和细胞类型marker的颜色几乎是完美对应的——这种结果如果直接拿去写文章审稿人一眼就能挑出来。所以数据整合的核心目标用一个词概括就是“去批次保生物”——在消除这批技术偏移的同时尽量不破坏不同细胞类型之间真实的转录差异。这本质上是一个高维空间对齐问题需要专门的算法去做不是简单用scale数据或回归掉样本变量就能解决的。1.2 整合方案的选型CCA、Harmony、scVI还是其他现在主流的数据整合工具大致分几类。Seurat的IntegrateData基于CCA典型相关分析MNN互近邻锚点机制在细胞群结构复杂、样本间共享细胞类型明确的时候表现稳。Harmony则走的是迭代软聚类加岭回归矫正路线速度极快、内存占用小十几万个细胞跑起来毫无压力。scVI用深度生成模型对超大数据的拟合能力很强模态灵活性也高但调参和训练时间对新手不太友好。还有fastMNN、Scanorama这类偏向单细胞剪接或大矩阵降维场景的。选型上我个人的习惯是十万细胞以下、样品数量十个以内、细胞类型跨样本相对保守的优先用Seurat的锚点整合尤其是RPCA加速版本。细胞数一上去几十万上百万的规模直接上Harmony快而且稳跑完效果也不会差。scVI适合那种样本量大、异质性极高、还要考虑多模态或者批次结构特别复杂的场景但它是一个模型训练过程需要你有GPU环境投入产出比要看项目预算。一句话总结没有绝对“最好的”整合算法只有“在这个数据结构下最合适”的。选型前先看自己数据量级、样本结构和硬件条件别一上来就追求花哨。2. 预处理细节不要跳过质量控制直接谈整合2.1 每个样本单独过滤比合并后统一过滤靠谱很多教程会告诉你先把所有样本合并成一个Seurat对象再统一跑QC过滤。这么做不是不行但非常不推荐。不同样本的细胞完整度、线粒体基因比例基线差异很大你用一个全局阈值过滤很可能把某个样本里的高质量细胞大批量误杀同时又放过了另一个样本里的死细胞和空液滴。我的标准操作是数据读进来之后每个样本单独建对象单独跑一遍QC。线粒体基因比例阈值一般在10%到20%之间调具体根据组织类型来——肝脏、心肌这类代谢旺盛的组织线粒体比率天生偏高硬卡在10%会把细胞群切掉一大块。检测到基因数(nFeature_RNA)下限通常卡在200到300上限根据测序深度放宽到5000到8000不等再结合UMI数(nCount_RNA)一起看优先过滤掉那些低复杂度、高线粒体、疑似doublet的细胞。实际上我在处理冷冻组织样本时线粒体比例阈值经常得放宽到25%因为冷冻解离过程本身就会造成线粒体损伤。这类经验必须结合自己的数据判断不要抄教程参数就完事。2.2 标准化和特征基因选择的一处关键区别数据经过QC之后接下来是NormalizeData和FindVariableFeatures。NormalizeData的LogNormalize方法是默认的选择它在每个细胞内做总量归一化后取对数运算快、效果稳定绝大多数场景够用。SCTransform是更进阶的选项它在归一化时同时建模并回归掉测序深度的影响对测序深度差异大的样本更友好。不过SCTransform运行慢、内存占用高而且整合流程里对锚点数目的计算方式有点不一样我自己除非样本间深度悬殊特别大一般还是走LogNormalize。Feature选择上FindVariableFeatures默认选2000个高变基因(hvg)这个数字是个性能和效果的平衡点。整合时锚点计算只基于这些高变基因不代表2000之外的基因全部忽略整合后的数据还是会包含全部基因的表达值只是锚点寻找和批次校正主要在特征基因空间内完成。我试过把特征数增到5000整合效果提升有限但运行时间明显拉长降到1000又会丢失一部分稀有细胞群的信息。所以800个2000个之间默认值优先特殊场景再调整。3. 核心实操Seurat整合全流程演示3.1 从拆分对象到CCA/RPCA锚点整合的完整步骤我用Seurat V5的语法来跑一遍整合流程。第一步是把合并对象拆回list逐样本归一化和找高变基因。这里有个细节如果样本数量多、每个样本细胞数量又不小建议用SelectIntegrationFeatures提前统一参与整合的特征基因再用PrepSCTIntegration在SCTransform流程里用做校正但我用的是LogNormalize流程所以直接走最标准的路线。library(Seurat) library(dplyr) # 假设已经有一个包含全部样本的seurat对象列名是样本ID obj_list - SplitObject(seurat_obj, split.by sample_id) # 对每个样本单独归一化、找特征基因、缩放到标准范围 for (i in names(obj_list)) { obj_list[[i]] - NormalizeData(obj_list[[i]], verbose FALSE) obj_list[[i]] - FindVariableFeatures(obj_list[[i]], selection.method vst, nfeatures 2000, verbose FALSE) } # 统一整合特征基因做数据缩放 features - SelectIntegrationFeatures(object.list obj_list, nfeatures 2000) obj_list - lapply(obj_list, function(x) ScaleData(x, features features, verbose FALSE)) # 找锚点这一步是关键 anchors - FindIntegrationAnchors( object.list obj_list, anchor.features features, reduction cca # 或 rpca大数据推荐rPCA ) # 执行整合 integrated - IntegrateData(anchorset anchors, dims 1:30)这一步有太多容易踩坑的细节。reduction cca是比较稳的默认选择但细胞数超过五万之后CCA的计算开销非常大。实际项目中我常换成reduction rpca它先做PCA降维再跑CCA速度提升非常明显整合效果我对比下来几乎没差别。注意dims参数范围也重要锚点寻找时默认dims是1到30如果你的数据异质性特别高、细胞类型特别多可以把dims放宽到1到50但放宽之后锚点质量可能下降需要结合结果来判断。整合完成之后从integrated这个Assay开始往下走ScaleData、PCA、UMAP、找邻居、聚类。这里有个坑我必须单独拿出来说。# 设置整合后的assay为默认跑统一降维聚类 DefaultAssay(integrated) - integrated integrated - ScaleData(integrated, verbose FALSE) # 这一步在新版本里未必需要 integrated - RunPCA(integrated, npcs 30, verbose FALSE) integrated - RunUMAP(integrated, reduction pca, dims 1:30) integrated - FindNeighbors(integrated, reduction pca, dims 1:30) integrated - FindClusters(integrated, resolution 0.5)3.2 整合后找marker必须切回RNA assay整合流程跑完之后最容易犯的一个错误就是直接用整合后的assay去找差异基因和marker。整合后的表达矩阵里存的是矫正后的残差而不是真实表达值用来聚类和UMAP已经足够好但用来分析表达差异数值含义已经扭曲了。正确做法是找marker、做差异表达、画feature plot的时候先把默认assay切回原始的RNA数据。# 找marker之前切回RNA assay DefaultAssay(integrated) - RNA markers - FindMarkers(integrated, ident.1 T_cell, only.pos TRUE)这一条我吃了不少亏早期有次项目里用了整合数据的表达量做featureplot结果某个marker基因的表达模式完全失真好在反复检查数据时发现了问题。现在我的习惯是聚类、UMAP阶段用integrated注释、marker、拟时序、差异分析阶段全部用RNA assay。这个分工讲清楚之后项目就顺畅多了。3.3 Harmony整合流程五万细胞起步的选择如果你的数据规模已经过十万或者想快速跑一个整合结果出来做前期探索我推荐用Harmony。它的原理是先用PCA把数据降到低维空间然后通过迭代软分配的方式把不同批次的数据往中心对齐整个过程不太依赖高变基因的选取运行速度极快。# 基础流程 seurat_obj - NormalizeData(seurat_obj, verbose FALSE) seurat_obj - FindVariableFeatures(seurat_obj, selection.method vst, nfeatures 2000, verbose FALSE) seurat_obj - ScaleData(seurat_obj, verbose FALSE) seurat_obj - RunPCA(seurat_obj, npcs 30, verbose FALSE) # 安装并加载harmony包 # install.packages(harmony) library(harmony) # 跑harmony整合 seurat_obj - RunHarmony(seurat_obj, group.by.vars sample_id, reduction pca, dims.use 1:30) # 用harmony降维结果替代PCA来聚类和UMAP seurat_obj - RunUMAP(seurat_obj, reduction harmony, dims 1:30) seurat_obj - FindNeighbors(seurat_obj, reduction harmony, dims 1:30) seurat_obj - FindClusters(seurat_obj, resolution 0.5)Harmony的参数里theta控制批次差异矫正强度默认是2数值越大矫正越强。有的批次差异特别严重的数据我会把theta调到4或更高但要注意过高的theta可能把生物学差异一起抹平。还有一个我没少忽略的点Harmony跑的是PCA空间所以跑RunHarmony之前不要用别的降维结果直接拿PCA的结果丢进去就好。Harmony的另一个优势是它不改变原始表达矩阵只是输出一个矫正过的低维坐标所以下游分析中不需要像Seurat整合那样反复切换assay所有差异表达分析直接用原始RNA assay就行省心不少。4. 整合效果评估怎么判断到底有没有整好4.1 看UMAP混匀程度但不只看表面整合完成后第一件事肯定是画UMAP按样本来源上色看看有没有混匀。如果同一细胞类型的细胞在整合后还按样本形成明显分离的cluster说明矫正不足这时要检查是不是锚点数量不够、dims范围太小或者样本间细胞类型组成差异太大导致锚点质量不高。但UMAP混匀只是表象一个更客观的评估方式是看一下不同样本中同一细胞亚群的marker表达是否一致如果某个cluster的marker只在部分样本中表达很可能是这个cluster实际上代表的是不同样本里的不同细胞状态而不是真正的共享细胞类型。我在实际项目里还常用一个方法提取某个明确的细胞类型比如CD3 T细胞检查不同样本来源的T细胞在整合后的UMAP上是否重叠。如果整合效果好这些细胞应该交织在一起如果仍然明显分层就要警惕整合不足。另一个技巧是计算不同样本来源细胞在相邻聚类中的比例如果某几个样本总是孤立成簇优先检查这几个样本的QC指标是不是异常。4.2 警惕“过度整合”的信号整合不足的问题很多人关注但过度整合往往更隐蔽。过度整合的本质是算法为了消除批次效应把真实的生物学差异也当作批次效应给抹掉了。信号有几个整合后某些预期中应该分开的不同细胞类型被强制拉到一起UMAP上一整片细胞类型marker的表达呈现出渐变而不是清晰区分另一个信号是本来已知生物学上差异很大的亚群在整合后距离显著缩小比如不同谱系间在UMAP上混在一起聚类的稳定性下降。我处理过一个案例样本里既有正常组织又有肿瘤组织肿瘤组织的上皮细胞和正常组织的上皮细胞在转录状态上差异本来就大。用Seurat整合时如果锚点设置过于宽松算法会把这两群细胞强行往一个方向拉导致上皮细胞的肿瘤相关亚群消失掉。后来我把k.anchor从默认的5调高到10降低了锚点匹配的噪声结果保留了原有的生物学分离。这里要说的是调节整合参数时永远要问自己一个问题这个分离是技术造成的还是生物学造成的如果答案是生物学就应该保留而不是一味追求混匀。4.3 量化评估整合效果的几个工具除了肉眼观察UMAP可以用几个量化方法来评估整合质量。Seurat内置的DimPlot按样本分组配合split展示是一种快速检查方式还可以用scater包的plotExprHeatmap来看不同批次间marker基因表达的一致性。如果你想玩得更细可以计算每个细胞在批次间的“混合熵”这个值越接近均匀分布说明批间混匀越好。市面上也有像kBET、LISI这样的专门指标来检测批次效应矫正效果LISI分数尤其适合Harmony整合后的结果分数越高代表局部混合程度越好。5. 常见问题与排查技巧实录5.1 整合时报内存不够或者跑得太慢这是出现频率最高的问题。Seurat整合找锚点那一步内存开销和样本间细胞数量乘积成正比样本数量越多、每个样本细胞数越多计算量越大。解决思路有几种。第一个是换用RPCA流程这个在3.1已经提到能把计算时间压缩到原来的五分之一甚至更少。第二个是降低参与整合的特征基因数量从2000减到1500或1000代价是精度有微小损失但对常规数据影响不大。第三个最直接把样本拆得更碎比如一个样本如果有8万细胞可以按细胞类型先粗略拆分成两个子对象再整合这个操作需要你对数据结构有预判不太适合探索性分析。Harmony流程本身对内存的占用比Seurat小很多遇到资源瓶颈时我通常直接切到Harmony。5.2 整合后发现某类细胞消失了怎么办有一种情况比较令人生气跑完整合某种稀少细胞类型在整合后的聚类里看不到了。最常见的原因是整合过程中锚点多数集中在丰度高的细胞类型上稀有细胞因为数量少、与其它细胞相似度低没能形成足够的优质锚点最后被改造成了像邻近的细胞。这时可以试着降低k.filter的值减少对锚点的过滤强度让更多边缘锚点参与计算或者先不整合稀有细胞把它提取出来单独做整合。另一个选择是先把稀有细胞类型通过marker标记出来然后用subset提取后另跑一遍整合流程。如果你用的是Harmony稀有细胞丢失的问题也偶尔出现可以把lambda调低一点减少对批次向量的收缩程度保留更多原始信号。5.3 样本间细胞类型组成差异过大整合难以收敛还有一类头疼情况样本A只有T细胞样本B只有B细胞两个样本几乎没有共享的细胞类型。这时候CCA找锚点很难找到跨样本对应的细胞整合结果容易失效——UMAP上两批样本仍然分居两侧强行整合还会让细胞群形变。遇到这种情况我的建议是不要盲目整合。先明确你要回答的生物学问题如果是想比较两个样本各自的细胞组成那不需要整合直接做聚类加差异丰度分析就够了如果是想找一个共同的亚群结构就得先保证样本间至少有一定的共享细胞类型基础。实在想整合也可以考虑从公共数据库拉一个参考转录组数据来填补锚点缺失的空白这个操作对数据质量要求比较高适合进阶玩家。5.4 常见问题速查表症状可能原因处理方案聚类结果仍然按样本分离锚点太少或dims太小批次效应强检查锚点数目适当增大k.anchor或dims或改用Harmony整合后marker表达失真用integrated assay做差异表达切换回RNA assay做marker分析稀有细胞类群消失锚点偏向丰度高的细胞类型降低k.filter或单独提取稀有细胞整合内存/时间爆炸数据量太大CCA开销高换用RPCA流程或改用Harmony整个UMAP结构糊成一团过度整合抹平了生物学差异调低Harmony的theta或降低k.anchor数值部分样本不参与整合样本间无共享细胞类型补充参考数据或放弃直接整合改用比较策略6. 写在最后的一点个人体会整合流程本身跑通不难真正花时间的往往是把各种边界情况想清楚。我个人的经验是别一上来就追求复杂的整合算法先用merge把所有样本跑一遍UMAP看看原始数据里哪些样本是混在一起的、哪些是分离的这能帮你预判整合难度。然后再决定走Seurat还是Harmony先拿少量参数快速跑一个效果预览再根据预览结果调参。另外强烈建议每跑一个整合版本就把关键参数和对应UMAP图存档我见过不少人在参数调试过程中把之前效果好的配置覆盖掉了最后无法复现。数据整合是整个单细胞分析流程的地基地基没打牢后头盖什么楼都悬。希望这篇分享能让你少走几步弯路。
返回列表