1 Installation

From Bioconductor:

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

BiocManager::install("SingleCellAlleleExperiment")

2 Introduction to the workflow

2.1 Biological background and motivation

Immune molecules such as B and T cell receptors, human leukocyte antigens (HLAs) or killer Ig-like receptors (KIRs) are encoded in the genetically most diverse loci of the human genome. Many of these immune genes are hyperpolymorphic, showing high allelic diversity across human populations. In addition, typical immune molecules are polygenic, which means that multiple functionally similar genes encode the same protein subunit.

However, interactive single-cell methods commonly used to analyze immune cells in large patient cohorts do not consider this. This leads to erroneous quantification of important immune mediators and impaired inter-donor comparability.

2.2 Workflow for unravelling the immunogenetic diversity in scData

We have developed a workflow, that allows quantification of expression and interactive exploration of donor-specific alleles of different immune genes. The workflow is divided into two software packages and one additional data package:

  1. The scIGD software package consist of a Snakemake workflow designed to automate and streamline the genotyping process for immune genes, focusing on key targets such as HLAs and KIRs, and enabling allele-specific quantification from single-cell RNA-sequencing (scRNA-seq) data using donor-specific references. For detailed information of the performed steps and how to utilize this workflow, please refer to its documentation.

  2. To harness the full analytical potential of the results, we’ve developed a dedicated R package, SingleCellAlleleExperiment presented in this repository. This package provides a comprehensive multi-layer data structure, enabling the representation of immune genes at specific levels, including alleles, genes, and groups of functionally similar genes and thus, allows data analysis across these immunologically relevant, different layers of annotation.

  3. The scaeData is an R/ExperimentHub data package providing datasets generated and processed by the scIGD software package which can be used to explore the data and potential downstream analysis workflows using the here presented novel SingleCellAlleleExperiment data structure. Refer to scaeData for more information regarding the available datasets and source of raw data.

This workflow is designed to support both 10x and BD Rhapsody data, encompassing amplicon/targeted sequencing as well as whole-transcriptome-based data, providing flexibility to users working with different experimental setups.

Workflow for scIGD Figure 1: Overview of the scIGD workflow for unraveling immunogenomic diversity in single-cell data, highlighting the integration of the SingleCellAlleleExperiment package for comprehensive data analysis.

3 Introduction to the SingleCellAlleleExperiment (SCAE) class

The SingleCellAlleleExperiment (SCAE) class serves as a comprehensive multi-layer data structure, enabling the representation of immune genes at specific levels, including alleles, genes, and groups of functionally similar genes and thus, allows data analysis across these immunologically relevant, different layers of annotation. The implemented data object is derived from the SingleCellExperiment (SCE) class and follows similar conventions, where rows should represent features (genes, transcripts) and columns should represent cells.


A schematics of the SingleCellAlleleExperiment class Figure 2: Scheme of SingleCellAlleleExperiment object structure with lookup table.


For the integration of the relevant additional data layers (see Figure 2), the quantification data for alleles, generated by the novel scIGD software package, is aggregated into two additional data layers via an ontology-based design principle using a lookup table during object generation.

For example, the counts of the alleles A*01:01:01:01 and A*02:01:01:01 that are present in the raw input data will be combined into the HLA-A immune gene layer (see Table 1 below). Next, all counts of immune genes corresponding to HLA-class I are combined into the HLA-class I functional class layer. See the structure of the used lookup table below.


Table 1: Scheme of the lookup table used to aggregate allele information into multiple data layers.

Allele Gene Function
A*01:01:01 HLA-A HLA class I
A*02:01:01 HLA-A HLA class I
DRB1*01:01:01 HLA-DRB1 HLA class II


The resulting SCAE data object can be used in combination with established single cell analysis packages like scater and scran to perform downstream analysis on immune gene expression, allowing data exploration on functional and allele level. See the vignette for further information and insights on how to perform downstream analysis using exemplary data from the accompanying R/Experimenthub package scaeData.

3.1 Expected input and dataset description

