Contents

1 Abstract of singIST as a Bioconductor library

Comparative transcriptomics aims to identify common and differing patterns (organisms, experimental conditions, tissues, and so on) between a set of conditions by using transcriptomics data The methodology to tackle the problem will vary greatly depending on which patterns we aim to compare, the amount and biological nature of them and which omics resolution. singIST is a comparative transcriptomics method that aims to compare a disease model against a human disease of interest. In drug discovery and pharmaceutical early development choosing a disease model becomes a fundamental endavour. For this reason, we present singIST as a method that compares the single-cell transcriptomics profile of a disease model, either In Vivo or In Vitro models, against a human disease that such model should mimick.

Other Bioconductor packages for comparative transcriptomics are available. One of such is HybridExpress which aims to compare the bulk transcriptomics (RNA-seq) profile for hybrids relative to their progenitor species. CoSIA is a Bioconductor package that enables organism-to-organism comparative bulk transcriptomics (RNAseq) across tissues. However, none of these packages are direct comparators to **singIST* since they are not conceived for single-cell comparative transcriptomics but for bulk transcriptomics instead.

2 Introduction

This Vignette demonstrates how to use the singIST package. singIST quantifies the direction and mangitude of single-cell transcriptomic changes between a human reference condition and a disease model at pathway, cell type, and gene resolution. Throughout this vignette we will work with the following data:

set.seed(003)
library(singIST)

3 Inputs of singIST

The method is defined around three inputs:

In the following subsections we explicitly construct these inputs. The rest of the vignette uses these inputs to: (1) learn a human reference model using adaptive sparse multi-block PLS-DA, and then (2) evaluate how human-like the disease model changes are, in the learned human reference space.

3.1 Superpathways

singIST does not operate on a pathway gene set alone. Instead, it defines a superpathway by three things:

In many practical cases, we use the same pathway gene set for every cell type. However, singIST also supports cell-type specific gene sets. Below we create two example of superpathways used later in this vignette.

First, because singIST relies on MSigDB curated pathways, we load the MSigDB gene set collection once and reuse it as necessary. We assume the reference to compare against is human org = "hs" and we extract both ENTREZID and HGNC gene symbols id = c("SYM", "EZID").

gse <- msigdb::getMsigdb(org = "hs", id = c("SYM", "EZID"),
                         version = msigdb::getMsigdbVersions()[1])

3.1.1 Example 1: Superpathway with the same pathway gene set for each cell type

In this first example, we define a pathway using its MSigDB metadata, choose the cell types we want to model, and then assign the same pathway gene set to each cell type gene set. This is the simplest and most common setup for a superpathway.

# Pathway definition (curated gene set identifier)
dc_pathway <- create_pathway(standard_name = "BIOCARTA_DC_PATHWAY",
                             dbsource = "BIOCARTA",
                             collection = "c2",
                             subcollection = "CP")
# Superpathway = pathway + modeled cell types + per cell type gene sets
dc_superpathway <- create_superpathway(pathway_info = dc_pathway,
                                       celltypes = c("Dendritic Cells",
                                                     "Langerhan Cells",
                                                     "T-cell"),
                                       list())
# Assign gene sets per cell type (retrieve gene set from MSigDB)
dc_superpathway <- setGeneSetsCelltype(dc_superpathway, gse = gse)

3.1.2 Example 2: Superpathway with different gene sets per cell type (optional)

In some applications, you may want different cell types to use different gene sets. singIST supports this by allowing the user to directly provide a list of gene vectors, one per cell type.

Here we demonstrate this capability by taking the curated pathway gene set and creating different subsets for each cell type. (This is purely illustrative; in real analyses these subsets would be defined biologically rather than randomly sampled.)

