# Introduction to SETA ecological transforms and sample-level latent spaces

## Introduction

### Why use SETA?

`SETA` (Single Cell Ecological Taxonomic Analysis) performs compositional analysis of single-cell data by combining established compositional data analysis (CoDA) methods with ecological principles. While other excellent Bioconductor packages address cell-type compositions, `SETA` is uniquely designed to teach and apply a variety of CoDA methods.

### Compositional Analysis of Single-Cell RNA-seq Data

Single-cell RNA-seq experiments produce data where cell-type abundances are inherently compositional - the proportions of different cell types sum to 1 (or 100%). This compositional constraint means that changes in one cell type affect all others, and standard statistical methods can produce misleading results. `SETA` applies well-established compositional data analysis (CoDA) methods from ecology to properly handle these constraints.

### What SETA Does Well

Several Bioconductor packages already address single-cell compositional analysis, each with strong statistical background, including `DCATS`, `speckle`, `muscat` and `sccomp`. While these packages, especially sccomp, provides the statistical rigor for complex analyses, `SETA` aims to open up the entire compositional analysis workflow to users who want to understand and apply these methods step-by-step. `SETA` focuses on simpler models that respect the compositional constraint.

SETA’s unique strengths: - Explicit distance calculations: Aitchison distances and compositional geometry - Ecological metrics: Balance transforms, diversity indices, and ecological framing - Comprehensive transforms: CLR, ALR, ILR, and user-defined balance transforms - Educational focus: separating each step of the workflow into separate functions, `SETA` aims to make the workflow transparent and easier to explain.

### Why These Steps Should Be Executed

The workflow presented in this vignette is essential because:

1. Proper Data Handling: Cell-type counts must be treated as compositional data to avoid spurious correlations and misleading statistical results
2. Appropriate Transforms: Log-ratio transforms (CLR, ALR, ILR) maintain compositional properties while enabling standard statistical analysis
3. Ecological Insights: Applying ecological methods provides novel perspectives on cell-type community structure and diversity
4. Educational Value: `SETA` opens up the entire compositional analysis workflow to users, teaching the mathematical foundations rather than hiding them in black-box functions
5. Unique Capabilities: `SETA` provides explicit distance calculations, ecological metrics, and comprehensive compositional transforms in a single, educational framework

### Package Overview

`SETA` provides a set of functions for compositional analysis of single-cell RNA-seq data.

In this vignette we show how to:

- Extract a taxonomic counts matrix from various objects (e.g. a `Seurat` object)
- Apply compositional transforms such as `CLR` and `ALR`
- Compute a latent space using `PCA` (with options for `PCoA` or `NMDS`)

This example works with the Tabula Muris Senis lung dataset which can be downloaded directly with bioconductor It contains lung single-cell profiles from 14 donor mice (mouse_id). Each mouse is assigned to one of five age groups, from young adult to old, recorded in the age column. Standard cell-type labels (free_annotation) and additional metadata (e.g. sex) are also included.

This vignette is modular; users can update or replace the dataset-specific settings (e.g., metadata column names, reference cell type) as appropriate for their own data.

## Installation

    if (!require("BiocManager", quietly = TRUE))
        install.packages("BiocManager")
    BiocManager::install("SETA")

## Loading the Data

    library(SingleCellExperiment)
    library(SETA)
    library(ggplot2)
    library(dplyr)
    library(TabulaMurisSenisData)
    library(reshape2)
    # library(tidytext)

## Load and prepare data

    sce <- TabulaMurisSenisDroplet(tissues = "Lung")$Lung

    # Two samples in this dataset have had certain celltype lineages depleted.
    # For compositional comparisons, we remove these for simplicity.
    sce <- sce[, colData(sce)$subtissue != "immune-endo-depleted"]

