Single-cell RNA sequencing (scRNA-seq) has revolutionized our understanding of gene expression heterogeneity within complex biological systems. As scRNA-seq technology becomes increasingly accessible and cost-effective, experiments are generating data from larger and larger numbers of cells. However, the analysis of large scRNA-seq data remains a challenge, particularly in terms of scalability. While numerous analysis tools have been developed to tackle the complexities of scRNA-seq data, their scalability is often limited, posing a major bottleneck in the analysis of large-scale experiments. In particular, the R package Seurat is one of the most widely used tools for exploring and analyzing scRNA-seq data, but its scalability is often limited by available memory.
To address this issue, we introduce a new R package called “SCArray.sat” that extends the Seurat classes and functions to support Genomic Data Structure (GDS) files as a DelayedArray backend for data representation. GDS files store multiple dense and sparse array-based data sets in a hierarchical structure. This package defines a new class, called “SCArrayAssay” (derived from the Seurat class “Assay”), which wraps raw counts, normalized expressions, and scaled data matrices based on GDS-specific DelayedMatrix. It is designed to integrate seamlessly with the Seurat package to provide common data analysis in a workflow, with optimized algorithms for GDS data files.
~
~
The SeuratObject package defines the Assay
class with three members/slots counts
, data
and scale.data
storing raw counts, normalized expressions and scaled data matrix respectively. However, counts
and data
should be either a dense matrix or a sparse matrix defined in the Matrix package. The scalability of the sparse matrix is limited by the number of non-zero values (should be < 2^31), since the Matrix package uses 32-bit indices internally. scale.data
in the Assay
class is defined as a dense matrix, so it is also limited by the available memory. The new class SCArrayAssay
is derived from Assay
, with three additional slots counts2
, data2
and scale.data2
replacing the old ones. These new slots can be DelayedMatrix wrapping an on-disk data matrix, without loading the data in memory.
The SCArray.sat package takes advantage of the S3 object-oriented methods defined in the SeuratObject and Seurat packages to reduce code redundancy, by implementing the functions specific to the classes SCArrayAssay
and SC_GDSMatrix
(GDS-specific DelayedMatrix). Table 1 shows a list of key S3 methods for data analysis. For example, the function NormalizeData.SC_GDSMatrix()
will be called when a SC_GDSMatrix
object is passed to the S3 generic NormalizeData()
, while NormalizeData.Assay()
and NormalizeData.Seurat()
are unchanged. In addition, the SCArray and SCArray.sat packages implement the optimized algorithms for the calculations, by reducing the on-disk data access and taking the GDS data structure into account.
~
Table 1: Key S3 methods implemented in the SCArray.sat package.
Methods | Description | Note |
---|---|---|
GetAssayData.SCArrayAssay() | Accessor function for ‘SCArrayAssay’ objects | |
SetAssayData.SCArrayAssay | Setter functions for ‘Assay’ objects | |
NormalizeData.SC_GDSMatrix() | Normalize raw count data | Store a DelayedMatrix |
ScaleData.SC_GDSMatrix() | Scale and center the normalized data | |
FindVariableFeatures.SC_GDSMatrix() | Identifies features | |
RunPCA.SC_GDSMatrix() | Run a PCA dimensionality reduction |
SC_GDSMatrix: GDS- and single-cell- specific DelayedMatrix.
~
~
Requires SCArray (≥ v1.7.13), SeuratObject (≥ v4.0), Seurat (≥ v4.0)
Bioconductor repository
To install this package, start R and enter:
if (!requireNamespace("BiocManager", quietly=TRUE))
install.packages("BiocManager")
BiocManager::install("SCArray.sat")
# Load the packages
suppressPackageStartupMessages({
library(Seurat)
library(SCArray)
library(SCArray.sat)
})
# Input GDS file with raw counts
fn <- system.file("extdata", "example.gds", package="SCArray")
# show the file contents
(f <- scOpen(fn))
## Object of class "SCArrayFileClass"
## File: /home/biocbuild/bbs-3.17-bioc/R/site-library/SCArray/extdata/example.gds (504.7K)
## + [ ] *
## |--+ feature.id { Str8 1000 LZMA_ra(59.0%), 3.7K }
## |--+ sample.id { Str8 850 LZMA_ra(13.2%), 1.8K }
## |--+ counts { SparseReal32 1000x850 LZMA_ra(12.4%), 495.3K }
## |--+ feature.data [ ]
## |--+ sample.data [ ]
## | |--+ Cell_ID { Str8 850 LZMA_ra(13.2%), 1.8K }
## | |--+ Cell_type { Str8 850 LZMA_ra(2.98%), 165B }
## | \--+ Timepoint { Str8 850 LZMA_ra(3.73%), 229B }
## \--+ meta.data [ ]
scClose(f) # close the file
# Create a Seurat object from the GDS file
d <- scNewSeuratGDS(fn)
## Input: /home/biocbuild/bbs-3.17-bioc/R/site-library/SCArray/extdata/example.gds
## counts: 1000 x 850
class(GetAssay(d)) # SCArrayAssay, derived from Assay
## [1] "SCArrayAssay"
## attr(,"package")
## [1] "SCArray.sat"
d <- NormalizeData(d)
## Performing log-normalization
d <- FindVariableFeatures(d, nfeatures=500)
## Calculating gene variances
## Calculating feature variances of standardized and clipped values
d <- ScaleData(d)
## Centering and scaling data matrix (SC_GDSMatrix [500x850])
Let’s check the internal data matrices,
# get the file name for the on-disk object
scGetFiles(d)
## [1] "/home/biocbuild/bbs-3.17-bioc/R/site-library/SCArray/extdata/example.gds"
# raw counts
m <- GetAssayData(d, "counts")
scGetFiles(m) # the file name storing raw count data
## [1] "/home/biocbuild/bbs-3.17-bioc/R/site-library/SCArray/extdata/example.gds"
m
## <1000 x 850> sparse SC_GDSMatrix object of type "double":
## 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06
## MRPL20 3 2 . 0
## GNB1 11 6 . 0
## RPL22 3 5 . 6
## PARK7 1 7 . 2
## ENO1 8 19 . 7
## ... . . . .
## SSR4 0 6 . 5
## RPL10 11 4 . 1
## SLC25A6-loc1 4 5 . 3
## RPS4Y1 0 5 . 2
## CD24 18 3 . 0
## 1772122_180_D09
## MRPL20 2
## GNB1 0
## RPL22 6
## PARK7 2
## ENO1 4
## ... .
## SSR4 1
## RPL10 3
## SLC25A6-loc1 1
## RPS4Y1 4
## CD24 2
# normalized expression
# the normalized data does not save in neither the file nor the memory
GetAssayData(d, "data")
## <1000 x 850> sparse SC_GDSMatrix object of type "double":
## 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06
## MRPL20 2.133695 1.423133 . 0.000000
## GNB1 3.342935 2.346631 . 0.000000
## RPL22 2.133695 2.183267 . 2.982560
## PARK7 1.247608 2.487018 . 1.980463
## ENO1 3.037644 3.431596 . 3.129447
## ... . . . .
## SSR4 0.000000 2.346631 . 2.810320
## RPL10 3.342935 1.987902 . 1.416593
## SLC25A6-loc1 2.391330 2.183267 . 2.338835
## RPS4Y1 0.000000 2.183267 . 1.980463
## CD24 3.821575 1.744869 . 0.000000
## 1772122_180_D09
## MRPL20 1.757196
## GNB1 0.000000
## RPL22 2.733620
## PARK7 1.757196
## ENO1 2.360130
## ... .
## SSR4 1.223211
## RPL10 2.103432
## SLC25A6-loc1 1.223211
## RPS4Y1 2.360130
## CD24 1.757196
# scaled and centered data matrix
# in this example, the scaled data does not save in neither the file nor the memory
GetAssayData(d, "scale.data")
## <500 x 850> SC_GDSMatrix object of type "double":
## 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06 1772122_180_D09
## MRPL20 1.0698786 0.2495111 . -1.39354354 0.63519819
## GNB1 1.9986433 0.9319892 . -1.58034238 -1.58034238
## PARK7 -0.7051228 0.7692228 . 0.16664801 -0.09894006
## MINOS1 0.8386726 0.8972573 . 1.08110724 -0.23734026
## ID3 -1.2795653 -1.2795653 . 1.26163229 -0.17349183
## ... . . . . .
## CETN2 0.4491348 -1.5497391 . 0.6670945 2.2490430
## SLC6A8 -0.9531097 0.1969921 . -0.9531097 -0.9531097
## RPL10 1.5151657 0.0120144 . -0.6217454 0.1401726
## RPS4Y1 -0.8929986 0.9293677 . 0.7600876 1.0769944
## CD24 2.2493072 0.1059022 . -1.6950086 0.1186249
Perform PCA and UMAP:
d <- RunPCA(d, ndims.print=1:2)
## Run PCA on the scaled data matrix ...
## Calculating the covariance matrix [500x500] ...
## PC_ 1
## Positive: NPM1, RPLP1, RPL35, HNRNPA1P10, RPS20, RPS6, RPS19, RPS3, HMGB2, RPL32
## RPL31P11-p1, HSPE1-MOB4, RPS23, HMGN2, RPS10-NUDT3, SNRPE, RPS24, CKS1B, H2AFZ, RPS14
## RPA3, RPL18A, RPS18-loc6, EEF1B2, SHFM1, TMA7, KIAA0101, RPS3A, RPL37A, SNRPG
## Negative: DCX, STMN2, MAP1B, NCAM1, GAP43, RTN1, BASP1, KIF5C, DPYSL3, DCC
## MIAT, TTC3, MALAT1, CRMP1, SOX11, TUBB3, GPM6A, TUBA1A, WSB1, TUBB2B
## RTN4, NNAT, SCG2, TUBB2A, MAP2, SEZ6L2, ONECUT2, MAP6, ENO2, CNTN2
## PC_ 2
## Positive: TUBA1B, NUCKS1, HNRNPA2B1, MARCKSL1, MARCKS, HNRNPD, NES, HNRNPA1, KHDRBS1, LOC644936-p1
## CKB, SET, MIR1244-3-loc4, TUBA1C, SNORD38A, DEK, SOX11, SFPQ, HNRNPU, IGF2BP1
## CBX5, NASP, RPS17-loc1, SMC4, RPS17-loc2, RPL41, CENPF, HMGB1, HDAC2, RRM1
## Negative: RPL13AP5, RPL31P11-p1, RPS14, RPS3A, TTR, RPL37A, RPL18A, PMCH, RPLP1, RPS19
## RPL23A, RPS3, OLFM3, ANXA2, RPL32, RPS13, SULF1, CDO1, TRPM3, COL1A1
## RPL18, MTRNR2L8, RNA5-8S5-loc2, MIR611, MALAT1, HTR2C, RNA5-8S5-loc1, RPS25, HES1, LDHA
DimPlot(d, reduction="pca")
d <- RunUMAP(d, dims=1:50) # use all PCs calculated by RunPCA()
DimPlot(d, reduction="umap")
~
Let’s download a large single-cell RNA-seq dataset from Bioconductor, and convert it to a GDS file. This step will take a while.
If the TENxBrainData
package is not installed, run:
# install a Bioconductor package
BiocManager::install("TENxBrainData")
Then,
library(TENxBrainData)
library(SCArray)
# scRNA-seq data for 1.3 million brain cells from E18 mice (10X Genomics)
# the data will be downloaded automatically at the first time.
# raw count data is stored in HDF5 format
tenx <- TENxBrainData()
rownames(tenx) <- rowData(tenx)$Ensembl # since rownames(tenx)=NULL
# save it to a GDS file
SCArray::scConvGDS(tenx, "1M_sc_neurons.gds")
After the file conversion, users can use this GDS file with Seurat to analyze the data.
~
~
The datasets used in the benchmark (Table 2) were generated from the 1.3 million brain cells, with the following R codes:
library(SCArray)
library(SCArray.sat)
sce <- scExperiment("1M_sc_neurons.gds") # load the full 1.3M cells
# D100 dataset
scConvGDS(sce[, 1:1e5], "1M_sc_neurons_d100.gds") # save to a GDS
# in-memory Seurat object
obj <- scMemory(scNewSeuratGDS("1M_sc_neurons_d100.gds"))
saveRDS(obj, "1M_sc_neurons_d100_seuratobj.rds") # save to a RDS
# D250 dataset
scConvGDS(sce[, 1:2.5e5], "1M_sc_neurons_d250.gds")
obj <- scMemory(scNewSeuratGDS("1M_sc_neurons_d250.gds"))
saveRDS(obj, "1M_sc_neurons_d250_seuratobj.rds")
# D500 dataset
scConvGDS(sce[, 1:5e5], "1M_sc_neurons_d500.gds")
obj <- scMemory(scNewSeuratGDS("1M_sc_neurons_d500.gds"))
saveRDS(obj, "1M_sc_neurons_d500_seuratobj.rds")
# Dfull dataset
scConvGDS(sce, "1M_sc_neurons_dfull.gds")
Table 2: Datasets in the benchmarks.
Dataset | # of features | # of cells | GDS file | RDS (Seurat Object) |
---|---|---|---|---|
D100 | 27,998 | 100K | 209MB | 419MB |
D250 | 27,998 | 250K | 529MB | 1.1GB |
D500 | 27,998 | 500K | 1.1GB | 2.2GB |
Dfull | 27,998 | 1.3 million | 2.8GB | Out of the limit of sparse matrix |
the number of non-zeros should be < 2^31 in a sparse matrix.
~
The following R script is used in the benchmark for testing GDS files, and the R codes for testing the Seurat Object are similar except the input file.
suppressPackageStartupMessages({
library(Seurat)
library(SCArray.sat)
})
# the input GDS file can be for d250, d500, dfull
fn <- "1M_sc_neurons_d100.gds"
d <- scNewSeuratGDS(fn)
d <- NormalizeData(d)
d <- FindVariableFeatures(d, nfeatures=2000) # using the default
d <- ScaleData(d)
d <- RunPCA(d)
d <- RunUMAP(d, dims=1:50)
saveRDS(d, "d100.rds") # or d250.rds, d500.rds, dfull.rds
q('no')
~
With large test datasets, the SCArray.sat package significantly reduces the memory usages compared with the Seurat package, while the in-memory implementation in Seurat is only 2 times faster than SCArray.sat. When the full dataset “Dfull” was tested, Seurat failed to load the data since the number of non-zeros is out of the limit of sparse matrix.
~
~
The Seurat object with SCArrayAssay
can be directly saved to a RDS (R object) file, in which the raw counts in the GDS file is not stored in the RDS file. This can avoid data duplication, and is helpful for faster meta data loading. Please keep the GDS and RDS files in the same directory or the same relative paths. The R object can be reloaded later in another R session, and GDS files are reopened internally when accessing the count data.
d # the example for the small dataset
## An object of class Seurat
## 1000 features across 850 samples within 1 assay
## Active assay: RNA (1000 features, 500 variable features)
## 2 dimensional reductions calculated: pca, umap
save_fn <- tempfile(fileext=".rds") # or specify an appropriate location
save_fn
## [1] "/tmp/RtmpZ4o5ou/file1e74170fdc3c.rds"
saveRDS(d, save_fn) # save to a RDS file
remove(d) # delete the variable d
gc() # trigger a garbage collection
## used (Mb) gc trigger (Mb) max used (Mb)
## Ncells 8755129 467.6 14759160 788.3 11171558 596.7
## Vcells 15499281 118.3 26246384 200.3 26246384 200.3
d <- readRDS(save_fn) # load from a RDS file
d
## An object of class Seurat
## 1000 features across 850 samples within 1 assay
## Active assay: RNA (1000 features, 500 variable features)
## 2 dimensional reductions calculated: pca, umap
GetAssayData(d, "counts") # reopens the GDS file automatically
## <1000 x 850> sparse SC_GDSMatrix object of type "double":
## 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06
## MRPL20 3 2 . 0
## GNB1 11 6 . 0
## RPL22 3 5 . 6
## PARK7 1 7 . 2
## ENO1 8 19 . 7
## ... . . . .
## SSR4 0 6 . 5
## RPL10 11 4 . 1
## SLC25A6-loc1 4 5 . 3
## RPS4Y1 0 5 . 2
## CD24 18 3 . 0
## 1772122_180_D09
## MRPL20 2
## GNB1 0
## RPL22 6
## PARK7 2
## ENO1 4
## ... .
## SSR4 1
## RPL10 3
## SLC25A6-loc1 1
## RPS4Y1 4
## CD24 2
~
The multicore and multi-process features are supported by SCArray and SCArray.sat via the Bioconductor package “BiocParallel”. To enable the parallel feature, users can use the function setAutoBPPARAM()
in the DelayedArray package to setup multi-process workers. For examples,
library(BiocParallel)
DelayedArray::setAutoBPPARAM(MulticoreParam(4)) # 4 child processes
The Seurat package utilizes the “future” package to perform parallel calculations. To enable multi-threading in RunUMAP()
, users should also set a “plan” for parallel processing, since Seurat uses nbrOfWorkers()
to determine the number of threads internally. E.g.,
library(future)
plan(multicore, workers=4) # 4 cores
message("Number of parallel workers: ", nbrOfWorkers())
In the function FindMarkers()
, users should use DelayedArray::setAutoBPPARAM()
to set a parallel environment and reset the future
package via plan(sequential)
.
~
The SCArrayAssay
object can be downgraded to the regular Assay
. It is useful when the downstream functions or packages do not support DelayedArray.
is(GetAssay(d))
## [1] "SCArrayAssay" "Assay"
new_d <- scMemory(d) # downgrade the active assay
is(GetAssay(new_d))
## [1] "Assay"
If users only want to ‘downgrade’ the scaled data matrix, then
is(GetAssayData(d, "scale.data")) # it is a DelayedMatrix
## [1] "SC_GDSMatrix" "DelayedMatrix" "SC_GDSArray"
## [4] "UnionMatrix" "UnionMatrix2" "DelayedArray"
## [7] "DelayedUnaryIsoOp" "DelayedUnaryOp" "DelayedOp"
## [10] "Array" "RectangularData"
new_d <- scMemory(d, slot="scale.data") # downgrade "scale.data" in the active assay
is(GetAssay(new_d)) # it is still SCArrayAssay
## [1] "SCArrayAssay" "Assay"
is(GetAssayData(new_d, "scale.data")) # in-memory matrix
## [1] "matrix" "array"
## [3] "mMatrix" "AnyMatrix"
## [5] "UnionMatrix2" "structure"
## [7] "matrix_OR_array_OR_table_OR_numeric" "vector"
## [9] "vector_OR_factor" "vector_OR_Vector"
~
A Seurat object with SCArrayAssay
can be converted to a Bioconductor SingleCellExperiment
object using as.SingleCellExperiment()
in the Seurat package. The DelayedMatrix in `SCArrayAssay will be saved in the new SingleCellExperiment object. For example,
is(d)
## [1] "Seurat"
sce <- as.SingleCellExperiment(d)
is(sce)
## [1] "SingleCellExperiment" "RangedSummarizedExperiment"
## [3] "SummarizedExperiment" "RectangularData"
## [5] "Vector" "Annotated"
## [7] "vector_OR_Vector"
sce
## class: SingleCellExperiment
## dim: 1000 850
## metadata(0):
## assays(2): counts logcounts
## rownames(1000): MRPL20 GNB1 ... RPS4Y1 CD24
## rowData names(0):
## colnames(850): 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06
## 1772122_180_D09
## colData names(7): orig.ident nCount_RNA ... Timepoint ident
## reducedDimNames(2): PCA UMAP
## mainExpName: RNA
## altExpNames(0):
counts(sce) # raw counts
## <1000 x 850> sparse SC_GDSMatrix object of type "double":
## 1772122_301_C02 1772122_180_E05 ... 1772122_180_B06
## MRPL20 3 2 . 0
## GNB1 11 6 . 0
## RPL22 3 5 . 6
## PARK7 1 7 . 2
## ENO1 8 19 . 7
## ... . . . .
## SSR4 0 6 . 5
## RPL10 11 4 . 1
## SLC25A6-loc1 4 5 . 3
## RPS4Y1 0 5 . 2
## CD24 18 3 . 0
## 1772122_180_D09
## MRPL20 2
## GNB1 0
## RPL22 6
## PARK7 2
## ENO1 4
## ... .
## SSR4 1
## RPL10 3
## SLC25A6-loc1 1
## RPS4Y1 4
## CD24 2
~
Not all of the functions in the Seurat package can be applied to the SCArrayAssay
object. Here is the list of currently supported and unsupported functions we have tested. The unsupported methods maybe available on request in the future release of SCArray.sat. Note that the supported states may depend on the package versions of Seurat and SeuratObject, and SeuratObject_4.1.3 and Seurat_4.3.0 were tested here.
Table 3: The states of functions and methods with the support of SCArrayAssay.
State | Functions | Notes |
---|---|---|
✓ | CreateSeuratObject() | |
✓ | FindVariableFeatures() | |
✓ | NormalizeData() | |
✓ | RunPCA() | |
✓ | ScaleData() | |
☑ | FindMarkers() | data read via blocking |
☑ | FoldChange() | |
☑ | RunICA() | |
☑ | RunSPCA() | |
☑ | RunLDA() | |
☑ | RunSLSI() | |
⦿ | FindNeighbors | |
⦿ | HVFInfo() | |
⦿ | RunUMAP() | |
⦿ | RunTSNE() | |
⦿ | ProjectDim() | |
⦿ | ProjectUMAP() | |
⦿ | SVFInfo() | |
⦿ | VariableFeatures() | |
✗ | CreateAssayObject() | use CreateAssayObject2() instead |
✗ | as.Seurat() | |
✗ | RunCCA() | |
✗ | SCTransform() |
~
options(SCArray.verbose=TRUE)
is used to enable displaying debug information when calling the functions in the SCArray and SCArray.sat packages. For example,
options(SCArray.verbose=TRUE)
d <- ScaleData(d)
## Centering and scaling data matrix (SC_GDSMatrix [500x850])
~
~
# print version information about R, the OS and attached or loaded packages
sessionInfo()
## R version 4.3.1 (2023-06-16)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 22.04.2 LTS
##
## Matrix products: default
## BLAS: /home/biocbuild/bbs-3.17-bioc/R/lib/libRblas.so
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_GB LC_COLLATE=C
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: America/New_York
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats4 stats graphics grDevices utils datasets methods
## [8] base
##
## other attached packages:
## [1] SCArray.sat_1.0.3 SCArray_1.8.3 DelayedArray_0.26.6
## [4] S4Arrays_1.0.4 IRanges_2.34.1 S4Vectors_0.38.1
## [7] MatrixGenerics_1.12.2 matrixStats_1.0.0 BiocGenerics_0.46.0
## [10] Matrix_1.6-0 gdsfmt_1.36.1 SeuratObject_4.1.3
## [13] Seurat_4.3.0.1 BiocStyle_2.28.0
##
## loaded via a namespace (and not attached):
## [1] RColorBrewer_1.1-3 jsonlite_1.8.7
## [3] magrittr_2.0.3 magick_2.7.4
## [5] spatstat.utils_3.0-3 farver_2.1.1
## [7] rmarkdown_2.23 zlibbioc_1.46.0
## [9] vctrs_0.6.3 ROCR_1.0-11
## [11] DelayedMatrixStats_1.22.2 spatstat.explore_3.2-1
## [13] RCurl_1.98-1.12 htmltools_0.5.5
## [15] sass_0.4.6 sctransform_0.3.5
## [17] parallelly_1.36.0 KernSmooth_2.23-22
## [19] bslib_0.5.0 htmlwidgets_1.6.2
## [21] ica_1.0-3 plyr_1.8.8
## [23] plotly_4.10.2 zoo_1.8-12
## [25] cachem_1.0.8 igraph_1.5.0
## [27] mime_0.12 lifecycle_1.0.3
## [29] pkgconfig_2.0.3 rsvd_1.0.5
## [31] R6_2.5.1 fastmap_1.1.1
## [33] GenomeInfoDbData_1.2.10 fitdistrplus_1.1-11
## [35] future_1.33.0 shiny_1.7.4.1
## [37] digest_0.6.33 colorspace_2.1-0
## [39] patchwork_1.1.2 tensor_1.5
## [41] irlba_2.3.5.1 GenomicRanges_1.52.0
## [43] beachmat_2.16.0 labeling_0.4.2
## [45] progressr_0.13.0 fansi_1.0.4
## [47] spatstat.sparse_3.0-2 httr_1.4.6
## [49] polyclip_1.10-4 abind_1.4-5
## [51] compiler_4.3.1 withr_2.5.0
## [53] BiocParallel_1.34.2 highr_0.10
## [55] MASS_7.3-60 tools_4.3.1
## [57] lmtest_0.9-40 httpuv_1.6.11
## [59] future.apply_1.11.0 goftest_1.2-3
## [61] glue_1.6.2 nlme_3.1-162
## [63] promises_1.2.0.1 grid_4.3.1
## [65] Rtsne_0.16 cluster_2.1.4
## [67] reshape2_1.4.4 generics_0.1.3
## [69] gtable_0.3.3 spatstat.data_3.0-1
## [71] tidyr_1.3.0 data.table_1.14.8
## [73] XVector_0.40.0 ScaledMatrix_1.8.1
## [75] BiocSingular_1.16.0 sp_2.0-0
## [77] utf8_1.2.3 spatstat.geom_3.2-2
## [79] RcppAnnoy_0.0.21 ggrepel_0.9.3
## [81] RANN_2.6.1 pillar_1.9.0
## [83] stringr_1.5.0 later_1.3.1
## [85] splines_4.3.1 dplyr_1.1.2
## [87] lattice_0.21-8 survival_3.5-5
## [89] deldir_1.0-9 tidyselect_1.2.0
## [91] SingleCellExperiment_1.22.0 miniUI_0.1.1.1
## [93] pbapply_1.7-2 knitr_1.43
## [95] gridExtra_2.3 bookdown_0.34
## [97] SummarizedExperiment_1.30.2 scattermore_1.2
## [99] xfun_0.39 Biobase_2.60.0
## [101] stringi_1.7.12 lazyeval_0.2.2
## [103] yaml_2.3.7 evaluate_0.21
## [105] codetools_0.2-19 tibble_3.2.1
## [107] BiocManager_1.30.21 cli_3.6.1
## [109] uwot_0.1.16 xtable_1.8-4
## [111] reticulate_1.30 munsell_0.5.0
## [113] jquerylib_0.1.4 GenomeInfoDb_1.36.1
## [115] Rcpp_1.0.11 globals_0.16.2
## [117] spatstat.random_3.1-5 png_0.1-8
## [119] parallel_4.3.1 ellipsis_0.3.2
## [121] ggplot2_3.4.2 bitops_1.0-7
## [123] sparseMatrixStats_1.12.2 listenv_0.9.0
## [125] viridisLite_0.4.2 scales_1.2.1
## [127] ggridges_0.5.4 leiden_0.4.3
## [129] purrr_1.0.1 crayon_1.5.2
## [131] rlang_1.1.1 cowplot_1.1.1