# Pathway definition (curated gene set identifier)
cytokine_pathway <- create_pathway(
    standard_name = "KEGG_CYTOKINE_CYTOKINE_RECEPTOR_INTERACTION",
    dbsource = "KEGG",
    collection = "c2",
    subcollection = "CP"
)
# Superpathway = pathway + modeled cell types + per cell type gene sets
cytokine_superpathway <- create_superpathway(
    pathway_info = cytokine_pathway,
    celltypes = c("Dendritic Cells", "Keratinocytes", "Langerhan Cells",
                  "Melanocytes", "T-cell"),
    list()
)
# Pull the curated gene set (retrieve gene set from MSigDB)
gene_set <- pullGeneSet(cytokine_pathway, gse = gse)
# Provide a different gene set per cell type
cytokine_superpathway$gene_sets_celltype <- list(
    "Dendritic Cells" = sample(gene_set, 50),
    "Keratinocytes" = sample(gene_set, 100),
    "Langerhan Cells" = sample(gene_set, 10),
    "Melanocytes" = sample(gene_set, 60),
    "T-cell" = sample(gene_set, 160)
)

3.2 Human scRNA-seq

The human reference data are used to train an adaptive sparse multi-block PLS on a superpathway. In this subsection we start from the human scRNA-seq that have already been aggregated into cell-type pseudobulk log-normalize expression. For instance, the pseudobulk can be performed with Seurat::AggregateExpression(). Concretely the pseudobulk matrix:

  • Each row corresponds to a pseudobulk profile for a cell type within a sample (e.g., T-cell_AD1).
  • Columns correspond to genes (typically HGNC symbols for human training).
  • Values correspond to log-normalized pseudobulk expression.

In addition to the pseudobulk matrix, singIST requires sample level labels defining a base class and a target class (e.g., HC vs AD). The target class is the human experimental group that the disease model such mimic precisely, while the base class is the human experimental group that such differentiate from the target class.

The training procedure includes a tuning step, so the human input is not only the pseudobulk matrix and sample labels - it also includes the hyperparameter search space and the cross-validation design the user wants. In practice, we tune:

  • Block-specific sparsity via a grid of quantile thresholds (one quantile per modeled cell type). Intuitively, higher quantiles enforce stronger sparsity (fewer genes retained) in that block.
  • Model complexity via number_PLS (maximum number of latent components to test).
  • Validation scheme via folds_CV and repetition_CV.

We store the former settings in a hyperparameters list and then combine all the information into the superpathway_input list, which is the main training input consumed by the training procedure fitOptimal()/ multiple_fitOptimal().

3.2.1 Example 1: Dendritic Cells in regulating Th1/Th2 Development

# Load the pseudobulk matrix
file_dc <- system.file("extdata", "DC_counts.rda", package = "singIST")
obj_dc <- load(file_dc)
counts_dc <- get(obj_dc[1])
# Remove the cell types that we are not going to model
counts_dc <- counts_dc[!(sub("_.*$", "", rownames(counts_dc)) %in% 
                        c("MastC-other", "Keratinocytes", "Melanocytes")), ]

Next, we define the sample identifiers and class labels.

# Load the pseudobulk matrix
sample_id_dc <- c("AD1", "AD13", "AD2", "AD3","AD4", "HC1-2", "HC3", "HC4",
                  "HC5")
sample_class_dc <- c(rep("AD", 5), rep("HC", 4))
base_class_dc <- "HC"
target_class_dc <- "AD"

Now we define the hyperparameters.

# Initialize hyperparameter object 
# Choose the array of quantile combinations that will be tested in the Cross
# Validation. Ideally one should choose a wide range of quantiles to test,
# as a narrow selection might not find the globally optimal model. The more
# quantiles one chooses the more computation time it will take, for the sake
# of simplicity we choose a small amount of quantiles to test
quantile_comb_table_dc <- as.matrix(
    RcppAlgos::permuteGeneral(seq(0.1, 0.95, by = 0.3),
                                m = length(dc_superpathway$celltypes), 
                                TRUE))
# Store tuning + CV design
dc_hyperparameters <- create_hyperparameters(
    quantile_comb_table = quantile_comb_table_dc,
    outcome_type = "binary",
    number_PLS = as.integer(3),
    folds_CV = as.integer(1), #LOOCV
    repetition_CV = as.integer(1)
)

Finally, we combine all the information into a superpathway_input list.