Quick data exploration:

    table(sce$free_annotation, sce$age)

    ##
    ##                                       1m   3m  18m  21m  30m
    ##   Adventitial Fibroblast              29   17   56   43   23
    ##   Airway Smooth Muscle                 2    0   10    0    0
    ##   Alveolar Epithelial Type 2           7    2   35   18   11
    ##   Alveolar Fibroblast                 87   37  166  101   26
    ##   Alveolar Macrophage                517  116  193   91  276
    ##   Artery                              13   16   32   26   11
    ##   B                                   98  135  459  198  180
    ##   Basophil                             9    8   19   46   40
    ##   CD4+ T                             143  122   72  107  106
    ##   CD8+ T                              38   70  234  335  193
    ##   Capillary                          194  119  617  522  343
    ##   Capillary Aerocyte                 125   35  129  148   93
    ##   Ccr7+ Dendritic                      2    2    2    5    7
    ##   Ciliated                            10    0    9    4    3
    ##   Classical Monocyte                 485  111  968  242 3615
    ##   Club                                 2    0    4    5    3
    ##   Intermediate Monocyte              111   29   95   19 1495
    ##   Interstitial Macrophage              7   13   16   21 1098
    ##   Ly6g5b+ T                            0    0   53   65   42
    ##   Lympatic                             6    2   11   12   10
    ##   Myeloid Dendritic Type 1            38   20   31   26   44
    ##   Myeloid Dendritic Type 2             8   10   17   16   26
    ##   Myofibroblast                       23   13    9    9    0
    ##   Natural Killer                     140  201  153  224  120
    ##   Natural Killer T                    20    7  152   50  191
    ##   Neuroendocrine                       0    0    0    0    0
    ##   Neutrophil                          64   22  413   29   22
    ##   Nonclassical Monocyte              157  160  187  194  287
    ##   Pericyte                            11    7    8    1    8
    ##   Plasma                               3    1    5   16   23
    ##   Plasmacytoid Dendritic              21    8   15   10   14
    ##   Proliferating Alveolar Macrophage   60    9    7    0   14
    ##   Proliferating Classical Monocyte     1    0   13    0 2473
    ##   Proliferating Dendritic              4    3    8    4    8
    ##   Proliferating NK                     5    2    5    3    9
    ##   Proliferating T                     15    9   11   18   36
    ##   Regulatory T                         8   11   28   20   55
    ##   Vein                                19   28   93  107   73
    ##   Zbtb32+ B                           26   18   41  216  115

    table(sce$free_annotation, sce$sex)

    ##
    ##                                     female male
    ##   Adventitial Fibroblast                85   83
    ##   Airway Smooth Muscle                   9    3
    ##   Alveolar Epithelial Type 2            24   49
    ##   Alveolar Fibroblast                  217  200
    ##   Alveolar Macrophage                  310  883
    ##   Artery                                48   50
    ##   B                                    583  487
    ##   Basophil                              57   65
    ##   CD4+ T                               267  283
    ##   CD8+ T                               474  396
    ##   Capillary                            851  944
    ##   Capillary Aerocyte                   204  326
    ##   Ccr7+ Dendritic                        7   11
    ##   Ciliated                               7   19
    ##   Classical Monocyte                   514 4907
    ##   Club                                   6    8
    ##   Intermediate Monocyte                 90 1659
    ##   Interstitial Macrophage               34 1121
    ##   Ly6g5b+ T                             95   65
    ##   Lympatic                              16   25
    ##   Myeloid Dendritic Type 1              61   98
    ##   Myeloid Dendritic Type 2              33   44
    ##   Myofibroblast                         25   29
    ##   Natural Killer                       506  332
    ##   Natural Killer T                     165  255
    ##   Neuroendocrine                         0    0
    ##   Neutrophil                            61  489
    ##   Nonclassical Monocyte                457  528
    ##   Pericyte                              12   23
    ##   Plasma                                20   28
    ##   Plasmacytoid Dendritic                23   45
    ##   Proliferating Alveolar Macrophage     12   78
    ##   Proliferating Classical Monocyte       1 2486
    ##   Proliferating Dendritic               10   17
    ##   Proliferating NK                       6   18
    ##   Proliferating T                       33   56
    ##   Regulatory T                          57   65
    ##   Vein                                 168  152
    ##   Zbtb32+ B                            256  160

    # Set up a color palette for plots
    # 3-group distinct categoricals
    age_palette <- c(
        "1m" = "#90EE90",
        "3m" = "#4CBB17",
        "18m" = "#228B22",
        "21m" = "#355E3B",
        "30m" = "#023020")

## Extracting the Taxonomic Counts Matrix

The first step in compositional analysis is to extract a cell-type counts matrix from single-cell objects. The `setaCounts()` function converts cell-level metadata into a sample-by-cell-type count matrix, where each row represents a sample and each column represents a cell type. This transformation is crucial because compositional analysis requires sample-level data, not individual cell data.

