空间分辨的基因表达谱对于理解组织结构和功能至关重要。然而,空间转录组学(Spatial Transcriptomics, ST)分析技术缺乏单细胞分辨率,并且需要结合scRNA-seq 信息来解析空间索引数据集。
利用这两种数据类型的优点,Holger Heyn 团队开发了 SPOTlight 这一计算工具,于2021年5月21号发表在NAR杂志上,标题为《SPOTlight: seeded NMF regression to deconvolute spatial transcriptomics spots with single-cell transcriptomes》。
它能够整合ST和scRNA-seq数据,以推断复杂组织中细胞类型和状态的位置。SPOTlight的核心是基于种子的非负矩阵分解(seeded Non-negative Matrix Factorization, NMF)回归,该方法通过使用细胞类型标记基因进行初始化,并利用非负最小二乘法(Non-negative Least Squares, NNLS)来进一步解析ST捕获位置(spot)的空间分布。
SPOTlight官网:https://github.com/MarcElosua/SPOTlight
我这里选择了github上面的最新版本:
# 设置一下镜像
## 使用西湖大学的 Bioconductor镜像
options(BioC_mirror="https://mirrors.westlake.edu.cn/bioconductor")
options("repos"=c(CRAN="https://mirrors.westlake.edu.cn/CRAN/"))
#install.packages("devtools")
library(devtools)
install_github("https://github.com/MarcElosua/SPOTlight")
本次分析参考教程:
https://github.com/MarcElosua/SPOTlight/blob/main/vignettes/SPOTlight_kidney.Rmd
输入的数据有:
SpatialExperiment
或SingleCellExperiment
对象本次使用数据,空转来自 10x 官网,为小鼠的肾脏:https://support.10xgenomics.com/spatial-gene-expression/datasets/1.1.0/V1_Mouse_Kidney
小鼠单细胞数据来自文章:https://www.nature.com/articles/s41586-020-2496-1,超过35w的细胞,我们这里只用其中的kidney。
这两个数据都可以从 TENxVisiumData
包中得到:
为了减少由年龄和批次效应引入的潜在噪声,这里的单细胞数据会选择来自同一年龄阶段的细胞。
rm(list=ls())
library(ggplot2)
library(SPOTlight)
library(SingleCellExperiment)
library(SpatialExperiment)
library(scater)
library(scran)
# 数据准备
# 空转数据
library(TENxVisiumData)
spe <- MouseKidneyCoronal()
spe
# 将spe的行名转为基因名字
head(rownames(spe))
rownames(spe) <- rowData(spe)$symbol
# 先保存一下,下次可以直接导入
library(qs)
qsave(spe, file="spe.qs")
# 单细胞数据
library(TabulaMurisSenisData)
sce <- TabulaMurisSenisDroplet(tissues = "Kidney")$Kidney
sce
# 先保存一下,下次可以直接导入
qsave(sce, file="sce.qs")
table(sce$free_annotation, sce$age)
# 选择同一个时间段的细胞:18m mice
sce <- sce[, sce$age == "18m"]
# Keep cells with clear cell type annotations
sce <- sce[, !sce$free_annotation %in% c("nan", "CD45")]
sce
如果数据集非常庞大,这里可以进行downsampling
来进一步优化性能和计算资源,在文献中,软件测试了将每种细胞类型的细胞数量降采样到约100个,以及 将基因集限制为每种细胞类型的标记基因以及最多3000个高变基因,并不会影响模型的性能,详细描述见文献:
https://academic.oup.com/nar/article/49/9/e50/6129341
我们的目标是识别那些驱动生物学异质性的高变基因。通过将这些基因输入到模型中,我们可以提高生物学结构的分辨率,并减少技术噪声。这里选择了非线粒体与核糖体的3000个hvg:
# Feature selection
sce <- logNormCounts(sce)
# Variance modelling
# 首先排除 ribosomal or mitochondrial 基因
genes <- !grepl(pattern = "^Rp[l|s]|Mt", x = rownames(sce))
table(genes)
dec <- modelGeneVar(sce, subset.row = genes)
plot(dec$mean, dec$total, xlab = "Mean log-expression", ylab = "Variance")
curve(metadata(dec)$trend(x), col = "blue", add = TRUE)
# Get the top 3000 genes.
hvg <- getTopHVGs(dec, n = 3000)
hvg
grep("CD3", hvg, ignore.case = T,value = T)
接下来,获取每种细胞类型的标记基因。你可以使用任何方法,只要它能够返回一个权重,以表明该基因对该细胞类型的重要性。例如,可以使用avgLogFC
(平均对数倍数变化)、AUC
(曲线下面积)、pct.expressed
(表达百分比)、p-value
等。
# 2.细胞类型的特征基因
colLabels(sce) <- colData(sce)$free_annotation
# Compute marker genes
mgs <- scoreMarkers(sce, subset.row = genes)
mgs_fil <- lapply(names(mgs), function(i) {
x <- mgs[[i]]
# Filter and keep relevant marker genes, those with AUC > 0.8
x <- x[x$mean.AUC > 0.8, ]
# Sort the genes from highest to lowest weight
x <- x[order(x$mean.AUC, decreasing = TRUE), ]
# Add gene and cluster id to the dataframe
x$gene <- rownames(x)
x$cluster <- i
data.frame(x)
})
mgs_df <- do.call(rbind, mgs_fil)
接下来,对每种细胞降采样到最多100个细胞。如果一种细胞类型的细胞数少于100个,则会使用所有细胞。
如果细胞身份在生物学上差异很大(例如B细胞、T细胞、巨噬细胞和上皮细胞),我们可以使用较少的细胞,因为它们的转录组特征会有很大差异。而在细胞身份转录组更相似的情况下,我们需要增加样本量(N),以便捕捉它们之间的生物学异质性。
根据作者的经验,在这个步骤中,如果你有一个来自多次实验的联合数据集,最好从同一批次中选择每种细胞类型的细胞。这将确保模型尽可能多地去除批次信号,从而真正学习到生物学信号。
为了本教程的目的,并加快分析速度,这里将每种细胞降采样到20个细胞:
# 3.Cell Downsampling
# 生成一个每种细胞类型的list对象
idx <- split(seq(ncol(sce)), sce$free_annotation)
# 降采样到每种细胞20个细胞,真实数据中需要使用100个以上
n_cells <- 20
cs_keep <- lapply(idx, function(i) {
n <- length(i)
if (n < n_cells)
n_cells <- n
sample(i, n_cells)
})
sce <- sce[, unlist(cs_keep)]
sce
table(sce$free_annotation)
接下来运行 SPOTlight 对每个空间spot进行反卷积
## 反卷积
res <- SPOTlight(
x = sce,
y = spe,
groups = as.character(sce$free_annotation),
mgs = mgs_df,
hvg = hvg,
weight_id = "mean.AUC",
group_id = "cluster",
gene_id = "gene")
# 提取反卷积矩阵:每一行为一个spot,每一列为一种细胞类型,值为细胞类型在spot中的相对百分比
# 行和一定为1
head(mat <- res$mat)[, 1:3]
dim(mat)
gead(rowSums(mat))
# 提取NMF模型
mod <- res$NMF
得到的mat
就是反卷积的结果了,每一行为一个spot,每一列为一种细胞类型,值为细胞类型在spot中的相对百分比,行和一定为1
首先,评估每个 Topic profiles
特征对每种细胞身份的特异性。理想情况下,每种细胞身份都会有一个独特的 Topic profiles
与之关联,如下所示:
## 可视化
plotTopicProfiles(
x = mod,
y = sce$free_annotation,
facet = FALSE,
min_prop = 0.01,
ncol = 1) +
theme(aspect.ratio = 1)
这里有两个细胞亚群有两个 Topic profiles
:
接下来,还需要确保来自同一细胞身份的所有细胞具有相似的 Topic profiles
,因为这将意味着SPOTlight
已经为同一细胞身份的所有细胞学习到了一个一致的特征 signature:
plotTopicProfiles(
x = mod,
y = sce$free_annotation,
facet = TRUE,
min_prop = 0.01,
ncol = 6)
最后,我们可以查看模型为每个Topic
学到的基因。较高的值表明该基因对该Topic
更为重要。在下表中,我们可以看到Topic1
的top基因特征为B细胞(例如CD79A、CD79B、MS4A1等)。
library(NMF)
sign <- basis(mod)
colnames(sign) <- paste0("Topic", seq_len(ncol(sign)))
head(sign)
# This can be dynamically visualized with DT as shown below
DT::datatable(sign, fillContainer = TRUE, filter = "top")
对反卷积得到的矩阵 mat 绘制一个相关性图,这个图是基于细胞类型比例得到的:
plotCorrelationMatrix(mat)
既然我们已经知道了每个spot中存在的细胞类型,我们就可以绘制一个表示空间相互作用的图,其中细胞类型之间的连接线(边)会根据它们在同一spot中共同出现的频率而变得更粗(即连接更强):
plotInteractions(mat, which = "heatmap", metric = "prop")
plotInteractions(mat, which = "heatmap", metric = "jaccard")
plotInteractions(mat, which = "network")
使用饼图的形式展示每个spot中注释得到细胞类型:
## 饼图
ct <- colnames(mat)
# 占比小于0.1的不展示
mat[mat < 0.1] <- 0
# 颜色设置
paletteMartin <- c(
"#000000", "#004949", "#009292", "#ff6db6", "#ffb6db",
"#490092", "#006ddb", "#b66dff", "#6db6ff", "#b6dbff",
"#920000", "#924900", "#db6d00", "#24ff24", "#ffff6d")
pal <- colorRampPalette(paletteMartin)(length(ct))
names(pal) <- ct
pal
plotSpatialScatterpie(
x = spe,
y = mat,
cell_types = colnames(mat),
img = FALSE,
scatterpie_alpha = 1,
pie_scale = 0.4) +
scale_fill_manual(
values = pal,
breaks = names(pal))
上面这个切片大饼图就是很多文献中使用的注释结果展示啦!在展示这个结果的时候作者将占比比较小的细胞类型进行了归0处理,后面绘图可以借鉴一下~