anglemania is a feature selection package that extracts genes from multi-batch scRNA-seq experiments for downstream dataset integration. The goal is to select genes that carry high biological information and low technical noise between the batches. Those genes are extracted from gene pairs that have an invariant and extremely narrow or wide angle between their expression vectors. Conventionally, highly-variable genes (HVGs) or sometimes all genes are used for integration tasks (https://www.nature.com/articles/s41592-021-01336-8). While HVGs are a great and easy way to reduce the noise and dimensionality of the data, they are not optimal for integration tasks. HVGs are sensitive to batch effects because the variance is a function of both the technical and biological variance. anglemania improves conventional usage of HVGs for integration tasks, especially when the transcriptional difference between cell types or cell states is subtle (showcased here with de.facLoc set to 0.1 (= mild differences between “Groups”)). The package can be used on top of SingleCellExperiment or Seurat objects.
Under the hood, anglemania works with file-backed big matrices (FBMs) from the bigstatsr package (https://github.com/privefl/bigstatsr/) for fast and memory efficient computation.
suppressPackageStartupMessages({
library(anglemania)
library(dplyr)
library(Seurat)
library(splatter)
library(SingleCellExperiment)
library(scater)
library(scran)
library(bluster)
library(batchelor)
})
batch.facLoc <- 0.1
de.facLoc <- 0.1
nBatches <- 4
nGroups <- 3
nGenes <- 5000
groupCells <- 300
sce <- splatSimulate(
batchCells = rep(groupCells * nGroups, nBatches),
batch.facLoc = batch.facLoc,
group.prob = rep(1/nGroups, nGroups),
nGenes = nGenes,
batch.facScale = 0.1,
method = "groups",
verbose = FALSE,
out.prob = 0.001,
de.prob = 0.1, # mild
de.facLoc = de.facLoc,
de.facScale = 0.1,
bcv.common = 0.1,
seed = 42
)
sce
## class: SingleCellExperiment
## dim: 5000 3600
## metadata(1): Params
## assays(6): BatchCellMeans BaseCellMeans ... TrueCounts counts
## rownames(5000): Gene1 Gene2 ... Gene4999 Gene5000
## rowData names(11): Gene BaseGeneMean ... DEFacGroup2 DEFacGroup3
## colnames(3600): Cell1 Cell2 ... Cell3599 Cell3600
## colData names(4): Cell Batch Group ExpLibSize
## reducedDimNames(0):
## mainExpName: NULL
## altExpNames(0):
sce_unintegrated <- sce
# Normalization.
sce_unintegrated <- logNormCounts(sce_unintegrated)
# Feature selection.
dec <- modelGeneVar(sce_unintegrated)
hvg <- getTopHVGs(dec, prop = 0.1)
# PCA.
set.seed(1234)
sce_unintegrated <- scater::runPCA(sce_unintegrated, ncomponents = 50, subset_row = hvg)
# Clustering.
colLabels(sce_unintegrated) <- clusterCells(sce_unintegrated,
use.dimred = "PCA",
BLUSPARAM = NNGraphParam(cluster.fun = "louvain")
)
# Visualization.
sce_unintegrated <- scater::runUMAP(sce_unintegrated, dimred = "PCA")
plotUMAP(sce_unintegrated, colour_by = "Batch")
plotUMAP(sce_unintegrated, colour_by = "Group")
head(colData(sce))
## DataFrame with 6 rows and 4 columns
## Cell Batch Group ExpLibSize
## <character> <character> <factor> <numeric>
## Cell1 Cell1 Batch1 Group1 46898.1
## Cell2 Cell2 Batch1 Group1 54688.2
## Cell3 Cell3 Batch1 Group2 52027.9
## Cell4 Cell4 Batch1 Group1 52319.5
## Cell5 Cell5 Batch1 Group3 37774.7
## Cell6 Cell6 Batch1 Group3 58112.3
batch_key <- "Batch"
angl <- create_anglemania_object(
sce,
batch_key = batch_key
)
## No dataset_key specified.
## Assuming that all samples belong to the same dataset and are separated by batch_key: Batch
## Extracting count matrices...
## Filtering each batch to at least 1 cells per gene...
## Using the intersection of filtered genes from all batches...
## Number of genes in intersected set: 4970
angl
## anglemania_object
## --------------
## Dataset key: NA
## Batch key: Batch
## Number of datasets: 1
## Total number of batches: 4
## Batches (showing first 5):
## Batch1, Batch2, Batch3, Batch4
## Number of intersected genes: 4970
## Intersected genes (showing first 10):
## Gene1, Gene2, Gene3, Gene4, Gene5, Gene6, Gene7, Gene8, Gene9, Gene10 , ...
## Min cells per gene: 1
angl <- anglemania(angl,
zscore_mean_threshold = 2,
zscore_sn_threshold = 2,
max_n_genes = 2000 # optionally define a max number of genes.
)
## Computing angles and transforming to z-scores...
## Computing statistics...
## Weighting matrix_list...
## Calculating mean...
## Calculating sds...
## Filtering features...
## Selecting features...
## If desired, you can re-run the selection of genes with a lower zscore_mean_threshold and/or zscore_sn_threshold by using the 'select_genes' function. e.g.: angl <- select_genes(angl, zscore_mean_threshold = 1, zscore_sn_threshold = 1, max_n_genes = 2000)
## Please inspect get_anglemania_genes(angl)$info for info on the scores of the selected gene pairs.
## [1] "Selected 1698 genes for integration."
# If you think the number of selected genes is too high or low you can adjust the thresholds:
angl <- select_genes(angl,
zscore_mean_threshold = 2.5,
zscore_sn_threshold = 2.5)
## If desired, you can re-run the selection of genes with a lower zscore_mean_threshold and/or zscore_sn_threshold by using the 'select_genes' function. e.g.: angl <- select_genes(angl, zscore_mean_threshold = 1, zscore_sn_threshold = 1, max_n_genes = 2000)
## Please inspect get_anglemania_genes(angl)$info for info on the scores of the selected gene pairs.
## [1] "Selected 348 genes for integration."
# Inspect the anglemania genes
integration_genes <- get_anglemania_genes(angl)
head(integration_genes)
## [1] "Gene71" "Gene3527" "Gene2470" "Gene4030" "Gene355" "Gene394"
length(integration_genes)
## [1] 348
plotReducedDim(sce, colour_by = "Batch", dimred = "umap_MNN_hvg_320") +
ggtitle("MNN integration using top 320 HVGs, colored by Batch")
plotReducedDim(sce, colour_by = "Group", dimred = "umap_MNN_hvg_320") +
ggtitle("MNN integration using top 320 HVGs, colored by Group")
plotReducedDim(sce, colour_by = "Batch", dimred = "umap_MNN_hvg_2000") +
ggtitle("MNN integration using top 2000 HVGs, colored by Batch")
plotReducedDim(sce, colour_by = "Group", dimred = "umap_MNN_hvg_2000") +
ggtitle("MNN integration using top 2000 HVGs, colored by Group")
plotReducedDim(sce, colour_by = "Batch", dimred = "umap_MNN_anglemania") +
ggtitle("MNN integration using anglemania genes, colored by Batch")
plotReducedDim(sce, colour_by = "Group", dimred = "umap_MNN_anglemania") +
ggtitle("MNN integration using anglemania genes, colored by Group")
sessionInfo()
## R Under development (unstable) (2025-02-19 r87757)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.2 LTS
##
## Matrix products: default
## BLAS: /home/biocbuild/bbs-3.21-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] batchelor_1.23.0 bluster_1.17.0
## [3] scran_1.35.0 scater_1.35.3
## [5] ggplot2_3.5.1 scuttle_1.17.0
## [7] splatter_1.31.0 Seurat_5.2.1
## [9] SeuratObject_5.0.2 sp_2.2-0
## [11] dplyr_1.1.4 SingleCellExperiment_1.29.1
## [13] SummarizedExperiment_1.37.0 Biobase_2.67.0
## [15] GenomicRanges_1.59.1 GenomeInfoDb_1.43.4
## [17] IRanges_2.41.3 S4Vectors_0.45.4
## [19] BiocGenerics_0.53.6 generics_0.1.3
## [21] MatrixGenerics_1.19.1 matrixStats_1.5.0
## [23] anglemania_0.99.0
##
## loaded via a namespace (and not attached):
## [1] RcppAnnoy_0.0.22 splines_4.5.0
## [3] later_1.4.1 tibble_3.2.1
## [5] polyclip_1.10-7 fastDummies_1.7.5
## [7] lifecycle_1.0.4 edgeR_4.5.6
## [9] doParallel_1.0.17 globals_0.16.3
## [11] lattice_0.22-6 MASS_7.3-65
## [13] backports_1.5.0 magrittr_2.0.3
## [15] limma_3.63.8 plotly_4.10.4
## [17] sass_0.4.9 rmarkdown_2.29
## [19] jquerylib_0.1.4 yaml_2.3.10
## [21] bigparallelr_0.3.2 metapod_1.15.0
## [23] httpuv_1.6.15 sctransform_0.4.1
## [25] spam_2.11-1 spatstat.sparse_3.1-0
## [27] reticulate_1.41.0 cowplot_1.1.3
## [29] pbapply_1.7-2 RColorBrewer_1.1-3
## [31] ResidualMatrix_1.17.0 abind_1.4-8
## [33] Rtsne_0.17 purrr_1.0.4
## [35] bigassertr_0.1.6 GenomeInfoDbData_1.2.13
## [37] ggrepel_0.9.6 irlba_2.3.5.1
## [39] listenv_0.9.1 spatstat.utils_3.1-2
## [41] goftest_1.2-3 RSpectra_0.16-2
## [43] dqrng_0.4.1 spatstat.random_3.3-2
## [45] fitdistrplus_1.2-2 parallelly_1.42.0
## [47] DelayedMatrixStats_1.29.1 codetools_0.2-20
## [49] DelayedArray_0.33.6 tidyselect_1.2.1
## [51] UCSC.utils_1.3.1 farver_2.1.2
## [53] viridis_0.6.5 ScaledMatrix_1.15.0
## [55] bigstatsr_1.6.1 spatstat.explore_3.3-4
## [57] flock_0.7 jsonlite_1.9.1
## [59] BiocNeighbors_2.1.3 progressr_0.15.1
## [61] ggridges_0.5.6 survival_3.8-3
## [63] iterators_1.0.14 foreach_1.5.2
## [65] tools_4.5.0 ica_1.0-3
## [67] Rcpp_1.0.14 glue_1.8.0
## [69] gridExtra_2.3 SparseArray_1.7.6
## [71] xfun_0.51 withr_3.0.2
## [73] fastmap_1.2.0 rsvd_1.0.5
## [75] digest_0.6.37 R6_2.6.1
## [77] mime_0.12 colorspace_2.1-1
## [79] scattermore_1.2 tensor_1.5
## [81] spatstat.data_3.1-4 tidyr_1.3.1
## [83] data.table_1.17.0 FNN_1.1.4.1
## [85] httr_1.4.7 htmlwidgets_1.6.4
## [87] S4Arrays_1.7.3 uwot_0.2.3
## [89] pkgconfig_2.0.3 gtable_0.3.6
## [91] lmtest_0.9-40 XVector_0.47.2
## [93] htmltools_0.5.8.1 dotCall64_1.2
## [95] scales_1.3.0 png_0.1-8
## [97] spatstat.univar_3.1-2 knitr_1.49
## [99] reshape2_1.4.4 checkmate_2.3.2
## [101] nlme_3.1-167 cachem_1.1.0
## [103] zoo_1.8-13 stringr_1.5.1
## [105] rmio_0.4.0 KernSmooth_2.23-26
## [107] vipor_0.4.7 parallel_4.5.0
## [109] miniUI_0.1.1.1 pillar_1.10.1
## [111] grid_4.5.0 vctrs_0.6.5
## [113] RANN_2.6.2 promises_1.3.2
## [115] BiocSingular_1.23.0 ff_4.5.2
## [117] beachmat_2.23.6 xtable_1.8-4
## [119] cluster_2.1.8 beeswarm_0.4.0
## [121] evaluate_1.0.3 cli_3.6.4
## [123] locfit_1.5-9.12 compiler_4.5.0
## [125] rlang_1.1.5 crayon_1.5.3
## [127] future.apply_1.11.3 labeling_0.4.3
## [129] ps_1.9.0 ggbeeswarm_0.7.2
## [131] plyr_1.8.9 stringi_1.8.4
## [133] viridisLite_0.4.2 deldir_2.0-4
## [135] BiocParallel_1.41.2 munsell_0.5.1
## [137] lazyeval_0.2.2 spatstat.geom_3.3-5
## [139] Matrix_1.7-2 RcppHNSW_0.6.0
## [141] patchwork_1.3.0 sparseMatrixStats_1.19.0
## [143] future_1.34.0 statmod_1.5.0
## [145] shiny_1.10.0 ROCR_1.0-11
## [147] igraph_2.1.4 bslib_0.9.0
## [149] bit_4.6.0