单细胞空间联合分析(scRNA-seq + 空间转录组整合)¶
一句话概述¶
单细胞空间联合分析是将单细胞RNA测序(scRNA-seq)的高通量细胞类型注释能力与空间转录组学(Spatial Transcriptomics)的组织空间定位信息相整合,利用Cell2location、Tangram、CellTrek等计算方法,实现对组织中细胞类型空间分布的精确解析和细胞-细胞空间互作推断。
核心知识点表格¶
| 知识点 | 说明 |
|---|---|
| Spatial Transcriptomics | 保留空间位置信息的转录组测序技术 |
| Visium | 10x Genomics空间转录组平台,55μm spot,~5000 spots |
| MERFISH | 基于FISH的亚细胞分辨率空间转录组 |
| Cell2location | 贝叶斯反卷积方法,将scRNA-seq细胞类型映射到空间spots |
| Tangram | 深度学习方法,学习scRNA-seq到空间数据的映射 |
| CellTrek | 基于共嵌入的方法,将单细胞映射到空间坐标 |
| Deconvolution | 反卷积,从混合spot信号分解出各细胞类型比例 |
| RCTD | Robust Cell Type Decomposition,空间spot反卷积 |
| SPOTlight | NMF-based空间反卷积方法 |
| 空间互作 | 基于空间邻近性推断细胞间通讯 |
| Niche分析 | 分析组织微环境中细胞类型的空间共定位模式 |
各步骤详解¶
第一步:技术背景与整合动机¶
白话解释: 单细胞测序能精确知道每个细胞的身份(是T细胞还是巨噬细胞),但做完测序后细胞已经被打散了,失去了在组织中的位置信息。空间转录组能保留基因表达的空间位置,但分辨率可能不够(一个spot可能包含多个细胞),且检测的基因数有限。联合分析就是"取长补短"——用单细胞的精确注释去解读空间数据的每个位置到底有哪些细胞类型。
技术细节:
空间转录组主流平台:
| 平台 | 分辨率 | 基因数 | 原理 |
|---|---|---|---|
| 10x Visium | 55μm (多细胞) | 全转录组 | NGS-based |
| 10x Visium HD | 2μm (亚细胞) | 全转录组 | NGS-based |
| MERFISH | 亚细胞 | 100-1000基因 | 多轮FISH |
| seqFISH+ | 亚细胞 | ~10,000基因 | 多轮FISH |
| Slide-seq | 10μm (单细胞级) | 全转录组 | Bead-based |
| HDST | 2μm | 全转录组 | Bead-based |
| Stereo-seq | 亚细胞(0.5μm) | 全转录组 | DNA nanoball |
整合策略分类: 1. 反卷积方法:估计每个空间spot中各细胞类型的比例(Cell2location, RCTD, SPOTlight) 2. 映射方法:将单细胞映射到空间坐标(Tangram, CellTrek, novoSpaRc) 3. 基因填充方法:利用scRNA-seq为空间数据补全未检测的基因(Tangram, gimVI)
第二步:数据预处理¶
白话解释: 整合之前,需要分别处理好两种数据:单细胞数据要完成聚类和细胞类型注释(知道每种细胞的"基因指纹"),空间数据要完成质量控制和归一化。
# ===== 单细胞数据预处理(Seurat) =====
library(Seurat)
# 读取scRNA-seq数据
sc_data <- Read10X("scRNA_filtered_feature_bc_matrix/")
sc_obj <- CreateSeuratObject(counts = sc_data, min.cells = 3, min.features = 200)
# 标准流程
sc_obj[["percent.mt"]] <- PercentageFeatureSet(sc_obj, pattern = "^MT-")
sc_obj <- subset(sc_obj, subset = nFeature_RNA > 200 & nFeature_RNA < 5000 & percent.mt < 20)
sc_obj <- NormalizeData(sc_obj)
sc_obj <- FindVariableFeatures(sc_obj, nfeatures = 2000)
sc_obj <- ScaleData(sc_obj)
sc_obj <- RunPCA(sc_obj)
sc_obj <- FindNeighbors(sc_obj, dims = 1:30)
sc_obj <- FindClusters(sc_obj, resolution = 0.5)
sc_obj <- RunUMAP(sc_obj, dims = 1:30)
# 细胞类型注释(假设已完成)
# sc_obj$cell_type <- ... # 通过marker gene或自动注释
DimPlot(sc_obj, group.by = "cell_type", label = TRUE)
# ===== 空间数据预处理(Seurat) =====
# 读取Visium数据
spatial_obj <- Load10X_Spatial(
data.dir = "visium_output/",
filename = "filtered_feature_bc_matrix.h5",
slice = "tissue_section"
)
# 质控
spatial_obj[["percent.mt"]] <- PercentageFeatureSet(spatial_obj, pattern = "^MT-")
SpatialFeaturePlot(spatial_obj, features = c("nCount_Spatial", "nFeature_Spatial", "percent.mt"))
# 过滤
spatial_obj <- subset(spatial_obj, subset = nFeature_Spatial > 200 & percent.mt < 25)
# 归一化和降维
spatial_obj <- SCTransform(spatial_obj, assay = "Spatial")
spatial_obj <- RunPCA(spatial_obj)
spatial_obj <- FindNeighbors(spatial_obj, dims = 1:30)
spatial_obj <- FindClusters(spatial_obj, resolution = 0.8)
spatial_obj <- RunUMAP(spatial_obj, dims = 1:30)
# 空间可视化
SpatialDimPlot(spatial_obj, label = TRUE)
SpatialFeaturePlot(spatial_obj, features = c("CD3D", "CD68", "KRT19"))
第三步:Cell2location空间反卷积¶
白话解释: Cell2location就像一个"人口普查员"——它利用单细胞数据中每种细胞类型的基因表达特征("指纹"),去估计空间转录组中每个spot里到底有多少个属于各细胞类型的细胞。它使用贝叶斯统计模型,可以给出不确定性估计。
技术细节: Cell2location模型假设每个spot的观测counts是各细胞类型贡献之和: - 从scRNA-seq学习每种细胞类型的参考基因表达谱 - 估计每个spot中各细胞类型的丰度(绝对数量,不仅是比例) - 使用变分推断(Variational Inference)进行贝叶斯估计
# ===== Cell2location (Python) =====
import scanpy as sc
import cell2location
import numpy as np
# 读取数据
adata_sc = sc.read_h5ad("scRNA_annotated.h5ad")
adata_sp = sc.read_visium("visium_output/")
# 预处理空间数据
adata_sp.var_names_make_unique()
sc.pp.filter_genes(adata_sp, min_cells=3)
# ===== 步骤1:训练参考模型(从scRNA-seq学习细胞类型signatures) =====
# 选择用于建模的基因
from cell2location.utils.filtering import filter_genes
selected = filter_genes(adata_sc, cell_count_cutoff=5, cell_percentage_cutoff2=0.03,
nonz_mean_cutoff=1.12)
adata_sc = adata_sc[:, selected].copy()
# 准备参考数据
cell2location.models.RegressionModel.setup_anndata(
adata=adata_sc,
batch_key="sample",
labels_key="cell_type"
)
# 训练NB回归模型
mod_ref = cell2location.models.RegressionModel(adata_sc)
mod_ref.train(max_epochs=250, use_gpu=True)
# 导出参考signatures
adata_sc = mod_ref.export_posterior(
adata_sc, sample_kwargs={"num_samples": 1000, "batch_size": 2500}
)
# 提取细胞类型表达谱
if "means_per_cluster_mu_fg" in adata_sc.varm:
inf_aver = adata_sc.varm["means_per_cluster_mu_fg"][[
f"means_per_cluster_mu_fg_{i}" for i in adata_sc.uns["mod"]["factor_names"]
]].copy()
else:
inf_aver = adata_sc.varm["means_per_cluster_mu_fg"].copy()
inf_aver.columns = adata_sc.uns["mod"]["factor_names"]
# ===== 步骤2:空间映射(将细胞类型映射到spots) =====
# 找两个数据集共有的基因
intersect_genes = np.intersect1d(adata_sp.var_names, inf_aver.index)
adata_sp = adata_sp[:, intersect_genes].copy()
inf_aver = inf_aver.loc[intersect_genes, :].copy()
# 设置空间模型
cell2location.models.Cell2location.setup_anndata(adata=adata_sp)
# 训练Cell2location模型
mod_sp = cell2location.models.Cell2location(
adata_sp, cell_state_df=inf_aver,
N_cells_per_location=30, # 每个spot预期细胞数
detection_alpha=20 # 技术灵敏度参数
)
mod_sp.train(max_epochs=30000, batch_size=None, train_size=1, use_gpu=True)
# 导出结果
adata_sp = mod_sp.export_posterior(
adata_sp, sample_kwargs={"num_samples": 1000, "batch_size": mod_sp.adata.n_obs}
)
# ===== 步骤3:可视化 =====
# 查看所有细胞类型的空间分布
mod_sp.plot_spatial_QC_across_batches()
# 特定细胞类型的空间分布
from cell2location.utils import select_slide
slide = select_slide(adata_sp, "tissue_section")
with mpl.rc_context({"figure.figsize": (15, 15)}):
fig = mod_sp.plot_spatial(
slide,
color=["T_cells", "Macrophages", "Fibroblasts", "Epithelial"],
max_color_quantile=0.992,
circle_diameter=6,
colorbar_position="right"
)
第四步:Tangram映射分析¶
白话解释: Tangram用深度学习来建立单细胞和空间数据之间的对应关系。它学习一个"映射矩阵",告诉我们每个单细胞最可能在空间上的哪个位置。这样就可以把单细胞的详细注释"投影"到空间图上。
# ===== Tangram (Python) =====
import tangram as tg
import scanpy as sc
import numpy as np
# 读取数据
adata_sc = sc.read_h5ad("scRNA_annotated.h5ad")
adata_sp = sc.read_visium("visium_output/")
# 预处理
sc.pp.normalize_total(adata_sc, target_sum=1e4)
sc.pp.log1p(adata_sc)
# 选择marker基因(用于映射)
# 方法1:使用所有空间数据中检测到的基因
# 方法2:使用差异表达的marker基因(推荐)
sc.tl.rank_genes_groups(adata_sc, groupby="cell_type", method="wilcoxon")
markers_df = sc.get.rank_genes_groups_df(adata_sc, None)
markers = markers_df[markers_df["pvals_adj"] < 0.05].groupby("group").head(100)["names"].tolist()
markers = list(set(markers) & set(adata_sp.var_names))
# 准备Tangram映射
tg.pp_adatas(adata_sc, adata_sp, genes=markers)
# 训练映射模型
# mode="cells": 映射单个细胞
# mode="clusters": 映射细胞类型(更快)
adata_map = tg.map_cells_to_space(
adata_sc, adata_sp,
mode="cells", # 或 "clusters"
density_prior="rna_count_based", # 考虑spot的RNA总量
num_epochs=500,
device="cuda:0" # 或 "cpu"
)
# 投影注释到空间
tg.project_cell_annotations(adata_map, adata_sp, annotation="cell_type")
# 可视化细胞类型空间分布
cell_types = adata_sc.obs["cell_type"].unique()
for ct in cell_types:
sc.pl.spatial(adata_sp, color=ct, spot_size=1.5,
title=ct, vmin=0, vmax="p99")
# 基因表达预测(填充空间数据中未检测的基因)
tg.project_genes(adata_map, adata_sp)
# 现在adata_sp.X包含了scRNA-seq预测的全基因组空间表达
# 评估映射质量
# 比较预测值和实测值的相关性
tg.compare_spatial_geneexp(adata_map, adata_sp)
第五步:CellTrek空间映射¶
白话解释: CellTrek的思路是:先把单细胞数据和空间数据放到同一个降维空间中(共嵌入),让它们"融合"在一起。然后利用这个共嵌入空间中的距离关系,把每个单细胞分配到最合适的空间坐标上。
# ===== CellTrek (R) =====
# BiocManager::install("CellTrek") # 或从GitHub安装
# devtools::install_github("navinlabcode/CellTrek")
library(CellTrek)
library(Seurat)
# 准备数据
# sc_obj: 已注释的单细胞Seurat对象
# spatial_obj: 空间Seurat对象
# 步骤1:共嵌入(Co-embedding)
# 将scRNA-seq和Spatial数据整合到同一UMAP空间
sc_obj$data_type <- "scRNA"
spatial_obj$data_type <- "Spatial"
# 重命名spatial assay
spatial_obj <- RenameCells(spatial_obj, add.cell.id = "ST")
sc_obj <- RenameCells(sc_obj, add.cell.id = "SC")
# CellTrek共嵌入
trks <- CellTrek::trstein(
st_data = spatial_obj,
sc_data = sc_obj,
sc_assay = "RNA",
st_assay = "Spatial",
norm = "LogNormalize",
nfeatures = 2000,
npcs = 30
)
# 步骤2:CellTrek映射
# 将每个单细胞映射到空间坐标
celltrek_result <- CellTrek::celltrek(
st_sc_int = trks,
int_assay = "trstein",
sc_data = sc_obj,
sc_assay = "RNA",
reduction = "pca",
intp = TRUE, # 是否插值
intp_pnt = 5000, # 插值点数
intp_lin = FALSE,
nPCs = 30,
ntree = 1000, # 随机森林树数
dist_thresh = 0.55, # 距离阈值
top_spot = 5, # 每个细胞映射到top几个spots
spot_n = 5, # 每个spot分配的细胞数
repel_r = 20, # 排斥半径(避免重叠)
repel_iter = 20
)
# 步骤3:可视化
# 查看映射结果
CellTrek::celltrek_vis(
celltrek_result,
group.by = "cell_type",
reduction = "cell_bindcoord", # CellTrek空间坐标
pt_size = 0.5
)
# 单个细胞类型的空间分布
celltrek_result$is_target <- ifelse(celltrek_result$cell_type == "T_cells", "T_cells", "Other")
SpatialDimPlot(celltrek_result, group.by = "is_target",
cols = c("T_cells" = "red", "Other" = "grey90"))
第六步:RCTD/SPOTlight空间反卷积¶
白话解释: RCTD和SPOTlight是另外两种常用的空间反卷积方法。RCTD用的是统计回归模型,SPOTlight用的是NMF(非负矩阵分解)。它们都能告诉你每个spot中各细胞类型占多少比例。
# ===== RCTD (R) =====
# install.packages("spacexr")
library(spacexr)
# 准备参考数据
reference <- Reference(
counts = GetAssayData(sc_obj, slot = "counts"),
cell_types = sc_obj$cell_type,
nUMI = sc_obj$nCount_RNA
)
# 准备空间数据
coords <- GetTissueCoordinates(spatial_obj)
counts_sp <- GetAssayData(spatial_obj, slot = "counts")
spatial_rctd <- SpatialRNA(coords = coords, counts = counts_sp, nUMI = colSums(counts_sp))
# 创建RCTD对象并运行
myRCTD <- create.RCTD(spatial_rctd, reference, max_cores = 8)
myRCTD <- run.RCTD(myRCTD, doublet_mode = "full") # "full"=多细胞类型模式
# 提取结果(每个spot的细胞类型比例)
results <- myRCTD@results
weights <- as.matrix(results$weights) # spot × cell_type比例矩阵
normalize_weights <- sweep(weights, 1, rowSums(weights), "/")
# 添加到Seurat对象
for (ct in colnames(normalize_weights)) {
spatial_obj[[paste0("RCTD_", ct)]] <- normalize_weights[, ct]
}
SpatialFeaturePlot(spatial_obj, features = paste0("RCTD_", c("T_cells", "Macrophages")))
# ===== SPOTlight (R) =====
# BiocManager::install("SPOTlight")
library(SPOTlight)
# 获取marker基因
Idents(sc_obj) <- "cell_type"
markers_sc <- FindAllMarkers(sc_obj, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.5)
markers_sc <- markers_sc %>% group_by(cluster) %>% top_n(100, avg_log2FC)
# 运行SPOTlight反卷积(新版API使用SPOTlight()函数)
# 注意:旧版函数名spotlight_deconvolution()已弃用
spotlight_res <- SPOTlight(
x = GetAssayData(sc_obj, slot = "counts"), # 单细胞counts矩阵
y = GetAssayData(spatial_obj, slot = "counts"), # 空间counts矩阵
groups = as.character(sc_obj$cell_type), # 细胞类型标签
mgs = markers_sc,
n_top = 100,
gene_id = "gene",
group_id = "cluster",
weight_id = "avg_log2FC"
)
# 提取比例矩阵
decon_matrix <- spotlight_res$mat
# 可视化
plotSpatialScatterpie(spatial_obj, decon_mtrx = decon_matrix)
第七步:空间细胞互作分析¶
白话解释: 知道了每种细胞在空间上的位置后,可以分析哪些细胞类型倾向于"住在一起"(共定位),以及相邻的细胞之间通过什么配体-受体对进行通讯。
# ===== 空间共定位分析 =====
library(ggplot2)
# 基于Cell2location/RCTD结果计算共定位
# 皮尔逊相关:两种细胞类型比例在所有spots上的相关性
cell_type_props <- normalize_weights # spot × cell_type
coloc_cor <- cor(cell_type_props, method = "pearson")
# 热图展示共定位模式
library(pheatmap)
pheatmap(coloc_cor,
color = colorRampPalette(c("blue", "white", "red"))(100),
main = "Cell Type Co-localization")
# ===== 空间细胞通讯(MISTy) =====
# BiocManager::install("mistyR")
library(mistyR)
# MISTy分析空间上下文对基因表达的影响
# 定义视图
misty_views <- create_initial_view(as.data.frame(t(spatial_data))) %>%
add_paraview(spatial_coords, l = 100) %>% # 100μm邻域
add_paraview(spatial_coords, l = 500) # 500μm邻域
# 运行模型
run_misty(misty_views, results_folder = "misty_results/")
misty_results <- collect_results("misty_results/")
plot_improvement_stats(misty_results)
plot_interaction_heatmap(misty_results, "paraview_100")
# ===== 空间Niche分析 =====
# 定义空间邻域(niche)
# 对每个spot,统计其邻近区域内各细胞类型的组成
library(spdep)
# 构建空间邻域图
coords <- GetTissueCoordinates(spatial_obj)
nb <- dnearneigh(as.matrix(coords), 0, 200) # 200μm邻域
# 计算邻域内细胞类型组成
niche_composition <- matrix(0, nrow = nrow(coords), ncol = ncol(cell_type_props))
colnames(niche_composition) <- colnames(cell_type_props)
for (i in seq_len(nrow(coords))) {
neighbors <- nb[[i]]
if (length(neighbors) > 0) {
niche_composition[i, ] <- colMeans(cell_type_props[neighbors, , drop = FALSE])
}
}
# 对niche组成做聚类,定义不同的微环境类型
niche_clusters <- kmeans(niche_composition, centers = 5)
spatial_obj$niche <- as.factor(niche_clusters$cluster)
SpatialDimPlot(spatial_obj, group.by = "niche")
实战命令(可复制)¶
Python完整Cell2location + Tangram流程¶
# ============================================
# scRNA + Spatial 联合分析(Python Pipeline)
# ============================================
import scanpy as sc
import cell2location
import tangram as tg
import numpy as np
import matplotlib.pyplot as plt
# 1. 读取数据
adata_sc = sc.read_h5ad("scRNA_annotated.h5ad") # 需含cell_type注释
adata_sp = sc.read_visium("visium_output/")
# 2. 预处理
adata_sp.var_names_make_unique()
sc.pp.filter_genes(adata_sp, min_cells=3)
sc.pp.normalize_total(adata_sp, target_sum=1e4)
sc.pp.log1p(adata_sp)
# 3. Cell2location反卷积
## 3a. 参考模型
from cell2location.utils.filtering import filter_genes
selected = filter_genes(adata_sc, cell_count_cutoff=5,
cell_percentage_cutoff2=0.03, nonz_mean_cutoff=1.12)
adata_sc_filt = adata_sc[:, selected].copy()
cell2location.models.RegressionModel.setup_anndata(
adata=adata_sc_filt, batch_key="sample", labels_key="cell_type")
mod_ref = cell2location.models.RegressionModel(adata_sc_filt)
mod_ref.train(max_epochs=250, use_gpu=True)
adata_sc_filt = mod_ref.export_posterior(adata_sc_filt,
sample_kwargs={"num_samples": 1000, "batch_size": 2500})
inf_aver = adata_sc_filt.varm["means_per_cluster_mu_fg"].copy()
inf_aver.columns = adata_sc_filt.uns["mod"]["factor_names"]
## 3b. 空间模型
intersect_genes = np.intersect1d(adata_sp.var_names, inf_aver.index)
adata_sp_c2l = adata_sp[:, intersect_genes].copy()
inf_aver_filt = inf_aver.loc[intersect_genes, :].copy()
cell2location.models.Cell2location.setup_anndata(adata=adata_sp_c2l)
mod_sp = cell2location.models.Cell2location(
adata_sp_c2l, cell_state_df=inf_aver_filt,
N_cells_per_location=30, detection_alpha=20)
mod_sp.train(max_epochs=30000, batch_size=None, train_size=1, use_gpu=True)
adata_sp_c2l = mod_sp.export_posterior(adata_sp_c2l,
sample_kwargs={"num_samples": 1000, "batch_size": adata_sp_c2l.n_obs})
# 4. Tangram映射
tg.pp_adatas(adata_sc, adata_sp, genes=markers)
adata_map = tg.map_cells_to_space(adata_sc, adata_sp, mode="clusters",
density_prior="rna_count_based", num_epochs=500, device="cuda:0")
tg.project_cell_annotations(adata_map, adata_sp, annotation="cell_type")
# 5. 保存结果
adata_sp_c2l.write("spatial_cell2location_results.h5ad")
adata_sp.write("spatial_tangram_results.h5ad")
面试常问点¶
Q1: Cell2location和Tangram有什么区别?什么时候用哪个?¶
A: Cell2location是贝叶斯反卷积方法,输出每个spot中各细胞类型的绝对丰度(可以说"这个spot有3个T细胞"),基于严格的统计模型且有不确定性估计。Tangram是深度学习映射方法,学习单细胞到空间的最优映射,可以同时做基因表达预测(补全空间数据未检测的基因)。选择依据:需要精确的细胞比例/数量估计用Cell2location;需要基因表达空间预测或亚细胞分辨率数据用Tangram;小数据集或快速分析用SPOTlight/RCTD。
Q2: Visium spot包含多个细胞时如何解读反卷积结果?¶
A: Visium的55μm spot通常包含1-10个细胞。反卷积结果给出的是比例或绝对数,不是说这个spot"属于"某种细胞类型。解读时应注意:(1) 高比例的细胞类型是该位置的主导类型;(2) 低比例可能是真实的少数细胞混入,也可能是噪声;(3) 结合H&E图像验证解读的合理性;(4) 新一代平台(Visium HD, Stereo-seq)能达到单细胞/亚细胞分辨率,减少此问题。
Q3: 为什么需要scRNA-seq参考数据?没有的话怎么办?¶
A: scRNA-seq参考提供了每种细胞类型的"基因指纹",是反卷积的基础。如果没有配对的scRNA-seq数据:(1) 使用公共数据集作为参考(如Human Cell Atlas);(2) 确保参考数据与空间数据来自相似组织/条件;(3) 使用BayesTME等不需要参考的方法(但精度较低);(4) 对于MERFISH等panel数据,可以使用自身数据做unsupervised分析。关键是参考数据要覆盖目标组织中的主要细胞类型。
Q4: 如何评估空间反卷积结果的可靠性?¶
A: (1) 与H&E组织学图像对比——免疫细胞应在免疫区域富集;(2) 已知marker基因的空间表达应与对应细胞类型的空间分布一致;(3) 使用模拟数据评估算法性能;(4) 多种方法交叉验证(Cell2location, RCTD, SPOTlight结果对比);(5) 免疫荧光/免疫组化实验验证关键细胞类型的空间位置。
Q5: 单细胞和空间数据的基因交集太少怎么办?¶
A: 这在MERFISH等panel方法(只检测几百个基因)中很常见。解决方案:(1) 使用高变异基因和marker基因的交集进行映射;(2) Tangram支持用少量基因做映射并预测其他基因;(3) 确保交集中包含关键的cell-type-specific marker基因;(4) 增加panel设计时纳入的discriminative基因。通常映射所需的基因数比想象中少——100-300个精选marker就足够进行准确映射。
Q6: 空间互作分析和CellChat/CellPhoneDB有什么关系?¶
A: CellChat和CellPhoneDB基于配体-受体数据库推断细胞间通讯,但不考虑空间距离。空间互作分析在此基础上加入空间约束——只有空间上邻近的细胞才可能通过paracrine信号互作。做法是:先用空间方法确定哪些细胞类型在空间上邻近,再用CellChat分析这些邻近细胞对之间的配体-受体互作。COMMOT和SpaTalk是专门整合空间信息和通讯推断的工具。
易错点¶
1. scRNA-seq参考与空间数据来源不匹配¶
问题: 用健康组织的scRNA-seq参考去反卷积肿瘤空间数据,缺少cancer cells的signature导致结果错误。 解决: 参考数据应尽量来自相同或相似的组织/条件。如果用公共数据,确认其cell type annotation覆盖了目标组织的主要细胞类型。
2. Cell2location的N_cells_per_location参数设置不当¶
问题: 设置过大会过度估计细胞丰度,过小会遗漏少数细胞类型。 解决: 对Visium数据,通常设置N_cells_per_location=5-30(取决于组织密度)。可以根据H&E图像估计每个spot的细胞数。脑组织细胞较稀疏(~5-10),肿瘤实体组织较密(~15-30)。
3. 没有对参考数据做质量控制就直接整合¶
问题: scRNA-seq参考中存在低质量细胞、doublets、错误注释,都会传播到空间映射结果中。 解决: 在整合前彻底清洗scRNA-seq数据:去除doublets(DoubletFinder)、过滤低质量细胞、验证细胞类型注释的准确性。
4. 忽略批次效应¶
问题: scRNA-seq和空间数据来自不同实验批次,技术差异(如基因检测灵敏度不同)影响映射准确性。 解决: Cell2location内置batch correction;Tangram通过选择稳健的marker基因减轻批次影响;也可在预处理阶段用Harmony/scVI先做批次校正。
5. 直接比较不同方法的输出值¶
问题: Cell2location输出绝对丰度,RCTD输出比例,ssGSEA输出活性分数,直接比较没有意义。 解决: 明确每种方法的输出含义。如果要比较,应使用同一方法跨条件比较,或将不同方法的结果转化为一致的形式(如都转为排名或二值化的presence/absence)。
补充知识¶
方法选择决策树¶
你的空间数据平台是:
├── Visium (55μm, 全转录组)
│ ├── 有配对scRNA-seq → Cell2location / RCTD / Tangram
│ └── 无配对scRNA-seq → 公共参考 + Cell2location / BayesTME
├── Visium HD / Stereo-seq (2μm, 单细胞级)
│ └── 可直接做细胞分割 + 注释(类似单细胞分析)
├── MERFISH/seqFISH (亚细胞, 有限基因)
│ ├── 聚类+注释 → 用Tangram预测全转录组
│ └── 与scRNA-seq联合 → Tangram mapping
└── Slide-seq (10μm, 全转录组)
└── RCTD(原生支持)/ Cell2location
新兴工具和方法¶
- COMMOT:整合空间信息的细胞通讯推断
- SpaTalk:空间转录组细胞互作推断
- Banksy:空间感知的细胞聚类
- GraphST:基于图神经网络的空间域识别
- STELLAR:多模态空间数据整合
- CytoSPACE:高分辨率单细胞空间映射