Seurat 分析空间组学:Visium 篇

Author

Han Wang

Published

January 23, 2024

简介

本教程展示了如何使用 Seurat (>=3.2) 分析空间解析 RNA 测序数据。虽然分析流程与单细胞 RNA 测序分析的 Seurat 工作流相似,但我们引入了更新的交互和可视化工具,特别强调空间和分子信息的整合。本教程将涵盖我们认为在许多空间分析中常见的以下任务:

  • 数据标准化

  • 降维和聚类

  • 检测空间变异特征

  • 交互式可视化

  • 与单细胞 RNA 测序数据整合

  • 多切片处理

在我们的第一个小册子中,我们分析了使用 Visium 技术 从 10x Genomics 生成的数据集。我们将在不久的将来扩展 Seurat 以处理其他数据类型,包括 SLIDE-SeqSTARmapMERFISH

首先,我们加载 Seurat 和本小册子所需的其他包。

library(Seurat)
Loading required package: SeuratObject
Loading required package: sp
The legacy packages maptools, rgdal, and rgeos, underpinning the sp package,
which was just loaded, will retire in October 2023.
Please refer to R-spatial evolution reports for details, especially
https://r-spatial.org/r/2023/05/15/evolution4.html.
It may be desirable to make the sf package available;
package maintainers should consider adding sf to Suggests:.
The sp package is now running under evolution status 2
     (status 2 uses the sf package in place of rgdal)

Attaching package: 'SeuratObject'
The following object is masked from 'package:base':

    intersect
library(SeuratData)
── Installed datasets ──────────────────────────────── SeuratData v0.2.2.9001 ──
✔ stxBrain 0.1.1                        
────────────────────────────────────── Key ─────────────────────────────────────
✔ Dataset loaded successfully
❯ Dataset built with a newer version of Seurat than installed
❓ Unknown version of Seurat installed
library(ggplot2)
library(patchwork)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union

数据集介绍

这里,我们将使用最近发布的使用 10x Genomics Visium v1 化学品生成的矢状鼠脑切片数据集。这包括两个连续的前部切片和两个(匹配的)连续的后部切片。

您可以在此下载数据,并使用 Load10X_Spatial() 函数将其加载到 Seurat 中。这会读取 spaceranger 管道的输出,并返回一个 Seurat 对象,该对象包含点位层次的表达数据以及组织切片的相关图像。您也可以使用我们的 SeuratData 包来方便地访问数据,如下所示。安装数据集后,您可以输入 ?stxBrain 了解更多信息。

InstallData("stxBrain")
brain <- LoadData('stxBrain', type = 'anterior1')
Validating object structure
Updating object slots
Ensuring keys are in the proper structure
Ensuring keys are in the proper structure
Ensuring feature names don't have underscores or pipes
Updating slots in Spatial
Updating slots in anterior1
Validating object structure for Assay 'Spatial'
Validating object structure for VisiumV1 'anterior1'
Object representation is consistent with the most current Seurat version

Seurat 中空间数据是如何存储的?

来自 10x 的 Visium 数据包含以下数据类型:

  • 一个点位与基因表达矩阵
  • 组织切片的图像(在数据获取期间通过 H&E 染色获得)
  • 将原始高分辨率图像与此处用于可视化的低分辨率图像关联的缩放因子。

在 Seurat 对象中,点位与基因表达矩阵类似于典型的 “RNA” Assay,但包含点位层次而非单细胞层次的数据。图像本身存储在 Seurat 对象的新 images 插槽中。images 插槽还存储将点位与组织图像上的物理位置相关联所需的信息。

数据预处理

我们对点位与基因表达数据执行的初始预处理步骤与典型的单细胞 RNA 测序(scRNA-seq)实验类似。首先,我们需要对数据进行归一化,以解释数据点之间测序深度的差异。我们注意到,在空间数据集中,每个点位的分子计数的变异可能非常大,特别是如果组织内的细胞密度存在差异。我们在这里观察到了显著的异质性,这需要有效的归一化处理。

plot1 <- VlnPlot(brain, features = 'nCount_Spatial', pt.size = 0.1) + NoLegend()
plot2 <- SpatialFeaturePlot(brain, features = 'nCount_Spatial') + theme(legend.position = "right")
wrap_plots(plot1, plot2)

这些图表展示了点位间分子计数的变异不仅仅是技术性的,还依赖于组织解剖。例如,神经元匮乏的组织区域(如皮层白质)可重复展示出较低的分子计数。因此,标准方法(如 LogNormalize() 函数),它强制每个数据点在归一化后具有相同的”大小”,可能会有问题。