For this dataset, we want the cell-type annotations to come from the `free_annotation` column and the sample identifiers from `mouse.id`. The function aggregates individual cells by sample and cell type, creating a counts matrix suitable for compositional analysis. If necessary, we reassign these columns before extraction to match the expected column names.

    # Extract the taxonomic counts matrix using custom metadata column names
    # (Users can specify these column names according to their dataset.)
    df <- data.frame(colData(sce))

    # Fix special characters in mouse.id before working with the data
    df$mouse.id <- gsub("/", "", df$mouse.id)

    taxa_counts <- setaCounts(
        df,
        cell_type_col = "free_annotation",
        sample_col = "mouse.id",
        bc_col = "cell")
    head(taxa_counts)

    ##
    ##           Adventitial Fibroblast Airway Smooth Muscle
    ##   1-M-62                      20                    1
    ##   1-M-63                       9                    1
    ##   18-F-50                      2                    1
    ##   18-F-51                     23                    8
    ##   18-M-52                     16                    1
    ##   18-M-53                     15                    0
    ##
    ##           Alveolar Epithelial Type 2 Alveolar Fibroblast Alveolar Macrophage
    ##   1-M-62                           7                  35                 148
    ##   1-M-63                           0                  52                 369
    ##   18-F-50                          0                   4                  15
    ##   18-F-51                          4                  75                  88
    ##   18-M-52                         20                  36                  49
    ##   18-M-53                         11                  51                  41
    ##
    ##           Artery   B Basophil Capillary Capillary Aerocyte Ccr7+ Dendritic
    ##   1-M-62       5  72        6       119                 90               1
    ##   1-M-63       8  26        3        75                 35               1
    ##   18-F-50      1  83        2        38                  7               0
    ##   18-F-51      5 167        1       172                 14               0
    ##   18-M-52     12 100       10       229                 59               1
    ##   18-M-53     14 109        6       178                 49               1
    ##
    ##           CD4+ T CD8+ T Ciliated Classical Monocyte Club Intermediate Monocyte
    ##   1-M-62      48     16        5                201    1                    50
    ##   1-M-63      95     22        5                284    1                    61
    ##   18-F-50      5     12        0                 39    1                     5
    ##   18-F-51     33     57        3                122    0                    37
    ##   18-M-52     11     79        2                462    1                    23
    ##   18-M-53     23     86        4                345    2                    30
    ##
    ##           Interstitial Macrophage Ly6g5b+ T Lympatic Myeloid Dendritic Type 1
    ##   1-M-62                        3         0        2                       17
    ##   1-M-63                        4         0        4                       21
    ##   18-F-50                       0         7        0                        4
    ##   18-F-51                       0        23        2                       11
    ##   18-M-52                       6        15        6                        3
    ##   18-M-53                      10         8        3                       13
    ##
    ##           Myeloid Dendritic Type 2 Myofibroblast Natural Killer
    ##   1-M-62                         4            10             53
    ##   1-M-63                         4            13             87
    ##   18-F-50                        1             1             27
    ##   18-F-51                        6             2             54
    ##   18-M-52                        7             2             34
    ##   18-M-53                        3             4             38
    ##
    ##           Natural Killer T Neutrophil Nonclassical Monocyte Pericyte Plasma
    ##   1-M-62                10         13                    80        8      1
    ##   1-M-63                10         51                    77        3      2
    ##   18-F-50               11          3                    41        2      0
    ##   18-F-51               97          7                    62        2      3
    ##   18-M-52                3        307                    45        3      0
    ##   18-M-53               41         96                    39        1      2
    ##
    ##           Plasmacytoid Dendritic Proliferating Alveolar Macrophage
    ##   1-M-62                       6                                10
    ##   1-M-63                      15                                50
    ##   18-F-50                      0                                 0
    ##   18-F-51                      5                                 3
    ##   18-M-52                      3                                 1
    ##   18-M-53                      7                                 3
    ##
    ##           Proliferating Classical Monocyte Proliferating Dendritic
    ##   1-M-62                                 1                       2
    ##   1-M-63                                 0                       2
    ##   18-F-50                                0                       1
    ##   18-F-51                                1                       2
    ##   18-M-52                               12                       1
    ##   18-M-53                                0                       4
    ##
    ##           Proliferating NK Proliferating T Regulatory T Vein Zbtb32+ B
    ##   1-M-62                 1               4            1   11        16
    ##   1-M-63                 4              11            7    8        10
    ##   18-F-50                0               2            4    3         9
    ##   18-F-51                1               4           22   30        13
    ##   18-M-52                1               1            1   30        11
    ##   18-M-53                3               4            1   30         8