The read in function of the SCAE package read_allele_counts() expects specific files that are generated by the previous steps of the workflow, performed in the scIGD package. One input parameter needs to state the path to a directory containing all the input files. The file identifiers can be specifically stated as parameters. The default file identifiers, as they are outputted from scIGD are listed below: The stated input directory should contain the following files:

  • cells_x_genes.barcodes.txt (list of barcodes/cell identifiers)
  • cells_x_genes.features.txt (list of feature identifiers)
  • cells_x_genes_mtx.mtx (Contains the quantification matrix)
  • lookup_table.csv (Info for generating multiple data layers)

The dataset used for the here shown downstream analysis is taken from the accompanying data package scaeData. Specifically we are using the pbmc_5k dataset. Find more information about the data, including the source of the raw data here: scaeData.


4 Exemplary downstream analysis

4.1 Loading packages

The following packages are abundant for performing the here stated downstream analysis and visualization. Make sure they are installed to use this vignette.

library(SingleCellAlleleExperiment)
library(scaeData)
library(scater)
library(scran)
library(patchwork)

4.2 Reading in data and performing quality check

4.2.1 Stating input directory containing the expected files

Download and save the “pbmc_10k” dataset from the accompanying ExperimentHub/scaeData package.

example_data_10k <- scaeData::scaeDataGet(dataset="pbmc_10k")
## Retrieving barcode identifiers for **pbmc 10k** dataset...DONE
## Retrieving feature identifiers for **pbmc 10k** dataset...DONE
## Retrieving quantification matrix for **pbmc 10k** dataset...DONE
example_data_10k
## $dir
## [1] "/home/pkgbuild/.cache/R/ExperimentHub/"
## 
## $barcodes
## [1] "ee29d178c01e2_9522"
## 
## $features
## [1] "ee29d30b454b8_9523"
## 
## $matrix
## [1] "ee29d51dd0305_9524"

The return value of the scaeDataGet() function is a list containing four elements. The $dir slot contains the directory where the files downloaded from ExperimentHub are saved on your device. The remaining three slots $barcodes, $features, $matrix contain the corresponding file names (named by ExperimentHub). The list is then used to pass the saved information to the related parameters of the read_allele_counts() function of the here presented package. See next section.