dc_superpathway_input <- create_superpathway_input(
    superpathway_info = dc_superpathway,
    hyperparameters_info = dc_hyperparameters,
    pseudobulk_lognorm = counts_dc,
    sample_id = sample_id_dc,
    sample_class = sample_class_dc,
    base_class = base_class_dc,
    target_class = target_class_dc
)

3.2.2 Example 2: Cytokine-Cytokine receptor interaction superpathway

# Load the pseudobulk matrix
file_cytokine <- system.file("extdata", "cytokine_counts.rda", package = "singIST")
obj_cytokine <- load(file_cytokine)
counts_cytokine <- get(obj_cytokine[1])
# Remove the cell types that we are not going to model
counts_cytokine <- counts_cytokine[!(sub("_.*$", "", rownames(counts_cytokine)) 
                                     %in% 
                                 c("MastC-other")), ]

Next, we define the sample identifiers and class labels.

# Load the pseudobulk matrix
sample_id_cytokine <- c("AD1", "AD13", "AD2", "AD3","AD4", "HC1-2", "HC3",
                        "HC4", "HC5")
sample_class_cytokine <- c(rep("AD", 5), rep("HC", 4))
base_class_cytokine <- "HC"
target_class_cytokine <- "AD"

Now we define the hyperparameters.

# Initialize hyperparameter object 
quantile_comb_table <- as.matrix(
    RcppAlgos::permuteGeneral(seq(0.05, 0.95, by = 0.2),
                                m = length(cytokine_superpathway$celltypes), 
                                TRUE))
# Store tuning + CV design
cytokine_hyperparameters <- create_hyperparameters(
                                    quantile_comb_table = quantile_comb_table,
                                    outcome_type = "binary",
                                    number_PLS = as.integer(2),
                                    folds_CV = as.integer(5),
                                    repetition_CV = as.integer(5))

Finally, we combine all the information into a superpathway_input list.

cytokine_superpathway_input <- create_superpathway_input(
    superpathway_info = cytokine_superpathway,
    hyperparameters_info = cytokine_hyperparameters,
    pseudobulk_lognorm = counts_cytokine,
    sample_id = sample_id_cytokine,
    sample_class = sample_class_cytokine,
    base_class = base_class_cytokine,
    target_class = target_class_cytokine)

3.3 Disease model scRNA-seq

The disease model input is a single-cell object (either a Seurat or SingleCellExperiment) containing scRNA-seq measurements from the model system we want to assess. Unlike the human input (which is provided as pseudobulk matrices), the disease model is kept at single-cell resolution and is summarized internally by singIST when computing recapitulation scores.

To use a disease model dataset, singIST needs:

  • A class label indicating the base and target experimental groups in the model (e.g. ETOH vs OXA).
  • A cell-type cluster label used to map model cell populations to the human cell types defined in the superpathway.
  • A donor / sample identifier.

Finally, we must provide a cell type mapping from th ehuman superpathway cell types (names used in celltypes = ...) to the disease model clusters. A human cell type can map to one or more model clusters. A human cell type can map to one or more model clusters; it is also valid to provide an empty mapping (meaning that block will not contribute for that model).

In the following example we load the oxazolone mouse dataset shipped in the package, standarize metadata column names to those expected by singIST, define the mapping, and construct a mapping organism list via create_mapping_organism().

3.3.1 Example: Oxazolone mouse model of Atopic Dermatitis

file_oxa <- system.file("extdata", "OXA.rda", package = "singIST")
obj_oxa <- load(file_oxa)
oxazolone <- get(obj_oxa[1])

Next, we ensure the object contains the metadata fields required by singIST. The package example dataset already contains the necessary information, but we rename the relevant columns to the standarized names used downstream: class, celltype_cluster, and donor.

# The example object included in singIST uses these column positions
colnames(slot(oxazolone, "meta.data"))[c(5,11,1)] <- c("class",
                                                       "celltype_cluster",
                                                       "donor")
# If the object is a SingleCellExperiment, metadata live in colData()

Now, we define the human to model cell type mapping. The names of the list must match the human cell types used in our superpathways. Values correspond to one or more model clusters present in celltype_cluster.