## Applying Compositional Transforms

Once we have our cell-type counts matrix, we must apply appropriate compositional transforms before statistical analysis. Raw count data cannot be used directly because it violates the compositional constraint (proportions must sum to 1). Log-ratio transforms solve this problem by converting the data to a space where standard statistical methods can be applied.

Below we demonstrate four different compositional transforms, each with specific advantages:

- Centered Log-Ratio (CLR): Centers log-transformed data around the geometric mean, useful when no reference cell type is available
- Additive Log-Ratio (ALR): Uses a specific cell type as reference, appropriate when one cell type remains stable across conditions
- Isometric Log-Ratio (ILR): Projects data onto orthonormal basis, reducing dimensionality by one while maintaining all information
- Simple transforms: Percentages and log-CPM for comparison and visualization

CLR Transformation

    # CLR Transformation with a pseudocount of 1 (default)
    clr_out <- setaCLR(taxa_counts, pseudocount = 1)
    print(clr_out$counts[1:5, 1:5])

    ##
    ##           Adventitial Fibroblast Airway Smooth Muscle
    ##   1-M-62               0.6493376           -1.7020377
    ##   1-M-63              -0.2008121           -1.8102500
    ##   18-F-50             -0.2928298           -0.6982949
    ##   18-F-51              0.7495280           -0.2313013
    ##   18-M-52              0.4036570           -1.7364092
    ##
    ##           Alveolar Epithelial Type 2 Alveolar Fibroblast Alveolar Macrophage
    ##   1-M-62                  -0.3157433           1.1883341           2.6087615
    ##   1-M-63                  -2.5033972           1.4668947           3.4101058
    ##   18-F-50                 -1.3914421           0.2179958           1.3811467
    ##   18-F-51                 -0.8190880           1.9022075           2.0601105
    ##   18-M-52                  0.6149661           1.1813615           1.4824666

