Skip to contents
library(wSIR) # package itself
library(magrittr) # for %>% 
library(ggplot2) # for ggplot
library(Rfast) # for distance correlation
library(doBy) # for which.maxn
library(vctrs) # for vec_rep_each
library(umap) # for umap
library(class) # for example wSIR application

Introduction

Weighted Sliced Inverse Regression (wSIR) is a supervised dimension reduction technique for spatial transcriptomics data. This method uses each cell’s gene expression values as covariates and spatial position as the response. This allows us to create a low-dimensional representation of the gene expression data that retains the information about spatial patterns that is present in the gene expression data. The resulting mapping from gene expression data to a spatially aware low-dimensional embedding can be used to project new single-cell gene expression data into a low-dimensional space which preserves the ability to predict each cell’s spatial location from its low-dimensional embedding.

Method Overview

wSIR is an extension of the supervised dimension reduction technique of Sliced Inverse Regression (SIR).

SIR is an existing supervised dimension reduction method which works by grouping together the observations with similar values for the response. For spatial transcriptomics data, this means grouping all the cells into a certain number of tiles based on their spatial position. For example, if we use 4 tiles, then the cells in the top right quadrant of the image/tissue go to one group, those in the top left to another, and so on. Each of those groups is then summarised by averaging the expression level of all cells in each group for each of the genes. From there, eigendecomposition is performed on the resulting matrix of tile-gene means, then returning the SIR directions and SIR scores.

The motivation behind wSIR is that SIR only uses each cell’s spatial position when we are separating the cells into the given number of groups/tiles. Once those groups are created, we lose the fact that some groups may be more spatially related (if they come from adjacent tiles) than other groups (if they come from opposite sides of the tissue). wSIR uses a weight matrix to incorporate the spatial correlation between all pairs of cells in the SIR algorithm. This matrix has dimension H*H, where H is the number of tiles, and the (i,j)th entry represents the distance between tiles i and j. This matrix is incorporated into the eigendeomposition step. The wSIR output has the same structure as the SIR output.

Vignette Overview

In this vignette, we will demonstrate how to use WSIR to obtain a low-dimensional embedding of gene expression data. We will then explore this embedding using the package’s built-in functions. However, this low-dimensional matrix is more importantly used for your own downstream tasks that would benefit from a lower-dimensional representation of gene expression data that preserves information about each cell’s spatial location. We perform some basic downstream analysis to demonstrate the practicality of wSIR.

Data

We use data from Lohoff et al, 2021. The code below would allow you to download the mouse data yourself using the MouseGastrulationData and scran R packages. We randomly sample 20% of the cells from each of the three biological replicate samples.

# Packages needed to download data
#library(scran) # for logNormCounts
#library(MouseGastrulationData) # to download the data for this vignette

set.seed(2024)

seqfish_data_sample1 <- LohoffSeqFISHData(samples = c(1,2))
seqfish_data_sample1 = logNormCounts(seqfish_data_sample1) # log transform variance stabilising
rownames(seqfish_data_sample1) <- rowData(seqfish_data_sample1)[,"SYMBOL"] # change rownames to gene symbols that are consistent
sample1_exprs = t(assay(seqfish_data_sample1, "logcounts")) # extract matrix of gene expressions
sample1_coords = spatialCoords(seqfish_data_sample1)[,1:2] %>% as.data.frame()
colnames(sample1_coords) = c("x", "y")

seqfish_data_sample2 <- LohoffSeqFISHData(samples = c(3,4))
seqfish_data_sample2 = logNormCounts(seqfish_data_sample2)
rownames(seqfish_data_sample2) <- rowData(seqfish_data_sample2)[,"SYMBOL"]
sample2_exprs = t(assay(seqfish_data_sample2, "logcounts"))
sample2_coords = spatialCoords(seqfish_data_sample2)[,1:2] %>% as.data.frame()
colnames(sample2_coords) = c("x", "y")

