From a2387793f47c5c44d1c7d9142266d0f2494c57d5 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Tue, 23 Jan 2024 15:47:32 +0100 Subject: [PATCH 01/15] modify dbr value according to parse kit --- pipeline-runner/R/gem2s-4-score_doublets.R | 30 ++++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index 3d813e04..35575e02 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -14,6 +14,7 @@ score_doublets <- function(input, pipeline_config, prev_out) { # NOTE: edrops is not required check_prev_out(prev_out, c("config", "counts_list", "annot")) + type <- input$input$type edrops_list <- prev_out$edrops counts_list <- prev_out$counts_list samples <- names(counts_list) @@ -30,7 +31,8 @@ score_doublets <- function(input, pipeline_config, prev_out) { sample_counts <- sample_counts[, keep] } - scores[[sample]] <- get_doublet_scores(sample_counts) + # TODO: Pass also parse_kit when available from the UI + scores[[sample]] <- get_doublet_scores(sample_counts, type = type) } @@ -51,9 +53,26 @@ score_doublets <- function(input, pipeline_config, prev_out) { #' #' @return data.frame with doublet scores and assigned classes #' -compute_sample_doublet_scores <- function(sample_counts) { +compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") { set.seed(RANDOM_SEED) - sce <- scDblFinder::scDblFinder(sample_counts) + + if (type == "parse") { + if (!exists("parse_kit")) { + stop("Parse kit is not defined") + } + + dbr <- switch(parse_kit, + "mini" = 0.046, + "WT" = 0.034, + "mega" = 0.064, + stop("Invalid parse kit value: ", parse_kit) + ) + + sce <- scDblFinder::scDblFinder(sample_counts, dbr = dbr) + } else { + sce <- scDblFinder::scDblFinder(sample_counts) + } + doublet_res <- data.frame( row.names = colnames(sce), barcodes = colnames(sce), @@ -65,7 +84,7 @@ compute_sample_doublet_scores <- function(sample_counts) { } -get_doublet_scores <- function(sample_counts, max_attempts = 5) { +get_doublet_scores <- function(sample_counts, max_attempts = 5, type) { # also filter low UMI as per scDblFinder:::.checkSCE() ntot <- Matrix::colSums(sample_counts) @@ -77,7 +96,8 @@ get_doublet_scores <- function(sample_counts, max_attempts = 5) { # make the threshold stricter in every attempt empty_cells_mask <- ntot > (200 * attempt) try({ - scores <- compute_sample_doublet_scores(sample_counts[, empty_cells_mask]) + # TODO: Pass also parse_kit when available from the UI + scores <- compute_sample_doublet_scores(sample_counts[, empty_cells_mask], type) retry <- "not null" }) attempt <- attempt + 1 From b309add3c2c0d4fcbdf02dba301c6f9d3c96f23f Mon Sep 17 00:00:00 2001 From: sara castellano Date: Wed, 24 Jan 2024 11:20:12 +0100 Subject: [PATCH 02/15] remove useless error --- pipeline-runner/R/gem2s-4-score_doublets.R | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index 35575e02..5136f9ab 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -57,17 +57,12 @@ compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") set.seed(RANDOM_SEED) if (type == "parse") { - if (!exists("parse_kit")) { - stop("Parse kit is not defined") - } - dbr <- switch(parse_kit, "mini" = 0.046, "WT" = 0.034, "mega" = 0.064, stop("Invalid parse kit value: ", parse_kit) ) - sce <- scDblFinder::scDblFinder(sample_counts, dbr = dbr) } else { sce <- scDblFinder::scDblFinder(sample_counts) From eabac469a68fd4bf024eee0dbcbae1e54432e750 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Wed, 24 Jan 2024 11:20:34 +0100 Subject: [PATCH 03/15] fix and add tests --- .../testthat/test-gem2s-4-score_doublets.R | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R index 3502b207..54853439 100644 --- a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R +++ b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R @@ -7,16 +7,22 @@ mock_counts <- function(...) { return(Matrix::Matrix(counts, sparse = T)) } +mock_input <- function(type) { + input <- list() + input$input$type <- type + return(input) +} test_that("score_doublets returns expected columns", { counts <- mock_counts() + input <- mock_input("10x") prev_out <- list( counts_list = list(sample1 = counts), config = list(), annot = list() ) - out <- score_doublets(NULL, NULL, prev_out)$output + out <- score_doublets(input, NULL, prev_out)$output expect_setequal( colnames(out$doublet_scores$sample1), @@ -29,6 +35,8 @@ test_that("score_doublets filters cells to avoid warning of extremely low read c counts <- mock_counts(ncells = c(200, 300, 400, 200, 500, 300)) counts <- round(counts / 2) + input <- mock_input("10x") + expect_warning(scDblFinder:::.checkSCE(counts), "extremely low read counts") prev_out <- list( @@ -36,7 +44,7 @@ test_that("score_doublets filters cells to avoid warning of extremely low read c config = list(), annot = list() ) - expect_warning(score_doublets(NULL, NULL, prev_out), NA) + expect_warning(score_doublets(input, NULL, prev_out), NA) }) @@ -47,15 +55,69 @@ test_that("score_doublets fails if prev_out is missing 'config', 'counts_list', config = list(), annot = list() ) + input <- mock_input("10x") prev_out$config <- NULL - expect_error(score_doublets(NULL, NULL, prev_out), "config is missing") + expect_error(score_doublets(input, NULL, prev_out), "config is missing") prev_out$config <- list() prev_out$annot <- NULL - expect_error(score_doublets(NULL, NULL, prev_out), "annot is missing") + expect_error(score_doublets(input, NULL, prev_out), "annot is missing") prev_out$annot <- data.frame() prev_out$counts_list <- NULL - expect_error(score_doublets(NULL, NULL, prev_out), "counts_list is missing") + expect_error(score_doublets(input, NULL, prev_out), "counts_list is missing") +}) + + +test_that("compute_sample_doublet_scores handles type 'parse' correctly", { + counts <- mock_counts() + input <- mock_input("parse") + + prev_out <- list( + counts_list = list(sample1 = counts), + config = list(), + annot = list() + ) + out <-suppressWarnings(score_doublets(input, NULL, prev_out)$output) + + expect_setequal( + colnames(out$doublet_scores$sample1), + c("barcodes", "doublet_class", "doublet_scores") + ) +}) + + +test_that("compute_sample_doublet_scores uses correct dbr for different Parse kits", { + counts <- mock_counts() + + dbr_mini <- DOUBLET_RATE_MINI + set.seed(RANDOM_SEED) + expected_sce_mini <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_mini)) + + dbr_wt <- DOUBLET_RATE_WT + set.seed(RANDOM_SEED) + expected_sce_wt <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_wt)) + + dbr_mega <- DOUBLET_RATE_MEGA + set.seed(RANDOM_SEED) + expected_sce_mega <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_mega)) + + observed_sce_mini <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "mini")) + observed_sce_wt <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "WT")) + observed_sce_mega <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "mega")) + + expect_identical(expected_sce_mini$scDblFinder.score, observed_sce_mini$doublet_scores) + expect_identical(expected_sce_wt$scDblFinder.score, observed_sce_wt$doublet_scores) + expect_identical(expected_sce_mega$scDblFinder.score, observed_sce_mega$doublet_scores) +}) + + +test_that("compute_sample_doublet_scores stops with an error for invalid parse_kit values", { + counts <- mock_counts() + + expect_error( + compute_sample_doublet_scores(counts, type = "parse", parse_kit = "invalid_kit"), + "Invalid parse kit value" + ) }) From c5c8f01e88d96d33221d3e3042d682d35d562be3 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Thu, 25 Jan 2024 10:39:07 +0100 Subject: [PATCH 04/15] get sample technology from config when doublet scores are recomputed --- pipeline-runner/R/qc-5-filter_doublets.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline-runner/R/qc-5-filter_doublets.R b/pipeline-runner/R/qc-5-filter_doublets.R index fa903c88..cf2e103d 100644 --- a/pipeline-runner/R/qc-5-filter_doublets.R +++ b/pipeline-runner/R/qc-5-filter_doublets.R @@ -28,7 +28,7 @@ filter_doublets <- function(scdata_list, config, sample_id, cells_id, task_name if ("recomputeDoubletScore" %in% names(config)) { if (config$recomputeDoubletScore) { - scores <- get_doublet_scores(sample_data@assays$RNA@counts) + scores <- get_doublet_scores(sample_data@assays$RNA@counts, type = config$sampleTechnology) sample_data <- add_dblscore(sample_data, scores) # update doublet scores in original scdata scdata_list[[sample_id]] <- add_dblscore(scdata_list[[sample_id]], scores) From 744dfba02512dd6d49908a61c1c6472888659ae8 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Thu, 25 Jan 2024 10:39:20 +0100 Subject: [PATCH 05/15] fix tests --- pipeline-runner/tests/testthat/test-qc-5-filter_doublets.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pipeline-runner/tests/testthat/test-qc-5-filter_doublets.R b/pipeline-runner/tests/testthat/test-qc-5-filter_doublets.R index 5595fa5a..ff8914e3 100644 --- a/pipeline-runner/tests/testthat/test-qc-5-filter_doublets.R +++ b/pipeline-runner/tests/testthat/test-qc-5-filter_doublets.R @@ -4,14 +4,15 @@ mock_ids <- function() { } -mock_config <- function(thr = 0.1, auto = FALSE, enabled = TRUE, recomputeDoubletScore = FALSE) { +mock_config <- function(thr = 0.1, auto = FALSE, enabled = TRUE, recomputeDoubletScore = FALSE, sampleTechnology = "10x") { config <- list( auto = auto, enabled = enabled, filterSettings = list( probabilityThreshold = thr ), - recomputeDoubletScore = recomputeDoubletScore + recomputeDoubletScore = recomputeDoubletScore, + sampleTechnology = sampleTechnology ) return(config) } From 5227b475403097d01e6cfc768633235941dcdc9e Mon Sep 17 00:00:00 2001 From: sara castellano Date: Thu, 25 Jan 2024 12:31:06 +0100 Subject: [PATCH 06/15] add default technology value to get_doublet_scores --- pipeline-runner/R/gem2s-4-score_doublets.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index 5136f9ab..2fb2f0e3 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -79,7 +79,7 @@ compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") } -get_doublet_scores <- function(sample_counts, max_attempts = 5, type) { +get_doublet_scores <- function(sample_counts, max_attempts = 5, type = "10x") { # also filter low UMI as per scDblFinder:::.checkSCE() ntot <- Matrix::colSums(sample_counts) From 248a38d9112e2f297aeeb455312d120132de1888 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Thu, 25 Jan 2024 14:27:58 +0100 Subject: [PATCH 07/15] refactor compute_sample_doublet_scores --- pipeline-runner/R/gem2s-4-score_doublets.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index 2fb2f0e3..f7bd1861 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -56,6 +56,7 @@ score_doublets <- function(input, pipeline_config, prev_out) { compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") { set.seed(RANDOM_SEED) + dbr <- NULL if (type == "parse") { dbr <- switch(parse_kit, "mini" = 0.046, @@ -63,10 +64,8 @@ compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") "mega" = 0.064, stop("Invalid parse kit value: ", parse_kit) ) - sce <- scDblFinder::scDblFinder(sample_counts, dbr = dbr) - } else { - sce <- scDblFinder::scDblFinder(sample_counts) } + sce <- scDblFinder::scDblFinder(sample_counts, dbr = dbr) doublet_res <- data.frame( row.names = colnames(sce), From e026c1833e0c93033481e8bb08b43de72aa0457c Mon Sep 17 00:00:00 2001 From: sara castellano Date: Fri, 26 Jan 2024 12:25:18 +0100 Subject: [PATCH 08/15] replace nick-invision with nick-fields --- .github/workflows/pr_validate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_validate.yaml b/.github/workflows/pr_validate.yaml index eec3c095..5e8f52fa 100644 --- a/.github/workflows/pr_validate.yaml +++ b/.github/workflows/pr_validate.yaml @@ -63,7 +63,7 @@ jobs: - id: reach-staging if: steps.extract-staging.outputs.url != 'N/A' name: Attempt to reach staging environments - uses: nick-invision/retry@v2 + uses: nick-fields/retry@v2 with: timeout_seconds: 30 max_attempts: 5 From c8f0ba4943f4b29a2a19707f0e58c37422cdd80c Mon Sep 17 00:00:00 2001 From: sara castellano Date: Fri, 26 Jan 2024 15:39:41 +0100 Subject: [PATCH 09/15] rename 'type' to 'technology' --- pipeline-runner/R/gem2s-4-score_doublets.R | 12 ++++++------ pipeline-runner/R/qc-5-filter_doublets.R | 2 +- .../tests/testthat/test-gem2s-4-score_doublets.R | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index f7bd1861..ae6da5c4 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -14,7 +14,7 @@ score_doublets <- function(input, pipeline_config, prev_out) { # NOTE: edrops is not required check_prev_out(prev_out, c("config", "counts_list", "annot")) - type <- input$input$type + technology <- input$input$type edrops_list <- prev_out$edrops counts_list <- prev_out$counts_list samples <- names(counts_list) @@ -32,7 +32,7 @@ score_doublets <- function(input, pipeline_config, prev_out) { } # TODO: Pass also parse_kit when available from the UI - scores[[sample]] <- get_doublet_scores(sample_counts, type = type) + scores[[sample]] <- get_doublet_scores(sample_counts, technology = technology) } @@ -53,11 +53,11 @@ score_doublets <- function(input, pipeline_config, prev_out) { #' #' @return data.frame with doublet scores and assigned classes #' -compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") { +compute_sample_doublet_scores <- function(sample_counts, technology, parse_kit = "WT") { set.seed(RANDOM_SEED) dbr <- NULL - if (type == "parse") { + if (technology == "parse") { dbr <- switch(parse_kit, "mini" = 0.046, "WT" = 0.034, @@ -78,7 +78,7 @@ compute_sample_doublet_scores <- function(sample_counts, type, parse_kit = "WT") } -get_doublet_scores <- function(sample_counts, max_attempts = 5, type = "10x") { +get_doublet_scores <- function(sample_counts, max_attempts = 5, technology = "10x") { # also filter low UMI as per scDblFinder:::.checkSCE() ntot <- Matrix::colSums(sample_counts) @@ -91,7 +91,7 @@ get_doublet_scores <- function(sample_counts, max_attempts = 5, type = "10x") { empty_cells_mask <- ntot > (200 * attempt) try({ # TODO: Pass also parse_kit when available from the UI - scores <- compute_sample_doublet_scores(sample_counts[, empty_cells_mask], type) + scores <- compute_sample_doublet_scores(sample_counts[, empty_cells_mask], technology) retry <- "not null" }) attempt <- attempt + 1 diff --git a/pipeline-runner/R/qc-5-filter_doublets.R b/pipeline-runner/R/qc-5-filter_doublets.R index cf2e103d..7db6e32b 100644 --- a/pipeline-runner/R/qc-5-filter_doublets.R +++ b/pipeline-runner/R/qc-5-filter_doublets.R @@ -28,7 +28,7 @@ filter_doublets <- function(scdata_list, config, sample_id, cells_id, task_name if ("recomputeDoubletScore" %in% names(config)) { if (config$recomputeDoubletScore) { - scores <- get_doublet_scores(sample_data@assays$RNA@counts, type = config$sampleTechnology) + scores <- get_doublet_scores(sample_data@assays$RNA@counts, technology = config$sampleTechnology) sample_data <- add_dblscore(sample_data, scores) # update doublet scores in original scdata scdata_list[[sample_id]] <- add_dblscore(scdata_list[[sample_id]], scores) diff --git a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R index 54853439..de8da031 100644 --- a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R +++ b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R @@ -7,9 +7,9 @@ mock_counts <- function(...) { return(Matrix::Matrix(counts, sparse = T)) } -mock_input <- function(type) { +mock_input <- function(technology) { input <- list() - input$input$type <- type + input$input$type <- technology return(input) } @@ -70,7 +70,7 @@ test_that("score_doublets fails if prev_out is missing 'config', 'counts_list', }) -test_that("compute_sample_doublet_scores handles type 'parse' correctly", { +test_that("compute_sample_doublet_scores handles technology 'parse' correctly", { counts <- mock_counts() input <- mock_input("parse") @@ -103,9 +103,9 @@ test_that("compute_sample_doublet_scores uses correct dbr for different Parse ki set.seed(RANDOM_SEED) expected_sce_mega <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_mega)) - observed_sce_mini <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "mini")) - observed_sce_wt <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "WT")) - observed_sce_mega <- suppressWarnings(compute_sample_doublet_scores(counts, type = "parse", parse_kit = "mega")) + observed_sce_mini <- suppressWarnings(compute_sample_doublet_scores(counts, technology = "parse", parse_kit = "mini")) + observed_sce_wt <- suppressWarnings(compute_sample_doublet_scores(counts, technology = "parse", parse_kit = "WT")) + observed_sce_mega <- suppressWarnings(compute_sample_doublet_scores(counts, technology = "parse", parse_kit = "mega")) expect_identical(expected_sce_mini$scDblFinder.score, observed_sce_mini$doublet_scores) expect_identical(expected_sce_wt$scDblFinder.score, observed_sce_wt$doublet_scores) @@ -117,7 +117,7 @@ test_that("compute_sample_doublet_scores stops with an error for invalid parse_k counts <- mock_counts() expect_error( - compute_sample_doublet_scores(counts, type = "parse", parse_kit = "invalid_kit"), + compute_sample_doublet_scores(counts, technology = "parse", parse_kit = "invalid_kit"), "Invalid parse kit value" ) }) From 4f727bf0ecb581b18620ad07d72467e51e380bbf Mon Sep 17 00:00:00 2001 From: sara castellano Date: Fri, 26 Jan 2024 15:46:08 +0100 Subject: [PATCH 10/15] update documentation --- pipeline-runner/R/gem2s-4-score_doublets.R | 2 ++ pipeline-runner/man/compute_sample_doublet_scores.Rd | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index ae6da5c4..20dccf33 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -50,6 +50,8 @@ score_doublets <- function(input, pipeline_config, prev_out) { #' Compute doublets scores per sample. #' #' @param sample_counts Sparse matrix with the counts for one sample. +#' @param technology Technology used to generate the data (e.g. 10x, Parse). +#' @param parse_kit Kit used to generate the data (specific to Parse data). #' #' @return data.frame with doublet scores and assigned classes #' diff --git a/pipeline-runner/man/compute_sample_doublet_scores.Rd b/pipeline-runner/man/compute_sample_doublet_scores.Rd index f4320b81..29e11252 100644 --- a/pipeline-runner/man/compute_sample_doublet_scores.Rd +++ b/pipeline-runner/man/compute_sample_doublet_scores.Rd @@ -4,10 +4,14 @@ \alias{compute_sample_doublet_scores} \title{Compute doublets scores per sample.} \usage{ -compute_sample_doublet_scores(sample_counts) +compute_sample_doublet_scores(sample_counts, technology, parse_kit = "WT") } \arguments{ \item{sample_counts}{Sparse matrix with the counts for one sample.} + +\item{technology}{Technology used to generate the data (e.g. 10x, Parse).} + +\item{parse_kit}{Kit used to generate the data (specific to Parse data).} } \value{ data.frame with doublet scores and assigned classes From acdd6f38122d072b1905eaa36cfbb0f18f419f66 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Fri, 26 Jan 2024 15:54:18 +0100 Subject: [PATCH 11/15] refactor doublet rate values for parse kits --- pipeline-runner/R/gem2s-4-score_doublets.R | 9 ++++----- pipeline-runner/data-raw/sysdata.R | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pipeline-runner/R/gem2s-4-score_doublets.R b/pipeline-runner/R/gem2s-4-score_doublets.R index 20dccf33..5bd3fd1f 100644 --- a/pipeline-runner/R/gem2s-4-score_doublets.R +++ b/pipeline-runner/R/gem2s-4-score_doublets.R @@ -60,12 +60,11 @@ compute_sample_doublet_scores <- function(sample_counts, technology, parse_kit = dbr <- NULL if (technology == "parse") { - dbr <- switch(parse_kit, - "mini" = 0.046, - "WT" = 0.034, - "mega" = 0.064, + if (parse_kit %in% names(DOUBLET_RATE_PARSE)) { + dbr <- DOUBLET_RATE_PARSE[[parse_kit]] + } else { stop("Invalid parse kit value: ", parse_kit) - ) + } } sce <- scDblFinder::scDblFinder(sample_counts, dbr = dbr) diff --git a/pipeline-runner/data-raw/sysdata.R b/pipeline-runner/data-raw/sysdata.R index f06838da..67141735 100644 --- a/pipeline-runner/data-raw/sysdata.R +++ b/pipeline-runner/data-raw/sysdata.R @@ -109,6 +109,9 @@ pipeline_version <- 2 UNISAMPLE <- "unisample" +# Parse kits empirical doublet rates +DOUBLET_RATE_PARSE <- list(mini = 0.046, WT = 0.034, mega = 0.064) + # pipeline error constants errors <- list( ERROR_SEURAT_RDS = 'ERROR_SEURAT_RDS', @@ -144,6 +147,7 @@ usethis::use_data( RIBOSOMAL_REGEX, pipeline_version, UNISAMPLE, + DOUBLET_RATE_PARSE, errors, internal = TRUE, overwrite = TRUE From 43298ad416da64a1669aab38dbe16d2e411f63bc Mon Sep 17 00:00:00 2001 From: sara castellano Date: Mon, 29 Jan 2024 10:36:34 +0100 Subject: [PATCH 12/15] fix parse doublet rates in test --- .../tests/testthat/test-gem2s-4-score_doublets.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R index de8da031..7b9bf7a8 100644 --- a/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R +++ b/pipeline-runner/tests/testthat/test-gem2s-4-score_doublets.R @@ -91,15 +91,15 @@ test_that("compute_sample_doublet_scores handles technology 'parse' correctly", test_that("compute_sample_doublet_scores uses correct dbr for different Parse kits", { counts <- mock_counts() - dbr_mini <- DOUBLET_RATE_MINI + dbr_mini <- DOUBLET_RATE_PARSE[["mini"]] set.seed(RANDOM_SEED) expected_sce_mini <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_mini)) - dbr_wt <- DOUBLET_RATE_WT + dbr_wt <- DOUBLET_RATE_PARSE[["WT"]] set.seed(RANDOM_SEED) expected_sce_wt <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_wt)) - dbr_mega <- DOUBLET_RATE_MEGA + dbr_mega <- DOUBLET_RATE_PARSE[["mega"]] set.seed(RANDOM_SEED) expected_sce_mega <- suppressWarnings(scDblFinder::scDblFinder(counts, dbr = dbr_mega)) From 50359358dfc94141a63de4d15977d6428030407b Mon Sep 17 00:00:00 2001 From: sara castellano Date: Mon, 29 Jan 2024 11:27:35 +0100 Subject: [PATCH 13/15] update sysdata.rds Signed-off-by: sara castellano --- pipeline-runner/R/sysdata.rda | Bin 4034 -> 4104 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pipeline-runner/R/sysdata.rda b/pipeline-runner/R/sysdata.rda index 1de37724890bf1d80924da82120b7aa9bfbb19dc..74400a76f42856c1d3fe57456c26b8520d22b434 100644 GIT binary patch literal 4104 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 Date: Mon, 29 Jan 2024 11:27:35 +0100 Subject: [PATCH 14/15] update sysdata.rds Signed-off-by: sara castellano From dd8a236f65a04c422e9ec64abca2ba20a91044e7 Mon Sep 17 00:00:00 2001 From: sara castellano Date: Mon, 29 Jan 2024 11:53:24 +0100 Subject: [PATCH 15/15] update sysdata.rda Signed-off-by: sara castellano --- pipeline-runner/R/sysdata.rda | Bin 4096 -> 4168 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pipeline-runner/R/sysdata.rda b/pipeline-runner/R/sysdata.rda index 24ac32ecf36e1200fa0d3d5100f6d035956638fd..6fd3be3b79242af4762674c40433e2e2ad68e5c6 100644 GIT binary patch literal 4168 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 SAV{}A#oUoj6eKH8T_OO~a+Li5 literal 4096 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