doubletrs detects doublets (droplets containing two cells) in single-cell RNA sequencing data with the DoubletFinder method of McGinnis, Murrow and Gartner (2019). The memory-heavy steps (artificial doublet simulation, nearest-neighbour search and pANN scoring) are written in Rust. DoubletFinder builds a dense cells x cells distance matrix; doubletrs never does.
doubletrs has the same functions and arguments as DoubletFinder, so
an existing workflow switches by replacing
library(DoubletFinder) with
library(doubletrs). Parent cells for artificial doublets
are drawn with the same R random number calls, so after the same
set.seed() the results are identical to
DoubletFinder’s.
The workflow functions need Seurat. This example uses
pbmc_small, an 80-cell dataset from SeuratObject that has
already been normalised, scaled and run through PCA.
paramSweep() computes pANN over a grid of pN and pK
values. summarizeSweep() measures how bimodal each pANN
distribution is, and find.pK() combines those into BCmvn.
The pK with the highest BCmvn is DoubletFinder’s recommended choice.
set.seed(1)
sweep <- paramSweep(seu, PCs = 1:5)
stats <- summarizeSweep(sweep)
bcmvn <- find.pK(stats)The expected number of doublets comes from the loading density of the experiment; here we assume 7.5%. Doublets of two cells of the same type (homotypic) are hard to detect, so the estimate is scaled down by the expected homotypic fraction from cluster labels.
set.seed(1)
seu <- doubletFinder(seu, PCs = 1:5, pN = 0.25, pK = pK, nExp = nExp)
calls <- grep("^DF.classifications", colnames(seu@meta.data), value = TRUE)
table(seu@meta.data[[calls]])
#>
#> Doublet Singlet
#> 4 76The metadata columns are named as DoubletFinder names them:
pANN_<pN>_<pK>_<nExp> and
DF.classifications_<pN>_<pK>_<nExp>.
The steps are also available without Seurat, for other preprocessing
pipelines. simulate_doublets() averages random pairs of
cells from a count matrix. After merging real cells and doublets, and
normalising and running PCA on the merged matrix in the pipeline of your
choice, compute_pann() scores each real cell.
set.seed(1)
counts <- abs(Matrix::rsparsematrix(200, 100, density = 0.2))
doublets <- simulate_doublets(counts, n = 33)
dim(doublets)
#> [1] 200 33
# Stand-in for PCs of the merged matrix: real cells first, then doublets.
pcs <- matrix(rnorm(133 * 5), ncol = 5)
pann <- compute_pann(pcs, n_real = 100, k = 10)
summary(pann)
#> Min. 1st Qu. Median Mean 3rd Qu. Max.
#> 0.000 0.100 0.200 0.199 0.300 0.500find_neighbors() returns the neighbour indices
themselves.
Every function takes a threads argument. The default,
doubletrs_threads(), uses all CPU cores unless
options(doubletrs.threads = n) is set. Results do not
depend on the number of threads.
McGinnis CS, Murrow LM, Gartner ZJ (2019). DoubletFinder: Doublet detection in single-cell RNA sequencing data using artificial nearest neighbors. Cell Systems 8(4), 329-337. https://doi.org/10.1016/j.cels.2019.03.003