seqfish_data_sample3 <- LohoffSeqFISHData(samples = c(5,6))
seqfish_data_sample3 = logNormCounts(seqfish_data_sample3)
rownames(seqfish_data_sample3) <- rowData(seqfish_data_sample3)[,"SYMBOL"]
sample3_exprs = t(assay(seqfish_data_sample3, "logcounts"))
sample3_coords = spatialCoords(seqfish_data_sample3)[,1:2] %>% as.data.frame()
colnames(sample3_coords) = c("x", "y")

keep1 = sample(c(TRUE, FALSE), nrow(sample1_exprs), replace = TRUE, prob = c(0.2, 0.8))
keep2 = sample(c(TRUE, FALSE), nrow(sample2_exprs), replace = TRUE, prob = c(0.2, 0.8))
keep3 = sample(c(TRUE, FALSE), nrow(sample3_exprs), replace = TRUE, prob = c(0.2, 0.8))

sample1_exprs = sample1_exprs[keep1,]
sample1_coords = sample1_coords[keep1,]
sample2_exprs = sample2_exprs[keep2,]
sample2_coords = sample2_coords[keep2,]
sample3_exprs = sample3_exprs[keep3,]
sample3_coords = sample3_coords[keep3,]

sample1_cell_types = seqfish_data_sample1$celltype[keep1]
sample2_cell_types = seqfish_data_sample2$celltype[keep2]
sample3_cell_types = seqfish_data_sample3$celltype[keep3]

save(sample1_exprs, sample1_coords, sample1_cell_types, 
     sample2_exprs, sample2_coords, sample2_cell_types, 
     sample3_exprs, sample3_coords, sample3_cell_types, 
     file = "../data/MouseData.rda", compress = "xz")

For this vignette and the examples for each function in wSIR, we simply load this data that has already been saved at data/MouseData.rda.

data(MouseData)

Supervised dimension reduction with wSIR

Parameter Study

wSIR contains two parameters, alpha and slices. Parameter slices is the number of groups along each spatial axis into which we split the observations in the wSIR algorithm. More slices means we could pick up more spatial information in the gene expression data, but we risk overfitting on the training set. Parameter alpha modifies the strength of the weight matrix. alpha = 0 is equivalent to no spatial weighting, meaning the weight matrix becomes the identity matrix. This is equivalent to SIR. Larger alpha values means there is more weight given to spatial correlation rather than gene expression differences alone in the computation of the wSIR directions.

The function exploreWSIRParams performs wSIR over many choices of alpha and slices to identify the most appropriate parameters moving forward. Here, we will use sample 1 only.

These parameters should both be tuned over some reasonable values. The function exploreWSIRParams computes and visualises the performance of wSIR for all given combinations of slices and alpha. The performance is computed from the following procedure: 1) For each combination of slices and alpha, split the data into 50% train and 50% test (where train and test halves both include gene expression data and cell coordinates). 2) Perform wSIR on the training set using the current combination of slices and alpha. 3) Project the gene expression data of the testing set into low-dimensional space using the wSIR results from step 2. 4) Evaluate wSIR’s performance by computing either the distance correlation (“DC”, default), or the correlation of distances (“CD”) between the projected gene expression data of the test set and the test coordinates, according to parameter metric. 5) Repeat steps 2-4 until it has been done nrep times (nrep is a parameter whose default is 5). Calculate the average metric value over each of the nrep iterations for this combination of slices and alpha. 6) Repeat steps 1-5 with all other combinations of slices and alpha to obtain an average metric value for each combination. 7) Return the combination of slices and alpha with the highest average metric value and display a plot showing the performance for every combination of parameters.