作为替代方案,我们推荐使用 sctransform(Hafemeister 和 Satija,Genome Biology 2019),该方法构建基因表达的规则化负二项式模型,以解释技术伪影,同时保留生物学差异。有关 sctransform 的更多细节,请参阅这篇论文和 Seurat 小册子这里。sctransform 归一化数据,检测高变异特征,并将数据存储在 SCT 检测中。

brain <- SCTransform(brain, assay = "Spatial", verbose = FALSE)

基因表达可视化

在 Seurat 中,我们提供了探索和交互空间数据的固有视觉特性的功能。Seurat 中的 SpatialFeaturePlot() 函数扩展了 FeaturePlot(),可以在组织组织学图像上覆盖分子数据。例如,在这个小鼠脑数据集中,Hpca 基因是海马区的强标记,而 Ttr 是脉络丛的标记。

SpatialFeaturePlot(brain, features = c("Hpca", "Ttr"))

library(ggplot2)
plot <- SpatialFeaturePlot(brain, features = c("Ttr")) + 
  theme(legend.text = element_text(size = 0), legend.title = element_text(size = 20), legend.key.size = unit(1, "cm")) 

Seurat 中的默认参数强调分子数据的可视化。然而,您也可以调整点位的大小(及其透明度)以改善组织学图像的可视化,通过更改以下参数:

  • pt.size.factor - 这将缩放点位的大小。默认值为 1.6。
  • alpha - 最小和最大透明度。默认为 c(1, 1)。
  • 尝试将 alpha 设置为 c(0.1, 1),以降低低表达点的透明度。
p1 <- SpatialFeaturePlot(brain, features = "Ttr", pt.size.factor = 1)
p2 <- SpatialFeaturePlot(brain, features = "Ttr", alpha = c(0.1, 1))
p1 + p2

降维、聚类和可视化

接下来,我们可以在 RNA 表达数据上进行降维和聚类,使用与单细胞 RNA 测序分析相同的工作流程。

brain <- RunPCA(brain, assay = "SCT", verbose = FALSE)
brain <- FindNeighbors(brain, reduction = "pca", dims = 1:30)
Computing nearest neighbor graph
Computing SNN
brain <- FindClusters(brain, verbose = FALSE)
brain <- RunUMAP(brain, reduction = "pca", dims = 1:30)
22:05:13 UMAP embedding parameters a = 0.9922 b = 1.112
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:05:13 Read 2696 rows and found 30 numeric columns
22:05:13 Using Annoy for neighbor search, n_neighbors = 30
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:05:13 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
22:05:13 Writing NN index file to temp file C:\Users\HANWAN~1\AppData\Local\Temp\RtmpWybVMp\fileab4c5f6748c2
22:05:13 Searching Annoy index using 1 thread, search_k = 3000
22:05:13 Annoy recall = 100%
22:05:14 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
22:05:14 Initializing from normalized Laplacian + noise (using RSpectra)
22:05:15 Commencing optimization for 500 epochs, with 105214 positive edges
22:05:19 Optimization finished

接着,我们可以在 UMAP 空间(使用 DimPlot())中或者覆盖在图像上(使用 SpatialDimPlot())可视化聚类结果。

p1 <- DimPlot(brain, reduction = "umap", label = TRUE) 
p2 <- SpatialDimPlot(brain, label = TRUE, label.size = 3) 
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
p1 + p2

由于存在许多颜色,确定哪个体素属于哪个簇可能具有挑战性。我们有一些策略可以帮助解决这个问题。设置 label 参数会在每个簇的中位数处放置一个彩色框(见上图)。

您还可以使用 cells.highlight 参数在 SpatialDimPlot() 上标记特定感兴趣的细胞。这对于区分个别簇的空间定位非常有用,如下所示:

SpatialDimPlot(brain, cells.highlight = CellsByIdentities(object = brain, idents = c(2, 1, 4, 3, 5, 8)), facet.highlight = TRUE, ncol = 3)

交互式绘图

我们还内置了许多交互式绘图功能。SpatialDimPlot()SpatialFeaturePlot() 现在都有一个 interactive 参数,当设置为 TRUE 时,会在 Rstudio 查看器面板中打开一个交互式的 Shiny 图表。下面的示例演示了一个交互式的 SpatialDimPlot(),您可以在其上悬停以查看细胞名称和当前的身份类别(类似于之前的 do.hover 行为)。

