
Discovering Spatial Ecotypes from a Single Spatial Transcriptomics Sample
Source:vignettes/SingleSample.Rmd
SingleSample.RmdOverview
In this tutorial, we will illustrate how to perform de novo discovery of spatial ecotypes from a single-cell spatial transcriptomics dataset using SpatialEcoTyper.
We will be analyzing single-cell spatial transcriptomics data from a melanoma sample (raw data available in Vizgen’s MERSCOPE FFPE Human Immuno-oncology). This demo data comprises the spatial expression of 500 genes across 27,907 cells, which are categorized into ten distinct cell types: B cells, CD4+ T cells, CD8+ T cells, NK cells, plasma cells, macrophages, dendritic cells (DC), fibroblasts, endothelial cells, and melanoma cells. For this tutorial, melanoma cells are excluded to reduce computational time; in practice, any cell types can be included.
All cells in this demo data are grouped into four spatial regions: tumor, inner margin, outer margin, and stroma. The tumor (tumor and inner margin) and stroma regions (stroma. and outer margin) are defined based on the density of cancer cells, as described in the CytoSPACE paper. The inner and outer margins are defined as regions extending 250 μm inside and outside the tumor boundaries, respectively. Furthermore, we quantified each cell’s distance to the tumor–stroma interface by calculating the shortest Euclidean distance to the nearest tumor region (for stromal cells) or stromal region (for tumor cells). A positive distance indicates cells located within the tumor region, while a negative distance indicates cells located within the stroma.
SpatialEcoTyper requires two input data objects:
- Gene expression matrix: a numeric matrix with genes as rows and cells as columns.
- Metadata: a data frame containing at least three columns: “X” (x-coordinate), “Y” (y-coordinate), and “CellType” (cell type annotation). Row names must match the column names (cell IDs) in the expression matrix.
First load required packages for this vignette
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(ggplot2))
suppressPackageStartupMessages(library(parallel))
suppressPackageStartupMessages(library(Seurat))
suppressPackageStartupMessages(library(data.table))
suppressPackageStartupMessages(library(R.utils))
library(SpatialEcoTyper)Quick start
## Load single-cell gene expression matrix (genes × cells)
url <- "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_counts.tsv.gz"
scdata <- fread(url, sep = "\t",header = TRUE, data.table = FALSE)
rownames(scdata) <- scdata[, 1]
scdata <- as.matrix(scdata[, -1])
## Normalize the gene expression data
normdata <- NormalizeData(scdata)
## Load single-cell metadata
## Required columns: "X", "Y", and "CellType"
## Row names must match the cell IDs in the expression matrix
url <- "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_scmeta.tsv"
scmeta <- read.table(url, sep = "\t", header = TRUE, row.names = 1)
scmeta <- scmeta[match(colnames(scdata), rownames(scmeta)), ]
head(scmeta[, c("X", "Y", "CellType")])
## Discover spatial ecotypes using the SpatialEcoTyper
se_results <- SpatialEcoTyper(normdata, scmeta,
outprefix = "Melanoma1_subset",
radius = 50, ncores = 2)Loading data
Text files as input
Large text files can be loaded into R using the fread
function from the data.table package.
## Load single-cell gene expression matrix (genes × cells)
url <- "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_counts.tsv.gz"
scdata <- fread(url, sep = "\t",header = TRUE, data.table = FALSE)
rownames(scdata) <- scdata[, 1]
scdata <- as.matrix(scdata[, -1])
head(scdata[, 1:5])## HumanMelanomaPatient1__cell_3655 HumanMelanomaPatient1__cell_3657
## PDK4 0 1
## TNFRSF17 0 0
## ICAM3 0 0
## FAP 1 0
## GZMB 0 0
## TSC2 0 0
## HumanMelanomaPatient1__cell_3658 HumanMelanomaPatient1__cell_3660
## PDK4 1 0
## TNFRSF17 0 0
## ICAM3 0 0
## FAP 0 0
## GZMB 0 0
## TSC2 0 0
## HumanMelanomaPatient1__cell_3661
## PDK4 0
## TNFRSF17 0
## ICAM3 0
## FAP 0
## GZMB 0
## TSC2 0
## Load single-cell metadata
## Required columns: "X", "Y", and "CellType"
## Row names must match the cell IDs in the expression matrix
url <- "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_scmeta.tsv"
scmeta <- read.table(url, sep = "\t", header = TRUE, row.names = 1)
scmeta <- scmeta[match(colnames(scdata), rownames(scmeta)), ]
head(scmeta[, c("X", "Y", "CellType")])## X Y CellType
## HumanMelanomaPatient1__cell_3655 1894.706 -6367.766 Fibroblast
## HumanMelanomaPatient1__cell_3657 1942.480 -6369.602 Fibroblast
## HumanMelanomaPatient1__cell_3658 1963.007 -6374.026 Fibroblast
## HumanMelanomaPatient1__cell_3660 1981.600 -6372.266 Fibroblast
## HumanMelanomaPatient1__cell_3661 1742.939 -6374.851 Fibroblast
## HumanMelanomaPatient1__cell_3663 1921.683 -6383.309 Fibroblast
Sparse matrix as input
SpatialEcoTyper
supports sparse matrix as input. Mtx files can be loaded into R using
the ReadMtx
function from the Seurat package.
scdata <- ReadMtx(mtx = "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_counts.mtx.gz",
cells = "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_cells.tsv.gz",
features = "https://spatialecotyper.stanford.edu/inc/inc.public.vignettes.php?file=Melanoma1_subset_genes.tsv.gz",
feature.column = 1, cell.column = 1)Data normalization
Gene expression data should be normalized prior to SpatialEcoTyper analysis. The data can be performed using either NormalizeData or SCTransform.
Here, we are normalizing using NormalizeData.
normdata <- NormalizeData(scdata)Using SCTransform for the normalization:
For SCTransform normalization, we recommend installing
the glmGamPoi package to accelerate computation.
if(!"glmGamPoi" %in% rownames(installed.packages())){
BiocManager::install("glmGamPoi")
}
tmpobj <- CreateSeuratObject(scdata) %>%
SCTransform(clip.range = c(-10, 10), verbose = FALSE)
seurat_version = as.integer(gsub("\\..*", "", as.character(packageVersion("SeuratObject"))))
if(seurat_version<5){
normdata <- GetAssayData(tmpobj, "data")
}else{
normdata <- tmpobj[["SCT"]]$data
}Preview of the sample
The SpatialView function can be used to visualize single cells within the tissue. You can color the cells by cell type or predefined spatial regions.
# Visualize the cell type annotations in the tissue
SpatialView(scmeta, by = "CellType") + scale_color_manual(values = pals::cols25())
# Visualize the regions in the tissue
SpatialView(scmeta, by = "Region") + scale_color_brewer(type = "qual", palette = "Set1")
The SpatialView function can also be used to visualize continuous characteristics, such as the minimum distance of each single cell to tumor/stroma margin. Here, positive distances indicate cells located within the tumor region, while negative distances denote cells within the stroma.
# Visualize the distance to tumor margin
SpatialView(scmeta, by = "Dist2Interface") +
scale_colour_gradient2(low = "#5e3c99", high = "#e66101",
mid = "#d9d9d9", midpoint = 0) +
labs(color = "Distance to\ntumor margin")
SE discovery using SpatialEcoTyper

