BioF3 组学数据分析

09 空间转录组学

导出日期:2026年5月11日

09 空间转录组学

空间转录组在 scRNA-seq 的基础上保留了一个关键维度:每条表达信号来自切片的哪个位置。不同技术的空间分辨率差别很大:10x Visium 把组织打在微珠芯片上,每个 spot 约 55 μm 包含数个细胞;Slide-seq、MERFISH、seqFISH+ 能做到单细胞或亚细胞分辨率,代价是数据量更大、分析工具更专。

本节用 10x Visium 小鼠脑数据演示最常用的一条流程:读入 → 质控 → 空间感知标准化(SCT)→ 聚类 → 空间变异基因 → 与参考 scRNA-seq 数据做 cell type 反卷积。

主流空间技术对比

技术 分辨率 数据量 常用工具
10x Visium spot(~55 μm) 中等 Seurat、Scanpy + Squidpy
Slide-seq V2 ~10 μm 中等 Seurat、RCTD
Stereo-seq 亚微米 StereoPy
MERFISH / seqFISH+ 单细胞 Squidpy、Giotto
Xenium 亚细胞 10x pipeline、Seurat

Visium 仍然是门槛最低的选择:一张切片产出的 spot 数在几千级,普通笔记本能直接分析,教材、社区工具链也最完整。

用 Seurat 分析 Visium 小鼠脑

从 10x Genomics 下载 Visium 小鼠脑切片数据后(或用 SeuratData 里的 stxBrain),基本流程和 scRNA-seq 很像,只是多了 spatial 这一层坐标和组织图片:

library(Seurat)
# 若要一键获取示例数据:
# InstallData("stxBrain")
# brain <- LoadData("stxBrain", type = "anterior1")

# 或者从本地目录读取
brain <- Load10X_Spatial(
  data.dir = "~/biof3-data/visium-mouse-brain/outs",
  filename = "filtered_feature_bc_matrix.h5",
  assay    = "Spatial",
  slice    = "slice1"
)

# 质控:在组织图上看每个 spot 的总表达量
VlnPlot(brain, features = "nCount_Spatial", pt.size = 0.1) + NoLegend()
SpatialFeaturePlot(brain, features = "nCount_Spatial")

两张 QC 图放一起看:有没有"测序偏低的区域"集中在切片边缘(组织脱落),或者局部异常高(气泡、污染)。

标准化这一步建议用 SCTransform,它对 spot 间差异较大的空间数据表现稳定:

brain <- SCTransform(brain, assay = "Spatial", verbose = FALSE)
brain <- RunPCA(brain, assay = "SCT")
brain <- FindNeighbors(brain, reduction = "pca", dims = 1:30)
brain <- FindClusters(brain, verbose = FALSE)
brain <- RunUMAP(brain, reduction = "pca", dims = 1:30)

# 聚类结果同时画在 UMAP 和组织切片上
DimPlot(brain, label = TRUE)
SpatialDimPlot(brain, label = TRUE, label.size = 3)

小鼠脑 Visium 在 SpatialDimPlot 里会呈现皮层、海马、胼胝体等解剖结构对应的聚类,不做任何 cell type 注释也能看出大致分区。

空间变异基因

常规的 FindVariableFeatures 只考虑表达变异,不考虑位置。空间变异基因是"表达分布不是随机的",通常也是真正有解剖学意义的基因:

brain <- FindSpatiallyVariableFeatures(
  brain,
  assay              = "SCT",
  features           = VariableFeatures(brain)[1:1000],
  selection.method   = "moransi"
)

top_features <- head(
  SpatiallyVariableFeatures(brain, selection.method = "moransi"),
  6
)
SpatialFeaturePlot(brain, features = top_features, ncol = 3, alpha = c(0.1, 1))

"moransi"(Moran's I)衡量的是邻近 spot 的表达相似度,值越高说明越空间聚集。alpha 是作图参数,用来突出高表达区域、弱化背景。

把 scRNA-seq 当作参考做反卷积

Visium 的每个 spot 通常包含多个细胞,直接做 cell type 注释会模糊。常用做法是把已经注释好的 scRNA-seq 数据当参考,把 spot 内的细胞组成估出来。常用工具有 RCTD、SPOTlight、CARD:

# SPOTlight 示例(输入:scRNA 参考 + Visium 对象)
library(SPOTlight)

sc_ref <- readRDS("pbmc_reference.rds")   # 假设已分析并注释
markers <- Seurat::FindAllMarkers(sc_ref)

spotlight_res <- SPOTlight(
  x      = sc_ref,
  y      = brain,
  groups = sc_ref$cell_type,
  mgs    = markers
)

plotSpatialScatterpie(
  x          = brain,
  y          = spotlight_res,
  cell_types = unique(sc_ref$cell_type),
  img        = FALSE
)

得到的 scatterpie 在每个 spot 位置画一个小饼图,显示各 cell type 的估计比例。常见验证方式:查看某种 cell type 的比例在组织切片上是否和已知解剖学位置一致。

用 Scanpy + Squidpy 做 Python 版

Python 生态下 scanpy 配合 squidpy 可以覆盖类似流程,并提供更丰富的空间统计(nhood enrichment、co-occurrence 等):

import scanpy as sc
import squidpy as sq

adata = sc.read_visium("~/biof3-data/visium-mouse-brain/outs")
adata.var_names_make_unique()

sc.pp.calculate_qc_metrics(adata, inplace=True)
sc.pl.spatial(adata, color="total_counts")

sc.pp.normalize_total(adata, inplace=True)
sc.pp.log1p(adata)
sc.pp.highly_variable_genes(adata, flavor="seurat", n_top_genes=2000)

sc.pp.pca(adata)
sc.pp.neighbors(adata)
sc.tl.umap(adata)
sc.tl.leiden(adata)
sc.pl.spatial(adata, color="leiden")

# 空间邻域富集:看哪些 cluster 在空间上彼此相邻
sq.gr.spatial_neighbors(adata)
sq.gr.nhood_enrichment(adata, cluster_key="leiden")
sq.pl.nhood_enrichment(adata, cluster_key="leiden")

Scanpy/Squidpy 侧最适合的场景是高分辨率空间数据(Stereo-seq、MERFISH),它们对大矩阵更友好。Visium 级别数据两套都能跑,选择看个人生态。

参考资源