SpatialDimPlot(brain, interactive = TRUE)

对于 SpatialFeaturePlot(),将 interactive 设置为 TRUE 会带来一个交互式面板,在该面板中,您可以调整点位的透明度、点的大小以及正在绘制的 Assay 和特征。在探索数据后,选择完成按钮将返回最后活跃的图表作为一个 ggplot 对象。

SpatialFeaturePlot(brain, features = "Ttr", interactive = TRUE)

LinkedDimPlot() 函数将 UMAP 表示与组织图像表示链接起来,并允许交互式选择。例如,您可以在 UMAP 图中选择一个区域,图像表示中的相应点位将被突出显示。

LinkedDimPlot(brain)

空间变异特征的识别

Seurat 提供了两种工作流程来识别与组织内部空间位置相关的分子特征。第一种是基于组织内预先标注的解剖区域进行差异表达分析,这些区域可以通过无监督聚类或先前的知识确定。这种策略在这种情况下效果很好,因为上面的簇表现出明显的空间限制。

de_markers <- FindMarkers(brain, ident.1 = 5, ident.2 = 6)
SpatialFeaturePlot(object = brain, features = rownames(de_markers)[1:3], alpha = c(0.1, 1), ncol = 3)

另一种方法,实现在 FindSpatiallyVariables() 中,是在没有预先标注的情况下寻找表现出空间模式的特征。默认方法 (method = 'markvariogram') 受 Trendsceek 启发,该方法将空间转录组数据建模为标记点过程,并计算 ‘变差图’,用于识别其表达水平依赖于空间位置的基因。更具体地说,这个过程计算 gamma(r) 值,用于测量两个点位之间特定 “r” 距离的依赖性。默认情况下,我们在这些分析中使用 r 值 ‘5’,并且仅为变异基因计算这些值(其中变异独立于空间位置计算),以节省时间。

我们注意到文献中有多种方法可以完成这项任务,包括 SpatialDESplotch。我们鼓励有兴趣的用户探索这些方法,并希望在不久的将来为它们提供支持。

brain <- FindSpatiallyVariableFeatures(brain, assay = 'SCT', features = VariableFeatures(brain)[1:1000], selection.method = 'moransi')
Computing Moran's I
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'

现在我们可视化通过这种方法识别出的前 6 个特征。

SpatiallyVariableFeatures_workaround <- function(object, assay="SCT", selection.method = "moransi") {
  #' This is work around function to replace SeuratObject::SpatiallyVariableFeatures function.
  #' return ranked list of Spatially Variable Features
  
  # Check if object is a Seurat object
  if (!inherits(object, "Seurat")) {
    stop("object must be a Seurat object")
  }

  # Check if assay is a valid assay
  if (!assay %in% names(object@assays)) {
    stop("assay must be a valid assay")
  }
  
  # Extract meta.features from the specified object and assay
  data <- object@assays[[assay]]@meta.features
  
  # Select columns starting with the provided col_prefix
  moransi_cols <- grep(paste0("^", selection.method), colnames(data), value = TRUE)

  # Filter rows where "moransi.spatially.variable" is TRUE
  filtered_data <- data[data[[paste0(selection.method, ".spatially.variable")]], moransi_cols]

  # Sort filtered data by "moransi.spatially.variable.rank" column in ascending order
  sorted_data <- filtered_data[order(filtered_data[[paste0(selection.method, ".spatially.variable.rank")]]), ]

  # Return row names of the sorted data frame
  rownames(sorted_data)
}

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

划分解剖区域

与单细胞对象一样,您可以对对象进行子集划分,以关注数据的一个子集。这里,我们大致划分了前额皮层。这个过程还有助于将这些数据与下一节中的皮层 scRNA-seq 数据集集成。首先,我们取一个簇的子集,然后根据精确位置进一步划分。划分后,我们可以在完整图像或裁剪图像上可视化皮层细胞。

cortex <- subset(brain, idents = c(1, 2, 3, 4, 6, 7))
# 现在移除额外的细胞,使用 SpatialDimPlots 查看需要移除的内容
# SpatialDimPlot(cortex,cells.highlight = WhichCells(cortex, expression = image_imagerow > 400 | image_imagecol < 150))
cortex <- subset(cortex, anterior1_imagerow > 400 | anterior1_imagecol < 150, invert = TRUE)
cortex <- subset(cortex, anterior1_imagerow > 275 & anterior1_imagecol > 370, invert = TRUE)
cortex <- subset(cortex, anterior1_imagerow > 250 & anterior1_imagecol > 440, invert = TRUE)
p1 <- SpatialDimPlot(cortex, crop = TRUE, label = TRUE) 
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
p2 <- SpatialDimPlot(cortex, crop = FALSE, label = TRUE, pt.size.factor = 1, label.size = 3) 
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.
p1 + p2

