ARTICLE DETAIL

资讯详情

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

单细胞轨迹分析实战:用monocle3揭示细胞状态转变的连续路径

单细胞轨迹分析实战:用monocle3揭示细胞状态转变的连续路径 上个月处理一批疾病模型的组织单细胞数据聚类之后UMAP很好看巨噬细胞三个亚群排成三角中性粒细胞一坨T细胞几朵。领导看完图只问了一句这三群巨噬细胞是什么关系是活化过程中的连续状态还是本质上独立的亚群这个问题聚类回答不了。我需要的是一条能把离散细胞群串起来的单细胞轨迹分析于是打开了monocle3。monocle3是目前R生态里最常用的单细胞轨迹分析工具之一。它的核心思路是根据细胞转录组的相似性把离散的细胞群重新排列成一条或多条连续路径再用拟时序值描述每个细胞在这条路径上的位置。对疾病研究来说它最常回答的问题就两个谁变成了谁以及状态切换发生在哪个节点。这篇文章会从环境准备一直写到结果解读中间穿插我实际跑数据时踩过的坑。如果你手头刚好有一份单细胞表达矩阵正打算做细胞发育轨迹或状态转变分析可以直接照着操作。1. 单细胞轨迹分析到底在解决什么问题1.1 聚类是快照疾病是过程所有单细胞测序实验本质上只给你一个快照取样的那一刻组织里所有细胞的状态都被固定下来。但疾病的发展是一个连续过程——巨噬细胞从静息态被激活T细胞从初始态逐步走向耗竭成纤维细胞从静息态过渡到肌成纤维细胞。快照里其实同时含有早期和晚期的细胞只是聚类算法天生只会把这些细胞分堆并不会告诉你怎么把堆与堆之间的先后顺序排出来。轨迹分析做的事情就是把这个过程从快照里恢复出来。它的基本假设是一个细胞沿着某个生物学过程推进时转录组会发生连续变化所以在降维空间里处于不同阶段的细胞会按相似程度排成一条路径。找到这条路径你就能推断哪些细胞在起点、哪些在末端、哪些是中间态。疾病研究里经常需要这种信息——比如证明某群致病细胞不是天生如此而是从正常状态逐渐转化来的。这种逐渐转化的证据往往比一张差异表达热图更有说服力。1.2 为什么选monocle3不选monocle2/slingshot/scVelo这年头做轨迹分析的选项很多我用一个表格简单梳理主干工具之间的区别工具基本思路适用场景主要限制monocle2DDRTree降维把细胞拟合到树状结构样本量小、轨迹结构简单的分化过程大数据慢分支结构单一灵活性不足slingshot基于已有聚类和降维坐标拟合平滑曲线起点和终点明确、轨迹接近线性的场景对聚类结果依赖很强复杂分支处理吃力scVeloRNA速率根据剪接/未剪接RNA比例推断速度向量有spliced/unspliced计数信息的实验对数据质量、预处理非常敏感学习曲线较陡monocle3UMAP降维 主图学习支持多分支大规模数据、复杂分支、多批次整合拟时序方向需要自己指定rootmonocle3最打动我的有三点一是性能UMAP降维加主图学习的方案在几万细胞的数据集上不会让人等到崩溃二是内置了批次校正接口多批次整合时省掉不少折腾三是它把轨迹理解成一张图而不是一棵固定的树处理炎症、纤维化这类存在多个细胞状态来回切换的场景比传统树模型自然得多。当然它也不是万能的轨迹方向要靠自己定义root主图有时会乱七八糟这些坑后面我会专门讲。2. 环境准备与数据输入最先翻车的地方2.1 安装与依赖在R生态里建一块干净的地盘先给一套我亲测能走的安装路径install.packages(BiocManager) BiocManager::install(c(BiocGenerics, DelayedArray, DelayedMatrixStats, limma, S4Vectors, SingleCellExperiment, SummarizedExperiment, batchelor)) install.packages(devtools) devtools::install_github(cole-trapnell-lab/monocle3)这套流程在ubuntu和macOS上都顺利跑通过。最容易翻车的点在devtools::install_github那一刻如果系统缺编译工具链或者本机igraph版本太旧会抛一堆Rcpp编译报错。我的建议是给monocle3单独建一个conda环境或者用renv做快照别和其他包搅在一起互相升级依赖。R版本尽量用4.2以上可以避开大部分兼容性问题。提示如果报错信息很长先判断是编译阶段失败还是依赖解析阶段失败。编译失败优先补系统库g、libgdal、udunits2依赖解析失败优先升级或回退igraph、Rcpp而不是反反复重重装monocle3。2.2 构建CDS对象矩阵喂对了后面才省心数据进入monocle3的第一个动作是构建cell_data_set对象。如果你有Seurat对象装了SeuratWrappers后可以直接用as.cell_data_set(seurat_obj)转换。如果你想少装一个包或者想彻底弄清楚数据是怎么进去的自己构建也很简单expression_matrix - GetAssayData(seurat_obj, assay RNA, slot counts) cell_metadata - seurat_objmeta.data gene_metadata - data.frame(gene_short_name rownames(seurat_obj)) rownames(gene_metadata) - rownames(seurat_obj) cds - new_cell_data_set(expression_matrix, cell_metadata cell_metadata, gene_metadata gene_metadata)如果从10X矩阵开始换成Read10X读入表达矩阵再配好cell_metadata和gene_metadata就行。这里有三个很容易踩的细节。第一表达矩阵必须保持细胞在列、基因在行而且最好是稀疏矩阵。直接把dense matrix塞进来几万细胞的内存会迅速爆炸。第二cell_metadata的行名要和矩阵列名一一对应gene_metadata的行名要和矩阵行名一一对应这个顺序错了后面所有步骤都会报rownames do not match。第三喂给monocle3的是原始counts。monocle3内部会自己做normalization和PCA你把Seurat里已经normalized甚至scale过的数据传进去反而会让结果变味。2.3 QC过滤的度轨迹分析比普通聚类更挑剔很多人的习惯是在Seurat里做严格QC把质量差的细胞全部滤掉再转给monocle3。这个思路对但要掌握好度。普通聚类巴不得只留轮廓清晰的细胞轨迹分析恰恰需要那些UMAP上夹在两群之间的中间态细胞这些细胞往往表达特征不够明显很容易被严格QC阈值误伤。QC阈值卡得太死连续轨迹会被硬生生拆成几个孤立的partition后续learn_graph学出来的东西就会缺胳膊少腿。我的建议是标准线只用于过滤低质量细胞和明显的双细胞稀有细胞群和过渡态在轨迹阶段先保留等轨迹结构确定之后再判断它们是不是技术噪声。3. 核心分析流程六步走通完整链路整个流程非常固定preprocess_cds→align_cds可选→reduce_dimension→cluster_cells→learn_graph→order_cells。后面每一步都建立在前一步的结果上所以建议放在同一个脚本里顺序执行方便追溯和复现。3.1 预处理和批次校正控制噪声的第一道闸cds - preprocess_cds(cds, num_dim 100)num_dim是PCA保留的维度数。官方教程默认100但我处理细胞只有几千的小课题时通常会缩到50因为后面的PC维度开始混入大量技术噪声。判断依据很简单换几个num_dim值跑一遍看后面UMAP的轨迹结构是否稳定。如果结果在50到150之间变化剧烈说明数据里的主生物学信号不够强此时应该回头检查QC和normalization而不是继续堆参数。如果样本来自不同批次可以在预处理后加一步批次校正cds - align_cds(cds, alignment_group batch) cds - reduce_dimension(cds, preprocess_method Aligned)alignment_group填的是cell_metadata里代表批次的列名。注意align_cds不是万能的如果某个细胞类型只在一个批次里出现它可能被当成批次效应削掉所以校正完一定要检查关键marker的表达还稳不在。3.2 UMAP降维和聚类轨迹的地基必须稳cds - reduce_dimension(cds, reduction_method UMAP) cds - cluster_cells(cds, resolution 1e-3)cluster_cells用的是Leiden算法它会先把细胞划分到不同partition这些partition决定了后续主图的基本框架。这里你只需要记住一个概念partition数量越多轨迹越容易被切碎所以resolution的选择需要反复试。我的习惯是先跑1e-3看能不能分出预期中的细胞群两群重要细胞糊在一起就把resolution调大一群细胞被切得稀碎就调小。最终标准永远是聚类结果能和你已知的marker对得上而不是单纯追求数字好看。3.3 learn_graph从点云里长出主干道cds - learn_graph(cds, use_partition TRUE, close_loop TRUE)learn_graph做的事情可以类比成画地铁图UMAP里每个细胞是一个停靠站算法要找出哪些站之间最可能直接相连再把大量连接关系简化成几条清晰的主干线。use_partition TRUE表示每个partition内部单独学习路径close_loop TRUE则允许轨迹连成环。发育过程一般用不到环状结构但细胞周期或某些可逆的状态切换反而适合环状轨迹。我通常的做法是先跑默认参数看主图结构和生物学预期是否一致不一致再回头改。别指望一版参数就完美这步就是体力活。3.4 order_cells给路径装一个时间箭头轨迹图本身没有方向必须由你来指定root。如果使用图形界面环境可以直接在plot_cells的结果上用choose_graph_segments()交互选择。如果想在代码里指定可以这样cds - order_cells(cds, root_cells colnames(cds)[...])更稳的办法是先根据已知marker选出一批早期细胞再让它们在主图上投票选出根节点。官方教程里有一个经典函数我稍微改编后一直在用get_earliest_principal_node - function(cds, time_bin cell_type) { cell_ids - which(pData(cds)[, time_bin] Progenitor) closest_vertex - cdsprincipal_graph_aux[[UMAP]]$pr_graph_cell_proj_closest_vertex closest_vertex - as.matrix(closest_vertex[colnames(cds), ]) root_pr_node - igraph::V(principal_graph(cds)[[UMAP]])$name[ as.numeric(names(which.max(table(closest_vertex[cell_ids, ])))) ] root_pr_node } cds - order_cells(cds, root_pr_nodes get_earliest_principal_node(cds))这个函数的逻辑是把所有标记为Progenitor的细胞映射到主图上的最近顶点找出映射最多的那个顶点作为根节点。root的选择直接决定所有细胞拟时序值的方向所以这一步值得多花时间做敏感性分析而不是随手选一个细胞就开始往下跑。3.5 找到拟时序相关基因彩虹图只是开始轨迹画出来之后别停在展示层面。用graph_test找沿着主图显著变化的基因pr_graph_test_res - graph_test(cds, neighbor_graph principal_graph, cores 4) pr_graph_test_res - pr_graph_test_res[order(pr_graph_test_res$q_value), ]然后挑感兴趣的基因用plot_genes_in_pseudotime绘制表达量随拟时序的变化cds_subset - cds[rowData(cds)$gene_short_name %in% c(geneA, geneB), ] plot_genes_in_pseudotime(cds_subset, color_cells_by pseudotime)graph_test会考虑细胞在主图上的邻接关系比简单把pseudotime和表达量做回归更合理也更贴近轨迹结构本身。如果想做更精细的建模可以用fit_models拟合~pseudotime的模型gene_fits - fit_models(cds, model_formula_str ~pseudotime) fit_coefs - coefficient_table(gene_fits) head(fit_coefs)另外很多人不知道pseudotime(cds)这个函数可以直接提取拟时序值返回的是一个命名向量barcode对应细胞名。这个向量很适合用来和临床性状做关联分析或者按拟时序区间把细胞分成几个bin做组间比较是下游分析里很常用的一个输出。4. 把轨迹图翻译成生物学结论4.1 三种解读框架方向、阶段和状态细胞沿轨迹排列不等于细胞沿着时间演化。我在实际课题里总结出至少三种解读方式拿到轨迹后应该先想清楚自己属于哪一种。第一种是分化。祖细胞沿轨迹逐步成熟末端细胞表达终末分化marker这是拟时序最经典的理解适用于发育生物学和干细胞研究。第二种是状态转换。同一类细胞在不同微环境信号下发生表型迁移比如巨噬细胞从促炎状态转向促修复状态这种转变不一定是单向不可逆的。第三种是连续状态。细胞本身就处在连续谱上聚类只是人为切出来的格子轨迹更多是描述状态空间而不是严格的时间顺序。拿到轨迹图的第一个动作不是急着截图放进PPT而是先列一组已知marker看它们在拟时序上的变化顺序是否和其中一种解释吻合。如果marker顺序乱套先怀疑root选反了再怀疑预处理有问题而不是直接开始写沿着拟时序逐渐上升这种话。4.2 分支点是谱系决定还是状态分岔轨迹出现分支时直觉会告诉你这里是命运决定的岔路口。但分支也可能只是同一群细胞对不同信号的不同反应未必是真正的谱系决定。怎么区分我有两个常用手段。第一看分支点两侧的细胞是否共享一批激活状态marker。如果两侧都高表达相同的早期激活基因只是后续走向不同那更像状态分岔如果两侧从一开始就表达完全不同的转录因子谱系决定的说法才更有底气。第二专门去搜分支富集的转录因子。某个TF只在其中一个支路被激活并且功能实验已经证实它能推动细胞命运转变那么这个分支点才配叫决定点。4.3 疾病研究里最常见的三种落地姿势这几年在肿瘤、自身免疫和纤维化方向的文献里轨迹分析的套路其实很固定。最经典的场景是T细胞耗竭轨迹初始T细胞到耗竭前体再到终末耗竭配合PD-1、TOX、TCF7这些marker的表达变化能支撑耗竭是逐步获得的结论。第二个是巨噬细胞极化轨迹从促炎状态到促修复状态往往是一个连续转换用轨迹分析可以定位转换发生在拟时序的哪一段。第三个是药物处理前后的状态迁移把对照组和处理组的细胞同时投射到轨迹上比较各组细胞在拟时序区间的占比从而说明药物是阻断还是加速了某一状态转换。举个例子。我之前在做纤维化模型的巨噬细胞数据时聚类分出了五群其中SPP1高表达亚群和FABP4高表达亚群离得很远。如果只做聚类结论大概率是存在一群促纤维化SPP1巨噬细胞。但monocle3轨迹显示大量中间态细胞分布在这两个亚群之间沿着轨迹还能看到一系列促纤维化因子的表达逐步上升。这就把一个静态结论升级成了动态结论巨噬细胞在纤维化进程中逐步获得促纤维化表型。这种讲法在文章里显然更有分量。5. 那些让轨迹图翻车的细节我的排查笔记5.1 root选择太随意结论会直接翻转同一个数据集我把root选在naive T细胞群轨迹显示耗竭逐渐加重换成某个过渡态细胞做root轨迹就变成了从耗竭往回走。拟时序的方向在数学上没有绝对依据所以必须做敏感性分析。具体操作准备两组或三组候选root细胞分别跑order_cells然后提取pseudotime向量检查目标marker基因与pseudotime的Spearman相关性方向是否一致。如果某个root导致主要marker的相关性方向和其他root相反说明这个root不稳健不能采用。如果趋势都稳定才能放心把结论写进论文。5.2 学出来的主图像毛线团learn_graph之后出现一团乱线最可能的原因有三个partition太多每条线各学各的组合起来就成了乱麻num_dim太大PCA把噪声也当成了结构数据里混了大量细胞周期差异导致相似的分裂状态被识别成不同的轨迹方向。排查顺序建议是先调resolution减少partition数量再降低num_dim最后考虑用Seurat的CellCycleScoring判断是否需要回归周期效应。如果这些方案都试完还是乱别硬跑全图直接用choose_graph_segments截取自己关心的子轨迹重新排序。这个方法在展示和下游分析里都更可控审稿人也不会要求你必须展示全部细胞连起来的主图。5.3 graph_test结果爆炸怎么筛真正重要的基因一次graph_test跑出来几千个q值显著的基因非常正常这时候要做的是筛选而不是把所有显著基因都放到图里。我的筛选顺序是先看q值再看基因表达变化的幅度最后结合功能通路基因集做交集。另外有一个很实用的正对照技巧如果已知的耗竭marker比如TOX、PDCD1都没有排在最前面那八成是轨迹或root出了问题而不是数据里真的没有相关基因。先修轨迹再谈基因列表顺序不能反。5.4 大数据集跑不动降采样验证参数全量出结果几万细胞跑learn_graph内存占用相当可观。一个实用策略是先用降采样版本调试参数比如每个cluster抽200个细胞确认轨迹结构和基因趋势稳定后再用全量数据跑最终结果。另外cluster_cells和graph_test都支持cores参数并行开足能省不少时间。R语言本身不擅长管理大对象跑完的中间结果记得及时清理别让全局环境变成一个几十GB的垃圾场。5.5 可重复性固定种子也固定版本monocle3的UMAP和聚类默认不固定随机种子所以正式脚本里要在preprocess_cds之前写一行set.seed(42)。不同版本的R和monocle3也可能带来细微结果差异提交论文代码时把sessionInfo()一起附上reviewer要复现的时候能少很多无谓的来回沟通。这个习惯在我投期刊时救过一次值得从第一天就坚持。按我自己的经验每次跑完monocle3拿到漂亮的轨迹图先别急着高兴。强制回答自己三个问题第一root选择有没有依据换个root结论还稳吗第二分支点的生物学解释有没有其他可能第三筛出来的关键基因能不能用流式、免疫荧光或体外实验验证一下。三个问题都答得上来这篇轨迹分析才算真正能放进文章里。希望这篇文章能让你少踩几个安装和调参的坑也希望你下次拿到彩虹图的时候能比之前的我冷静一点。
返回列表