Weighted Sliced Inverse Regression (WSIR)
Max Woollard
Pratibha Panwar
Linh Nghiem
Shila Ghazanfar
Source:vignettes/wSIR_vignette.Rmd
wSIR_vignette.Rmd
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
(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)
Sys.time()-a
## 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