与单细胞数据的整合

在约 50 微米的尺度上,Visium 测验的点位将包括多个细胞的表达轮廓。对于那些可获得单细胞 RNA 测序(scRNA-seq)数据的系统,用户可能有兴趣“解卷”每个空间体素以预测细胞类型的基础组成。在准备这个小册子时,我们测试了多种解卷和整合方法,使用了一个由 Allen 研究所生成的大约 14,000 个成年小鼠皮层细胞分类的参考 scRNA-seq 数据集,该数据集采用 SMART-Seq2 协议生成。 我们一致发现使用整合方法(而不是解卷方法)的性能更好,这可能是由于空间和单细胞数据集特有的不同噪声模型,以及整合方法特别设计用于对这些差异具有鲁棒性。因此,我们应用了 Seurat v3 中引入的基于“锚点”的整合工作流,它实现了从参考集到查询集的注释的概率转移。因此,我们遵循这里引入的标签转移工作流,利用 sctransform 归一化,但期望开发新方法来完成这项任务。

我们首先加载数据(下载地址这里),对 scRNA-seq 参考进行预处理,然后执行标签转移。该过程为每个点位输出每个 scRNA-seq 派生类的概率分类。我们将这些预测作为一个新的检测添加到 Seurat 对象中。

allen_reference <- readRDS("allen_cortex.rds")
# 注意,设置 ncells=3000 归一化整个数据集,但在 3k 细胞上学习噪声模型
# 这极大地加快了 SCTransform 的速度,且不会损失性能
library(dplyr)
allen_reference <- SCTransform(allen_reference, ncells = 3000, verbose = FALSE) %>% RunPCA(verbose = FALSE) %>% RunUMAP(dims = 1:30)
22:08:24 UMAP embedding parameters a = 0.9922 b = 1.112
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:08:24 Read 14249 rows and found 30 numeric columns
22:08:24 Using Annoy for neighbor search, n_neighbors = 30
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:08:24 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
22:08:25 Writing NN index file to temp file C:\Users\HANWAN~1\AppData\Local\Temp\RtmpWybVMp\fileab4c2b712c3d
22:08:25 Searching Annoy index using 1 thread, search_k = 3000
22:08:27 Annoy recall = 100%
22:08:29 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
22:08:30 Found 5 connected components, falling back to 'spca' initialization with init_sdev = 1
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:08:30 Using 'irlba' for PCA
22:08:30 PCA: 2 components explained 36.42% variance
22:08:30 Scaling init to sdev = 1
22:08:30 Commencing optimization for 200 epochs, with 560988 positive edges
22:08:40 Optimization finished
# 子集后,我们重新归一化皮层
cortex <- SCTransform(cortex, assay = 'Spatial', verbose = FALSE) %>% RunPCA(verbose = FALSE)
# 注释存储在对象元数据的 'subclass' 列中
DimPlot(allen_reference, group.by = 'subclass', label = TRUE)

anchors <- FindTransferAnchors(reference = allen_reference, query = cortex, normalization.method = "SCT")
Normalizing query using reference SCT model
Performing PCA on the provided reference using 2734 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
    Found 280 anchors
predictions.assay <- TransferData(anchorset = anchors, refdata = allen_reference$subclass, prediction.assay = TRUE, weight.reduction = cortex[["pca"]], dims = 1:30)
Finding integration vectors
Finding integration vector weights
Predicting cell labels
cortex[["predictions"]] <- predictions.assay

现在我们获得了每个点位每个类别的预测分数。在前额皮层区域,特别感兴趣的是层状兴奋性神经元。在这里,我们可以区分这些神经元亚型的不同连续层,例如:

DefaultAssay(cortex) <- "predictions"
SpatialFeaturePlot(cortex, features = c("L2/3 IT", "L4"), pt.size.factor = 1.6, ncol = 2, crop = TRUE)

基于这些预测分数,我们也可以预测位置

受空间限制的细胞类型。我们使用基于标记点过程的同样方法来定义空间变异特征,但使用细胞类型预测分数作为“标记”而不是基因表达。

