跳转至

382_单细胞时间序列分析


一句话说明

单细胞时间序列分析就是研究"细胞如何从一个状态变化到另一个状态",比如干细胞分化过程,通过RNA速度和拟时序推断细胞的"成熟程度"。


核心知识点

要点1:两类主要问题

  1. 拟时序分析(Pseudotime):用静态快照数据推断细胞"分化时间线"(细胞不同步,所以快照里有各阶段细胞)
  2. RNA速度(RNA Velocity):利用未剪接(unspliced)和剪接(spliced)mRNA的比例推断细胞状态的"变化方向和速度"

要点2:主要工具对比

工具方法特点
Monocle3DDRTree 降维轨迹经典,可处理复杂分叉
Slingshot平滑样条曲线简单,与Seurat无缝
scVeloRNA 速度(ODE模型)揭示动力学方向
PAGA图抽象 + 轨迹适合大数据集
Palantir随机游走轨迹细胞命运概率

要点3:RNA 速度原理

  • 每个基因有:未剪接mRNA(nascent)剪接mRNA(mature)
  • 如果未剪接量 >> 剪接量:基因"正在被激活"(上调中)
  • 如果未剪接量 << 剪接量:基因"正在被关闭"(下调中)
  • 结合多基因的速度向量,推断细胞在状态空间中的运动方向

要点4:Monocle3 核心步骤

  1. 输入预处理好的 Seurat 对象
  2. learn_graph():学习细胞轨迹图(主图)
  3. order_cells():手动指定根节点(起始细胞状态)
  4. 提取拟时序值,可视化沿轨迹的基因表达变化

要点5:注意事项

  • 拟时序≠真实时间,它只是"顺序"不是"小时数"
  • 轨迹分析对细胞类型注释质量很敏感
  • RNA速度需要 STARsolo 或 Alevin-fry 同时统计spliced/unspliced reads

实战代码

Slingshot 拟时序(R,与Seurat配合)

library(Seurat)      # 单细胞分析
library(slingshot)   # 拟时序分析
library(SingleCellExperiment)  # SCE格式
library(ggplot2)

# 将 Seurat 对象转换为 SingleCellExperiment 格式
sce <- as.SingleCellExperiment(seurat_obj)

# 提取 UMAP 坐标作为降维结果
reducedDims(sce)$UMAP <- Embeddings(seurat_obj, "umap")

# 运行 Slingshot(指定起始细胞类型)
sce <- slingshot(
  sce,
  clusterLabels = "cell_type",     # 细胞类型标签列
  reducedDim = "UMAP",             # 使用 UMAP 坐标拟合轨迹
  start.clus = "Stem_cell",        # 起始细胞类型(祖细胞/干细胞)
  end.clus = c("Mature_cell_A", "Mature_cell_B")  # 终点(可选)
)

# 提取拟时序值
pseudotime_matrix <- slingPseudotime(sce)   # 每条轨迹一列
print(head(pseudotime_matrix))

# 取每个细胞的最小拟时序(如果有多条轨迹,取主轨迹时序)
seurat_obj$Pseudotime <- apply(pseudotime_matrix, 1, function(x) {
  x[!is.na(x)][which.min(abs(x[!is.na(x)]))]   # 取最近的非NA值
})

# 可视化:UMAP + 拟时序着色
FeaturePlot(
  seurat_obj,
  features = "Pseudotime",
  reduction = "umap",
  order = TRUE
) + scale_color_viridis_c(option = "B") +    # 黄-橙-紫配色
  ggtitle("Slingshot 拟时序分析")

# 分析沿拟时序的基因表达变化
# 找随拟时序显著变化的基因(用 tradeSeq 包)
library(tradeSeq)

# 过滤掉NA(未处于轨迹上的细胞)
keep_cells <- !is.na(seurat_obj$Pseudotime)
seurat_sub <- seurat_obj[, keep_cells]