Note: a key advantage of wSIR over SIR is parameter robustness. Here, we can see that SIR’sperformance deteriorates as you use larger values for slices. However, wSIR’s performance is relatively stable as you vary both slices and alpha across reasonable values (e.g. among the default values we optimise over). In the following plot, the metric value becomes smaller as we increase the number of slices for α=0\alpha = 0 (which corresponds to SIR). Performance is stable for all non-zero alpha and slice combinations (which correspond to wSIR).

a <- Sys.time()
optim_obj = exploreWSIRParams(X = sample1_exprs, 
                              coords = sample1_coords, 
                              # optim_alpha = c(0,.5, 1,2,4,8), 
                              # optim_slices = c(5,10,15),
                              optim_alpha = c(0, 4),
                              optim_slices = c(10, 15),
                              nrep = 5,
                              metric = "DC")
## set up nrep random splits of the data into training and test sets
## completed runs of wSIR and metric calculation
## Optimal (alpha, slices) pair: (0, 10)
## Time difference of 6.785663 secs
optim_obj$plot

wSIR Computation

We next perform wSIR using the optimal parameter combination that we found in the previous section. We use the gene expression matrix and spatial coordinates from sample 1 here. This returns a list of results with 5 (named) slots, whose details can be found at ?wSIR::wSIR.

wsir_obj = wSIR(X = sample1_exprs, 
                coords = sample1_coords, 
                slices = optim_obj$best_slices, 
                alpha = optim_obj$best_alpha,
                optim_params = FALSE)

names(wsir_obj)
## [1] "scores"     "directions" "estd"       "W"          "evalues"

wSIR Results Analysis

In this section, we will demonstrate how to use the built-in analysis functions to better understand how wSIR creates a spatially-informed low-dimensional embedding. These functions all use a wSIR result as an input. Here, we use the output from the previous section, meaning we are studying the result of performing wSIR on sample 1 only.

wSIR Top Genes

The findTopGenes function finds and plots the genes with highest loading in the specified wSIR directions (default is direction 1). If a gene has high loading (in terms of magnitude), it is more important to the wSIR direction. Since the wSIR directions are designed to retain information about each cell’s spatial position, the genes with high loading should be spatially-related genes.

In the plot below, we can see which genes have the highest loading in wSIR direction 1. This is useful as it gives us an intuition about how wSIR creates the low-dimensional embedding. We can see that some of the genes are known spatial genes (e.g. Cdx2, Hox-), which is what we would expect to see.

We can simultaneously plot top genes for multiple directions, and utilise the

top_genes_obj = findTopGenes(WSIR = wsir_obj, highest = 8) # create top genes object
top_genes_plot = top_genes_obj$plot # select plot
top_genes_plot # print plot

top_genes_obj = findTopGenes(WSIR = wsir_obj, highest = 8, dirs = 2:4)
top_genes_plot = top_genes_obj$plot
top_genes_plot

Visualising wSIR Scores

The visualiseWSIRDirections function plots each cell at its spatial position, coloured by its value for each of the specified wSIR columns. This gives us an understanding of what each column of the low-dimensional embedding represents.

Below, we visualise the cells at their spatial positions, coloured by each of the 5 wSIR directions The top left plot illustrates how, for this example, wSIR direction 1 captures information about the “y” spatial axis, since cells with higher “y” coordinate have low wSIR1 value, while cells with lower “y” coordinate have higher wSIR1 value. wSIR2 is shown in the next plot over (the one titled “2”), and we can see that wSIR column two appears to capture information about the “x” spatial coordinate. The remaining three wSIR columns all contain information about cell types, which we can tell by the regions of high and low wSIR column values spread across the tissue.

vis_obj = visualiseWSIRDirections(coords = sample1_coords, WSIR = wsir_obj, dirs = 8) # create visualisations
vis_obj

UMAP on low-dimensional embedding

The two functions generateUmapFromWSIR and plotUmapFromWSIR create and display UMAP dimension reduction calculated on the wSIR low-dimensional embedding. We can colour the UMAP plot (where each point represents a cell) by its value for various genes of interest. This visualises the structure of the wSIR dimension reduction space, which is useful to gain more intuition about what the space represents. Specifically, we can see if the wSIR space contains neighbourhoods of high expression for specific genes, thus better understanding how this space is made.