cortex <- FindSpatiallyVariableFeatures(cortex, assay = "predictions", selection.method = "moransi", features = rownames(cortex), r.metric = 5, slot = "data")
top.clusters <- head(SpatiallyVariableFeatures_workaround(cortex, selection.method = "moransi"), 4)
SpatialPlot(object = cortex, features = top.clusters, ncol = 2)

最后,我们展示了我们的整合程序能够恢复已知的神经和非神经亚集的空间定位模式,包括层状兴奋性、第一层星形胶质细胞和皮层灰质。

SpatialFeaturePlot(cortex, features = c("Astro", "L2/3 IT", "L4", "L5 PT", "L5 IT", "L6 CT", "L6 IT", "L6b", "Oligo"), pt.size.factor = 1, ncol = 2, crop = FALSE, alpha = c(0.1, 1))

在 Seurat 中处理多个切片

这个小鼠脑数据集包含了另一个与大脑的另一半对应的切片。在这里,我们将其读入并进行相同的初始归一化。

brain2 <- LoadData('stxBrain', type = 'posterior1')
Validating object structure
Updating object slots
Ensuring keys are in the proper structure
Ensuring keys are in the proper structure
Ensuring feature names don't have underscores or pipes
Updating slots in Spatial
Updating slots in posterior1
Validating object structure for Assay 'Spatial'
Validating object structure for VisiumV1 'posterior1'
Object representation is consistent with the most current Seurat version
brain2 <- SCTransform(brain2, assay = "Spatial", verbose = FALSE)

为了在同一个 Seurat 对象中处理多个切片,我们提供了 merge 函数。

brain.merge <- merge(brain, brain2)

这使得我们可以在底层 RNA 表达数据上进行联合降维和聚类。