The SpatialEcoTyper function is designed to identify spatial ecotypes (SEs) from single-cell spatial transcriptomics data. The workflow begins by defining spatial neighborhoods (SNs) on a regular grid and constructing cell type–specific gene expression profiles (GEPs) for each SN. For each cell type, a similarity network is constructed, where nodes represent SNs and edges reflect transcriptional similarity. These cell type–specific networks are then integrated using Similarity Network Fusion (SNF), originally developed for multi-omics data integration (Wang et al., 2014). This yields a fused similarity network that captures shared spatial transcriptomic variation across cell types, enabling the identification of SEs through clustering. Before start, please check key arguments here.
Selection of Spatial EcoTyper parameters
Choosing the radius parameter: The optimal spatial
neighborhood radius depends on the biological question.
Larger radii capture broader regional patterns, whereas smaller radii
resolve more localized cellular niches. In our analysis, we used a
radius of 50 µm, which typically includes ~10–80 neighboring cells and
provides a practical balance between granularity and robustness.
Choosing the number of variable features: The
nfeatures argument should be selected according to the
underlying data platform. For Vizgen MERSCOPE V1 (500-gene panel), we
used 200 highly variable features, whereas for Xenium Prime data, we
used 300 variable features. As single-cell spatial transcriptomics
technologies continue to evolve, the number of detectable genes per cell
has steadily increased. For example, the latest 10x Aera platform
captures over 1,000 genes per cell on average. For such high-complexity
datasets, we recommend using up to 1,000 highly variable genes for
downstream analysis to better capture biological variability.
Limiting the number of cell types per spatial
neighborhood: The min.cts.per.region argument
allows users to filter spatial regions based on cellular diversity by
requiring a minimum number of distinct cell types per region (default: ≥
2). This helps ensure that downstream analyses focus on biologically
informative neighborhoods with sufficient cellular heterogeneity.
filter.region.by.celltypes
argument allows users to define a set of cell types of interest. When
specified, only spatial regions that contain all of the selected cell
types are retained for downstream analysis. This enables focused
interrogation of neighborhoods enriched for predefined cellular
compositions, facilitating the identification of multicellular
ecosystems associated with the target cell types.
se_results <- SpatialEcoTyper(normdata, scmeta,
outprefix = "Melanoma1_subset",
radius = 50, ncores = 2)Optimizing memory usage
The similarity network fusion (Step 2 of SpatialEcoTyper) is the most computationally demanding step and its runtime increases with the number of spatial neighborhoods (SNs) per sample. Performance depends on dataset size and structure rather than tissue technology.
For example, an analysis involving 10 cell types and 18,564 SNs required
436 minutes of runtime and reached a peak memory usage of 169 GB. This
benchmark was obtained on a computing cluster equipped with an AMD EPYC
7543 processor (2.75 GHz) and 256 GB RAM, using a single CPU core and
excluding data loading time.
To reduce runtime, users can increase the number of cores via the
ncores parameter. However, this parallelization comes at
the cost of increased memory consumption.
When memory resources are limited, users can increase the
grid.size parameter, which will reduce the number of
spatial neighborhoods, thereby substantially lowering memory usage and
improving computational efficiency.
SpatialEcoTyper result
When the outprefix is specified, the SpatialEcoTyper result will
be saved as a RDS file named
outprefix_SpatialEcoTyper_results.rds. The result can be
loaded into R using readRDS.
se_results <- readRDS("Melanoma1_subset_SpatialEcoTyper_results.rds")The SpatialEcoTyper result is a list containing two key components:
- Seurat object constructed from the fused network embedding of spatial neighborhoods, enabling clustering and downstream visualization of SEs.
- Metadata, single-cell metadata with SE annotations appended.
# Extract the Seurat object and updated single-cell metadata
obj <- se_results$obj # A Seurat object
scmeta <- se_results$metadata # Single-cell meta data, with SE annotation added
head(scmeta)## X Y CellType CellTypeName
## HumanMelanomaPatient1__cell_3655 1894.706 -6367.766 Fibroblast Fibroblasts
## HumanMelanomaPatient1__cell_3657 1942.480 -6369.602 Fibroblast Fibroblasts
## HumanMelanomaPatient1__cell_3658 1963.007 -6374.026 Fibroblast Fibroblasts
## HumanMelanomaPatient1__cell_3660 1981.600 -6372.266 Fibroblast Fibroblasts
## HumanMelanomaPatient1__cell_3661 1742.939 -6374.851 Fibroblast Fibroblasts
## HumanMelanomaPatient1__cell_3663 1921.683 -6383.309 Fibroblast Fibroblasts
## Region Dist2Interface SE
## HumanMelanomaPatient1__cell_3655 Stroma -883.1752 SE3
## HumanMelanomaPatient1__cell_3657 Stroma -894.8463 SE6
## HumanMelanomaPatient1__cell_3658 Stroma -904.1115 SE6
## HumanMelanomaPatient1__cell_3660 Stroma -907.8909 SE6
## HumanMelanomaPatient1__cell_3661 Stroma -874.2712 SE1
## HumanMelanomaPatient1__cell_3663 Stroma -903.6559 SE3
table(scmeta$SE) ## The number of cells in each SE##
## SE0 SE1 SE2 SE3 SE4 SE5 SE6 SE7
## 4067 2480 6907 4297 3952 1232 1801 2244
Note: SpatialEcoTyper applies stringent quality
control to exclude low-quality spatial neighborhoods, including those
with insufficient detected genes (min.features), too few
cells (min.cells), or insufficient cell type diversity
(min.cts.per.region). These regions are labeled as NA.
Regions excluded at one resolution (radius,
grid.size) may be retained at a lower resolution if they
pass QC. In practice, different resolutions have minimal impact on
overall results. By default, NA regions are removed. To retain all
cells, set dropcell = FALSE.
Embedding of spatial architecture
The embedding of spatial neighborhoods can be visualized using
standard Seurat
functions such as DimPlot and FeaturePlot.
These visualizations help to explore the spatial organization and
heterogeneity within the tissue.
Note: The embedding and clustering results differ slightly between Seurat v4 and v5. However, the overall clustering patterns remain largely consistent, with an Adjusted Rand Index (ARI) of 0.7 for the demonstration dataset. This consistency ensures that, despite minor variations, the key biological insights are preserved across versions.
Visualizing tumor/stroma regions in the embedding
DimPlot(obj, group.by = "Region") + scale_color_brewer(type = "qual", palette = "Set1")
Visualizing the distance of SNs to tumor/stroma interface
This plot shows the distance of each SN to the tumor/stroma interface. Here, positive distances indicate SNs located within the tumor region, while negative distances denote SNs within the stroma.
FeaturePlot(obj, "Dist2Interface", min.cutoff = -600, max.cutoff = 600) +
scale_colour_gradient2(low = "#5e3c99", high = "#e66101", mid = "#d9d9d9", midpoint = 0)
Visualizing spatial ecotypes in the embedding
This plot visualizes the SEs within the spatial embedding. Each SE represents a distinct spatial ecosystem with unique molecular and spatial characteristics, and may also differ in cell type composition.
DimPlot(obj, group.by = "SE") + scale_color_manual(values = pals::kelly()[-1])
SE characteristics
Visualizing SEs in the tissue
The spatial distribution of SEs within the tissue can be visualized using the SpatialView function.
SpatialView(scmeta, by = "SE")
Visualizing the cell type composition of SEs
The bar plot below illustrates the cell type composition within each SE.
gg <- scmeta %>% filter(!is.na(SE)) %>% count(SE, CellType)
ggplot(gg, aes(SE, n, fill = CellType)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_manual(values = pals::cols25()) +
theme_bw(base_size = 14) + coord_flip() +
labs(y = "Cell type abundance")
Visualizing the association between SEs and pre-annotated regions
This bar plot shows the enrichment of SEs in pre-defined regions (e.g., tumor and stroma).
gg <- scmeta %>% filter(!is.na(SE)) %>% count(SE, Region)
ggplot(gg, aes(SE, n, fill = Region)) +
geom_bar(stat = "identity", position = "fill") +
scale_fill_brewer(type = "qual", palette = "Set1") +
theme_bw(base_size = 14) + coord_flip() +
labs(y = "Fraction")
Visualizing the distance of SEs to tumor/stroma interface
This box plot visualizes the distribution of distances of SEs to the tumor/stroma interface. Positive distances indicate cells located within the tumor region, while negative distances denote cells within the stroma. The SEs are ordered by their median distance, highlighting their spatial localization relative to the tumor/stroma interface.
gg <- scmeta %>% filter(!is.na(SE))
## Order SEs by their distance to tumor/stroma interface
tmp <- gg %>% group_by(SE) %>% summarise(Mid = median(Dist2Interface)) %>% arrange(Mid) %>% pull(SE)
gg$SE = factor(gg$SE, levels = tmp)
ggplot(gg, aes(SE, Dist2Interface)) +
geom_boxplot() + theme_bw() + labs(y = "Distance to tumor/stroma interface (μm)")
Identification of cell-type-specific SE markers
Differential expression analysis
To identify cell-type-specific SE markers, differential expression
analysis can be performed using the presto
package package. The script below demonstrates how to identify
SE-specific markers within each cell type.
if(!"presto" %in% installed.packages()){
BiocManager::install("devtools")
devtools::install_github("immunogenomics/presto")
}
library("presto")
# Ensure normalized data is aligned with metadata
normdata = normdata[, rownames(scmeta)]
# Perform differential expression analysis
degs = lapply(unique(scmeta$CellType), function(ct){
idx = which(scmeta$CellType==ct & !is.na(scmeta$SE))
degs = wilcoxauc(normdata[, idx], scmeta$SE[idx])
degs$CellType = ct
degs
})
degs = do.call(rbind, degs)
# Example: top markers for CD4 T cells
degs %>% filter(CellType=="CD4T") %>%
filter(logFC>0 & padj<0.05) %>%
arrange(desc(logFC)) %>% head()## feature group avgExpr logFC statistic auc pval
## 1 FN1 SE0 1.806530 0.6629562 1183830.0 0.6591026 2.224246e-31
## 2 FOS SE7 1.925442 0.6490756 1075864.0 0.7009114 5.421664e-41
## 3 DUSP1 SE1 1.395032 0.6396178 72771.5 0.7583841 9.937244e-06
## 4 CTNNB1 SE5 1.354324 0.6391400 214553.5 0.7757488 1.038918e-15
## 5 PKM SE5 2.373492 0.5948805 214342.5 0.7749859 8.231702e-15
## 6 FOS SE3 1.803486 0.4922663 608225.5 0.6483962 9.791820e-15
## padj pct_in pct_out CellType
## 1 4.581948e-29 83.26446 63.97198 CD4T
## 2 2.233725e-38 96.04938 79.36675 CD4T
## 3 4.094145e-03 86.95652 64.76510 CD4T
## 4 4.280343e-13 94.02985 68.19283 CD4T
## 5 1.695731e-12 100.00000 97.31105 CD4T
## 6 8.068460e-13 93.67089 80.21728 CD4T
Visualizing the expression of cell state markers
Once candidate markers are identified, their expression can be visualized across SEs.
Visualizing SE-specific markers using heatmap
The example below identifies SE-specific markers in CD4 T cells and visualizes their relative expression across SEs using a heatmap.
library(tidyr)
# Identify significant DEGs
sigDegs = degs %>% filter(CellType=="CD4T") %>% filter(logFC>0 & padj<0.05)
# Identify genes with highest specificity per SE
top_markers <- sigDegs %>% group_by(feature) %>% arrange(desc(logFC)) %>%
summarize(Highest_SE = first(group), Highest_logFC = first(logFC),
Second_logFC = nth(logFC, 2, default = 0),
Delta = Highest_logFC - Second_logFC) %>% ungroup() %>%
# Keep top 5 markers per SE based on Delta
group_by(Highest_SE) %>% arrange(desc(Delta)) %>%
slice_head(n = 5) %>% ungroup()
# Prepare logFC matrix for heatmap
lfcs = degs %>% filter(CellType=="CD4T") %>%
pivot_wider(id_cols = feature, names_from = group,
values_from = logFC) %>% as.data.frame
rownames(lfcs) = lfcs$feature
lfcs = lfcs[, -1]
gg <- lfcs[top_markers$feature, unique(top_markers$Highest_SE), drop = FALSE]
# Scale for visualization
gg = scale(t(scale(t(gg), center = FALSE)))
# Plot heatmap
HeatmapView(gg, breaks = c(0, 1, 1.5))
Visualizing an individual marker using box plot
You can also visualize the expression of an individual gene across SEs.
# Subset CD4 T cells with SE annotation
idx = which(scmeta$CellType=="CD4T" & !is.na(scmeta$SE))
gg = scmeta[idx, ]
gg$Expression = normdata["FOXP3", idx]
ggplot(gg, aes(SE, Expression)) + geom_boxplot(outlier.shape = NA) +
theme_classic(base_size = 14) + ylab("FOXP3 expression")
Visualizing expression of SE marker in the tissue
You can also visualize the expression of an individual marker (e.g., FOXP3) or average expression of SE-specific markers in the tissue.
# Visualize gene expression in tissue
gg <- scmeta
gg$Expression = normdata["FOXP3", ]
SpatialView(gg, by = "Expression") +
scale_color_viridis_c() +
labs(color = "FOXP3\nexpression")
Session info
The session info allows users to replicate the exact environment and identify potential discrepancies in package versions or configurations that might be causing problems.
## R version 4.4.1 (2024-06-14)
## Platform: aarch64-apple-darwin20
## Running under: macOS 26.4.1
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: America/Los_Angeles
## tzcode source: internal
##
## attached base packages:
## [1] parallel stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] tidyr_1.3.1 presto_1.0.0 Rcpp_1.0.13
## [4] SpatialEcoTyper_1.0.2 NMF_0.28 Biobase_2.64.0
## [7] BiocGenerics_0.50.0 cluster_2.1.6 rngtools_1.5.2
## [10] registry_0.5-1 RANN_2.6.2 Matrix_1.7-0
## [13] R.utils_2.12.3 R.oo_1.26.0 R.methodsS3_1.8.2
## [16] data.table_1.16.0 Seurat_5.1.0 SeuratObject_5.0.2
## [19] sp_2.1-4 ggplot2_3.5.1 dplyr_1.1.4
##
## loaded via a namespace (and not attached):
## [1] RcppAnnoy_0.0.22 splines_4.4.1
## [3] later_1.3.2 tibble_3.2.1
## [5] polyclip_1.10-7 fastDummies_1.7.4
## [7] lifecycle_1.0.4 sf_1.1-0
## [9] doParallel_1.0.17 pals_1.9
## [11] globals_0.16.3 lattice_0.22-6
## [13] MASS_7.3-60.2 magrittr_2.0.3
## [15] plotly_4.10.4 sass_0.4.9
## [17] rmarkdown_2.28 jquerylib_0.1.4
## [19] yaml_2.3.10 httpuv_1.6.15
## [21] glmGamPoi_1.16.0 sctransform_0.4.1
## [23] spam_2.10-0 spatstat.sparse_3.1-0
## [25] reticulate_1.39.0 mapproj_1.2.11
## [27] cowplot_1.1.3 pbapply_1.7-2
## [29] DBI_1.3.0 RColorBrewer_1.1-3
## [31] maps_3.4.2 zlibbioc_1.50.0
## [33] abind_1.4-5 GenomicRanges_1.56.1
## [35] Rtsne_0.17 purrr_1.0.2
## [37] GenomeInfoDbData_1.2.12 circlize_0.4.16
## [39] IRanges_2.38.1 S4Vectors_0.42.1
## [41] ggrepel_0.9.6 irlba_2.3.5.1
## [43] listenv_0.9.1 spatstat.utils_3.1-0
## [45] units_1.0-1 goftest_1.2-3
## [47] RSpectra_0.16-2 spatstat.random_3.3-1
## [49] fitdistrplus_1.2-1 parallelly_1.38.0
## [51] DelayedMatrixStats_1.26.0 pkgdown_2.1.0
## [53] DelayedArray_0.30.1 leiden_0.4.3.1
## [55] codetools_0.2-20 tidyselect_1.2.1
## [57] shape_1.4.6.1 farver_2.1.2
## [59] UCSC.utils_1.0.0 matrixStats_1.4.1
## [61] stats4_4.4.1 spatstat.explore_3.3-2
## [63] jsonlite_1.8.8 GetoptLong_1.0.5
## [65] e1071_1.7-16 progressr_0.14.0
## [67] ggridges_0.5.6 survival_3.6-4
## [69] iterators_1.0.14 systemfonts_1.1.0
## [71] foreach_1.5.2 tools_4.4.1
## [73] ragg_1.3.2 ica_1.0-3
## [75] glue_1.7.0 SparseArray_1.4.8
## [77] gridExtra_2.3 xfun_0.52
## [79] MatrixGenerics_1.16.0 GenomeInfoDb_1.40.1
## [81] withr_3.0.1 BiocManager_1.30.25
## [83] fastmap_1.2.0 boot_1.3-30
## [85] fansi_1.0.6 spData_2.3.4
## [87] digest_0.6.37 R6_2.5.1
## [89] mime_0.12 wk_0.9.5
## [91] textshaping_0.4.0 colorspace_2.1-1
## [93] scattermore_1.2 tensor_1.5
## [95] dichromat_2.0-0.1 spatstat.data_3.1-2
## [97] utf8_1.2.4 generics_0.1.3
## [99] class_7.3-22 S4Arrays_1.4.1
## [101] httr_1.4.7 htmlwidgets_1.6.4
## [103] spdep_1.4-2 uwot_0.2.2
## [105] pkgconfig_2.0.3 gtable_0.3.5
## [107] ComplexHeatmap_2.20.0 lmtest_0.9-40
## [109] XVector_0.44.0 htmltools_0.5.8.1
## [111] dotCall64_1.1-1 clue_0.3-65
## [113] scales_1.3.0 png_0.1-8
## [115] spatstat.univar_3.0-1 knitr_1.48
## [117] rstudioapi_0.16.0 reshape2_1.4.4
## [119] rjson_0.2.22 nlme_3.1-164
## [121] proxy_0.4-27 cachem_1.1.0
## [123] zoo_1.8-12 GlobalOptions_0.1.2
## [125] stringr_1.5.1 KernSmooth_2.23-24
## [127] miniUI_0.1.1.1 s2_1.1.9
## [129] desc_1.4.3 pillar_1.9.0
## [131] grid_4.4.1 vctrs_0.6.5
## [133] promises_1.3.0 xtable_1.8-4
## [135] evaluate_0.24.0 magick_2.8.5
## [137] cli_3.6.3 compiler_4.4.1
## [139] rlang_1.1.4 crayon_1.5.3
## [141] future.apply_1.11.2 labeling_0.4.3
## [143] classInt_0.4-11 plyr_1.8.9
## [145] fs_1.6.4 stringi_1.8.4
## [147] viridisLite_0.4.2 deldir_2.0-4
## [149] gridBase_0.4-7 munsell_0.5.1
## [151] lazyeval_0.2.2 spatstat.geom_3.3-2
## [153] RcppHNSW_0.6.0 patchwork_1.2.0
## [155] sparseMatrixStats_1.16.0 future_1.34.0
## [157] shiny_1.9.1 highr_0.11
## [159] SummarizedExperiment_1.34.0 ROCR_1.0-11
## [161] igraph_2.0.3 bslib_0.8.0