For the usage of the corresponding lookup table, specify a directory containing the lookup table as seen in the next chunk. For the available example datasets in scaeData, you can choose one of the following names. This provides the lookup table to the corresponding dataset:

  • “pbmc_5k_lookup_table.csv” is used for the `pbmc_5k" dataset.
  • “pbmc_10k_lookup_table.csv” is used for the `pbmc_10k" dataset.
  • “pbmc_20k_lookup_table.csv” is used for the `pbmc_20k" dataset.

These are part of the scaeData package, but are not fetched from ExperimentHub but rather read in as internal files.

lookup <- utils::read.csv(system.file("extdata", "pbmc_10k_lookup_table.csv", package="scaeData"))
lookup
##              Allele     Gene     Function
## 1     A*02:01:01:01    HLA-A  HLA_class_I
## 2     A*24:02:01:01    HLA-A  HLA_class_I
## 3     B*15:01:01:01    HLA-B  HLA_class_I
## 4        B*44:03:01    HLA-B  HLA_class_I
## 5        C*03:03:01    HLA-C  HLA_class_I
## 6        C*16:01:01    HLA-C  HLA_class_I
## 7  DRB1*07:01:01:01 HLA-DRB1 HLA_class_II
## 8     DRB1*13:01:01 HLA-DRB1 HLA_class_II
## 9     DPB1*03:01:01 HLA-DPB1 HLA_class_II
## 10    DPB1*11:01:01 HLA-DPB1 HLA_class_II
## 11       DQA1*02:01 HLA-DQA1 HLA_class_II

4.2.2 Generate SingleCellAlleleExperiment object

This is an essential step. The read in function read_allele_counts() is the function you use to read in your data and to generate a SCAE object.

4.2.2.1 Read in parameters

If you used the scIGD package to generate your input files (RECOMMENDED AND EXPECTED), then state the path containing all expected files to the sample_dir parameter in the read_allele_counts() function. In case you renamed the files, specify the new file identifiers in the corresponding parameters lookup_file, barcode_file, gene_file, matrix_file, otherwise leave it to the stated default values.

The read_allele_counts() function also provides multiple parameters to perform filtering steps on the cells contained in your data. For this, multiple parameter combinations are possible and showcased below. Filtering is performed on a knee plot.

The first parameter, where you state which filter mode you want to use, gives the following valid options: filter = c("yes", "no", "custom").

  • filter = "no": Shows a knee plot for determining a potential threshold for filtering by yourself. No SCAE object will be generated.
  • filter = "yes" : Automatically filters the cells on the inflection point of the computed knee plot and generates a SCAE object.
  • filter = "custom" : The user can give a custom threshold for manually filtering cells, which is different than the automatically computed one in filter = "yes". State your custom threshold as an integer in the filter_threshold parameter.

Additionally, the verbose parameter gives you an option to toggle runtime-information for the different steps during object generation. Messages regarding the filtering process (suggested inflection point threshold) will not be toggled off if verbose = FALSE.

Exemplary read in using all described filter-modes generating a SCAE object are shown in the following code-chunks:


4.2.2.2 filter = "no"

Only show the knee plot without generating a SCAE object. All function parameters are shown. Information messages (verbose = FALSE) are turned off.

scae <- read_allele_counts(example_data_10k$dir,
                           sample_names="example_data",
                           filter_mode="no",
                           lookup_file=lookup,
                           barcode_file=example_data_10k$barcodes,
                           gene_file=example_data_10k$features,
                           matrix_file=example_data_10k$matrix,
                           filter_threshold=NULL,
                           verbose=FALSE)
## Suggested threshold based on inflection point is at: 979 UMI counts.

4.2.2.3 filter = "yes"

Automatic filtering mode and generation of a SCAE object. Filtering is performed on the computed inflection point of the knee plot.

scae <- read_allele_counts(example_data_10k$dir,
                           sample_names="example_data",
                           filter_mode="yes",
                           lookup_file=lookup,
                           barcode_file=example_data_10k$barcodes,
                           gene_file=example_data_10k$features,
                           matrix_file=example_data_10k$matrix,
                           filter_threshold=NULL,
                           verbose=TRUE)
## Filtering performed based on the inflection point at: 979 UMI counts.
## Runtime check (1/2) Read_in: 1.3 seconds
## Using org.Hs to retrieve NCBI gene identifiers.
##      Generating SCAE (1/5) extending rowData: 10.31 seconds
##      Generating SCAE (2/5) filtering and normalization: 1.42 seconds
##      Generating SCAE (3/5) alleles2genes: 7.06 seconds
##      Generating SCAE (4/5) genes2functional: 4.79 seconds
##      Generating SCAE (5/5) log_transform: 3.87 seconds
## Runtime check (2/2) Generating SCAE completed: 28.56 seconds
## Total runtime, completed read_in, filtering and normalization and generating scae object 2 seconds

scae
## class: SingleCellAlleleExperiment 
## dim: 62765 11465 
## metadata(0):
## assays(2): counts logcounts
## rownames(62765): ENSG00000279928.2 ENSG00000228037.1 ... HLA_class_I
##   HLA_class_II
## rowData names(4): Ensembl_ID Symbol NI_I Quant_type
## colnames(11465): AAACCCAAGGTAGTCG AAACCCACAATCCAGT ... TTTGTTGTCTGTACAG
##   TTTGTTGTCTTCTAAC
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

4.2.2.4 filter = "custom"

Custom filtering mode and generation of a SCAE object. Filtering performed on the threshold stated in the filter_threshold parameter.

#this is the object used in the further workflow
scae <- read_allele_counts(example_data_10k$dir,
                           sample_names="example_data",
                           filter_mode="custom",
                           lookup_file=lookup,
                           barcode_file=example_data_10k$barcodes,
                           gene_file=example_data_10k$features,
                           matrix_file=example_data_10k$matrix,
                           filter_threshold=282)
scae
## class: SingleCellAlleleExperiment 
## dim: 62765 11791 
## metadata(0):
## assays(2): counts logcounts
## rownames(62765): ENSG00000279928.2 ENSG00000228037.1 ... HLA_class_I
##   HLA_class_II
## rowData names(4): Ensembl_ID Symbol NI_I Quant_type
## colnames(11791): AAACCCAAGGTAGTCG AAACCCACAATCCAGT ... TTTGTTGTCTGTACAG
##   TTTGTTGTCTTCTAAC
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

4.2.3 Showcasing different object slots

4.2.3.1 RowData slot

Two new classification columns are introduced in the rowData slot. Namely the NI_I column (classification of each row as NI = non_immune or I = immune) and Quant_type column (classification of each row to which data layer it is corresponding to). Both columns are used jointly to identify each row of the object to its corresponding data layer (see figure 1).

rowData(scae)
## DataFrame with 62765 rows and 4 columns
##                            Ensembl_ID       Symbol        NI_I  Quant_type
##                           <character>  <character> <character> <character>
## ENSG00000279928.2   ENSG00000279928.2           NA          NI           G
## ENSG00000228037.1   ENSG00000228037.1 LOC100996583          NI           G
## ENSG00000142611.17 ENSG00000142611.17       PRDM16          NI           G
## ENSG00000284616.1   ENSG00000284616.1           NA          NI           G
## ENSG00000157911.11 ENSG00000157911.11        PEX10          NI           G
## ...                               ...          ...         ...         ...
## HLA-DRB1                     HLA-DRB1     HLA-DRB1           I           G
## HLA-DPB1                     HLA-DPB1     HLA-DPB1           I           G
## HLA-DQA1                     HLA-DQA1     HLA-DQA1           I           G
## HLA_class_I               HLA_class_I  HLA_class_I           I           F
## HLA_class_II             HLA_class_II HLA_class_II           I           F

4.2.3.2 ColData slot

As the object extends the count matrix during the object generation, its abundant to compute scaling factors on the raw data prior to extending and integrating the data layers. The scaling factors are used for scaling normalization in a later step of the SCAE constructor.

colData(scae)
## DataFrame with 11791 rows and 3 columns
##                        Sample          Barcode sizeFactor
##                   <character>      <character>  <numeric>
## AAACCCAAGGTAGTCG example_data AAACCCAAGGTAGTCG  0.8465753
## AAACCCACAATCCAGT example_data AAACCCACAATCCAGT  2.1356903
## AAACCCACACCGTCTT example_data AAACCCACACCGTCTT  1.1468791
## AAACCCACATAGATCC example_data AAACCCACATAGATCC  0.6096001
## AAACCCACATCGTCCT example_data AAACCCACATCGTCCT  0.0735877
## ...                       ...              ...        ...
## TTTGTTGGTTTACGTG example_data TTTGTTGGTTTACGTG   0.565270
## TTTGTTGTCCACAGGC example_data TTTGTTGTCCACAGGC   0.900278
## TTTGTTGTCCCGAGGT example_data TTTGTTGTCCCGAGGT   1.578779
## TTTGTTGTCTGTACAG example_data TTTGTTGTCTGTACAG   0.774001
## TTTGTTGTCTTCTAAC example_data TTTGTTGTCTTCTAAC   1.256691

4.2.4 Utilize layer specific getter-functions()

Additionally to the established getters from the SCE package, new getters are implemented to retrieve the different data layers integrated in the SCAE object.


4.2.4.1 Non-immune genes

scae_nonimmune_subset <- scae_subset(scae, "nonimmune")

head(rownames(scae_nonimmune_subset))
## [1] "ENSG00000279928.2"  "ENSG00000228037.1"  "ENSG00000142611.17"
## [4] "ENSG00000284616.1"  "ENSG00000157911.11" "ENSG00000269896.2"


4.2.4.2 Alleles

scae_alleles_subset <- scae_subset(scae, "alleles")

head(rownames(scae_alleles_subset))
## [1] "A*02:01:01:01" "A*24:02:01:01" "B*15:01:01:01" "B*44:03:01"   
## [5] "C*03:03:01"    "C*16:01:01"


4.2.4.3 Immune genes

scae_immune_genes_subset <- scae_subset(scae, "immune_genes")

head(rownames(scae_immune_genes_subset))
## [1] "HLA-A"    "HLA-B"    "HLA-C"    "HLA-DRB1" "HLA-DPB1" "HLA-DQA1"


4.2.4.4 Functional gene group

scae_functional_groups_subset <- scae_subset(scae, "functional_groups")

head(rownames(scae_functional_groups_subset))
## [1] "HLA_class_I"  "HLA_class_II"



4.3 Expression evaluation

Checking the expression for the allele-layer, immune gene layer and functional class layer. Allele identifiers are in the form of A*02:01:01:01. The immune genes are in the form of HLA-A and the functional classes HLA_class_I. Here we see that HLA_class_I and HLA-C are the most abundant functional class and immune gene respectively, given the underlying dataset.

scae_immune_layers_subset <- c(rownames(scae_subset(scae, "alleles")),
                               rownames(scae_subset(scae, "immune_genes")),
                               rownames(scae_subset(scae, "functional_groups")))

scater::plotExpression(scae, scae_immune_layers_subset)


5 Downstream analysis

In the following sections, main steps for dimensional reduction are performed, offering insights into the different data layers of the SCAE object as well giving an idea on how to perform immune gene expression analysis.

5.1 Subsetting the different layers

The non-imune genes are combined with each of the integrated immune gene allele-aware layers to determine three different subsets.

5.1.1 Non-immune genes + alleles

scae_nonimmune__allels_subset <- scae[c(rownames(scae_subset(scae, "nonimmune")), rownames(scae_subset(scae, "alleles"))), ]

scae_nonimmune__allels_subset
## class: SingleCellAlleleExperiment 
## dim: 62757 11791 
## metadata(0):
## assays(2): counts logcounts
## rownames(62757): ENSG00000279928.2 ENSG00000228037.1 ... DPB1*11:01:01
##   DQA1*02:01
## rowData names(4): Ensembl_ID Symbol NI_I Quant_type
## colnames(11791): AAACCCAAGGTAGTCG AAACCCACAATCCAGT ... TTTGTTGTCTGTACAG
##   TTTGTTGTCTTCTAAC
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):


5.1.2 Non-immune genes + immune genes

scae_nonimmune__immune <- scae[c(rownames(scae_subset(scae, "nonimmune")), rownames(scae_subset(scae, "immune_genes"))), ]

scae_nonimmune__immune
## class: SingleCellAlleleExperiment 
## dim: 62752 11791 
## metadata(0):
## assays(2): counts logcounts
## rownames(62752): ENSG00000279928.2 ENSG00000228037.1 ... HLA-DPB1
##   HLA-DQA1
## rowData names(4): Ensembl_ID Symbol NI_I Quant_type
## colnames(11791): AAACCCAAGGTAGTCG AAACCCACAATCCAGT ... TTTGTTGTCTGTACAG
##   TTTGTTGTCTTCTAAC
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):


5.1.3 Non-immune genes + functional class

scae_nonimmune__functional <- scae[c(rownames(scae_subset(scae, "nonimmune")), rownames(scae_subset(scae, "functional_groups"))), ]

scae_nonimmune__functional
## class: SingleCellAlleleExperiment 
## dim: 62748 11791 
## metadata(0):
## assays(2): counts logcounts
## rownames(62748): ENSG00000279928.2 ENSG00000228037.1 ... HLA_class_I
##   HLA_class_II
## rowData names(4): Ensembl_ID Symbol NI_I Quant_type
## colnames(11791): AAACCCAAGGTAGTCG AAACCCACAATCCAGT ... TTTGTTGTCTGTACAG
##   TTTGTTGTCTTCTAAC
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):

5.2 Dimensional Reduction

5.2.1 Model variance and HVGs for all data layers

Using the modelGeneVar() function prior to getTopHVGs. Both functions are part from the scran package. Compute a list of HVGs for each data layer. Return the top 0.1 % HVGs per layer using getTopHVGs.

df_ni_a <- modelGeneVar(scae_nonimmune__allels_subset)
## Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
## collapsing to unique 'x' values
top_ni_a <- getTopHVGs(df_ni_a, prop=0.1)
df_ni_g <- modelGeneVar(scae_nonimmune__immune)
## Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
## collapsing to unique 'x' values
top_ni_g <- getTopHVGs(df_ni_g, prop=0.1)
df_ni_f <- modelGeneVar(scae_nonimmune__functional)
## Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
## collapsing to unique 'x' values
top_ni_f <- getTopHVGs(df_ni_f, prop=0.1)

5.2.2 PCA

Compute PCA for each layer and store the results in the object. Its Important to make unique identifiers for each layer/run or the results will be overwritten and just saved as PCA. Here, the runPCA functions from the scater package is used.

scae <- runPCA(scae, ncomponents=10, subset_row=top_ni_a, exprs_values="logcounts", name="PCA_a")
scae <- runPCA(scae, ncomponents=10, subset_row=top_ni_g, exprs_values="logcounts", name="PCA_g")
scae <- runPCA(scae, ncomponents=10, subset_row=top_ni_f, exprs_values="logcounts", name="PCA_f")
reducedDimNames(scae)
## [1] "PCA_a" "PCA_g" "PCA_f"

5.2.3 t-SNE

The same goes for running t-SNE with the runTSNE function from the scater package. Unique identifiers are stated here for each layer as well. For simplicity, we only compute the t-SNE on the gene layer. Information regarding how this process is conducted on the other additional layers, can be seen in the non evaluated code chunks below:

set.seed(18)
scae <- runTSNE(scae, dimred="PCA_g",  name="TSNE_g")

The following two chunks show how the t-SNE could be computed on the allele and functional_group layer. These chunks are not run by default:

set.seed(18)
scae <- runTSNE(scae, dimred="PCA_a",  name="TSNE_a")
set.seed(18)
scae <- runTSNE(scae, dimred="PCA_f",  name="TSNE_f")

List of results from the performed reduced dimension analysis.

reducedDimNames(scae)
## [1] "PCA_a"  "PCA_g"  "PCA_f"  "TSNE_g"

5.2.4 HLA-A immune gene and alleles

which_tsne <- "TSNE_g"

tsne_g_a  <- plotReducedDim(scae, dimred=which_tsne, colour_by="HLA-DRB1") + ggtitle("HLA-DRB1 gene")
tsne_g_a1 <- plotReducedDim(scae, dimred=which_tsne, colour_by="DRB1*07:01:01:01") + ggtitle("Allele DRB1*07:01:01:01")
tsne_g_a2 <- plotReducedDim(scae, dimred=which_tsne, colour_by="DRB1*13:01:01") + ggtitle("Allele DRB1*13:01:01")

p2 <- tsne_g_a + tsne_g_a1 + tsne_g_a2
p2


5.3 Visualization

Exemplary visualization for the t-SNE results on gene level for immune genes that relate to HLA-class I. In the given dataset, these are the immune genes HLA-A, HLA-B and HLA-C plotted alongside their alleles. This allows for insights into potential genetic differences shown on allele-level.


6 Additional

As the SCAE object is extending the SCE object, it is also compatible with the iSEE package for interactive data exploration.

7 Session Information

sessionInfo()
## R Under development (unstable) (2024-03-18 r86148)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 22.04.4 LTS
## 
## Matrix products: default
## BLAS:   /home/biocbuild/bbs-3.19-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] patchwork_1.2.0                   scran_1.31.3                     
##  [3] scater_1.31.2                     ggplot2_3.5.0                    
##  [5] scuttle_1.13.1                    scaeData_0.99.4                  
##  [7] SingleCellAlleleExperiment_0.99.3 SingleCellExperiment_1.25.1      
##  [9] SummarizedExperiment_1.33.3       Biobase_2.63.0                   
## [11] GenomicRanges_1.55.4              GenomeInfoDb_1.39.9              
## [13] IRanges_2.37.1                    S4Vectors_0.41.5                 
## [15] BiocGenerics_0.49.1               MatrixGenerics_1.15.0            
## [17] matrixStats_1.2.0                 BiocStyle_2.31.0                 
## 
## loaded via a namespace (and not attached):
##   [1] jsonlite_1.8.8            magrittr_2.0.3           
##   [3] ggbeeswarm_0.7.2          farver_2.1.1             
##   [5] rmarkdown_2.26            zlibbioc_1.49.3          
##   [7] vctrs_0.6.5               memoise_2.0.1            
##   [9] DelayedMatrixStats_1.25.1 htmltools_0.5.8          
##  [11] S4Arrays_1.3.6            AnnotationHub_3.11.3     
##  [13] curl_5.2.1                BiocNeighbors_1.21.2     
##  [15] Rhdf5lib_1.25.1           SparseArray_1.3.4        
##  [17] rhdf5_2.47.6              sass_0.4.9               
##  [19] bslib_0.6.2               cachem_1.0.8             
##  [21] igraph_2.0.3              mime_0.12                
##  [23] lifecycle_1.0.4           pkgconfig_2.0.3          
##  [25] rsvd_1.0.5                Matrix_1.7-0             
##  [27] R6_2.5.1                  fastmap_1.1.1            
##  [29] GenomeInfoDbData_1.2.12   digest_0.6.35            
##  [31] colorspace_2.1-0          AnnotationDbi_1.65.2     
##  [33] dqrng_0.3.2               irlba_2.3.5.1            
##  [35] ExperimentHub_2.11.1      RSQLite_2.3.5            
##  [37] org.Hs.eg.db_3.19.0       beachmat_2.19.2          
##  [39] labeling_0.4.3            filelock_1.0.3           
##  [41] fansi_1.0.6               httr_1.4.7               
##  [43] abind_1.4-5               compiler_4.4.0           
##  [45] bit64_4.0.5               withr_3.0.0              
##  [47] BiocParallel_1.37.1       viridis_0.6.5            
##  [49] DBI_1.2.2                 highr_0.10               
##  [51] HDF5Array_1.31.6          R.utils_2.12.3           
##  [53] rappdirs_0.3.3            DelayedArray_0.29.9      
##  [55] bluster_1.13.0            tools_4.4.0              
##  [57] vipor_0.4.7               beeswarm_0.4.0           
##  [59] R.oo_1.26.0               glue_1.7.0               
##  [61] rhdf5filters_1.15.4       grid_4.4.0               
##  [63] Rtsne_0.17                cluster_2.1.6            
##  [65] generics_0.1.3            gtable_0.3.4             
##  [67] R.methodsS3_1.8.2         BiocSingular_1.19.0      
##  [69] ScaledMatrix_1.11.1       metapod_1.11.1           
##  [71] utf8_1.2.4                XVector_0.43.1           
##  [73] ggrepel_0.9.5             BiocVersion_3.19.1       
##  [75] pillar_1.9.0              limma_3.59.6             
##  [77] dplyr_1.1.4               BiocFileCache_2.11.2     
##  [79] lattice_0.22-6            bit_4.0.5                
##  [81] tidyselect_1.2.1          locfit_1.5-9.9           
##  [83] Biostrings_2.71.5         knitr_1.45               
##  [85] gridExtra_2.3             bookdown_0.38            
##  [87] edgeR_4.1.19              xfun_0.43                
##  [89] statmod_1.5.0             DropletUtils_1.23.1      
##  [91] yaml_2.3.8                evaluate_0.23            
##  [93] codetools_0.2-19          tibble_3.2.1             
##  [95] BiocManager_1.30.22       cli_3.6.2                
##  [97] munsell_0.5.0             jquerylib_0.1.4          
##  [99] Rcpp_1.0.12               dbplyr_2.5.0             
## [101] png_0.1-8                 parallel_4.4.0           
## [103] blob_1.2.4                sparseMatrixStats_1.15.0 
## [105] viridisLite_0.4.2         scales_1.3.0             
## [107] purrr_1.0.2               crayon_1.5.2             
## [109] rlang_1.1.3               cowplot_1.1.3            
## [111] KEGGREST_1.43.0