DefaultAssay(brain.merge) <- "SCT"
VariableFeatures(brain.merge) <- c(VariableFeatures(brain), VariableFeatures(brain2))
brain.merge <- RunPCA(brain.merge, verbose = FALSE)
brain.merge <- FindNeighbors(brain.merge, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
brain.merge <- FindClusters(brain.merge, verbose = FALSE)
brain.merge <- RunUMAP(brain.merge, dims = 1:30)
22:09:50 UMAP embedding parameters a = 0.9922 b = 1.112
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:09:50 Read 6049 rows and found 30 numeric columns
22:09:50 Using Annoy for neighbor search, n_neighbors = 30
Found more than one class "dist" in cache; using the first, from namespace 'spam'
Also defined by 'BiocGenerics'
22:09:50 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
22:09:51 Writing NN index file to temp file C:\Users\HANWAN~1\AppData\Local\Temp\RtmpWybVMp\fileab4c7b842599
22:09:51 Searching Annoy index using 1 thread, search_k = 3000
22:09:52 Annoy recall = 100%
22:09:52 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
22:09:53 Initializing from normalized Laplacian + noise (using RSpectra)
22:09:53 Commencing optimization for 500 epochs, with 242018 positive edges
22:10:04 Optimization finished

最后,可以在单个 UMAP 图中联合可视化这些数据。SpatialDimPlot()SpatialFeaturePlot() 默认情况下会将所有切片作为列,将分组/特征作为行进行绘制。

DimPlot(brain.merge, reduction = "umap", group.by = c("ident", "orig.ident"))

SpatialDimPlot(brain.merge)

SpatialFeaturePlot(brain.merge, features = c('Hpca', 'Plp1'))

工作环境

sessionInfo()
R version 4.3.1 (2023-06-16 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 11 x64 (build 22621)

Matrix products: default


locale:
[1] LC_COLLATE=Chinese (Simplified)_China.utf8 
[2] LC_CTYPE=Chinese (Simplified)_China.utf8   
[3] LC_MONETARY=Chinese (Simplified)_China.utf8
[4] LC_NUMERIC=C                               
[5] LC_TIME=Chinese (Simplified)_China.utf8    

time zone: Asia/Hong_Kong
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_1.1.3               patchwork_1.1.2          
[3] ggplot2_3.4.4             stxBrain.SeuratData_0.1.1
[5] SeuratData_0.2.2.9001     Seurat_5.0.1             
[7] SeuratObject_5.0.1        sp_2.0-0                 

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3          rstudioapi_0.15.0          
  [3] jsonlite_1.8.7              magrittr_2.0.3             
  [5] spatstat.utils_3.0-3        ggbeeswarm_0.7.2           
  [7] farver_2.1.1                rmarkdown_2.23             
  [9] zlibbioc_1.46.0             vctrs_0.6.3                
 [11] ROCR_1.0-11                 DelayedMatrixStats_1.22.6  
 [13] spatstat.explore_3.2-1      RCurl_1.98-1.12            
 [15] S4Arrays_1.0.6              htmltools_0.5.5            
 [17] sctransform_0.4.1           parallelly_1.36.0          
 [19] KernSmooth_2.23-21          htmlwidgets_1.6.2          
 [21] ica_1.0-3                   plyr_1.8.8                 
 [23] plotly_4.10.2               zoo_1.8-12                 
 [25] igraph_1.5.0                mime_0.12                  
 [27] lifecycle_1.0.3             pkgconfig_2.0.3            
 [29] Matrix_1.6-5                R6_2.5.1                   
 [31] fastmap_1.1.1               GenomeInfoDbData_1.2.10    
 [33] MatrixGenerics_1.12.3       fitdistrplus_1.1-11        
 [35] future_1.33.0               shiny_1.7.4.1              
 [37] digest_0.6.33               colorspace_2.1-0           
 [39] S4Vectors_0.38.2            tensor_1.5                 
 [41] RSpectra_0.16-1             irlba_2.3.5.1              
 [43] GenomicRanges_1.52.1        labeling_0.4.2             
 [45] RcppZiggurat_0.1.6          progressr_0.13.0           
 [47] fansi_1.0.4                 spatstat.sparse_3.0-2      
 [49] httr_1.4.6                  polyclip_1.10-4            
 [51] abind_1.4-5                 compiler_4.3.1             
 [53] withr_2.5.0                 fastDummies_1.7.3          
 [55] MASS_7.3-60                 DelayedArray_0.26.7        
 [57] rappdirs_0.3.3              tools_4.3.1                
 [59] vipor_0.4.5                 lmtest_0.9-40              
 [61] beeswarm_0.4.0              httpuv_1.6.11              
 [63] future.apply_1.11.0         Rfast2_0.1.5.1             
 [65] goftest_1.2-3               glmGamPoi_1.12.2           
 [67] glue_1.6.2                  nlme_3.1-162               
 [69] promises_1.2.0.1            grid_4.3.1                 
 [71] Rtsne_0.16                  cluster_2.1.4              
 [73] reshape2_1.4.4              generics_0.1.3             
 [75] gtable_0.3.3                spatstat.data_3.0-1        
 [77] tidyr_1.3.0                 data.table_1.14.8          
 [79] XVector_0.40.0              utf8_1.2.3                 
 [81] BiocGenerics_0.46.0         spatstat.geom_3.2-4        
 [83] RcppAnnoy_0.0.21            ggrepel_0.9.3              
 [85] RANN_2.6.1                  pillar_1.9.0               
 [87] stringr_1.5.0               limma_3.56.2               
 [89] spam_2.9-1                  RcppHNSW_0.4.1             
 [91] later_1.3.1                 splines_4.3.1              
 [93] lattice_0.21-8              survival_3.5-7             
 [95] deldir_1.0-9                tidyselect_1.2.0           
 [97] miniUI_0.1.1.1              pbapply_1.7-2              
 [99] knitr_1.45                  gridExtra_2.3              
[101] IRanges_2.34.1              SummarizedExperiment_1.30.2
[103] scattermore_1.2             stats4_4.3.1               
[105] xfun_0.39                   Biobase_2.60.0             
[107] matrixStats_1.0.0           stringi_1.7.12             
[109] lazyeval_0.2.2              yaml_2.3.7                 
[111] evaluate_0.21               codetools_0.2-19           
[113] tibble_3.2.1                cli_3.6.1                  
[115] RcppParallel_5.1.7          uwot_0.1.16                
[117] xtable_1.8-4                reticulate_1.30            
[119] munsell_0.5.0               GenomeInfoDb_1.36.4        
[121] Rcpp_1.0.11                 globals_0.16.2             
[123] spatstat.random_3.1-5       png_0.1-8                  
[125] Rfast_2.1.0                 ggrastr_1.0.2              
[127] parallel_4.3.1              ellipsis_0.3.2             
[129] presto_1.0.0                dotCall64_1.0-2            
[131] sparseMatrixStats_1.12.2    bitops_1.0-7               
[133] listenv_0.9.0               viridisLite_0.4.2          
[135] scales_1.2.1                ggridges_0.5.4             
[137] leiden_0.4.3                purrr_1.0.2                
[139] crayon_1.5.2                rlang_1.1.1                
[141] cowplot_1.1.1