For the ALR transform we need to choose a reference cell type whose numbers remain stable across conditions pre-sampling. In an ideal scenario this could a spike-in of a known number of cells. In this example, we assume that one of the reference cell types are “NK cells”. If it does not exist in your dataset, change the reference accordingly or use CLR.

    # ALR Transformation: using "NK cell" as the reference cell type
    if ("Natural Killer" %in% colnames(taxa_counts)) {
        alr_out <- setaALR(taxa_counts, ref = "Natural Killer", pseudocount = 1)
        print(alr_out$counts[1:5, 1:5])
    } else {
        message("Reference 'NK cell' not found in taxa_counts.
                Please choose an available cell type.")
    }

    ##
    ##           Adventitial Fibroblast Airway Smooth Muscle
    ##   1-M-62             -0.94446161          -3.29583687
    ##   1-M-63             -2.17475172          -3.78418963
    ##   18-F-50            -2.23359222          -2.63905733
    ##   18-F-51            -0.82927935          -1.81010861
    ##   18-M-52            -0.72213472          -2.86220088
    ##
    ##           Alveolar Epithelial Type 2 Alveolar Fibroblast Alveolar Macrophage
    ##   1-M-62                 -1.90954250         -0.40546511          1.01496226
    ##   1-M-63                 -4.47733681         -0.50704490          1.43616619
    ##   18-F-50                -3.33220451         -1.72276660         -0.55961579
    ##   18-F-51                -2.39789527          0.32340016          0.48130318
    ##   18-M-52                -0.51082562          0.05556985          0.35667494

ILR Transform using Helmert basis (boxcox_p = 0 for standard ILR) This is the method used by Cacoa (Viktor Petukhov & Kharchenko Lab)

    ilr_out <- setaILR(taxa_counts, boxcox_p = 0, pseudocount = 1)
    print(ilr_out$counts[1:5, 1:5])

    ##
    ##                 [,1]       [,2]      [,3]     [,4]       [,5]
    ##   1-M-62  -1.6626734  0.1719597 1.4241631 2.373621 -0.9942587
    ##   1-M-63  -1.1380445 -1.2230026 2.5735802 3.731548 -0.3456867
    ##   18-F-50 -0.2867071 -0.7314827 0.8765777 1.719348 -0.4944201
    ##   18-F-51 -0.6935510 -0.8803477 1.7342112 1.484547 -1.2497730
    ##   18-M-52 -1.5132553  1.0462115 1.2302961 1.222300 -0.2317007

Simple Percentage Transform

    pct_out <- setaPercent(taxa_counts)
    print(pct_out$counts[1:5, 1:5])

    ##
    ##           Adventitial Fibroblast Airway Smooth Muscle
    ##   1-M-62              1.85528757           0.09276438
    ##   1-M-63              0.62937063           0.06993007
    ##   18-F-50             0.60422961           0.30211480
    ##   18-F-51             1.98446937           0.69025022
    ##   18-M-52             0.99812851           0.06238303
    ##
    ##           Alveolar Epithelial Type 2 Alveolar Fibroblast Alveolar Macrophage
    ##   1-M-62                  0.64935065          3.24675325         13.72912801
    ##   1-M-63                  0.00000000          3.63636364         25.80419580
    ##   18-F-50                 0.00000000          1.20845921          4.53172205
    ##   18-F-51                 0.34512511          6.47109577          7.59275237
    ##   18-M-52                 1.24766064          2.24578915          3.05676856

LogCPM (counts per 10k) Transform

    lcpm_out <- setaLogCPM(taxa_counts, pseudocount = 1)
    print(lcpm_out$counts[1:5, 1:5])

    ##
    ##           Adventitial Fibroblast Airway Smooth Muscle
    ##   1-M-62               14.248407            10.856089
    ##   1-M-63               12.770689            10.448761
    ##   18-F-50              13.141492            12.556529
    ##   18-F-51              14.336622            12.921584
    ##   18-M-52              13.371573            10.284110
    ##
    ##           Alveolar Epithelial Type 2 Alveolar Fibroblast Alveolar Macrophage
    ##   1-M-62                   12.856089           15.026014           17.075258
    ##   1-M-63                    9.448761           15.176681           17.980142
    ##   18-F-50                  11.556529           13.878457           15.556529
    ##   18-F-51                  12.073588           15.999587           16.227393
    ##   18-M-52                  13.676428           14.493564           14.927966

## Latent Space Analysis

After applying compositional transforms, we can perform dimensionality reduction to visualize and analyze the relationships between samples. The transformed data can be analyzed using standard multivariate methods, but it’s important to remember that we’re now working in log-ratio space, not the original count space.

We demonstrate three different latent space methods, each with specific advantages:

- Principal Component Analysis (PCA): Linear dimensionality reduction that maximizes variance, provides loadings for interpretation
- Principal Coordinate Analysis (PCoA): Works with distance matrices, useful when relationships between samples are more important than individual features
- Non-metric Multidimensional Scaling (NMDS): Non-linear method that preserves rank-order relationships, robust to outliers

    latent_pca <- setaLatent(clr_out, method = "PCA", dims = 5)
    # PCA Latent Space Coordinates:
    head(latent_pca$latentSpace)

    ##                PC1        PC2        PC3         PC4         PC5
    ## 1-M-62  -0.3350223  3.1938325 -0.4848707  0.04635990 -0.76225078
    ## 1-M-63  -0.5097368  4.8874855  0.9040002  0.04858906  0.36921601
    ## 18-F-50 -1.5751990 -0.8085481  1.1262639 -1.31190158 -0.78190046
    ## 18-F-51 -2.3212913 -0.6116102  1.3477482 -2.63828824 -2.03933177
    ## 18-M-52  0.3601475 -0.5633859 -4.5736259 -1.47565863 -0.01092873
    ## 18-M-53 -1.1101814  0.2754001 -2.4996341 -0.80132618  0.66184605

    # Variance Explained:
    latent_pca$varExplained

    ## [1] 0.49890109 0.15856671 0.10540212 0.06273258 0.05150588

## Visualization

Below we show how to create basic visualizations of SETA latent spaces

### Variance Explained Plot

Plot the variance explained by the first few principal components.

    ve_df <- data.frame(
        PC = factor(seq_along(latent_pca$varExplained),
                    levels = seq_along(latent_pca$varExplained)),
        VarianceExplained = cumsum(latent_pca$varExplained)
    )

    ggplot(ve_df, aes(x = PC, y = VarianceExplained)) +
        geom_line(color = "#2ca1db", size = 1.5, group = 1) +
        geom_point(color = "#f44323", size = 3) +
        labs(title = "Cumulative Variance Explained by Principal Components",
            x = "Principal Component", y = "Cumulative Variance Explained (%)") +
        theme_minimal() +
        ylim(c(0, 1)) +
        theme(text = element_text(size = 12))

    ## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
    ## ℹ Please use `linewidth` instead.
    ## This warning is displayed once per session.
    ## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
    ## generated.

### PCA Scatter plot

We overlay the PCA latent space coordinates (using the `CLR` transform) with metadata from the `Seurat` object. In this example, points are colored by age.

    # Extract latent space coordinates from PCA result.
    pca_coords <- latent_pca$latentSpace

    # Extract metadata
    meta_df <- setaMetadata(
        df,
        sample_col = "mouse.id",
        meta_cols = c("age", "sex"))

    # Merge latent coordinates with metadata
    pca_plot_df <- cbind(pca_coords, meta_df)

    # Create the scatter plot
    ggplot(pca_plot_df, aes(x = PC1, y = PC2, color = age)) +
        geom_text(aes(label = sample_id), size = 5) +
        scale_color_manual(
            values = age_palette
        ) +
        labs(title = "PCA Scatter Plot",
            x = "PC1", y = "PC2", color = "Age") +
        theme_linedraw() +
        xlab(sprintf("PC1 (%s%%)", signif(latent_pca$varExplained[1], 4) * 100)) +
        ylab(sprintf("PC2 (%s%%)", signif(latent_pca$varExplained[2], 4) * 100))

### Loadings Plot

Plot the variance explained by interesting principal components.

    loadings_df <- as.data.frame(latent_pca$loadings)
    loadings_df$Celltype <- rownames(loadings_df)

    # Melt the loadings into long format for `ggplot2`
    loadings_long <- melt(loadings_df, id.vars = "Celltype",
                        variable.name = "PC", value.name = "Loading")

Visualize the PC loadings for each cell

    # Subset rows where PC is "PC1" or "PC2"
    loading_df <- loadings_long[loadings_long$PC %in% c("PC1", "PC2"), ]

    # Append PC to Celltype to mimic tidytext::reorder_within()
    loading_df$Celltype_PC <- paste(loading_df$Celltype, loading_df$PC, sep = "___")

    # Reorder by Loading *within each PC*
    loading_df$Celltype_PC <- with(loading_df, reorder(Celltype_PC, Loading))

    ggplot(loading_df,
            aes(x = Loading, y = Celltype, label = Celltype, fill = Loading)) +
        geom_col() +
        facet_wrap(~PC, scales = "free") +
        # scale_y_reordered() +
        labs(title = "PC Loadings",
            x = "Loading",
            y = "Cell Type") +
        theme_linedraw() +
        scale_fill_viridis_c() +
        expand_limits(x = c(-0.2, 0.4))

## Conclusion

This vignette illustrated a basic workflow using the SETA package:

1. Extracting a taxonomic counts matrix from a `Seurat` Object with `setaCounts()`

2. Applying compositional transforms (`CLR`, `ALR`, `ILR`, and simple transforms)

3. Performing latent space analysis (`PCA`)

4. Visualizing sample-level latent spaces

Each step is modular, so you can easily swap in your own data, update metadata column names, or choose different parameters (e.g., pseudocount, reference cell type) to suit your analysis needs.

For further details, see the package documentation and relevant literature on compositional data analysis (e.g., Aitchison, 1982).

## Session Info

    sessionInfo()

    ## R version 4.6.0 (2026-04-24)
    ## Platform: x86_64-pc-linux-gnu
    ## Running under: Ubuntu 24.04.4 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.26.so;  LAPACK version 3.12.0
    ##
    ## locale:
    ##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
    ##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
    ##  [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: Etc/UTC
    ## tzcode source: system (glibc)
    ##
    ## attached base packages:
    ## [1] stats4    stats     graphics  grDevices utils     datasets  methods
    ## [8] base
    ##
    ## other attached packages:
    ##  [1] reshape2_1.4.5              rhdf5_2.56.0
    ##  [3] TabulaMurisSenisData_1.16.0 caret_7.0-1
    ##  [5] lattice_0.22-9              corrplot_0.95
    ##  [7] tidyr_1.3.2                 dplyr_1.2.1
    ##  [9] ggplot2_4.0.3               SETA_1.2.0
    ## [11] SingleCellExperiment_1.34.0 SummarizedExperiment_1.42.0
    ## [13] Biobase_2.72.0              GenomicRanges_1.64.0
    ## [15] Seqinfo_1.2.0               IRanges_2.46.0
    ## [17] S4Vectors_0.50.0            BiocGenerics_0.58.0
    ## [19] generics_0.1.4              MatrixGenerics_1.24.0
    ## [21] matrixStats_1.5.0
    ##
    ## loaded via a namespace (and not attached):
    ##   [1] RColorBrewer_1.1-3   sys_3.4.3            jsonlite_2.0.0
    ##   [4] shape_1.4.6.1        magrittr_2.0.5       farver_2.1.2
    ##   [7] rmarkdown_2.31       vctrs_0.7.3          memoise_2.0.1
    ##  [10] htmltools_0.5.9      S4Arrays_1.12.0      AnnotationHub_4.2.0
    ##  [13] curl_7.1.0           Rhdf5lib_2.0.0       SparseArray_1.12.0
    ##  [16] pROC_1.19.0.1        sass_0.4.10          parallelly_1.47.0
    ##  [19] bslib_0.10.0         plyr_1.8.9           httr2_1.2.2
    ##  [22] lubridate_1.9.5      cachem_1.1.0         buildtools_1.0.0
    ##  [25] igraph_2.3.0         lifecycle_1.0.5      iterators_1.0.14
    ##  [28] pkgconfig_2.0.3      Matrix_1.7-5         R6_2.6.1
    ##  [31] fastmap_1.2.0        future_1.70.0        digest_0.6.39
    ##  [34] AnnotationDbi_1.74.0 ExperimentHub_3.2.0  RSQLite_2.4.6
    ##  [37] filelock_1.0.3       labeling_0.4.3       timechange_0.4.0
    ##  [40] gdata_3.0.1          httr_1.4.8           abind_1.4-8
    ##  [43] compiler_4.6.0       proxy_0.4-29         bit64_4.8.0
    ##  [46] withr_3.0.2          S7_0.2.2             DBI_1.3.0
    ##  [49] HDF5Array_1.40.0     MASS_7.3-65          lava_1.9.0
    ##  [52] rappdirs_0.3.4       DelayedArray_0.38.1  gtools_3.9.5
    ##  [55] ModelMetrics_1.2.2.2 tools_4.6.0          otel_0.2.0
    ##  [58] future.apply_1.20.2  nnet_7.3-20          glue_1.8.1
    ##  [61] h5mread_1.4.0        nlme_3.1-169         rhdf5filters_1.24.0
    ##  [64] grid_4.6.0           recipes_1.3.2        gtable_0.3.6
    ##  [67] class_7.3-23         data.table_1.18.2.1  tidygraph_1.3.1
    ##  [70] XVector_0.52.0       BiocVersion_3.23.1   foreach_1.5.2
    ##  [73] pillar_1.11.1        stringr_1.6.0        splines_4.6.0
    ##  [76] BiocFileCache_3.2.0  survival_3.8-6       bit_4.6.0
    ##  [79] tidyselect_1.2.1     maketools_1.3.2      Biostrings_2.80.0
    ##  [82] knitr_1.51           xfun_0.57            hardhat_1.4.3
    ##  [85] timeDate_4052.112    stringi_1.8.7        yaml_2.3.12
    ##  [88] evaluate_1.0.5       codetools_0.2-20     tibble_3.3.1
    ##  [91] BiocManager_1.30.27  cli_3.6.6            rpart_4.1.27
    ##  [94] jquerylib_0.1.4      Rcpp_1.1.1-1.1       globals_0.19.1
    ##  [97] dbplyr_2.5.2         png_0.1-9            parallel_4.6.0
    ## [100] gower_1.0.2          blob_1.3.0           listenv_0.10.1
    ## [103] glmnet_4.1-10        viridisLite_0.4.3    ipred_0.9-15
    ## [106] e1071_1.7-17         scales_1.4.0         prodlim_2026.03.11
    ## [109] purrr_1.2.2          crayon_1.5.3         BiocStyle_2.40.0
    ## [112] rlang_1.2.0          KEGGREST_1.52.0