| Title: | Memory-Efficient Doublet Detection for Single-Cell RNA Sequencing |
|---|---|
| Description: | Detects doublets in single-cell RNA sequencing data with the 'DoubletFinder' method of McGinnis, Murrow and Gartner (2019) <doi:10.1016/j.cels.2019.03.003>, implemented in 'Rust' to use less memory and time. Provides artificial doublet simulation, exact nearest-neighbour search and pANN (proportion of artificial nearest neighbours) scoring, and reproduces 'DoubletFinder' scores exactly. |
| Authors: | Mayank Gandhi [aut, cre, cph], Authors of the dependency Rust crates [cph] (see inst/AUTHORS for the bundled crates, their authors and licenses) |
| Maintainer: | Mayank Gandhi <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-10-16 17:47:53 UTC |
| Source: | https://github.com/cran/doubletrs |
The proportion of artificial nearest neighbours: for each real cell, the
fraction of its k nearest neighbours that are artificial doublets
(rows after n_real in pcs).
compute_pann(pcs, n_real, k, threads = doubletrs_threads())compute_pann(pcs, n_real, k, threads = doubletrs_threads())
pcs |
Numeric matrix, points x PCs, with the real cells in the
first |
n_real |
Number of real cells. |
k |
Number of neighbours per cell (excluding the cell itself). |
threads |
Number of threads to use. See |
Numeric vector of length n_real, named by rownames(pcs).
pcs <- matrix(rnorm(200), ncol = 2) pann <- compute_pann(pcs, n_real = 80, k = 5) summary(pann)pcs <- matrix(rnorm(200), ncol = 2) pann <- compute_pann(pcs, n_real = 80, k = 5) summary(pann)
Drop-in replacement for DoubletFinder::doubletFinder(). Simulates
artificial doublets, preprocesses the merged real and artificial cells
with the parameters already used on seu, computes pANN (the proportion
of artificial nearest neighbours) for every cell, and calls the nExp
highest-scoring cells doublets. With the same seed, results are
identical to DoubletFinder's, but the dense cells x cells distance matrix
is never built.
doubletFinder( seu, PCs, pN = 0.25, pK, nExp, reuse.pANN = NULL, sct = FALSE, annotations = NULL, threads = doubletrs_threads() )doubletFinder( seu, PCs, pN = 0.25, pK, nExp, reuse.pANN = NULL, sct = FALSE, annotations = NULL, threads = doubletrs_threads() )
seu |
A Seurat object on which |
PCs |
Principal components to use, e.g. |
pN |
Artificial doublets as a proportion of the merged real and artificial data. |
pK |
Neighbourhood size as a proportion of the merged data. Choose
it with |
nExp |
Number of cells to call doublets, e.g. the expected doublet
rate times the number of cells, adjusted with |
reuse.pANN |
Name of an existing pANN metadata column. If given, no
pANN is computed and only new calls for |
sct |
Whether |
annotations |
Not supported yet; must be |
threads |
Number of threads to use. See |
seu with metadata columns pANN_<pN>_<pK>_<nExp> and
DF.classifications_<pN>_<pK>_<nExp> ("Singlet" or "Doublet"),
named as DoubletFinder names them.
seu <- SeuratObject::pbmc_small set.seed(1) seu <- doubletFinder(seu, PCs = 1:5, pN = 0.25, pK = 0.1, nExp = 5) table(seu$DF.classifications_0.25_0.1_5)seu <- SeuratObject::pbmc_small set.seed(1) seu <- doubletFinder(seu, PCs = 1:5, pN = 0.25, pK = 0.1, nExp = 5) table(seu$DF.classifications_0.25_0.1_5)
The default for every threads argument. It is
getOption("doubletrs.threads") if set, otherwise the number of CPU
cores, capped by the OMP_THREAD_LIMIT environment variable if set.
During R CMD check --as-cran it is at most 2, as CRAN policy requires.
doubletrs_threads()doubletrs_threads()
A single positive integer.
doubletrs_threads() old <- options(doubletrs.threads = 1) doubletrs_threads() options(old)doubletrs_threads() old <- options(doubletrs.threads = 1) doubletrs_threads() options(old)
Exact k-nearest-neighbour search in PC space, giving the same neighbours as DoubletFinder.
find_neighbors(pcs, n_real, k, threads = doubletrs_threads())find_neighbors(pcs, n_real, k, threads = doubletrs_threads())
pcs |
Numeric matrix, points x PCs, with the real cells in the
first |
n_real |
Number of real cells. |
k |
Number of neighbours per cell (excluding the cell itself). |
threads |
Number of threads to use. See |
Integer matrix, n_real x k: 1-based row indices of each real
cell's neighbours in pcs, nearest first.
pcs <- matrix(rnorm(200), ncol = 2) nn <- find_neighbors(pcs, n_real = 80, k = 5) dim(nn)pcs <- matrix(rnorm(200), ncol = 2) nn <- find_neighbors(pcs, n_real = 80, k = 5) dim(nn)
Drop-in replacement for DoubletFinder::find.pK(): the mean-variance
normalised bimodality coefficient (BCmvn) for each pK across all pN. The
pK with the highest BCmetric is DoubletFinder's recommended choice.
find.pK(sweep.stats, plot = TRUE)find.pK(sweep.stats, plot = TRUE)
sweep.stats |
Output of |
plot |
Whether to plot BCmvn against pK, as DoubletFinder does. |
A data frame with columns ParamID, pK, MeanBC, VarBC and
BCmetric (plus MeanAUC when the sweep was summarised with ground
truth).
Drop-in replacement for DoubletFinder::modelHomotypic(): the expected
fraction of doublets formed by two cells of the same type, given cell
type annotations. Homotypic doublets are hard to detect, so the expected
doublet count is often scaled by 1 - modelHomotypic(annotations).
modelHomotypic(annotations)modelHomotypic(annotations)
annotations |
Cell type or cluster label for each cell. |
A number between 0 and 1: the sum of squared type frequencies.
modelHomotypic(c("T", "T", "B", "B"))modelHomotypic(c("T", "T", "B", "B"))
Drop-in replacement for DoubletFinder::paramSweep(). For each pN from
0.05 to 0.3, simulates doublets once and computes pANN at every pK from
0.0005 to 0.3. Each cell's neighbours are found once, up to the largest
pK, instead of sorting all distances. Datasets with more than 10,000
cells are down-sampled to 10,000, as in DoubletFinder.
paramSweep( seu, PCs = 1:10, sct = FALSE, num.cores = 1, threads = doubletrs_threads() )paramSweep( seu, PCs = 1:10, sct = FALSE, num.cores = 1, threads = doubletrs_threads() )
seu |
A Seurat object on which |
PCs |
Principal components to use, e.g. |
sct |
Whether |
num.cores |
Accepted for compatibility with DoubletFinder and
ignored; use |
threads |
Number of threads to use. See |
A named list with one data frame (column pANN, one row per
cell) per pN-pK pair, named pN_<pN>_pK_<pK>, for summarizeSweep().
seu <- SeuratObject::pbmc_small set.seed(1) sweep <- paramSweep(seu, PCs = 1:5) stats <- summarizeSweep(sweep) bcmvn <- find.pK(stats)seu <- SeuratObject::pbmc_small set.seed(1) sweep <- paramSweep(seu, PCs = 1:5) stats <- summarizeSweep(sweep) bcmvn <- find.pK(stats)
Builds n artificial doublets by averaging pairs of cells drawn
independently and with replacement, as DoubletFinder does. Parent cells
are drawn with R's random number generator, in the same order as
DoubletFinder, so set.seed() makes results reproducible and identical
to DoubletFinder's. The count matrix is read directly from R memory; it
is not copied.
simulate_doublets(counts, n, threads = doubletrs_threads())simulate_doublets(counts, n, threads = doubletrs_threads())
counts |
Genes x cells count matrix, ideally a |
n |
Number of doublets to simulate. |
threads |
Number of threads to use. See |
A genes x n dgCMatrix of doublets, with an attribute
"parents": a data frame of the two parent cell indices (1-based
columns of counts) for each doublet.
counts <- abs(Matrix::rsparsematrix(100, 50, density = 0.1)) set.seed(1) doublets <- simulate_doublets(counts, n = 20) dim(doublets) head(attr(doublets, "parents"))counts <- abs(Matrix::rsparsematrix(100, 50, density = 0.1)) set.seed(1) doublets <- simulate_doublets(counts, n = 20) dim(doublets) head(attr(doublets, "parents"))
Drop-in replacement for DoubletFinder::summarizeSweep(): the bimodality
coefficient of each pANN distribution (from a Gaussian kernel density
estimate), and optionally the AUC against known doublets.
summarizeSweep(sweep.list, GT = FALSE, GT.calls = NULL)summarizeSweep(sweep.list, GT = FALSE, GT.calls = NULL)
sweep.list |
Output of |
GT |
Whether ground-truth doublet calls are available. |
GT.calls |
If |
A data frame with columns pN, pK (factors), BCreal, and
AUC when GT = TRUE, for find.pK().