单细胞轨迹分析(Trajectory Analysis)¶
一句话概述¶
单细胞轨迹分析通过对细胞进行拟时序排列(pseudotime ordering),重建细胞分化或状态转变的动态过程,核心工具包括Monocle3、Slingshot、RNA velocity(scVelo)和CytoTRACE。
核心知识点表格¶
| 知识点 | 说明 |
|---|---|
| Pseudotime(拟时序) | 对细胞赋予一个连续的"时间"值,反映其在分化轨迹上的位置 |
| Monocle3 | 基于UMAP+学习图(principal graph)的轨迹推断工具 |
| Slingshot | 基于MST(最小生成树)+ 曲线拟合的轨迹方法 |
| RNA velocity | 利用剪接/未剪接mRNA比值推断细胞状态变化方向 |
| CytoTRACE | 基于基因表达数来评估细胞分化潜能(stemness) |
| 分支分析 | 识别细胞分化过程中的分叉点(bifurcation) |
| 差异表达基因沿轨迹 | 找到随拟时序显著变化的基因 |
| Root cell | 轨迹分析的起点细胞,需要生物学先验知识确定 |
| Spliced/Unspliced | RNA velocity中的核心概念:成熟mRNA vs 前体mRNA |
| Graph-based trajectory | Monocle3使用的reversed graph embedding方法 |
各步骤详解¶
第一步:数据准备与预处理¶
白话解释: 轨迹分析建立在高质量的降维和聚类结果之上。先做好标准scRNA-seq分析流程(QC→归一化→高变基因→PCA→UMAP→聚类),再进入轨迹分析。
技术细节: - 输入数据通常是Seurat或SingleCellExperiment对象 - Monocle3使用自己的cell_data_set (cds)对象 - 建议仅选取与分化相关的细胞亚群(如祖细胞+分化细胞),去除无关细胞减少噪声 - 确保batch effect已校正(Harmony/Seurat Integration)
代码示例:
# Seurat对象转Monocle3
library(Seurat)
library(monocle3)
library(SeuratWrappers)
# 假设已有seurat_obj
# 子集选取相关细胞
sub_obj <- subset(seurat_obj, idents = c("HSC", "Progenitor", "Monocyte", "DC"))
# 转换为monocle3 cds对象
cds <- as.cell_data_set(sub_obj)
cds <- cluster_cells(cds, resolution = 1e-3)
第二步:Monocle3轨迹推断¶
白话解释: Monocle3在UMAP空间中学习一个"图"(像树枝一样的结构),把细胞投影到这个图上,然后从你指定的起点开始计算每个细胞的"拟时间"。
技术细节: - learn_graph() 使用reversed graph embedding在降维空间拟合主图 - order_cells() 需要指定root node(起点) - 支持多分支轨迹 - 使用graph_test()找沿轨迹变化的基因(Moran's I统计量)
代码示例:
# 学习轨迹图
cds <- learn_graph(cds, use_partition = TRUE)
# 可视化轨迹
plot_cells(cds,
color_cells_by = "cluster",
label_groups_by_cluster = FALSE,
label_leaves = FALSE,
label_branch_points = FALSE)
# 指定root并排序(交互式或程序化)
# 方法1:交互式
cds <- order_cells(cds)
# 方法2:程序化指定root
# 找到特定cluster中最早分化状态的细胞
get_earliest_principal_node <- function(cds, cluster_name = "HSC"){
cell_ids <- which(colData(cds)[, "seurat_clusters"] == cluster_name)
closest_vertex <- cds@principal_graph_aux[["UMAP"]]$pr_graph_cell_proj_closest_vertex
closest_vertex <- as.matrix(closest_vertex[colnames(cds), ])
root_pr_nodes <- igraph::V(principal_graph(cds)[["UMAP"]])$name
root_pr_nodes <- closest_vertex[cell_ids, ]
root_pr_nodes <- names(which.max(table(root_pr_nodes)))
root_pr_nodes
}
cds <- order_cells(cds, root_pr_nodes = get_earliest_principal_node(cds))
# 可视化pseudotime
plot_cells(cds,
color_cells_by = "pseudotime",
label_cell_groups = FALSE,
label_leaves = FALSE,
label_branch_points = FALSE,
graph_label_size = 1.5)
# 找沿轨迹变化基因
track_genes <- graph_test(cds, neighbor_graph = "principal_graph", cores = 4)
track_genes <- track_genes[order(track_genes$morans_I, decreasing = TRUE), ]
head(track_genes, 20)
第三步:Slingshot轨迹分析¶
白话解释: Slingshot先用最小生成树(MST)连接各个cluster,确定大致的分支结构,再用平滑曲线拟合每条分支的轨迹,最后给每个细胞在每条曲线上投影得到pseudotime。
技术细节: - 输入:降维坐标(PCA或UMAP)+ 聚类标签 - 步骤1:MST连接cluster质心 - 步骤2:simultaneous principal curves拟合 - 输出:每条lineage的pseudotime - 适合有明确分支结构的数据
代码示例:
library(slingshot)
library(SingleCellExperiment)
# 从Seurat转SCE
sce <- as.SingleCellExperiment(sub_obj)
# 运行slingshot
sce <- slingshot(sce,
clusterLabels = 'seurat_clusters',
reducedDim = 'PCA',
start.clus = "0") # 指定起始cluster
# 获取pseudotime
pseudotime_mat <- slingPseudotime(sce)
head(pseudotime_mat)
# 获取曲线
curves <- slingCurves(sce)
# 可视化
library(ggplot2)
library(RColorBrewer)
# 在UMAP上画轨迹
umap_coords <- reducedDim(sce, "UMAP")
df <- data.frame(UMAP1 = umap_coords[,1],
UMAP2 = umap_coords[,2],
pseudotime = pseudotime_mat[,1],
cluster = colData(sce)$seurat_clusters)
ggplot(df, aes(x = UMAP1, y = UMAP2, color = pseudotime)) +
geom_point(size = 0.5) +
scale_color_viridis_c() +
theme_minimal()
# 找沿轨迹差异表达基因(tradeSeq)
library(tradeSeq)
# 拟合GAM模型
sce_gam <- fitGAM(counts = counts(sce),
sds = SlingshotDataSet(sce),
nknots = 6,
verbose = TRUE)
# 关联检验
assoc_res <- associationTest(sce_gam)
assoc_res <- assoc_res[order(assoc_res$pvalue), ]
head(assoc_res, 20)
第四步:RNA velocity分析¶
白话解释: RNA velocity利用一个简单的物理思想——如果一个基因的未剪接mRNA(前体)多于成熟mRNA,说明这个基因正在"加速转录";反之就是在"减速"。把所有基因的这种趋势综合起来,就能推断每个细胞下一步会往哪个方向变化。
技术细节: - 需要从BAM文件中定量spliced/unspliced counts(使用velocyto或STARsolo) - scVelo(Python)是目前最主流的RNA velocity工具 - 支持stochastic model和dynamical model - dynamical model更准确但计算量更大
代码示例:
# 1. 用velocyto从BAM生成loom文件
velocyto run10x /path/to/cellranger_output /path/to/genes.gtf
# 或使用STARsolo直接输出spliced/unspliced
STAR --runMode alignReads \
--soloType CB_UMI_Simple \
--soloFeatures Gene Velocyto \
--outSAMtype BAM SortedByCoordinate \
...
# 2. Python: scVelo分析
import scvelo as scv
import scanpy as sc
import anndata as ad
# 读取数据
adata = sc.read_h5ad("processed_adata.h5ad")
ldata = scv.read("velocyto_output.loom", cache=True)
# 合并spliced/unspliced信息
adata = scv.utils.merge(adata, ldata)
# 预处理
scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=2000)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
# 3. 动态模型(推荐)
scv.tl.recover_dynamics(adata, n_jobs=8)
scv.tl.velocity(adata, mode='dynamical')
scv.tl.velocity_graph(adata)
# 4. 可视化
scv.pl.velocity_embedding_stream(adata, basis='umap', color='celltype')
scv.pl.velocity_embedding_grid(adata, basis='umap', color='celltype')
# 5. Latent time(类似pseudotime)
scv.tl.latent_time(adata)
scv.pl.scatter(adata, color='latent_time', cmap='gnuplot')
# 6. 找driver基因
top_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index[:20]
scv.pl.scatter(adata, basis=top_genes[:5], ncols=5, frameon=False)
第五步:CytoTRACE分化潜能评估¶
白话解释: CytoTRACE基于一个观察:越"干"(越年轻)的细胞,表达的基因种类数(gene count)越多。它利用这个规律,结合基因间的相关性,给每个细胞打一个"分化潜能分数"。
技术细节: - 核心指标:基因表达丰富度(number of genes expressed) - 不需要指定root cell,是无监督方法 - CytoTRACE2是最新版本,基于深度学习 - 可以辅助确定轨迹分析的起点
代码示例:
# CytoTRACE(R版本)
# 安装
# devtools::install_github("digitalcytometry/cytotrace")
library(CytoTRACE)
# 输入:基因×细胞的表达矩阵(raw counts或normalized均可)
expr_matrix <- as.matrix(GetAssayData(sub_obj, slot = "counts"))
# 运行CytoTRACE
results <- CytoTRACE(expr_matrix, ncores = 8)
# 结果包含:
# CytoTRACE score: 每个细胞的分化潜能(0=分化完全, 1=最干)
# CytoTRACErank: 排名
# 添加到Seurat对象
sub_obj$CytoTRACE <- results$CytoTRACE[colnames(sub_obj)]
# 可视化
FeaturePlot(sub_obj, features = "CytoTRACE") +
scale_color_viridis_c(option = "plasma")
# 按cluster查看分化潜能
VlnPlot(sub_obj, features = "CytoTRACE", group.by = "seurat_clusters")
# CytoTRACE 2(Python版本)
import cytotrace2 as ct2
# 输入AnnData
results_adata = ct2.cytotrace2(adata)
# 结果存储在 adata.obs['CytoTRACE2_Score']
sc.pl.umap(results_adata, color='CytoTRACE2_Score', cmap='RdYlBu_r')
第六步:轨迹差异基因分析与可视化¶
白话解释: 找到那些随着轨迹(pseudotime)变化而表达量显著改变的基因,画出基因表达沿轨迹的趋势图。
代码示例:
# Monocle3: 模块化基因表达
# 已有track_genes结果
sig_genes <- track_genes %>%
filter(q_value < 0.05) %>%
arrange(desc(morans_I)) %>%
head(200)
# 基因模块分析
gene_module_df <- find_gene_modules(cds[rownames(cds) %in% sig_genes$gene_short_name, ],
resolution = 1e-2)
# 热图展示基因模块沿轨迹的表达
plot_cells(cds,
genes = gene_module_df %>% filter(module %in% c(1, 2, 3)),
label_cell_groups = FALSE,
show_trajectory_graph = FALSE)
# tradeSeq: 沿轨迹的基因表达趋势
library(tradeSeq)
plotSmoothers(sce_gam, counts(sce), gene = "CD34")
plotSmoothers(sce_gam, counts(sce), gene = "GATA1")
# 在分支点比较两条lineage
branch_res <- earlyDETest(sce_gam, knots = c(3, 4))
branch_res <- branch_res[order(branch_res$pvalue), ]
实战命令(可复制)¶
# ===== 环境安装 =====
# R包安装
# install.packages("BiocManager")
# BiocManager::install(c("monocle3", "slingshot", "tradeSeq"))
# devtools::install_github("digitalcytometry/cytotrace")
# Python包安装
pip install scvelo scanpy velocyto anndata
# velocyto安装(需要samtools)
pip install velocyto
conda install -c bioconda samtools
# ===== velocyto生成loom =====
velocyto run10x \
/data/cellranger/sample1/outs \
/ref/refdata-gex-GRCh38-2020-A/genes/genes.gtf \
-o /data/velocity/
# ===== 完整Monocle3流程 =====
# 见上文R代码
# ===== scVelo完整流程 =====
# 见上文Python代码
面试常问点¶
Q1: Pseudotime和real time有什么关系?¶
A: Pseudotime是一个相对的排序指标,反映细胞在分化轨迹上的相对位置,但不直接对应真实的物理时间。同一pseudotime值的两个细胞处于相似的分化状态,但不一定在同一时刻被采集。如需关联真实时间,需要时间序列实验(time-course)设计。
Q2: 如何选择轨迹分析的root cell?¶
A: 通常基于生物学先验:(1) 使用已知干细胞marker(如CD34+)所在的cluster;(2) 使用CytoTRACE评分最高的细胞;(3) 如果有时间序列数据,用最早时间点的细胞;(4) RNA velocity的起点方向也可辅助判断。
Q3: Monocle3和Slingshot的核心区别?¶
A: Monocle3使用UMAP+reversed graph embedding学习主图,更适合复杂拓扑结构;Slingshot基于PCA空间的MST+principal curves,计算更轻量,统计框架更完善(结合tradeSeq),更适合较清晰的线性/分支结构。
Q4: RNA velocity的dynamical model比stochastic model好在哪?¶
A: Stochastic model假设所有基因处于稳态(steady-state),用全局斜率拟合;dynamical model为每个基因独立拟合完整的splicing动力学参数(转录速率α、剪接速率β、降解速率γ),不需要稳态假设,能捕捉暂态基因行为,结果更可靠。
Q5: CytoTRACE的假设是什么?什么时候会失效?¶
A: 假设是"分化潜能越高的细胞表达更多种基因"。在以下情况可能失效:(1) 高度活跃的免疫细胞(如activated T cells基因数也很高但并非干细胞);(2) 应激状态下基因表达模式异常;(3) 数据技术质量不均导致gene count差异。
Q6: 如何验证轨迹分析结果的可靠性?¶
A: (1) 生物学验证:已知marker基因是否沿轨迹呈现预期表达模式;(2) 方法一致性:多种工具(Monocle3/Slingshot/PAGA)结果是否一致;(3) RNA velocity方向是否与pseudotime方向吻合;(4) 子采样稳定性:随机去除20%细胞后轨迹是否稳定;(5) 实验验证(如lineage tracing)。
Q7: PAGA是什么?和上述方法有什么关系?¶
A: PAGA(Partition-based Graph Abstraction)是Scanpy中的轨迹方法,它在cluster层面计算连通性,生成粗粒度的轨迹图。常用于:(1) 作为轨迹分析前的探索性分析;(2) 初始化UMAP布局使其更反映轨迹结构;(3) 与Monocle3/Slingshot结合使用进行交叉验证。
Q8: scVelo分析中如何处理多batch数据?¶
A: (1) 各batch分别跑velocyto/STARsolo得到loom文件后合并;(2) 合并后的adata先做batch correction(如scVI或bbknn),使用校正后的邻域图;(3) velocity计算本身基于基因的splicing动力学,理论上不受batch影响,但邻域图质量很重要。
易错点¶
1. 不进行细胞子集选择直接跑全量数据¶
问题: 把所有细胞类型都放在一起跑轨迹分析,结果混乱。 正确做法: 只选取与目标分化过程相关的细胞亚群。比如研究造血分化,只取HSC到各谱系的细胞。
2. Monocle3中忘记指定正确的root¶
问题: 不指定root或指定错误,pseudotime方向反转。 正确做法: 结合生物学知识(marker基因、CytoTRACE分数)确定root位置。
3. RNA velocity中unspliced reads比例过低¶
问题: 如果unspliced比例<5-10%,velocity估计不稳定。 正确做法: 检查loom文件中spliced/unspliced比例;确认GTF注释包含intron信息;考虑使用更完整的基因注释。
4. 把UMAP坐标当作Slingshot输入¶
问题: UMAP是非线性降维,距离不保真,Slingshot在UMAP上拟合曲线可能产生artifact。 正确做法: Slingshot推荐使用PCA坐标(前15-30个PC)而非UMAP。
5. 混淆RNA velocity中的embedding stream方向和实际预测¶
问题: 过度解读UMAP上的箭头方向,忽略了UMAP本身的扭曲。 正确做法: 箭头方向在局部有参考意义,但全局拓扑应结合velocity graph的transition probability来解读。
6. tradeSeq拟合基因数过多导致内存爆炸¶
问题: 对所有基因运行fitGAM非常耗内存和时间。 正确做法: 先筛选高变基因或Slingshot/Monocle3预筛选的候选基因(通常2000-5000个),再运行tradeSeq。
7. 忽略轨迹分支的多重比较校正¶
问题: 在多条lineage上分别做差异基因检验,累计大量假阳性。 正确做法: 使用tradeSeq的多lineage检验框架(如patternTest、earlyDETest),已内置多重比较校正。
补充知识¶
工具选择指南¶
| 场景 | 推荐工具 |
|---|---|
| 复杂拓扑(多分支+环) | Monocle3 |
| 清晰线性/分支结构 | Slingshot + tradeSeq |
| 想知道分化方向 | RNA velocity (scVelo) |
| 评估干性/分化潜能 | CytoTRACE |
| 粗粒度探索 | PAGA (Scanpy) |
| 多条件差异轨迹 | condiments (结合Slingshot) |
新兴方法¶
- CellRank - 结合RNA velocity和转移概率,定义细胞命运概率,比单纯velocity更稳健。
- UniTVelo - 统一的RNA velocity框架,处理基因特异性动力学参数。
- Palantir - 基于扩散图(diffusion map)和Markov chain的命运概率方法。
- VeloVAE - 使用变分自编码器学习velocity,对噪声更稳健。
与空间转录组结合¶
- spateo - 可在空间坐标上做RNA velocity
- SpatialDM - 空间域内的分化轨迹推断
- 空间+轨迹结合可验证分化路径是否有空间组织模式
可视化建议¶
- 使用
plot_cells()(Monocle3)或sc.pl.velocity_embedding_stream()(scVelo)展示流向 - 热图展示top基因沿pseudotime的表达趋势(pheatmap/ComplexHeatmap)
- 使用
plotSmoothers()(tradeSeq)展示单基因沿轨迹的平滑表达曲线 - 考虑制作动画(ggplot2 + gganimate)展示分化过程
本教程涵盖了单细胞轨迹分析的主流方法,建议根据数据特点和生物学问题选择合适的工具组合,并进行多方法交叉验证。