celltype_mapping <- list(
                    "Dendritic Cells" = c("cDC2", "cDC1", "migratory DCs"),
                    "Keratinocytes" = c("Keratinocytes"),
                    "Langerhan Cells" = c("LC"), 
                    "Melanocytes" = c(),
                    "T-cell" = c("DETC", "T"))

Finally, we combine the model object and mapping into a mapping_organism list. This list specifies the model organism we are working with, and which class labels correspond to the base and target conditions.

oxa_org <- create_mapping_organism(organism = "Mus musculus", 
                target_class = "OXA", base_class = "ETOH",
                celltype_mapping = list(
                    "Dendritic Cells" = c("cDC2", "cDC1", "migratory DCs"),
                    "Keratinocytes" = c("Keratinocytes"),
                    "Langerhan Cells" = c("LC"), 
                    "Melanocytes" = c(),
                    "T-cell" = c("DETC", "T")),
                counts = oxazolone)

4 Fit Optimal asmbPLS-DA and compute validation metrics for the optimal model

At this stage we have constructed two key training inputs: dc_superpathway_input and cytokine_superpathway_input. The next step is to train an adaptive sparse multi-block PLS-DA model (asmbPLS-DA) for each superpathway, while selecting an optimal configuration (sparsity per cell type block and number of PLS components) under the cross-validation design specified in the corresponding hyperparameters object.

singIST provides two entry points to do it:

If the CV design is a leave-one-out CV (LOOCV), the functions provide with a parallelization option to make the computations faster via using parallel = TRUE and setting the desired paralellization scheme with BiocParallel::register().

In addition to fitting the optimal model, singIST can compute model validation metrics, including permutation based global significance test (npermut = 100) and resampling permutation tests (type, e.g., jackknife or subsampling). The number of permutation for the tests can be chosen as desired.

4.1 Example: fitting cytokine-cytokine receptor interaction and dendritic cells

4.2 in regulating

If the human pseudobulk input contains missing values (e.g., a cell type block absent for some samples), fitOptimal() / multiple_fitOptimal() automatically imputes them during model fitting. Further details of the imputation procedure are provided in the Appendix.

# Register a parallel backend (SnowParam works on Windows/macOS/Linux);
# on Linux you may prefer MulticoreParam.
BiocParallel::register(BiocParallel::SnowParam(workers = 2,
                        exportglobals = FALSE, progressbar = FALSE),
                        default = TRUE) 
# Combine all human training inputs into a list
superpathways <- list(dc_superpathway_input,
                      cytokine_superpathway_input)
# Fit Optimal asmbPLS-DA models + compute validation metrics
# parallel: logical vector indicating whether the CV design should be parallel
# npermut: number of permutations used for permutation based tests
# type : validation strategy (jackknife or subsampling)
# nsubsampling: number of subsampling repeats (only used when type includes
# subsampling).
superpathways_fit <- multiple_fitOptimal(
    superpathways,
    parallel = c(TRUE, FALSE), 
    npermut = c(100),
    type = c("jackknife","subsampling"), 
    nsubsampling = c(NULL, 5)
    )
# Restore serial backend (good practice)
BiocParallel::register(BiocParallel::SerialParam(), default = TRUE) 

5 Compute recapitulation metrics

After fitting the optimal human reference models (superpathway_fit), the final step is to quantify how well a disease model recapitulates the human target vs base signal for each superpathway. singIST does this by applying the disease model changes to the human base class reference and then evaluating how “target-like” the samples become. The resulting recapitulation outputs can be summarized at pathway, cell type, and gene resolution.

There are two functions to do this:

Below we compute recapitulations for both fitted superpathways against the oxazolone mapping organism oxa_org, and then render the results into a plotting friendly output

5.1 Example: Evaluating Oxazolone mouse model of Atopic Dermatitis for Dendritic

5.2 Cells and cytokine superpathways