To specify which genes we would like to include, we can use the output from the findTopGenes function from above, which finds spatially-related genes by ranking those with the highest loading in relevant wSIR directions. This output is then the value for the highest_genes parameter. Otherwise, we could also specify our own genes of interest if there are some specific genes we would like to visualise. For example, if we wanted to visualise the expression distribution for Cdx2 and Hoxb4, we could use genes = c("Cdx2", "Hoxb4") as an argument in plotUmapFromWSIR (and leave highest_genes blank).

Below, we use the UMAP function to visualise the wSIR space computed on the gene expression data from sample 1. We colour each cell by their values for the 6 genes with highest value in wSIR direction 1 (as found by the findTopGenes function previously). We can see that for some of these genes, there are specific regions of high expression in the UMAP plots, suggesting that the wSIR space separates cells based on their expression for those genes.

umap_coords = generateUmapFromWSIR(WSIR = wsir_obj)
umap_plots = plotUmapFromWSIR(X = sample1_exprs,
                              umap_coords = umap_coords,
                              highest_genes = top_genes_obj,
                              n_genes = 6)
umap_plots

Projection of new data with wSIR

A key functionality of the wSIR package is the ability to project new single-cell data into the wSIR low-dimensional space. This will allow for a low-dimensional representation of gene expression data that contains information about each cell’s spatial position even though we do not have access to the spatial coordinates for this new data. This low-dimensional wSIR embedding would be especially useful for downstream applications, like spatial alignment or spatial clustering (where we don’t have spatial coordinates).

Here, we will demonstrate the steps for that, as well as a specific application.

For each projection example, we will perform wSIR on a spatial transcriptomics dataset which includes gene expression data and spatial coordinates. We will then project a “single-cell” dataset, which only contains the gene expression matrix, into wSIR low-dimensional space.

Single-sample spatial dataset, single-sample single-cell dataset

Here, we demonstrate the steps to project a new sample single-cell dataset into wSIR low-dimensional space having already performed wSIR on the spatial transcriptomics dataset from the first sample.

We have already performed wSIR on sample 1, so here we project sample 2’s gene expression matrix into the wSIR low-dimensional space, which will therefore have an ability to predict sample 2’s (unknown at this stage) spatial locations.

sample2_low_dim_exprs = projectWSIR(wsir = wsir_obj, newdata = sample2_exprs)

Check the dimension of sample 2’s low-dimensional gene expression data:

dim(sample2_low_dim_exprs)
## [1] 2986   50

Observe some of sample 2’s low-dimensional gene expression data:

head(sample2_low_dim_exprs)
##            [,1]       [,2]       [,3]       [,4]     [,5]       [,6]       [,7]
## [1,]  0.4033853 -1.1406906  0.9342274 -0.6690855 1.183150 -0.4204610 -1.6947454
## [2,] -0.3976906 -1.1332231  0.2099434 -0.9262752 1.452108 -0.3434242  0.2826430
## [3,]  2.8521104  0.5786873  1.7263464 -0.4939740 1.545942  1.9442965 -0.9077402
## [4,]  3.0851403  0.8251517  0.6092021 -0.5964077 1.349482  1.1061599 -0.8913565
## [5,]  1.7075150 -1.5915042 -0.4335929 -0.6712088 1.829439  1.3707372  0.8978932
## [6,]  1.5921375 -1.3481260 -0.4065247  0.1631131 1.854096  0.2094885  0.5237807
##            [,8]        [,9]       [,10]      [,11]      [,12]        [,13]
## [1,] -1.2416277  1.59455858  0.87866970  0.1545180 -0.1226031  0.007213118
## [2,] -0.9156203 -0.01792227  0.17127514 -0.6272048  0.3600095  0.041998794
## [3,] -1.1414361  0.88014085  0.10466509 -1.1003643  0.2268346  0.104254143
## [4,] -1.5925948  0.98843793  0.55220381 -0.9919387  1.0468458  0.535745625
## [5,] -1.5563948 -0.37715216 -0.29244180 -0.4787738  0.4219472 -0.599094022
## [6,] -1.1472571 -0.85171920  0.06985137 -0.5388220 -0.4363146 -1.735954576
##            [,14]      [,15]      [,16]       [,17]      [,18]        [,19]
## [1,] -0.26403842 -0.1667892 -0.1674317 -0.84025812 -1.2032348 -1.923318769
## [2,]  0.02027922 -0.1912650  0.4971098 -0.03229449 -0.6919699 -0.005947897
## [3,] -1.07366605 -0.6264570  1.6476511 -1.85407880 -1.2730726 -0.277065604
## [4,] -0.26912731  0.1633992  1.3475923 -1.08817836 -0.8927717  0.104622662
## [5,] -0.11342803 -0.7663365  0.3608360  1.22403405 -0.8265268  1.261045070
## [6,]  0.66379966 -1.2086104 -0.1218102  0.83435792 -1.8961773  1.151805215
##           [,20]       [,21]       [,22]      [,23]      [,24]       [,25]
## [1,] -1.3525301 -0.90228595  0.21986234  1.7140137  1.7309975  0.74602409
## [2,] -0.8091922 -2.27363291  0.20854077 -0.1540283 -1.1164174 -0.05310395
## [3,]  0.5378237 -0.03316571 -0.27216629 -0.5039993 -0.1865994 -0.79026591
## [4,] -0.3044058  0.39915731 -0.54615606 -1.0883485 -0.6943658 -1.00291231
## [5,] -0.1299859 -1.53565884  0.03956295 -0.5873782  0.1000467 -1.63964609
## [6,] -0.6253794 -0.99818155  0.14399058 -0.4114348 -0.3822816 -0.54459743
##            [,26]      [,27]      [,28]      [,29]      [,30]        [,31]
## [1,]  0.35040014 -1.9244114  1.0994142 -0.8017454 0.06630328  0.018409306
## [2,] -0.04033414 -2.4342194  0.1775621  0.4003911 1.99938051 -0.001305528
## [3,] -0.16474872 -1.2637777  0.1026267  0.2428714 0.58283064  0.195284812
## [4,] -0.32528226 -0.6078707 -0.1877018  0.3551147 0.25573785 -0.906437879
## [5,] -1.00529104 -1.7031747  1.2513900  0.4557731 0.18030373  0.863123205
## [6,] -1.13653850 -1.8444441 -0.1379572  0.2130001 0.62098951  0.555737550
##           [,32]       [,33]       [,34]       [,35]      [,36]       [,37]
## [1,]  0.3581564 -0.29453308 -1.22243261 -0.83359997  0.5626265  0.27147656
## [2,] -0.2014190 -0.32778650 -0.54913966 -1.38506568 -0.2047991 -0.55698357
## [3,] -0.3263791 -0.32772422  0.19305454 -0.01615896  0.1681945 -0.03517469
## [4,] -0.5392833 -0.06349666 -0.32993882 -1.26199626  0.3987478  0.47164230
## [5,] -0.2326273 -0.49177219 -0.22691262 -1.14278654  1.1506724  0.50078317
## [6,]  0.5669336 -0.12087072  0.00246478 -0.45564116  0.2931448  0.29778589
##           [,38]       [,39]      [,40]       [,41]     [,42]      [,43]
## [1,] -0.7485256  0.41777984  0.4383434 -2.50295425 0.3573508 -1.6484713
## [2,]  0.7616677  0.77111513 -1.1546844 -0.50097650 0.6704736  0.9528671
## [3,]  0.2185019  0.16336572 -0.3048052 -0.92621527 0.7283678  0.2140502
## [4,]  0.1381785 -0.05387757 -0.4122033 -0.09306071 0.7372195 -0.0273528
## [5,]  0.3879110  0.19807621 -0.5889438 -0.69818005 0.6589139  0.4508320
## [6,]  0.6482173  0.20978183 -1.0426766 -1.42117800 0.4702585  0.2349036
##           [,44]       [,45]       [,46]       [,47]      [,48]      [,49]
## [1,] -0.4895815 -0.59446975  0.71408399 -1.23068370  0.3799625 -0.3894140
## [2,] -0.2195819  0.62827717 -0.52972392  1.08694513  0.5879184 -2.0141168
## [3,]  0.2567104 -0.48469833  0.12967940  0.37968571  0.4806475 -0.5887303
## [4,]  0.1112703  0.02530738 -0.21172745  0.99822491  0.5224671 -0.6332211
## [5,]  0.2628886  0.19487445 -0.07063846 -0.59261012 -0.2320150 -0.7073504
## [6,]  0.9833049 -0.10346168  0.52593553  0.01375025  0.1666974 -0.5304861
##            [,50]
## [1,] -0.39796516
## [2,] -0.02853424
## [3,] -0.06295446
## [4,]  0.44533316
## [5,] -0.25872334
## [6,] -0.23373365