# 拟合 GAM(广义加法模型)
sce_sub <- as.SingleCellExperiment(seurat_sub)
sce_sub <- fitGAM(
  counts = assay(sce_sub, "counts"),  # 使用raw counts
  pseudotime = data.frame(
    Lineage1 = seurat_sub$Pseudotime
  ),
  nknots = 6    # 样条曲线节点数
)

# 检验哪些基因随时间显著变化
at_test <- associationTest(sce_sub)    # 关联检验
sig_genes <- at_test[at_test$pvalue < 0.05, ]
sig_genes <- sig_genes[order(sig_genes$waldStat, decreasing = TRUE), ]
cat("随拟时序显著变化的基因数:", nrow(sig_genes), "\n")

scVelo RNA 速度分析(Python)

import scvelo as scv    # RNA速度分析(pip install scvelo)
import scanpy as sc
import pandas as pd
import matplotlib.pyplot as plt

# 设置绘图参数
scv.settings.verbosity = 2    # 显示关键信息
scv.settings.presenter_view = True

# ============================================================
# 前置:需要 STARsolo/Alevin 输出的 spliced + unspliced counts
# ============================================================
# 读取 velocyto 输出的 loom 文件
loom_file = "sample.loom"   # Alevin-fry 或 STARsolo 输出
adata = scv.read_loom(loom_file)

# 或者读取预计算好的 h5ad(已包含 spliced/unspliced 层)
adata = sc.read_h5ad("rna_velocity_data.h5ad")

# 检查必要的层是否存在
print(adata.layers.keys())   # 应该包含 'spliced' 和 'unspliced'

# 步骤1:预处理(过滤、归一化)
scv.pp.filter_and_normalize(
    adata,
    min_shared_counts=20,    # 基因在至少20个细胞中有共享reads
    n_top_genes=2000         # 保留2000个高变基因
)

# 步骤2:PCA 和 UMAP(如果还没做)
sc.pp.pca(adata, n_comps=50)
sc.pp.neighbors(adata, n_pcs=30)
sc.tl.umap(adata)

# 步骤3:计算 RNA 速度
scv.pp.moments(
    adata,
    n_pcs=30,   # 使用前30个PC计算kNN
    n_neighbors=30   # kNN邻居数
)

# 动态模型(推荐,比随机模型更准)
scv.tl.recover_dynamics(adata, n_jobs=4)   # 4线程,计算较慢
scv.tl.velocity(adata, mode="dynamical")   # 动态模型

# 构建速度图
scv.tl.velocity_graph(adata)

# 步骤4:可视化 RNA 速度
scv.pl.velocity_embedding_stream(
    adata,
    basis="umap",               # 在 UMAP 上展示速度流
    color="cell_type",          # 按细胞类型着色
    save="velocity_stream.pdf", # 保存图片
    title="RNA Velocity"
)

# 查看速度置信度(confidence低的细胞轨迹预测不可信)
scv.tl.velocity_confidence(adata)
scv.pl.scatter(
    adata,
    c="velocity_confidence",   # 用置信度着色
    cmap="Greens",
    perc=[5, 95]               # 显示5%-95%范围(去极端值)
)

# 步骤5:细胞命运预测(latent time)
scv.tl.latent_time(adata)
scv.pl.scatter(
    adata,
    color="latent_time",       # 潜在时间(类似拟时序)
    color_map="gnuplot",
    size=80,
    title="Latent Time"
)

面试常问点

  1. 拟时序和真实时间有什么区别? 拟时序是基于细胞状态相似性排序,只反映分化顺序,不能换算为真实小时数
  2. RNA速度为什么需要未剪接reads? 未剪接mRNA代表"新转录",剪接mRNA代表"稳态",两者比值推断基因是上调还是下调趋势
  3. scVelo动态模型比随机模型好在哪? 动态模型对每个基因单独估计动力学参数(转录、剪接、降解速率),更准确但更慢

速查表

工具分析类型语言优势
Monocle3拟时序R复杂分叉轨迹
Slingshot拟时序R简单易用,Seurat友好
scVeloRNA速度Python揭示动力学方向
PAGA轨迹 + 速度Python大数据集
tradeSeq拟时序DEGR沿轨迹的差异基因