By default, the function assumes the organism for which the asmbPLS-DA model was trained is “hsapiens”, it also assumes that the gene annotation for the mapped organism is “external_gene_name”. These parameters are by default but other cases can also be considered by specifying the parameters from_to = "hsapiens" and object_gene_identifiers = "external_gene_name".

# Compute singIST recapitulations for all fitted superpathways
recapitulations_multiple <- multiple_singISTrecapitulations(oxa_org,
                                                            superpathways_fit)

In many workflows you will want the outputs in a standarized format that is convenient for downstream plotting and reporting. The helper function render_multiple_outputs() reshapes the recapitulation objects accordingly. It makes a list that is ready-to-plot.

final_output <- render_multiple_outputs(objects= list(recapitulations_multiple))

6 Appendix: Missing value handling in human pseudobulk matrices

During CV, singIST may encounter missing entries in the human pseudobulk predictor (e.g., entire cell type blocks missing for some samples). Rather than discarding those samples, singIST imputes missing values inside each CV split in a way that preserves the multiblock structure and avoids information leakage. This procedure is performed automatically by fitOptimal() and multiple_fitOptimal() and the user does not provide any input on this, this section is merely for informative purposes.

6.1 Problem setup

Assume we have a multi-block dataset for which we aim to impute an asmbPLS-DA, \(D = \{\left(x_i, y_i\right) \}_{i=1}^n\) where \(x_i \in \mathbb{R}^p, y_i \in \{0,1\}\). We perform a K-fold CV, thus splitting the dataset into \(K\) folds; \(\mathcal{T}_k\) training indices, \(\mathcal{V}_k\) validation indices, \(k = 1, \dots, K\). Then we have a classic train-test dataset \(\left(X_{train}^{(k)}, Y_{train}^{(k)} \right) \in \{(x_i, y_i): i \in \mathcal{T}_k \}\) and \(\left(X_{val}^{(k)}, Y_{val}^{(k)} \right) \in \{(x_i, y_i): i \in \mathcal{V}_k \}\)

The problem lies in that \(X\) matrices we might have missing values.

6.2 Step 1: Impute the training fold using multiblock MFA.

We first impute \(X_{train}^{(k)}\) using Multple Factor Analysis (MFA) imputation (via missMDA::imputeMFA()), which explicitly accounts for the multiblock structure (blocks correspond to modeled cell types). This produces an imputed training matrix \(\widetilde{X}_{train}^{(k)}\).

6.3 Step 2: Impute the validation fold by projection (no re-fitting).

To avoid data leakage, we do not refit the MFA model on the validation data. Instead, we compute MFA loadings and means from the imputed training fold and project each validation sample using its observed features only.

Let \(\mu \in \mathbb{R}^p\) be the vector of variable means and \(P \in \mathbb{R}^{p \times ncp}\) the matrix of MFA loadings estimated from \(\widetilde{X}_{train}^{(k)}\). For each validation sample \(x_i \in X_{val}^{(k)}\) define the set of observed feature indices:

\[O_i = \{j: (x_i)_j \neq NA\}, \quad |O_i| \geq 1\]

We estimate the latent scores \(t_i\) by solving a least-squares problem restricted to the observed features:

\[t_i = \arg \min_{u_i \in \mathbb{R}^{ncp}} ||x_{i, O_i}-\left(\mu_{O_i}+P_{O_i}u_i \right)||_2^2 = \left(P_{O_i}^TP_{O_i}\right)^{-1} P_{O_i}^T \left(x_{i,O_i}-\mu_{O_i} \right)\]

We then reconstruct the feature full vector by:

\[\widehat{x}_{ij} = \mu_{j} + P_{j\cdot} t_i\]

The imputation validation entries are defined as:

\[ \begin{align} \widehat{x}_{ij} = \begin{cases} x_{ij}, & j \in O_i\\ \widetilde{x}_{ij}, & j \notin O_i \end{cases} \quad i \in \mathcal{V}_k \end{align} \]

This preserves the same latent space used for training-fold imputation and prevents leakage from validation into the imputation model. Imputation is intended for partial missingness (e.g., one missing block while other blocks are observed). If missingness is extensive (e.g., many samples missing most blocks), model stability and interpretability may degrade; in that case consider restricting the set of modeled cell types and/or increasing sample size.