This low-dimensional gene expression data can then be used for any later tasks which would benefit from a low-dimensional embedding of the gene expression data for all the samples, rather than just the gene expression data.

Multi-sample spatial dataset, single-sample single-cell dataset

Here, we perform wSIR on samples 1 and 2 together. This requires the gene expression matrices from both samples joined together into one matrix, and the coords dataframes joined into one dataframe. We do this concatenation using rbind. We then specify the sample IDs for each row in the joined expression matrix and coordinates dataframe using the samples argument. This is a vector with a “1” for each row in sample 1 and a “2” for each row in sample 2.

We use the resulting wSIR output to project the gene expression data from sample 3 into low-dimensional space. We check the dimension of the resulting matrix.

wsir_obj_samples12 <- wSIR(X = rbind(sample1_exprs, sample2_exprs),
                           coords = rbind(sample1_coords, sample2_coords),
                           samples = c(rep(1, nrow(sample1_coords)), rep(2, nrow(sample2_coords))),
                           slices = optim_obj$best_slices, 
                           alpha = optim_obj$best_alpha,
                           optim_params = FALSE)

sample3_low_dim_exprs <- projectWSIR(wsir = wsir_obj_samples12, newdata = sample3_exprs)
dim(sample3_low_dim_exprs)
## [1] 4607   50

This low-dimensional matrix can then be used for downstream tasks which would benefit from a low-dimensional embedding of sample 3’s gene expression matrix that contains information about each cell’s location. Examples for downstream use include spatial alignment of single-cell and spatial gene expression data via Tangram, using the wSIR scores as the input rather than the (unreduced) gene expression matrix.

An example of a very simple application is using the wSIR scores as an input to a KNN cell type classification algorithm. This is demonstrated below, using the ‘knn’ function from ‘class’ package.

samples12_cell_types = append(sample1_cell_types, sample2_cell_types)

knn_classification_object = knn(train = wsir_obj_samples12$scores, 
                                test = sample3_low_dim_exprs,
                                cl = samples12_cell_types,
                                k = 10)

tail(knn_classification_object)
## [1] Presomitic mesoderm Presomitic mesoderm Presomitic mesoderm
## [4] Presomitic mesoderm Presomitic mesoderm Presomitic mesoderm
## 24 Levels: Allantois Anterior somitic tissues ... Surface ectoderm

In the above code, we use the wSIR scores as input for a simple KNN-based cell type classification tool. We print the tail of the prediction vector, demonstrating how we can use the wSIR-based low-dimensional gene expression data from a new sample as a step in a realistic analysis pipeline.

Session Info
## R version 4.4.1 (2024-06-14)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 22.04.5 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.20.so;  LAPACK version 3.10.0
## 
## locale:
##  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
##  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
##  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
## [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
## 
## time zone: UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] class_7.3-22       umap_0.2.10.0      vctrs_0.6.5        doBy_4.6.24       
##  [5] Rfast_2.1.0        RcppParallel_5.1.9 RcppZiggurat_0.1.6 Rcpp_1.0.13       
##  [9] ggplot2_3.5.1      magrittr_2.0.3     wSIR_0.99.3        BiocStyle_2.32.1  
## 
## loaded via a namespace (and not attached):
##  [1] rlang_1.1.4                 matrixStats_1.4.1          
##  [3] compiler_4.4.1              png_0.1-8                  
##  [5] systemfonts_1.1.0           stringr_1.5.1              
##  [7] pkgconfig_2.0.3             SpatialExperiment_1.14.0   
##  [9] crayon_1.5.3                fastmap_1.2.0              
## [11] backports_1.5.0             magick_2.8.5               
## [13] XVector_0.44.0              labeling_0.4.3             
## [15] utf8_1.2.4                  rmarkdown_2.28             
## [17] UCSC.utils_1.0.0            ragg_1.3.3                 
## [19] purrr_1.0.2                 xfun_0.48                  
## [21] zlibbioc_1.50.0             cachem_1.1.0               
## [23] GenomeInfoDb_1.40.1         jsonlite_1.8.9             
## [25] highr_0.11                  DelayedArray_0.30.1        
## [27] BiocParallel_1.38.0         Deriv_4.1.6                
## [29] broom_1.0.7                 parallel_4.4.1             
## [31] R6_2.5.1                    bslib_0.8.0                
## [33] stringi_1.8.4               reticulate_1.39.0          
## [35] boot_1.3-30                 GenomicRanges_1.56.2       
## [37] jquerylib_0.1.4             bookdown_0.41              
## [39] SummarizedExperiment_1.34.0 knitr_1.48                 
## [41] modelr_0.1.11               IRanges_2.38.1             
## [43] Matrix_1.7-0                tidyselect_1.2.1           
## [45] abind_1.4-8                 yaml_2.3.10                
## [47] codetools_0.2-20            lattice_0.22-6             
## [49] tibble_3.2.1                Biobase_2.64.0             
## [51] withr_3.0.1                 askpass_1.2.1              
## [53] evaluate_1.0.1              desc_1.4.3                 
## [55] pillar_1.9.0                BiocManager_1.30.25        
## [57] MatrixGenerics_1.16.0       stats4_4.4.1               
## [59] generics_0.1.3              S4Vectors_0.42.1           
## [61] munsell_0.5.1               scales_1.3.0               
## [63] glue_1.8.0                  tools_4.4.1                
## [65] distances_0.1.11            RSpectra_0.16-2            
## [67] fs_1.6.4                    cowplot_1.1.3              
## [69] grid_4.4.1                  tidyr_1.3.1                
## [71] colorspace_2.1-1            SingleCellExperiment_1.26.0
## [73] GenomeInfoDbData_1.2.12     cli_3.6.3                  
## [75] textshaping_0.4.0           fansi_1.0.6                
## [77] S4Arrays_1.4.1              dplyr_1.1.4                
## [79] gtable_0.3.5                sass_0.4.9                 
## [81] digest_0.6.37               BiocGenerics_0.50.0        
## [83] SparseArray_1.4.8           rjson_0.2.23               
## [85] farver_2.1.2                htmltools_0.5.8.1          
## [87] pkgdown_2.1.1               lifecycle_1.0.4            
## [89] httr_1.4.7                  microbenchmark_1.5.0       
## [91] openssl_2.2.2               MASS_7.3-60.2