7 Session Info

utils::sessionInfo()
## R Under development (unstable) (2025-12-22 r89219)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 LTS
## 
## Matrix products: default
## BLAS:   /home/biocbuild/bbs-3.23-bioc/R/lib/libRblas.so 
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.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] GSEABase_1.73.0      graph_1.89.1         annotate_1.89.0     
##  [4] XML_3.99-0.20        AnnotationDbi_1.73.0 IRanges_2.45.0      
##  [7] S4Vectors_0.49.0     Biobase_2.71.0       BiocGenerics_0.57.0 
## [10] generics_0.1.4       msigdb_1.19.0        singIST_0.99.85     
## [13] BiocStyle_2.39.0    
## 
## loaded via a namespace (and not attached):
##   [1] matrixStats_1.5.0           spatstat.sparse_3.1-0      
##   [3] doParallel_1.0.17           httr_1.4.7                 
##   [5] RColorBrewer_1.1-3          tools_4.6.0                
##   [7] sctransform_0.4.3           backports_1.5.0            
##   [9] R6_2.6.1                    DT_0.34.0                  
##  [11] lazyeval_0.2.2              uwot_0.2.4                 
##  [13] jomo_2.7-6                  withr_3.0.2                
##  [15] sp_2.2-0                    prettyunits_1.2.0          
##  [17] gridExtra_2.3               progressr_0.18.0           
##  [19] cli_3.6.5                   spatstat.explore_3.6-0     
##  [21] fastDummies_1.7.5           flashClust_1.01-2          
##  [23] sandwich_3.1-1              sass_0.4.10                
##  [25] Seurat_5.4.0                mvtnorm_1.3-3              
##  [27] S7_0.2.1                    spatstat.data_3.1-9        
##  [29] ggridges_0.5.7              pbapply_1.7-4              
##  [31] dichromat_2.0-0.1           parallelly_1.46.1          
##  [33] limma_3.67.0                RSQLite_2.4.5              
##  [35] shape_1.4.6.1               ica_1.0-3                  
##  [37] spatstat.random_3.4-3       car_3.1-3                  
##  [39] dplyr_1.1.4                 leaps_3.2                  
##  [41] Matrix_1.7-4                abind_1.4-8                
##  [43] lifecycle_1.0.5             scatterplot3d_0.3-44       
##  [45] multcomp_1.4-29             yaml_2.3.12                
##  [47] edgeR_4.9.2                 carData_3.0-5              
##  [49] SummarizedExperiment_1.41.0 SparseArray_1.11.10        
##  [51] BiocFileCache_3.1.0         Rtsne_0.17                 
##  [53] grid_4.6.0                  blob_1.2.4                 
##  [55] promises_1.5.0              dqrng_0.4.1                
##  [57] ExperimentHub_3.1.0         mitml_0.4-5                
##  [59] crayon_1.5.3                miniUI_0.1.2               
##  [61] lattice_0.22-7              beachmat_2.27.2            
##  [63] cowplot_1.2.0               KEGGREST_1.51.1            
##  [65] pillar_1.11.1               knitr_1.51                 
##  [67] metapod_1.19.1              GenomicRanges_1.63.1       
##  [69] boot_1.3-32                 estimability_1.5.1         
##  [71] future.apply_1.20.1         codetools_0.2-20           
##  [73] pan_1.9                     glue_1.8.0                 
##  [75] spatstat.univar_3.1-5       data.table_1.18.0          
##  [77] Rdpack_2.6.4                vctrs_0.6.5                
##  [79] png_0.1-8                   spam_2.11-3                
##  [81] gtable_0.3.6                cachem_1.1.0               
##  [83] xfun_0.55                   rbibutils_2.4              
##  [85] S4Arrays_1.11.1             mime_0.13                  
##  [87] Seqinfo_1.1.0               reformulas_0.4.3.1         
##  [89] coda_0.19-4.1               survival_3.8-3             
##  [91] SingleCellExperiment_1.33.0 iterators_1.0.14           
##  [93] gmp_0.7-5                   statmod_1.5.1              
##  [95] bluster_1.21.0              fitdistrplus_1.2-4         
##  [97] TH.data_1.1-5               ROCR_1.0-11                
##  [99] nlme_3.1-168                bit64_4.6.0-1              
## [101] progress_1.2.3              filelock_1.0.3             
## [103] RcppAnnoy_0.0.23            bslib_0.9.0                
## [105] irlba_2.3.5.1               rpart_4.1.24               
## [107] KernSmooth_2.23-26          otel_0.2.0                 
## [109] DBI_1.2.3                   nnet_7.3-20                
## [111] tidyselect_1.2.1            emmeans_2.0.1              
## [113] bit_4.6.0                   compiler_4.6.0             
## [115] curl_7.0.0                  glmnet_4.1-10              
## [117] httr2_1.2.2                 missMDA_1.20               
## [119] BiocNeighbors_2.5.0         mice_3.19.0                
## [121] xml2_1.5.1                  DelayedArray_0.37.0        
## [123] plotly_4.11.0               bookdown_0.46              
## [125] checkmate_2.3.3             scales_1.4.0               
## [127] lmtest_0.9-40               multcompView_0.1-10        
## [129] rappdirs_0.3.3              stringr_1.6.0              
## [131] digest_0.6.39               goftest_1.2-3              
## [133] minqa_1.2.8                 spatstat.utils_3.2-1       
## [135] rmarkdown_2.30              XVector_0.51.0             
## [137] htmltools_0.5.9             pkgconfig_2.0.3            
## [139] lme4_1.1-38                 MatrixGenerics_1.23.0      
## [141] FactoMineR_2.13             dbplyr_2.5.1               
## [143] fastmap_1.2.0               rlang_1.1.7                
## [145] htmlwidgets_1.6.4           asmbPLS_1.0.0              
## [147] shiny_1.12.1                farver_2.1.2               
## [149] jquerylib_0.1.4             zoo_1.8-15                 
## [151] jsonlite_2.0.0              BiocParallel_1.45.0        
## [153] BiocSingular_1.27.1         magrittr_2.0.4             
## [155] Formula_1.2-5               scuttle_1.21.0             
## [157] dotCall64_1.2               patchwork_1.3.2            
## [159] Rcpp_1.1.1                  reticulate_1.44.1          
## [161] stringi_1.8.7               MASS_7.3-65                
## [163] org.Hs.eg.db_3.22.0         AnnotationHub_4.1.0        
## [165] plyr_1.8.9                  parallel_4.6.0             
## [167] listenv_0.10.0              ggrepel_0.9.6              
## [169] deldir_2.0-4                Biostrings_2.79.4          
## [171] splines_4.6.0               tensor_1.5.1               
## [173] RcppAlgos_2.9.3             hms_1.1.4                  
## [175] locfit_1.5-9.12             igraph_2.2.1               
## [177] ggpubr_0.6.2                spatstat.geom_3.6-1        
## [179] ggsignif_0.6.4              RcppHNSW_0.6.0             
## [181] reshape2_1.4.5              biomaRt_2.67.1             
## [183] ScaledMatrix_1.19.0         BiocVersion_3.23.1         
## [185] evaluate_1.0.5              SeuratObject_5.3.0         
## [187] scran_1.39.0                BiocManager_1.30.27        
## [189] nloptr_2.2.1                foreach_1.5.2              
## [191] httpuv_1.6.16               RANN_2.6.2                 
## [193] tidyr_1.3.2                 purrr_1.2.1                
## [195] polyclip_1.10-7             future_1.68.0              
## [197] scattermore_1.2             ggplot2_4.0.1              
## [199] rsvd_1.0.5                  broom_1.0.11               
## [201] xtable_1.8-4                RSpectra_0.16-2            
## [203] rstatix_0.7.3               later_1.4.5                
## [205] viridisLite_0.4.2           snow_0.4-4                 
## [207] tibble_3.3.1                memoise_2.0.1              
## [209] cluster_2.1.8.1             globals_0.18.0