From abbae2e08e50280ced8852612b854665e7e570e4 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:02:44 -0500 Subject: [PATCH 01/74] develop branch setup --- DESCRIPTION | 4 ++-- NEWS.md | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9c101ac2c..9d3230915 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4 -Date: 2025-12-10 +Version: 3.2.4.0001 +Date: 2025-12-12 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), diff --git a/NEWS.md b/NEWS.md index e480c085a..4fff5eae9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,20 @@ +# scCustomize 3.X.X (202X-XX-XX) +## Added + + + + +## Changed + + +## Fixes + + + + + + + # scCustomize 3.2.4 (2025-12-10) ## Added - Add progress bar support to following functions with `parallel` parameter using mcprogress package: `Read10X_GEO`, `Read10X_h5_GEO`, `Read10X_Multi_Directory`, `Read10X_h5_Multi_Directory`, `Read_GEO_Delim`, `Read_CellBender_h5_Multi_Directory`, `Read_CellBender_h5_Multi_File`. From f0ccba2e834b93fd957dd5499824389b1d92d166 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:02:59 -0500 Subject: [PATCH 02/74] Rename R/ scripts --- R/{QC_Utilities_Seurat.R => Utilities_QC_Seurat.R} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename R/{QC_Utilities_Seurat.R => Utilities_QC_Seurat.R} (100%) diff --git a/R/QC_Utilities_Seurat.R b/R/Utilities_QC_Seurat.R similarity index 100% rename from R/QC_Utilities_Seurat.R rename to R/Utilities_QC_Seurat.R From fb2fd3df12853a19ca560b9b1e0294cac13c44f6 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:07:22 -0500 Subject: [PATCH 03/74] add parameters if grouping by sample --- R/Plotting_Statistics.R | 71 +++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index 0abaf8cd6..c699506f5 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -666,17 +666,19 @@ Plot_Median_Other <- function( #' Plot_Cells_per_Sample <- function( - seurat_object, - sample_col = "orig.ident", - group.by = NULL, - colors_use = NULL, - dot_size = 1, - plot_title = "Cells/Nuclei per Sample", - y_axis_label = "Number of Cells", - x_axis_label = NULL, - legend_title = NULL, - x_lab_rotate = TRUE, - color_seed = 123 + seurat_object, + sample_col = "orig.ident", + group.by = NULL, + colors_use = NULL, + dot_size = 1, + plot_title = "Cells/Nuclei per Sample", + y_axis_label = "Number of Cells", + x_axis_label = NULL, + legend_title = NULL, + x_lab_rotate = TRUE, + reorder = FALSE, + plot_median = TRUE, + color_seed = 123 ) { # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -730,15 +732,40 @@ Plot_Cells_per_Sample <- function( } } - # Generate base plot - plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + - geom_boxplot(fill = "white") + - geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + - scale_fill_manual(values = colors_use) + - theme_ggprism_mod() + - ggtitle(plot_title) + - ylab(y_axis_label) + - xlab("") + if (length(x = unique(seurat_object[[group.by]])) == length(unique(seurat_object[[sample_col]]))) { + if (isFALSE(x = reorder)) { + plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + + geom_boxplot(fill = "white") + + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + + scale_fill_manual(values = colors_use) + + theme_ggprism_mod() + + ggtitle(plot_title) + + ylab(y_axis_label) + + xlab("") + } else { + plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]]), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + + geom_boxplot(fill = "white") + + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + + scale_fill_manual(values = colors_use) + + theme_ggprism_mod() + + ggtitle(plot_title) + + ylab(y_axis_label) + + xlab("") + } + if (isTRUE(x = plot_median)) { + plot <- plot + geom_hline(yintercept = median(merged[["Number_of_Cells"]])) + } + } else { + # Generate base plot + plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + + geom_boxplot(fill = "white") + + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + + scale_fill_manual(values = colors_use) + + theme_ggprism_mod() + + ggtitle(plot_title) + + ylab(y_axis_label) + + xlab("") + } # Modify base plot if (isTRUE(x = x_lab_rotate)) { @@ -753,6 +780,10 @@ Plot_Cells_per_Sample <- function( plot <- plot + labs(fill = legend_title) } + if (length(x = unique(seurat_object[[group.by]])) == length(unique(seurat_object[[sample_col]]))) { + plot <- plot & NoLegend() + } + # Return plot return(plot) } From 10e2ba35edd0162b457843edd92e020d8eafd84b Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:11:58 -0500 Subject: [PATCH 04/74] update manual --- R/Plotting_Statistics.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index c699506f5..3cf0dfaf9 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -645,6 +645,9 @@ Plot_Median_Other <- function( #' @param x_axis_label Label for x axis. #' @param legend_title Label for plot legend. #' @param x_lab_rotate logical. Whether to rotate the axes labels on the x-axis. Default is FALSE. +#' @param reorder logical, if plotting by sample should x-axis be reordered according to number of cells, +#' default is FALSE. +#' @param plot_median logical, if plotting by sample should line be added showing median, default is TRUE. #' @param color_seed random seed for the "varibow" palette shuffle if `colors_use = NULL` and number of #' groups plotted is greater than 36. Default = 123. #' @@ -756,6 +759,9 @@ Plot_Cells_per_Sample <- function( plot <- plot + geom_hline(yintercept = median(merged[["Number_of_Cells"]])) } } else { + if (isTRUE(x = reorder) || isFALSE(x = plot_median)) { + cli_warn(message = "The {.code reorder} and {.code plot_median} parameters were ignored as they are only valid if samples are not grouped by additional variable.") + } # Generate base plot plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + From 927c1510ae7641aa5db8e31ee54badeaf1cc828e Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:12:46 -0500 Subject: [PATCH 05/74] Update changelog --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 4fff5eae9..c39d3a97e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # scCustomize 3.X.X (202X-XX-XX) ## Added - +- Added `reorder` and `plot_median` parameters to `Plot_Cells_per_Sample` when grouping by sample. From 40f2973f5041dc4b3b8a53fecd5ef7b83215de13 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:20:14 -0500 Subject: [PATCH 06/74] add reorder_decreasing param --- R/Plotting_Statistics.R | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index 3cf0dfaf9..52377bfc0 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -647,6 +647,8 @@ Plot_Median_Other <- function( #' @param x_lab_rotate logical. Whether to rotate the axes labels on the x-axis. Default is FALSE. #' @param reorder logical, if plotting by sample should x-axis be reordered according to number of cells, #' default is FALSE. +#' @param reorder_decreasing logical, if `reorder = TRUE` should points be ordered from fewest to +#' greatest number of cells (FALSE) or reverse (TRUE), default is FALSE. #' @param plot_median logical, if plotting by sample should line be added showing median, default is TRUE. #' @param color_seed random seed for the "varibow" palette shuffle if `colors_use = NULL` and number of #' groups plotted is greater than 36. Default = 123. @@ -664,7 +666,15 @@ Plot_Median_Other <- function( #' #' @examples #' \dontrun{ -#' Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "Treatment") +#' # Plot cells per sample grouped by "Diagnosis" +#' Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "Diagnosis") +#' +#' # Plot cells per sample individually +#' Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "orig.ident") +#' +#' #' # Plot cells per sample individually and reorder x-axis by cell number +#' Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "orig.ident", order = TRUE) +#' #' } #' @@ -680,6 +690,7 @@ Plot_Cells_per_Sample <- function( legend_title = NULL, x_lab_rotate = TRUE, reorder = FALSE, + reorder_decreasing = FALSE, plot_median = TRUE, color_seed = 123 ) { @@ -746,7 +757,7 @@ Plot_Cells_per_Sample <- function( ylab(y_axis_label) + xlab("") } else { - plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]]), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + + plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]], decreasing = decreasing), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + From b9d1beaf2ebaa537551ef64384cdbde7f54d7ad9 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:20:39 -0500 Subject: [PATCH 07/74] reorder decreasing --- R/Plotting_Statistics.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index 52377bfc0..33dc9d108 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -757,7 +757,7 @@ Plot_Cells_per_Sample <- function( ylab(y_axis_label) + xlab("") } else { - plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]], decreasing = decreasing), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + + plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]], decreasing = reorder_decreasing), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + From ec53ff43b54ae4ceb047a2b4c7209ff81d1c6d3c Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:21:08 -0500 Subject: [PATCH 08/74] Update changelog --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c39d3a97e..d2c0deb88 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # scCustomize 3.X.X (202X-XX-XX) ## Added -- Added `reorder` and `plot_median` parameters to `Plot_Cells_per_Sample` when grouping by sample. +- Added `reorder`, `reorder_decreasing`, and `plot_median` parameters to `Plot_Cells_per_Sample` when grouping by sample. From 4e2e98aecddca18bdac9112392dd4e808eab80bd Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:22:19 -0500 Subject: [PATCH 09/74] Update docs --- man/Add_CellBender_Diff.Rd | 2 +- man/Add_Cell_Complexity.Rd | 2 +- man/Add_Cell_QC_Metrics.Rd | 2 +- man/Add_Hemo.Rd | 2 +- man/Add_MALAT1_Threshold.Rd | 2 +- man/Add_Mito_Ribo.Rd | 2 +- man/Add_Top_Gene_Pct.Rd | 2 +- man/Plot_Cells_per_Sample.Rd | 21 ++++++++++++++++++++- man/exAM_Scoring.Rd | 2 +- 9 files changed, 28 insertions(+), 9 deletions(-) diff --git a/man/Add_CellBender_Diff.Rd b/man/Add_CellBender_Diff.Rd index fe93c7843..19639266d 100644 --- a/man/Add_CellBender_Diff.Rd +++ b/man/Add_CellBender_Diff.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/QC_Utilities_Seurat.R +% Please edit documentation in R/Utilities_QC_Seurat.R \name{Add_CellBender_Diff} \alias{Add_CellBender_Diff} \title{Calculate and add differences post-cell bender analysis} diff --git a/man/Add_Cell_Complexity.Rd b/man/Add_Cell_Complexity.Rd index a905db38c..cdc3d181a 100644 --- a/man/Add_Cell_Complexity.Rd +++ b/man/Add_Cell_Complexity.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Generics.R, R/LIGER_Utilities.R, -% R/QC_Utilities_Seurat.R +% R/Utilities_QC_Seurat.R \name{Add_Cell_Complexity} \alias{Add_Cell_Complexity} \alias{Add_Cell_Complexity.liger} diff --git a/man/Add_Cell_QC_Metrics.Rd b/man/Add_Cell_QC_Metrics.Rd index f72a93b05..d21756750 100644 --- a/man/Add_Cell_QC_Metrics.Rd +++ b/man/Add_Cell_QC_Metrics.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Generics.R, R/LIGER_Utilities.R, -% R/QC_Utilities_Seurat.R +% R/Utilities_QC_Seurat.R \name{Add_Cell_QC_Metrics} \alias{Add_Cell_QC_Metrics} \alias{Add_Cell_QC_Metrics.liger} diff --git a/man/Add_Hemo.Rd b/man/Add_Hemo.Rd index ccb3897b0..f824c84d2 100644 --- a/man/Add_Hemo.Rd +++ b/man/Add_Hemo.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Generics.R, R/LIGER_Utilities.R, -% R/QC_Utilities_Seurat.R +% R/Utilities_QC_Seurat.R \name{Add_Hemo} \alias{Add_Hemo} \alias{Add_Hemo.liger} diff --git a/man/Add_MALAT1_Threshold.Rd b/man/Add_MALAT1_Threshold.Rd index 98db74ab9..169d3468f 100644 --- a/man/Add_MALAT1_Threshold.Rd +++ b/man/Add_MALAT1_Threshold.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/Generics.R, R/QC_Utilities_Seurat.R +% Please edit documentation in R/Generics.R, R/Utilities_QC_Seurat.R \name{Add_MALAT1_Threshold} \alias{Add_MALAT1_Threshold} \alias{Add_MALAT1_Threshold.Seurat} diff --git a/man/Add_Mito_Ribo.Rd b/man/Add_Mito_Ribo.Rd index 184ebdbc8..8d8e6f092 100644 --- a/man/Add_Mito_Ribo.Rd +++ b/man/Add_Mito_Ribo.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Generics.R, R/LIGER_Utilities.R, -% R/QC_Utilities_Seurat.R +% R/Utilities_QC_Seurat.R \name{Add_Mito_Ribo} \alias{Add_Mito_Ribo} \alias{Add_Mito_Ribo.liger} diff --git a/man/Add_Top_Gene_Pct.Rd b/man/Add_Top_Gene_Pct.Rd index a1a93a5e5..0334c2fe2 100644 --- a/man/Add_Top_Gene_Pct.Rd +++ b/man/Add_Top_Gene_Pct.Rd @@ -1,6 +1,6 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/Generics.R, R/LIGER_Utilities.R, -% R/QC_Utilities_Seurat.R +% R/Utilities_QC_Seurat.R \name{Add_Top_Gene_Pct} \alias{Add_Top_Gene_Pct} \alias{Add_Top_Gene_Pct.liger} diff --git a/man/Plot_Cells_per_Sample.Rd b/man/Plot_Cells_per_Sample.Rd index 498f0e4d6..e45c2a1c9 100644 --- a/man/Plot_Cells_per_Sample.Rd +++ b/man/Plot_Cells_per_Sample.Rd @@ -15,6 +15,9 @@ Plot_Cells_per_Sample( x_axis_label = NULL, legend_title = NULL, x_lab_rotate = TRUE, + reorder = FALSE, + reorder_decreasing = FALSE, + plot_median = TRUE, color_seed = 123 ) } @@ -39,6 +42,14 @@ Plot_Cells_per_Sample( \item{x_lab_rotate}{logical. Whether to rotate the axes labels on the x-axis. Default is FALSE.} +\item{reorder}{logical, if plotting by sample should x-axis be reordered according to number of cells, +default is FALSE.} + +\item{reorder_decreasing}{logical, if \code{reorder = TRUE} should points be ordered from fewest to +greatest number of cells (FALSE) or reverse (TRUE), default is FALSE.} + +\item{plot_median}{logical, if plotting by sample should line be added showing median, default is TRUE.} + \item{color_seed}{random seed for the "varibow" palette shuffle if \code{colors_use = NULL} and number of groups plotted is greater than 36. Default = 123.} } @@ -50,7 +61,15 @@ Plot of total cell or nuclei number per sample grouped by another meta data vari } \examples{ \dontrun{ -Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "Treatment") +# Plot cells per sample grouped by "Diagnosis" +Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "Diagnosis") + +# Plot cells per sample individually +Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "orig.ident") + +#' # Plot cells per sample individually and reorder x-axis by cell number +Plot_Cells_per_Sample(seurat_object = obj, sample_col = "orig.ident", group.by = "orig.ident", order = TRUE) + } } diff --git a/man/exAM_Scoring.Rd b/man/exAM_Scoring.Rd index ad339f55b..50c9dac14 100644 --- a/man/exAM_Scoring.Rd +++ b/man/exAM_Scoring.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/QC_Utilities_Seurat.R +% Please edit documentation in R/Utilities_QC_Seurat.R \name{exAM_Scoring} \alias{exAM_Scoring} \title{Add exAM Gene List Module Scores} From 378aacbdcf818116ebf56f629880f7fa0ef013d2 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:22:32 -0500 Subject: [PATCH 10/74] bump version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9d3230915..efa9e1693 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0001 +Version: 3.2.4.0002 Date: 2025-12-12 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), From e52b90a8634c31bc68ab0949e4cca467ec807b84 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:25:14 -0500 Subject: [PATCH 11/74] check fix --- R/Plotting_Statistics.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index 33dc9d108..e3764607d 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -770,7 +770,7 @@ Plot_Cells_per_Sample <- function( plot <- plot + geom_hline(yintercept = median(merged[["Number_of_Cells"]])) } } else { - if (isTRUE(x = reorder) || isFALSE(x = plot_median)) { + if (isTRUE(x = reorder)) { cli_warn(message = "The {.code reorder} and {.code plot_median} parameters were ignored as they are only valid if samples are not grouped by additional variable.") } # Generate base plot From 5aa1aea980ba8207763a50ca8995b776d07b17f2 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:49:01 -0500 Subject: [PATCH 12/74] fix warning checks and add binwidth when by sample --- R/Plotting_Statistics.R | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/R/Plotting_Statistics.R b/R/Plotting_Statistics.R index e3764607d..1a28e3704 100644 --- a/R/Plotting_Statistics.R +++ b/R/Plotting_Statistics.R @@ -746,11 +746,13 @@ Plot_Cells_per_Sample <- function( } } - if (length(x = unique(seurat_object[[group.by]])) == length(unique(seurat_object[[sample_col]]))) { + if (nrow(x = unique(x = seurat_object[[group.by]])) == nrow(x = unique(x = seurat_object[[sample_col]]))) { + # create sample binwidth to avoid printing irrelevent warning + sample_binwidth <- (max(merged[["Number_of_Cells"]]) - min(merged[["Number_of_Cells"]])) * 1/30 + if (isFALSE(x = reorder)) { plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + - geom_boxplot(fill = "white") + - geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size, binwidth = sample_binwidth) + scale_fill_manual(values = colors_use) + theme_ggprism_mod() + ggtitle(plot_title) + @@ -758,8 +760,7 @@ Plot_Cells_per_Sample <- function( xlab("") } else { plot <- ggplot(data = merged, mapping = aes(x = reorder(.data[[group.by]], .data[["Number_of_Cells"]], decreasing = reorder_decreasing), y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + - geom_boxplot(fill = "white") + - geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size, binwidth = sample_binwidth) + scale_fill_manual(values = colors_use) + theme_ggprism_mod() + ggtitle(plot_title) + @@ -770,8 +771,8 @@ Plot_Cells_per_Sample <- function( plot <- plot + geom_hline(yintercept = median(merged[["Number_of_Cells"]])) } } else { - if (isTRUE(x = reorder)) { - cli_warn(message = "The {.code reorder} and {.code plot_median} parameters were ignored as they are only valid if samples are not grouped by additional variable.") + if (isTRUE(x = reorder) || isFALSE(x = plot_median) || isTRUE(x = reorder_decreasing)) { + cli_warn(message = "The {.code reorder}, {.code plot_median}, and {.code reorder_decreasing} parameters were ignored as they are only valid if samples are not grouped by additional variable.") } # Generate base plot plot <- ggplot(data = merged, mapping = aes(x = .data[[group.by]], y = .data[["Number_of_Cells"]], fill = .data[[group.by]])) + From db9de0338a175fa9ecd06932e880e668e0354365 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:27:08 -0500 Subject: [PATCH 13/74] Add velocyto loom read functions --- R/Read_Write_Data.R | 81 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 131ad7d86..1b1aae099 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1534,6 +1534,87 @@ Read_CellBender_h5_Multi_File <- function( } + +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +#################### READ loom DATA #################### +#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +#' Read Loom Files +#' +#' Internalfunction to read velocyto loom files using hdf5r package. +#' +#' @param loom_file loom file to read +#' +#' @return list of sparse matrices containing spliced, unspliced, and ambiguous counts +#' @noRd +#' @keywords internal +#' +#' @references Function is updated from velocyto.R `read.loom.matrices` \url{https://github.com/velocyto-team/velocyto.R}. +#' Function included in scCustomize to avoid installation issues related to other aspects of +#' velocyto.R package. +#' + +ReadLoomMatrices <- function( + loom_file +) { + # Check hdf5r installed + hdf5r_check <- is_installed(pkg = "hdf5r") + if (isFALSE(x = hdf5r_check)) { + cli_abort(message = c( + "Please install the {.val hdf5r} package to use {.code Read_CellBender_h5_Mat} and read HDF5 files.", + "i" = "This can be accomplished with the following commands: ", + "----------------------------------------", + "{.field `install.packages({symbol$dquote_left}hdf5r{symbol$dquote_right})`}", + "----------------------------------------" + )) + } + + f <- hdf5r::H5File$new(loom_file, mode='r') + cells <- f[["col_attrs/CellID"]][] + genes <- f[["row_attrs/Gene"]][] + dl <- c(spliced="layers/spliced", + unspliced="layers/unspliced", + ambiguous="layers/ambiguous") + if("layers/spanning" %in% hdf5r::list.datasets(f)) { + dl <- c(dl, c(spanning="layers/spanning")) + } + + dlist <- lapply(dl, function(path) { + m <- as.sparse(t(f[[path]][,])) + rownames(m) <- genes; colnames(m) <- cells; + return(m) + }) + f$close_all() + return(dlist) +} + + +#' Load RNA Velocity results +#' +#' Wrapper function to +#' +#' @param loom_file +#' +#' @return list of sparse matrices containing spliced, unspliced, and ambiguous counts +#' +#' @export +#' +#' @examples +#' \dontrun{ +#' velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom") +#' } + +Read_Velocity <- function( + loom_file +) { + invisible(x = capture.output(ldat <- ReadLoomMatrices( + loom_file = loom_file, + ))) + return(ldat) +} + + #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% #################### READ OTHER DATA #################### #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 37f41ebb45ea9e563f3560ebe0e18c117fd65681 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:27:15 -0500 Subject: [PATCH 14/74] Update changelog --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index d2c0deb88..640f00397 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # scCustomize 3.X.X (202X-XX-XX) ## Added - Added `reorder`, `reorder_decreasing`, and `plot_median` parameters to `Plot_Cells_per_Sample` when grouping by sample. +- Added `Read_Velocity` function to enable reading of velocyto loom output files without needing to install velocyto.R (which currently suffers from significant installation issues unrelated to reading loom files). From 68fcaaedd94862484ff31aef19eb53e2126f5343 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:27:27 -0500 Subject: [PATCH 15/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index efa9e1693..f264f30d0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0002 -Date: 2025-12-12 +Version: 3.2.4.0003 +Date: 2026-01-07 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From 4f8fdd6aa77c0c7e381e1bf0861a2a39dd4c6ada Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:31:45 -0500 Subject: [PATCH 16/74] Update manual --- R/Read_Write_Data.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 1b1aae099..aafeaa0a3 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1600,6 +1600,12 @@ ReadLoomMatrices <- function( #' #' @export #' +#' @references Function is updated from `SeuratWrappers::ReadVelocity` +#' \url{https://github.com/satijalab/seurat-wrappers/blob/master/R/velocity.R} (License: GPL-3). +#' Function included in scCustomize to avoid installation issues related to other aspects of +#' velocyto.R package which `SeuratWrappers::ReadVelocity` requires. +#' +#' #' @examples #' \dontrun{ #' velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom") From 1ea6cd036efd9a5ed2d7dfa5978111185407bdb3 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:32:06 -0500 Subject: [PATCH 17/74] Update docs --- NAMESPACE | 1 + man/Read_Velocity.Rd | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 man/Read_Velocity.Rd diff --git a/NAMESPACE b/NAMESPACE index fc0717b1e..45b74d9bc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -144,6 +144,7 @@ export(Read_CellBender_h5_Multi_File) export(Read_GEO_Delim) export(Read_Metrics_10X) export(Read_Metrics_CellBender) +export(Read_Velocity) export(Reduction_Loading_Present) export(Rename_Clusters) export(Replace_Suffix) diff --git a/man/Read_Velocity.Rd b/man/Read_Velocity.Rd new file mode 100644 index 000000000..31aec9636 --- /dev/null +++ b/man/Read_Velocity.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Read_Write_Data.R +\name{Read_Velocity} +\alias{Read_Velocity} +\title{Load RNA Velocity results} +\usage{ +Read_Velocity(loom_file) +} +\arguments{ +\item{loom_file}{} +} +\value{ +list of sparse matrices containing spliced, unspliced, and ambiguous counts +} +\description{ +Wrapper function to +} +\examples{ +\dontrun{ +velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom") +} +} +\references{ +Function is updated from \code{SeuratWrappers::ReadVelocity} +\url{https://github.com/satijalab/seurat-wrappers/blob/master/R/velocity.R} (License: GPL-3). +Function included in scCustomize to avoid installation issues related to other aspects of +velocyto.R package which \code{SeuratWrappers::ReadVelocity} requires. +} From 88d2cc08ee4fac30139b25a45b96805085cc7cb8 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:39:20 -0500 Subject: [PATCH 18/74] typo fix --- R/Read_Write_Data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index aafeaa0a3..7f0c62cc4 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1615,7 +1615,7 @@ Read_Velocity <- function( loom_file ) { invisible(x = capture.output(ldat <- ReadLoomMatrices( - loom_file = loom_file, + loom_file = loom_file ))) return(ldat) } From 0eac3660e00af8ad6a66cd8187d905fcf9377739 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 9 Jan 2026 12:31:32 -0500 Subject: [PATCH 19/74] Add unique naming to match Read10X functionality --- R/Read_Write_Data.R | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 7f0c62cc4..99d633a65 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1556,7 +1556,8 @@ Read_CellBender_h5_Multi_File <- function( #' ReadLoomMatrices <- function( - loom_file + loom_file, + gene_symbol = TRUE ) { # Check hdf5r installed hdf5r_check <- is_installed(pkg = "hdf5r") @@ -1572,7 +1573,12 @@ ReadLoomMatrices <- function( f <- hdf5r::H5File$new(loom_file, mode='r') cells <- f[["col_attrs/CellID"]][] - genes <- f[["row_attrs/Gene"]][] + if (isTRUE(x = gene_symbol)) { + genes <- f[["row_attrs/Gene"]][] + } else { + genes <- f[["row_attrs/Accession"]][] + } + dl <- c(spliced="layers/spliced", unspliced="layers/unspliced", ambiguous="layers/ambiguous") @@ -1582,10 +1588,20 @@ ReadLoomMatrices <- function( dlist <- lapply(dl, function(path) { m <- as.sparse(t(f[[path]][,])) - rownames(m) <- genes; colnames(m) <- cells; + rownames(x = m) <- genes; colnames(x = m) <- cells; return(m) }) f$close_all() + + # check unique and edit gene names/ids if needed + num_dup <- sum(duplicated(x = rownames(x = dlist[["spliced"]]))) + + if (num_dup > 0) { + rownames(dlist[["spliced"]]) <- make.unique(names = rownames(dlist[["spliced"]])) + rownames(dlist[["unspliced"]]) <- make.unique(names = rownames(dlist[["unspliced"]])) + rownames(dlist[["ambiguous"]]) <- make.unique(names = rownames(dlist[["ambiguous"]])) + } + return(dlist) } From 2446ba50d6df545b977b0a5973cd589809fa7c0e Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 9 Jan 2026 12:41:36 -0500 Subject: [PATCH 20/74] pass gene symbol --- R/Read_Write_Data.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 99d633a65..65fdb0c39 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1597,6 +1597,8 @@ ReadLoomMatrices <- function( num_dup <- sum(duplicated(x = rownames(x = dlist[["spliced"]]))) if (num_dup > 0) { + cli_inform(message = c("A total of {.field {num_dup}} duplicate row names were found.", + "i" = "Making unique with {.code make.unique()}.")) rownames(dlist[["spliced"]]) <- make.unique(names = rownames(dlist[["spliced"]])) rownames(dlist[["unspliced"]]) <- make.unique(names = rownames(dlist[["unspliced"]])) rownames(dlist[["ambiguous"]]) <- make.unique(names = rownames(dlist[["ambiguous"]])) @@ -1628,10 +1630,12 @@ ReadLoomMatrices <- function( #' } Read_Velocity <- function( - loom_file + loom_file, + gene_symbol = TRUE ) { invisible(x = capture.output(ldat <- ReadLoomMatrices( - loom_file = loom_file + loom_file = loom_file, + gene_symbol = gene_symbol ))) return(ldat) } From fe5f1fd13f8ff0c9256a9ebbdddecb6a2f60a212 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:45:41 -0500 Subject: [PATCH 21/74] Updating Velocity functions --- R/Read_Write_Data.R | 178 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 169 insertions(+), 9 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 65fdb0c39..179260085 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1557,7 +1557,8 @@ Read_CellBender_h5_Multi_File <- function( ReadLoomMatrices <- function( loom_file, - gene_symbol = TRUE + gene_symbol = TRUE, + verbose = TRUE ) { # Check hdf5r installed hdf5r_check <- is_installed(pkg = "hdf5r") @@ -1597,8 +1598,11 @@ ReadLoomMatrices <- function( num_dup <- sum(duplicated(x = rownames(x = dlist[["spliced"]]))) if (num_dup > 0) { - cli_inform(message = c("A total of {.field {num_dup}} duplicate row names were found.", - "i" = "Making unique with {.code make.unique()}.")) + if (isTRUE(x = verbose)) { + cli_inform(message = c("A total of {.field {num_dup}} duplicate row names were found.", + "i" = "Making unique with {.code make.unique()}.")) + } + rownames(dlist[["spliced"]]) <- make.unique(names = rownames(dlist[["spliced"]])) rownames(dlist[["unspliced"]]) <- make.unique(names = rownames(dlist[["unspliced"]])) rownames(dlist[["ambiguous"]]) <- make.unique(names = rownames(dlist[["ambiguous"]])) @@ -1608,11 +1612,141 @@ ReadLoomMatrices <- function( } + +#' Read multiple loom files from same directory +#' +#' Internal wrapper to be used be `Read_Velocity` if data directory is provided. +#' +#' @param data_dir Directory containing the loom files provided by velocyto. +#' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, +#' default is TRUE (symbols). +#' @param sample_list A vector of file prefixes/names if specific samples are desired. Default is `NULL` and +#' will load all samples in given directory. +#' @param sample_names a set of sample names to use for each sample entry in returned list. If `NULL` +#' will set names to the file name of each sample. +#' @param shared_suffix a suffix and file extension shared by all samples. +#' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. +#' Only possible on Linux based systems. +#' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. +#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each +#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample +#' +#' @returns +#' @noRd +#' @keywords internal +#' + +ReadVelocity_Multi_File_Internal <- function( + data_dir = NULL, + gene_symbol = TRUE, + sample_list = NULL, + sample_names = NULL, + shared_suffix = NULL, + parallel = FALSE, + num_cores = NULL, + sort_type = FALSE +) { + if (!dir.exists(paths = data_dir)) { + cli_abort(message = "Directory provided does not exist") + } + if (length(x = data_dir) > 1) { + cli_abort(message = "{.code Read_Velocity} only supports reading from single data directory at a time.") + } + + # Confirm num_cores specified + if (isTRUE(x = parallel) && is.null(x = num_cores)) { + cli_abort("If {.code parallel = TRUE} then {.code num_cores} must be specified.") + } + + file.list <- list.files(path = data_dir, pattern = ".loom", full.names = FALSE) + + # Remove file suffix if provided + if (!is.null(x = shared_suffix)) { + shared_suffix <- gsub(pattern = ".loom", replacement = "", x = shared_suffix) + } + + if (is.null(x = sample_list)) { + if (is.null(x = shared_suffix)) { + sample_list <- gsub(pattern = ".loom", x = file.list, replacement = "") + } else { + sample_list <- gsub(pattern = paste0(shared_suffix, ".loom"), x = file.list, replacement = "") + } + } + + # Check sample_names length is ok + if (!is.null(x = sample_names) && length(x = sample_names) != length(x = sample_list)) { + cli_abort(message = "Length of {.code sample_names} {.field {length(x = sample_names)}} must be equal to number of samples {.field {length(x = sample_list)}}.") + } + + cli_inform(message = "{.field Reading Loom files from directory}") + pboptions(char = "=") + if (isTRUE(x = parallel)) { + cli_inform(message = c("NOTE: Parallel processing may not report informative error messages.", + "i" = "If function fails set {.code parallel = FALSE} and re-run for informative error reporting.")) + raw_data_list <- pmclapply(mc.cores = num_cores, 1:length(x = sample_list), function(i) { + loom_loc <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) + data <- Read_Velocity(loom_file = loom_loc, gene_symbol = gene_symbol) + }) + } else { + raw_data_list <- pblapply(1:length(x = sample_list), function(i) { + loom_loc <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) + data <- Read_Velocity(loom_file = loom_loc, gene_symbol = gene_symbol) + }) + } + + # Name the matrices + if (is.null(x = sample_names)) { + names(x = raw_data_list) <- sample_list + } else { + names(x = raw_data_list) <- sample_names + } + + if (isTRUE(x = sort_type)) { + spliced_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["spliced"]] + }) + names(x = spliced_list) <- names(x = raw_data_list) + + unspliced_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["unspliced"]] + }) + names(x = unspliced_list) <- names(x = raw_data_list) + + ambiguous_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["ambiguous"]] + }) + names(x = ambiguous_list) <- names(x = raw_data_list) + + raw_data_list <- list("spliced" = spliced_list, + "unspliced" = unspliced_list, + "ambiguous" = ambiguous_list) + + } + + # return object + return(raw_data_list) +} + #' Load RNA Velocity results #' #' Wrapper function to #' -#' @param loom_file +#' @param loom_file path and name of loom file to read +#' @param data_dir path to data directory containing all loom files to read +#' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, +#' default is TRUE (symbols). +#' @param sample_list A vector of file prefixes/names if specific samples are desired. Default is `NULL` and +#' will load all samples in given directory. +#' @param sample_names a set of sample names to use for each sample entry in returned list. If `NULL` +#' will set names to the file name of each sample. +#' @param shared_suffix a suffix and file extension shared by all samples. +#' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. +#' Only possible on Linux based systems. +#' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. +#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each +#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' #' @return list of sparse matrices containing spliced, unspliced, and ambiguous counts #' @@ -1630,14 +1764,40 @@ ReadLoomMatrices <- function( #' } Read_Velocity <- function( - loom_file, - gene_symbol = TRUE + loom_file = NULL, + data_dir = NULL, + multi_dir = TRUE, + gene_symbol = TRUE, + sample_list = NULL, + sample_names = NULL, + shared_suffix = NULL, + parallel = FALSE, + num_cores = NULL, + sort_type = FALSE ) { - invisible(x = capture.output(ldat <- ReadLoomMatrices( + if (!is.null(x = loom_file) && !is.null(x = data_dir)) { + cli_abort(message = "Cannot specify values for both {.code loom_file} and {.code data_dir}.") + } + + if (!is.null(x = loom_file)) { + invisible(x = capture.output(data <- ReadLoomMatrices( loom_file = loom_file, - gene_symbol = gene_symbol + gene_symbol = gene_symbol, + verbose = TRUE ))) - return(ldat) + return(data) + } + # single data directory + if (isFALSE(x = multi_dir)) { + data_list <- ReadVelocity_Multi_File_Internal(data_dir = data_dir, gene_symbol = gene_symbol, sample_list = sample_list, sample_names = sample_names, shared_suffix = shared_suffix, parallel = parallel, num_cores = num_cores, sort_type = sort_type) + return(data_list) + } + + # multi directory + if (isTRUE(x = multi_dir)) { + data_list <- ReadVelocity_Multi_Dir_Internal(data_dir = data_dir, gene_symbol = gene_symbol, sample_list = sample_list, sample_names = sample_names, shared_suffix = shared_suffix, parallel = parallel, num_cores = num_cores, sort_type = sort_type) + return(data_list) + } } From d3cb6c9b5f068274a90483e73e9d1a3a6598a21e Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:47:29 -0500 Subject: [PATCH 22/74] Update docs --- man/Read_Velocity.Rd | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/man/Read_Velocity.Rd b/man/Read_Velocity.Rd index 31aec9636..401bab99c 100644 --- a/man/Read_Velocity.Rd +++ b/man/Read_Velocity.Rd @@ -4,10 +4,43 @@ \alias{Read_Velocity} \title{Load RNA Velocity results} \usage{ -Read_Velocity(loom_file) +Read_Velocity( + loom_file = NULL, + data_dir = NULL, + multi_dir = TRUE, + gene_symbol = TRUE, + sample_list = NULL, + sample_names = NULL, + shared_suffix = NULL, + parallel = FALSE, + num_cores = NULL, + sort_type = FALSE +) } \arguments{ -\item{loom_file}{} +\item{loom_file}{path and name of loom file to read} + +\item{data_dir}{path to data directory containing all loom files to read} + +\item{gene_symbol}{logical, should rownames of returned matrices have gene symbols or accession ID #s, +default is TRUE (symbols).} + +\item{sample_list}{A vector of file prefixes/names if specific samples are desired. Default is \code{NULL} and +will load all samples in given directory.} + +\item{sample_names}{a set of sample names to use for each sample entry in returned list. If \code{NULL} +will set names to the file name of each sample.} + +\item{shared_suffix}{a suffix and file extension shared by all samples.} + +\item{parallel}{logical (default FALSE). Whether to use multiple cores when reading in data. +Only possible on Linux based systems.} + +\item{num_cores}{if \code{parallel = TRUE} indicates the number of cores to use for multicore processing.} + +\item{sort_type}{logical, default is FALSE and will return list with 1 sample per entry. Each +sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +3 matrix types (spliced, unspliced, ambiguous) with one entry per sample} } \value{ list of sparse matrices containing spliced, unspliced, and ambiguous counts From 8fa7493e585dfac10cd72754930b13a5a7ce972f Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:47:36 -0500 Subject: [PATCH 23/74] Update manual --- R/Read_Write_Data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 179260085..e9f938849 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1632,7 +1632,7 @@ ReadLoomMatrices <- function( #' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of #' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' -#' @returns +#' @returns list of list of matrices #' @noRd #' @keywords internal #' From 03e01184c1eb6249c47cc70da4efb174cee4122d Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:34:52 -0500 Subject: [PATCH 24/74] rearrange parameters --- R/Read_Write_Data.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index e9f938849..43f238474 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1702,6 +1702,7 @@ ReadVelocity_Multi_File_Internal <- function( names(x = raw_data_list) <- sample_names } + # sort matrices by read type if (isTRUE(x = sort_type)) { spliced_list <- lapply(1:length(x = raw_data_list), function(x){ data <- testing[[x]][["spliced"]] @@ -1721,7 +1722,6 @@ ReadVelocity_Multi_File_Internal <- function( raw_data_list <- list("spliced" = spliced_list, "unspliced" = unspliced_list, "ambiguous" = ambiguous_list) - } # return object @@ -1736,6 +1736,9 @@ ReadVelocity_Multi_File_Internal <- function( #' @param data_dir path to data directory containing all loom files to read #' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, #' default is TRUE (symbols). +#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each +#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' @param sample_list A vector of file prefixes/names if specific samples are desired. Default is `NULL` and #' will load all samples in given directory. #' @param sample_names a set of sample names to use for each sample entry in returned list. If `NULL` @@ -1744,9 +1747,6 @@ ReadVelocity_Multi_File_Internal <- function( #' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. #' Only possible on Linux based systems. #' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. -#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each -#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of -#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' #' @return list of sparse matrices containing spliced, unspliced, and ambiguous counts #' @@ -1768,12 +1768,12 @@ Read_Velocity <- function( data_dir = NULL, multi_dir = TRUE, gene_symbol = TRUE, + sort_type = FALSE, sample_list = NULL, sample_names = NULL, shared_suffix = NULL, parallel = FALSE, - num_cores = NULL, - sort_type = FALSE + num_cores = NULL ) { if (!is.null(x = loom_file) && !is.null(x = data_dir)) { cli_abort(message = "Cannot specify values for both {.code loom_file} and {.code data_dir}.") From 9aed30a5f9b586de8215ab7df03affeab9cd2383 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 08:45:10 -0500 Subject: [PATCH 25/74] Update to use internal and add single duplicate warning when mutli-file --- R/Read_Write_Data.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 43f238474..bf687c0da 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1686,15 +1686,19 @@ ReadVelocity_Multi_File_Internal <- function( "i" = "If function fails set {.code parallel = FALSE} and re-run for informative error reporting.")) raw_data_list <- pmclapply(mc.cores = num_cores, 1:length(x = sample_list), function(i) { loom_loc <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) - data <- Read_Velocity(loom_file = loom_loc, gene_symbol = gene_symbol) + data <- ReadLoomMatrices(loom_file = loom_loc, gene_symbol = gene_symbol, verbose = FALSE) }) } else { raw_data_list <- pblapply(1:length(x = sample_list), function(i) { loom_loc <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) - data <- Read_Velocity(loom_file = loom_loc, gene_symbol = gene_symbol) + data <- ReadLoomMatrices(loom_file = loom_loc, gene_symbol = gene_symbol, verbose = FALSE) }) } + # add call to report duplicates single time + reporter_file_path <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) + reporter_data <- scCustomize:::ReadLoomMatrices(loom_file = reporter_file_path, gene_symbol = gene_symbol, verbose = TRUE) + # Name the matrices if (is.null(x = sample_names)) { names(x = raw_data_list) <- sample_list From 6f12dc0be2976344c36a539e494e3ff4329096e5 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:00:03 -0500 Subject: [PATCH 26/74] add multi dir internal --- R/Read_Write_Data.R | 159 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 147 insertions(+), 12 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index bf687c0da..ea893d22d 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -502,8 +502,6 @@ Read10X_GEO <- function( #' @import mcprogress #' @import parallel #' @import pbapply -#' @importFrom Matrix readMM -#' @importFrom utils read.delim txtProgressBar setTxtProgressBar #' #' @export #' @@ -767,7 +765,6 @@ Read10X_Multi_Directory <- function( #' @import pbapply #' @importFrom Seurat Read10X_h5 #' @importFrom stringr str_extract -#' @importFrom utils txtProgressBar setTxtProgressBar #' #' @export #' @@ -1612,7 +1609,6 @@ ReadLoomMatrices <- function( } - #' Read multiple loom files from same directory #' #' Internal wrapper to be used be `Read_Velocity` if data directory is provided. @@ -1620,6 +1616,9 @@ ReadLoomMatrices <- function( #' @param data_dir Directory containing the loom files provided by velocyto. #' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, #' default is TRUE (symbols). +#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each +#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' @param sample_list A vector of file prefixes/names if specific samples are desired. Default is `NULL` and #' will load all samples in given directory. #' @param sample_names a set of sample names to use for each sample entry in returned list. If `NULL` @@ -1628,24 +1627,25 @@ ReadLoomMatrices <- function( #' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. #' Only possible on Linux based systems. #' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. -#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each -#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of -#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample #' #' @returns list of list of matrices #' @noRd #' @keywords internal #' +#' @import mcprogress +#' @import parallel +#' @import pbapply +#' ReadVelocity_Multi_File_Internal <- function( data_dir = NULL, gene_symbol = TRUE, + sort_type = FALSE, sample_list = NULL, sample_names = NULL, shared_suffix = NULL, parallel = FALSE, - num_cores = NULL, - sort_type = FALSE + num_cores = NULL ) { if (!dir.exists(paths = data_dir)) { cli_abort(message = "Directory provided does not exist") @@ -1697,7 +1697,7 @@ ReadVelocity_Multi_File_Internal <- function( # add call to report duplicates single time reporter_file_path <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) - reporter_data <- scCustomize:::ReadLoomMatrices(loom_file = reporter_file_path, gene_symbol = gene_symbol, verbose = TRUE) + reporter_data <- ReadLoomMatrices(loom_file = reporter_file_path, gene_symbol = gene_symbol, verbose = TRUE) # Name the matrices if (is.null(x = sample_names)) { @@ -1732,13 +1732,132 @@ ReadVelocity_Multi_File_Internal <- function( return(raw_data_list) } + +#' Read loom files from multiple directories +#' +#' Wrapper to read loom files from multiple directory and return a list +#' +#' @param base_path path to the parent directory which contains all of the subdirectories of interest. +#' @param secondary_path path from the parent directory to loom files for each sample. +#' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, +#' default is TRUE (symbols). +#' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each +#' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +#' 3 matrix types (spliced, unspliced, ambiguous) with one entry per sample +#' @param sample_list A vector of file prefixes/names if specific samples are desired. Default is `NULL` and +#' will load all samples in given directory. +#' @param sample_names a set of sample names to use for each sample entry in returned list. If `NULL` +#' will set names to the file name of each sample. +#' @param shared_suffix a suffix and file extension shared by all samples. +#' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. +#' Only possible on Linux based systems. +#' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. +#' +#' @returns list of list of matrices +#' @noRd +#' @keywords internal +#' +#' @import mcprogress +#' @import parallel +#' @import pbapply +#' + +ReadVelocity_Multi_Directory <- function( + base_path, + secondary_path = NULL, + gene_symbol = TRUE, + sort_type = FALSE, + sample_list = NULL, + sample_names = NULL, + parallel = FALSE, + num_cores = NULL +) { + # Confirm num_cores specified + if (isTRUE(x = parallel) && is.null(x = num_cores)) { + cli_abort("If {.code parallel = TRUE} then {.code num_cores} must be specified.") + } + # Confirm directory exists + if (dir.exists(paths = base_path) == FALSE) { + cli_abort(message = "Directory: {.val {base_path}} specified by {.code base_path} does not exist.") + } + # Detect libraries if sample_list is NULL + if (is.null(x = sample_list)) { + sample_list <- Pull_Directory_List(base_path = base_path) + } + + # set "" if no secondary path + if (is.null(x = secondary_path)) { + secondary_path <- "" + } + # Check if full directory path exists + for (i in 1:length(x = sample_list)) { + full_directory_path <- file.path(base_path, sample_list[i], secondary_path) + if (dir.exists(paths = full_directory_path) == FALSE) { + cli_abort(message = "Full Directory does not exist {.val {full_directory_path}} was not found.") + } + } + + # read data + cli_inform(message = "{.field Reading Loom files.}") + if (isTRUE(x = parallel)) { + cli_inform(message = c("NOTE: Parallel processing may not report informative error messages.", + "i" = "If function fails set {.code parallel = FALSE} and re-run for informative error reporting.")) + # if parallel + raw_data_list <- pmclapply(mc.cores = num_cores, 1:length(x = sample_list), function(x) { + file_path <- file.path(base_path, sample_list[x], secondary_path, paste0(sample_list[x], ".loom")) + raw_data <- ReadLoomMatrices(loom_file = file_path, gene_symbol = gene_symbol, verbose = FALSE) + return(raw_data) + }) + } else { + raw_data_list <- pblapply(1:length(x = sample_list), function(x) { + file_path <- file.path(base_path, sample_list[x], secondary_path, paste0(sample_list[x], ".loom")) + raw_data <- ReadLoomMatrices(loom_file = file_path, gene_symbol = gene_symbol, verbose = FALSE) + }) + } + + # add call to report duplicates single time + reporter_file_path <- file.path(base_path, sample_list[1], secondary_path, paste0(sample_list[1], ".loom")) + reporter_data <- ReadLoomMatrices(loom_file = reporter_file_path, gene_symbol = gene_symbol, verbose = TRUE) + + # Name the list items + if (is.null(x = sample_names)) { + names(x = raw_data_list) <- sample_list + } else { + names(x = raw_data_list) <- sample_names + } + + # sort matrices by read type + if (isTRUE(x = sort_type)) { + spliced_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["spliced"]] + }) + names(x = spliced_list) <- names(x = raw_data_list) + + unspliced_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["unspliced"]] + }) + names(x = unspliced_list) <- names(x = raw_data_list) + + ambiguous_list <- lapply(1:length(x = raw_data_list), function(x){ + data <- testing[[x]][["ambiguous"]] + }) + names(x = ambiguous_list) <- names(x = raw_data_list) + + raw_data_list <- list("spliced" = spliced_list, + "unspliced" = unspliced_list, + "ambiguous" = ambiguous_list) + } + return(raw_data_list) +} + + #' Load RNA Velocity results #' #' Wrapper function to #' #' @param loom_file path and name of loom file to read #' @param data_dir path to data directory containing all loom files to read -#' @param gene_symbol logical, should rownames of returned matrices have gene symbols or accession ID #s, +#' @param gene_symbol logical, should row names of returned matrices have gene symbols or accession ID #s, #' default is TRUE (symbols). #' @param sort_type logical, default is FALSE and will return list with 1 sample per entry. Each #' sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of @@ -1750,7 +1869,7 @@ ReadVelocity_Multi_File_Internal <- function( #' @param shared_suffix a suffix and file extension shared by all samples. #' @param parallel logical (default FALSE). Whether to use multiple cores when reading in data. #' Only possible on Linux based systems. -#' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multicore processing. +#' @param num_cores if `parallel = TRUE` indicates the number of cores to use for multi-core processing. #' #' @return list of sparse matrices containing spliced, unspliced, and ambiguous counts #' @@ -1764,7 +1883,23 @@ ReadVelocity_Multi_File_Internal <- function( #' #' @examples #' \dontrun{ +#' # Read single velocity loom file #' velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom") +#' +#' # Read single velocity loom file with ensembl IDs +#' velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom", gene_symbol = FALSE) +#' +#' # Read multiple files from a parent directory with many subdirectories & return 1 list entry per sample +#' # Each entry will contain 3 matrices (spliced, unspliced, ambiguous) +#' velo_res_list <- Read_Velocity(loom_file = "PATH/", secondary_path = "SEC_PATH/") +#' +#' # Sort returned list by read type instead of by sample +#' # list will contain 3 entries (spliced, unspliced, ambiguous) +#' # each entry will contain 1 matrix per sample +#' velo_res_list <- Read_Velocity(loom_file = "PATH/", secondary_path = "SEC_PATH/", sort_type = TRUE) +#' +#' # Read multiple files from a single directory +#' velo_res_list <- Read_Velocity(loom_file = "PATH/", multi_dir = FALSE) #' } Read_Velocity <- function( From 30777b97a420ad736d5ecd1421cbfae360f50cf2 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:00:32 -0500 Subject: [PATCH 27/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f264f30d0..dbd3f91ef 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0003 -Date: 2026-01-07 +Version: 3.2.4.0004 +Date: 2026-01-13 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From ce9cfad5317f492e014e99fa0f80cf9b05ec3906 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:01:08 -0500 Subject: [PATCH 28/74] Update docs --- man/Read_Velocity.Rd | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/man/Read_Velocity.Rd b/man/Read_Velocity.Rd index 401bab99c..4dda22689 100644 --- a/man/Read_Velocity.Rd +++ b/man/Read_Velocity.Rd @@ -9,12 +9,12 @@ Read_Velocity( data_dir = NULL, multi_dir = TRUE, gene_symbol = TRUE, + sort_type = FALSE, sample_list = NULL, sample_names = NULL, shared_suffix = NULL, parallel = FALSE, - num_cores = NULL, - sort_type = FALSE + num_cores = NULL ) } \arguments{ @@ -22,9 +22,13 @@ Read_Velocity( \item{data_dir}{path to data directory containing all loom files to read} -\item{gene_symbol}{logical, should rownames of returned matrices have gene symbols or accession ID #s, +\item{gene_symbol}{logical, should row names of returned matrices have gene symbols or accession ID #s, default is TRUE (symbols).} +\item{sort_type}{logical, default is FALSE and will return list with 1 sample per entry. Each +sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of +3 matrix types (spliced, unspliced, ambiguous) with one entry per sample} + \item{sample_list}{A vector of file prefixes/names if specific samples are desired. Default is \code{NULL} and will load all samples in given directory.} @@ -36,11 +40,7 @@ will set names to the file name of each sample.} \item{parallel}{logical (default FALSE). Whether to use multiple cores when reading in data. Only possible on Linux based systems.} -\item{num_cores}{if \code{parallel = TRUE} indicates the number of cores to use for multicore processing.} - -\item{sort_type}{logical, default is FALSE and will return list with 1 sample per entry. Each -sample entry will contain 3 matrices (spliced, unspliced, ambiguous). If TRUE will return list of -3 matrix types (spliced, unspliced, ambiguous) with one entry per sample} +\item{num_cores}{if \code{parallel = TRUE} indicates the number of cores to use for multi-core processing.} } \value{ list of sparse matrices containing spliced, unspliced, and ambiguous counts @@ -50,7 +50,23 @@ Wrapper function to } \examples{ \dontrun{ +# Read single velocity loom file velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom") + +# Read single velocity loom file with ensembl IDs +velo_res <- Read_Velocity(loom_file = "PATH/sample01.loom", gene_symbol = FALSE) + +# Read multiple files from a parent directory with many subdirectories & return 1 list entry per sample +# Each entry will contain 3 matrices (spliced, unspliced, ambiguous) +velo_res_list <- Read_Velocity(loom_file = "PATH/", secondary_path = "SEC_PATH/") + +# Sort returned list by read type instead of by sample +# list will contain 3 entries (spliced, unspliced, ambiguous) +# each entry will contain 1 matrix per sample +velo_res_list <- Read_Velocity(loom_file = "PATH/", secondary_path = "SEC_PATH/", sort_type = TRUE) + +# Read multiple files from a single directory +velo_res_list <- Read_Velocity(loom_file = "PATH/", multi_dir = FALSE) } } \references{ From 524c97467621d54e8b3fe075ebb32a74fb17aa75 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:04:42 -0500 Subject: [PATCH 29/74] Update manual --- R/Read_Write_Data.R | 2 ++ man/Read_Velocity.Rd | 2 ++ 2 files changed, 4 insertions(+) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index ea893d22d..3eb1086ed 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1879,6 +1879,8 @@ ReadVelocity_Multi_Directory <- function( #' \url{https://github.com/satijalab/seurat-wrappers/blob/master/R/velocity.R} (License: GPL-3). #' Function included in scCustomize to avoid installation issues related to other aspects of #' velocyto.R package which `SeuratWrappers::ReadVelocity` requires. +#' scCustomize version also adds multi-file and multi-directory support, ability to return Ensembl IDs, +#' and multi-core parallel file reading. #' #' #' @examples diff --git a/man/Read_Velocity.Rd b/man/Read_Velocity.Rd index 4dda22689..b05b73491 100644 --- a/man/Read_Velocity.Rd +++ b/man/Read_Velocity.Rd @@ -74,4 +74,6 @@ Function is updated from \code{SeuratWrappers::ReadVelocity} \url{https://github.com/satijalab/seurat-wrappers/blob/master/R/velocity.R} (License: GPL-3). Function included in scCustomize to avoid installation issues related to other aspects of velocyto.R package which \code{SeuratWrappers::ReadVelocity} requires. +scCustomize version also adds multi-file and multi-directory support, ability to return Ensembl IDs, +and multi-core parallel file reading. } From 8b4f6e939259342c58c6bf823ef74c5f98555ed1 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 14 Jan 2026 08:40:49 -0500 Subject: [PATCH 30/74] remove old notes --- R/Read_Write_Data.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 3eb1086ed..4e8429e33 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -689,7 +689,7 @@ Read10X_Multi_Directory <- function( if (isTRUE(x = parallel)) { cli_inform(message = c("NOTE: Parallel processing may not report informative error messages.", "i" = "If function fails set {.code parallel = FALSE} and re-run for informative error reporting.")) - # *** Here is where the swap of mclapply or pbmclapply is occuring *** + raw_data_list <- pmclapply(mc.cores = num_cores, 1:length(x = sample_list), function(x) { if (isTRUE(x = cellranger_multi)) { file_path <- file.path(base_path, sample_list[x], secondary_path, sample_list[x], multi_extra_path) @@ -839,7 +839,7 @@ Read10X_h5_Multi_Directory <- function( if (isTRUE(x = parallel)) { cli_inform(message = c("NOTE: Parallel processing may not report informative error messages.", "i" = "If function fails set {.code parallel = FALSE} and re-run for informative error reporting.")) - # *** Here is where the swap of mclapply or pbmclapply is occuring *** + raw_data_list <- pmclapply(mc.cores = num_cores, 1:length(x = sample_list), function(x) { if (isTRUE(x = cellranger_multi)) { file_path <- file.path(base_path, sample_list[x], secondary_path, sample_list[x], multi_extra_path, h5_filename) From 91c79cf33e656fd12ef04e55e62ab869b43fb962 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 16 Jan 2026 08:24:25 -0500 Subject: [PATCH 31/74] styling --- R/Internal_Utilities.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/Internal_Utilities.R b/R/Internal_Utilities.R index 7c3a2250e..f06b2e476 100644 --- a/R/Internal_Utilities.R +++ b/R/Internal_Utilities.R @@ -590,7 +590,7 @@ Percent_Expressing_Meta <- function( row_dim_names <- features_list col_dim_names <- names(x = percent_expressing) mat_dims <- list(row_dim_names, col_dim_names) - final_df <- data.frame(matrix(unlist(percent_expressing), nrow = length(features_list), byrow = FALSE, dimnames = mat_dims), stringsAsFactors = FALSE) + final_df <- data.frame(matrix(unlist(x = percent_expressing), nrow = length(features_list), byrow = FALSE, dimnames = mat_dims), stringsAsFactors = FALSE) return(final_df) } @@ -645,7 +645,7 @@ Middle_Number <- function( max ) { min_max <- c(min, max) - middle <- min_max[-length(min_max)] + diff(min_max) / 2 + middle <- min_max[-length(x = min_max)] + diff(x = min_max) / 2 return(middle) } From 580a5e9b07ec81be9f2fd2d8dcc4b1d118eefaf0 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:11:34 -0500 Subject: [PATCH 32/74] add check dots --- R/Generics.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/Generics.R b/R/Generics.R index 29479e106..7a08cf2da 100644 --- a/R/Generics.R +++ b/R/Generics.R @@ -153,6 +153,7 @@ Add_MALAT1_Threshold <- function(object, ...) { #' Add_Cell_QC_Metrics <- function(object, ...) { + rlang::check_dots_used() UseMethod(generic = "Add_Cell_QC_Metrics", object = object) } From 9932c8127a7b336ad16dbeb79d206c8b6a45a1b9 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:13:55 -0500 Subject: [PATCH 33/74] change to warning --- R/Generics.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Generics.R b/R/Generics.R index 7a08cf2da..a44347e10 100644 --- a/R/Generics.R +++ b/R/Generics.R @@ -153,7 +153,7 @@ Add_MALAT1_Threshold <- function(object, ...) { #' Add_Cell_QC_Metrics <- function(object, ...) { - rlang::check_dots_used() + rlang::check_dots_used(error = function(e) warn(e)) UseMethod(generic = "Add_Cell_QC_Metrics", object = object) } From 7b93089bd6dbd4c13c8bd7156e40a38d4c095d0c Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:19:25 -0500 Subject: [PATCH 34/74] Seurat version --- R/Generics.R | 1 - R/Utilities_QC_Seurat.R | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/R/Generics.R b/R/Generics.R index a44347e10..29479e106 100644 --- a/R/Generics.R +++ b/R/Generics.R @@ -153,7 +153,6 @@ Add_MALAT1_Threshold <- function(object, ...) { #' Add_Cell_QC_Metrics <- function(object, ...) { - rlang::check_dots_used(error = function(e) warn(e)) UseMethod(generic = "Add_Cell_QC_Metrics", object = object) } diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index 7749bdf12..c595f81df 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -119,6 +119,8 @@ Add_Cell_QC_Metrics.Seurat <- function( overwrite = FALSE, ... ) { + Seurat:::CheckDots(...) + # Set assay assay <- assay %||% DefaultAssay(object = object) From 5f023aa3c38bbc9883e77bcb1226c0a1fb805516 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:24:14 -0500 Subject: [PATCH 35/74] base --- R/Utilities_QC_Seurat.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index c595f81df..7ef58fea8 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -119,7 +119,7 @@ Add_Cell_QC_Metrics.Seurat <- function( overwrite = FALSE, ... ) { - Seurat:::CheckDots(...) + chkDots(...) # Set assay assay <- assay %||% DefaultAssay(object = object) From 1d6bfbe7d3210983e4fb47f53f0343cf80d6faf7 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:31:37 -0500 Subject: [PATCH 36/74] remove unused parameter --- R/LIGER_Utilities.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/LIGER_Utilities.R b/R/LIGER_Utilities.R index 37587733a..408ee8479 100644 --- a/R/LIGER_Utilities.R +++ b/R/LIGER_Utilities.R @@ -1096,7 +1096,6 @@ Add_Cell_QC_Metrics.liger <- function( add_IEG = TRUE, add_hemo = TRUE, add_lncRNA = TRUE, - add_cell_cycle = TRUE, species, mito_name = "percent_mito", ribo_name = "percent_ribo", From 39869e08bb759a880a41f749d59e5033f056460a Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:31:54 -0500 Subject: [PATCH 37/74] Add native feature check before scoring --- R/Utilities_QC_Seurat.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index 7ef58fea8..e7fe42cae 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -241,7 +241,10 @@ Add_Cell_QC_Metrics.Seurat <- function( # Add Cell Cycle Scoring cli_inform(message = "Calculating {.field Cell Cycle Scores}.") - object <- CellCycleScoring(object = object, s.features = Seurat::cc.genes.updated.2019$s.genes, g2m.features = Seurat::cc.genes.updated.2019$g2m.genes) + s.genes_found <- Feature_PreCheck(object = object, features = Seurat::cc.genes.updated.2019$s.genes) + g2m.genes_found <- Feature_PreCheck(object = object, features = Seurat::cc.genes.updated.2019$g2m.genes) + + object <- CellCycleScoring(object = object, s.features = s.genes_found, g2m.features = g2m.genes_found) } } From 67047cc69995f894b3b097a492d52985d885b74c Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:49:34 -0500 Subject: [PATCH 38/74] add chkdots --- R/Utilities_QC_Seurat.R | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index e7fe42cae..5f2d9eee9 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -119,6 +119,7 @@ Add_Cell_QC_Metrics.Seurat <- function( overwrite = FALSE, ... ) { + # check dots chkDots(...) # Set assay @@ -323,6 +324,9 @@ Add_Mito_Ribo.Seurat <- function( species_prefix = NULL, ... ) { + # check dots + chkDots(...) + # Accepted species names accepted_names <- data.frame( Mouse_Options = c("Mouse", "mouse", "Ms", "ms", "Mm", "mm"), @@ -561,6 +565,9 @@ Add_Hemo.Seurat <- function( list_species_names = FALSE, ... ) { + # check dots + chkDots(...) + # Accepted species names accepted_names <- data.frame( Mouse_Options = c("Mouse", "mouse", "Ms", "ms", "Mm", "mm"), @@ -716,8 +723,8 @@ Add_Cell_Complexity.Seurat <- function( overwrite = FALSE, ... ) { - # Check Seurat - Is_Seurat(seurat_object = object) + # check dots + chkDots(...) # Add assay warning message if (assay != "RNA") { @@ -800,6 +807,9 @@ Add_Top_Gene_Pct.Seurat <- function( verbose = TRUE, ... ){ + # check dots + chkDots(...) + # Check for scuttle first scuttle_check <- is_installed(pkg = "scuttle") if (isFALSE(x = scuttle_check)) { @@ -999,8 +1009,8 @@ Add_MALAT1_Threshold.Seurat <- function( rough_max = 2, ... ) { - # Check Seurat - Is_Seurat(seurat_object = object) + # check dots + chkDots(...) # Check for sample column if (is.null(x = sample_col) && isFALSE(x = whole_object)) { From d0bc01d206ccc37cc6a4f0a2577b76d3f2c46a5d Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:50:06 -0500 Subject: [PATCH 39/74] remove from liger --- man/Add_Cell_QC_Metrics.Rd | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/man/Add_Cell_QC_Metrics.Rd b/man/Add_Cell_QC_Metrics.Rd index d21756750..a05a37e52 100644 --- a/man/Add_Cell_QC_Metrics.Rd +++ b/man/Add_Cell_QC_Metrics.Rd @@ -18,7 +18,6 @@ Add_Cell_QC_Metrics(object, ...) add_IEG = TRUE, add_hemo = TRUE, add_lncRNA = TRUE, - add_cell_cycle = TRUE, species, mito_name = "percent_mito", ribo_name = "percent_ribo", @@ -105,9 +104,6 @@ object (Default is TRUE).} \item{add_lncRNA}{logical, whether to add percentage of counts belonging to lncRNA genes to object (Default is TRUE).} -\item{add_cell_cycle}{logical, whether to addcell cycle scores and phase based on -\code{\link[Seurat]{CellCycleScoring}}. Only applicable if \code{species = "human"}. (Default is TRUE).} - \item{species}{Species of origin for given Seurat Object. If mouse, human, marmoset, zebrafish, rat, drosophila, rhesus macaque, or chicken (name or abbreviation) are provided the function will automatically generate patterns and features.} @@ -180,6 +176,9 @@ function will abort if column with name provided to \code{meta_col_name} is pres \item{add_IEG_module_score}{logical, whether to add module score belonging to IEG genes to object (Default is TRUE).} +\item{add_cell_cycle}{logical, whether to addcell cycle scores and phase based on +\code{\link[Seurat]{CellCycleScoring}}. Only applicable if \code{species = "human"}. (Default is TRUE).} + \item{ieg_module_name}{name to use for new meta data column for module score of IEGs. Default is "ieg_score".} } \value{ From e590d59db98d949482cfeed7fff99036b2f3d718 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:51:41 -0500 Subject: [PATCH 40/74] Update changelog --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 640f00397..4d6c1f8ce 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,7 +6,7 @@ ## Changed - +- Add checks for appropriate `...` usage. ## Fixes From 3408f332fdb6702048f719984886f1650fb692a5 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Fri, 23 Jan 2026 10:51:57 -0500 Subject: [PATCH 41/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index dbd3f91ef..8917ce518 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0004 -Date: 2026-01-13 +Version: 3.2.4.0005 +Date: 2026-01-23 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From 904ab2b9e691ef186cecc883f9275c55e9c9393c Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 29 Jan 2026 11:11:56 -0500 Subject: [PATCH 42/74] fix single directory --- R/Read_Write_Data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Read_Write_Data.R b/R/Read_Write_Data.R index 4e8429e33..4612d0e4f 100644 --- a/R/Read_Write_Data.R +++ b/R/Read_Write_Data.R @@ -1696,7 +1696,7 @@ ReadVelocity_Multi_File_Internal <- function( } # add call to report duplicates single time - reporter_file_path <- file.path(data_dir, paste0(sample_list[i], shared_suffix, ".loom")) + reporter_file_path <- file.path(data_dir, paste0(sample_list[1], shared_suffix, ".loom")) reporter_data <- ReadLoomMatrices(loom_file = reporter_file_path, gene_symbol = gene_symbol, verbose = TRUE) # Name the matrices From 17096fc6bb86a7e11e742adfe3d9d37caabc65a1 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:33:53 -0500 Subject: [PATCH 43/74] add assay_suffix parameter --- R/Utilities_QC_Seurat.R | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index 5f2d9eee9..650433d7b 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -19,6 +19,8 @@ #' @param add_lncRNA logical, whether to add percentage of counts belonging to lncRNA genes to object (Default is TRUE). #' @param add_cell_cycle logical, whether to addcell cycle scores and phase based on #' \code{\link[Seurat]{CellCycleScoring}}. Only applicable if `species = "human"`. (Default is TRUE). +#' @param assay_suffix logical, whether to add assay suffix to the QC column name added to meta.data. +#' Default is FALSE. #' @param mito_name name to use for the new meta.data column containing percent mitochondrial counts. #' Default is "percent_mito". #' @param ribo_name name to use for the new meta.data column containing percent ribosomal counts. @@ -94,6 +96,7 @@ Add_Cell_QC_Metrics.Seurat <- function( add_hemo = TRUE, add_lncRNA = TRUE, add_cell_cycle = TRUE, + assay_suffix = FALSE, mito_name = "percent_mito", ribo_name = "percent_ribo", mito_ribo_name = "percent_mito_ribo", @@ -153,6 +156,11 @@ Add_Cell_QC_Metrics.Seurat <- function( macaque_options <- accepted_names$Macaque_Options chicken_options <- accepted_names$Chicken_Options + # pull column names before adding QC + if (isTRUE(x = assay_suffix)) { + old_col_names <- colnames(x = Fetch_Meta(object = object)) + } + # Add mito/ribo if (isTRUE(x = add_mito_ribo)) { cli_inform(message = c("*" = "Adding {.field Mito/Ribo Percentages} to meta.data.")) @@ -249,6 +257,20 @@ Add_Cell_QC_Metrics.Seurat <- function( } } + # pull new column names after adding QC + if (isTRUE(x = assay_suffix)) { + new_col_names <- colnames(x = Fetch_Meta(object = object)) + + added_cols <- setdiff(x = new_col_names, y = old_col_names) + + assay <- DefaultAssay(object = object) + + added_cols_new <- paste0(added_cols, "_", assay) + + # Add assay suffix to new columns + colnames(x = object@meta.data) <- c(old_col_names, added_cols_new) + } + # Log Command object <- LogSeuratCommand(object = object) From 5e63d6786bb09789006970938133201fa220ac61 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:51:44 -0500 Subject: [PATCH 44/74] assay suffix to exam and MALAT1 functions --- R/Utilities_QC_Seurat.R | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/R/Utilities_QC_Seurat.R b/R/Utilities_QC_Seurat.R index 650433d7b..c8f80abdc 100644 --- a/R/Utilities_QC_Seurat.R +++ b/R/Utilities_QC_Seurat.R @@ -929,6 +929,8 @@ Add_Top_Gene_Pct.Seurat <- function( #' @param sample_col column name in meta.data that contains sample ID information. #' @param malat1_threshold_name name to use for the new meta.data column containing percent IEG gene counts. #' Default is set dependent on species gene symbol. +#' @param assay_suffix logical, whether to add assay suffix to the QC column name added to meta.data. +#' Default is FALSE. #' @param ensembl_ids logical, whether feature names in the object are gene names or #' ensembl IDs (default is FALSE; set TRUE if feature names are ensembl IDs). #' @param assay Assay to use (default is the current object default assay). @@ -1011,6 +1013,7 @@ Add_MALAT1_Threshold.Seurat <- function( species, sample_col = NULL, malat1_threshold_name = NULL, + assay_suffix = FALSE, ensembl_ids = FALSE, assay = NULL, overwrite = FALSE, @@ -1086,6 +1089,11 @@ Add_MALAT1_Threshold.Seurat <- function( } } + # pull column names before adding QC + if (isTRUE(x = assay_suffix)) { + old_col_names <- colnames(x = Fetch_Meta(object = object)) + } + # Overwrite check if (malat1_threshold_name %in% colnames(x = object@meta.data)) { if (isFALSE(x = overwrite)) { @@ -1247,6 +1255,20 @@ Add_MALAT1_Threshold.Seurat <- function( object[[malat1_threshold_name]] <- factor(object[[malat1_threshold_name]][,1], levels = c("TRUE","FALSE")) } + # pull new column names after adding QC + if (isTRUE(x = assay_suffix)) { + new_col_names <- colnames(x = Fetch_Meta(object = object)) + + added_cols <- setdiff(x = new_col_names, y = old_col_names) + + assay <- DefaultAssay(object = object) + + added_cols_new <- paste0(added_cols, "_", assay) + + # Add assay suffix to new columns + colnames(x = object@meta.data) <- c(old_col_names, added_cols_new) + } + object <- LogSeuratCommand(object = object) return(object) @@ -1260,6 +1282,8 @@ Add_MALAT1_Threshold.Seurat <- function( #' @param seurat_object object name. #' @param species Species of origin for given Seurat Object. Only accepted species are: mouse, human (name or abbreviation). #' @param exam_module_name name to use for the new meta.data column containing module scores. +#' @param assay_suffix logical, whether to add assay suffix to the QC column name added to meta.data. +#' Default is FALSE. #' @param method method to use for module scoring, currently only "Seurat" is supported but more to be added. . #' @param ensembl_ids logical, whether feature names in the object are gene names or #' ensembl IDs (default is FALSE; set TRUE if feature names are ensembl IDs). @@ -1289,6 +1313,7 @@ exAM_Scoring <- function( seurat_object, species, exam_module_name = NULL, + assay_suffix = FALSE, method = "Seurat", ensembl_ids = FALSE, assay = NULL, @@ -1330,6 +1355,11 @@ exAM_Scoring <- function( # Check Seurat Is_Seurat(seurat_object = seurat_object) + # pull column names before adding QC + if (isTRUE(x = assay_suffix)) { + old_col_names <- colnames(x = Fetch_Meta(object = seurat_object)) + } + if (method == "UCell") { # Check Nebulosa installed UCell_check <- is_installed(pkg = "UCell") @@ -1412,6 +1442,20 @@ exAM_Scoring <- function( } } + # pull new column names after adding QC + if (isTRUE(x = assay_suffix)) { + new_col_names <- colnames(x = Fetch_Meta(object = seurat_object)) + + added_cols <- setdiff(x = new_col_names, y = old_col_names) + + assay <- DefaultAssay(object = seurat_object) + + added_cols_new <- paste0(added_cols, "_", assay) + + # Add assay suffix to new columns + colnames(x = seurat_object@meta.data) <- c(old_col_names, added_cols_new) + } + # Log Command seurat_object <- LogSeuratCommand(object = seurat_object) From a535b8af4b56a4f1162229839a7a01063bead9dc Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:30:50 -0500 Subject: [PATCH 45/74] deprecate plot_by and replace with group.by for Seq_QC_Plot_* family --- R/Plotting_QC_Seq_10X.R | 627 +++++++++++++++++++++++++--------------- 1 file changed, 389 insertions(+), 238 deletions(-) diff --git a/R/Plotting_QC_Seq_10X.R b/R/Plotting_QC_Seq_10X.R index 6ebf4c9bf..e9208f27f 100644 --- a/R/Plotting_QC_Seq_10X.R +++ b/R/Plotting_QC_Seq_10X.R @@ -8,7 +8,8 @@ #' Plot the mean number of reads per cell #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param plot_by `r lifecycle::badge("deprecated")` soft-deprecated. See `group.by`. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -35,45 +36,55 @@ Seq_QC_Plot_Reads_per_Cell <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Reads_per_Cell(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") + } + + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use } } - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Mean_Reads_per_Cell"]])) + @@ -89,7 +100,7 @@ Seq_QC_Plot_Reads_per_Cell <- function( xlab("") + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Mean_Reads_per_Cell"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Mean_Reads_per_Cell"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + theme(legend.position = "none", @@ -100,7 +111,7 @@ Seq_QC_Plot_Reads_per_Cell <- function( scale_fill_manual(values = colors_use) + ggtitle("Mean Reads per Cell per Sample") + ylab('Mean Reads per Cell') + - xlab(plot_by) + + xlab(group.by) + theme_ggprism_mod() } @@ -120,10 +131,10 @@ Seq_QC_Plot_Reads_per_Cell <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by ,colors_use = NULL,]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -142,7 +153,7 @@ Seq_QC_Plot_Reads_per_Cell <- function( #' Plot the number of cells per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -169,45 +180,55 @@ Seq_QC_Plot_Reads_per_Cell <- function( Seq_QC_Plot_Number_Cells <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Number_Cells(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use } } - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Estimated_Number_of_Cells"]])) + @@ -223,7 +244,7 @@ Seq_QC_Plot_Number_Cells <- function( xlab("") + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Estimated_Number_of_Cells"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Estimated_Number_of_Cells"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + theme(legend.position = "none", @@ -234,7 +255,7 @@ Seq_QC_Plot_Number_Cells <- function( scale_fill_manual(values = colors_use) + ggtitle("Cells per Sample") + ylab('Cells') + - xlab(plot_by) + + xlab(group.by) + theme_ggprism_mod() } @@ -254,10 +275,10 @@ Seq_QC_Plot_Number_Cells <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -276,7 +297,7 @@ Seq_QC_Plot_Number_Cells <- function( #' Plot the median genes per cell per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -303,45 +324,55 @@ Seq_QC_Plot_Number_Cells <- function( Seq_QC_Plot_Genes <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Genes(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { cli_abort(message = " is not a column in the provided `metrics_dataframe`.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use } } - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Median_Genes_per_Cell"]])) + @@ -352,13 +383,13 @@ Seq_QC_Plot_Genes <- function( xlab("") + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Median_Genes_per_Cell"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Median_Genes_per_Cell"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Median Genes per Cell") + ylab('Median Genes') + - xlab(plot_by) + + xlab(group.by) + theme_ggprism_mod() } @@ -378,10 +409,10 @@ Seq_QC_Plot_Genes <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -400,7 +431,7 @@ Seq_QC_Plot_Genes <- function( #' Plot the median UMIs per cell per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -427,45 +458,55 @@ Seq_QC_Plot_Genes <- function( Seq_QC_Plot_UMIs <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_UMIs(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use } } - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Median_UMI_Counts_per_Cell"]])) + @@ -476,13 +517,13 @@ Seq_QC_Plot_UMIs <- function( xlab("") + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Median_UMI_Counts_per_Cell"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Median_UMI_Counts_per_Cell"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Median UMIs per Cell") + ylab('Median UMIs') + - xlab(plot_by) + + xlab(group.by) + theme_ggprism_mod() } @@ -502,10 +543,10 @@ Seq_QC_Plot_UMIs <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -524,7 +565,7 @@ Seq_QC_Plot_UMIs <- function( #' Plot the total genes detected per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -551,45 +592,55 @@ Seq_QC_Plot_UMIs <- function( Seq_QC_Plot_Total_Genes <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Total_Genes(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") + } + + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use } } - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Total_Genes_Detected"]])) + @@ -600,13 +651,13 @@ Seq_QC_Plot_Total_Genes <- function( xlab("") + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Total_Genes_Detected"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Total_Genes_Detected"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Total Genes Detected per Sample") + ylab('Total Genes') + - xlab(plot_by) + + xlab(group.by) + theme_ggprism_mod() } @@ -626,10 +677,10 @@ Seq_QC_Plot_Total_Genes <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -648,7 +699,7 @@ Seq_QC_Plot_Total_Genes <- function( #' Plot the sequencing saturation percentage per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -676,38 +727,48 @@ Seq_QC_Plot_Total_Genes <- function( Seq_QC_Plot_Saturation <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Saturation(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -718,7 +779,7 @@ Seq_QC_Plot_Saturation <- function( metrics_dataframe[,"Sequencing_Saturation"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Sequencing_Saturation"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Sequencing_Saturation"]]), color = .data[["samples_plotting"]]) + @@ -730,13 +791,13 @@ Seq_QC_Plot_Saturation <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Sequencing_Saturation"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Sequencing_Saturation"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Sequencing Saturation") + ylab('Sequencing Saturation Percent') + - xlab(plot_by)+ + xlab(group.by)+ scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -757,10 +818,10 @@ Seq_QC_Plot_Saturation <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -779,7 +840,7 @@ Seq_QC_Plot_Saturation <- function( #' Plot the fraction of reads in cells per sample #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -807,38 +868,48 @@ Seq_QC_Plot_Saturation <- function( Seq_QC_Plot_Reads_in_Cells <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Reads_in_Cells(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") + } + + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -849,7 +920,7 @@ Seq_QC_Plot_Reads_in_Cells <- function( metrics_dataframe[,"Fraction_Reads_in_Cells"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Fraction_Reads_in_Cells"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Fraction_Reads_in_Cells"]]), color = .data[["samples_plotting"]]) + @@ -861,13 +932,13 @@ Seq_QC_Plot_Reads_in_Cells <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Fraction_Reads_in_Cells"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Fraction_Reads_in_Cells"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Fraction of Reads in Cells per Sample") + ylab('Fraction of Reads in Cells') + - xlab(plot_by)+ + xlab(group.by)+ scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -888,10 +959,10 @@ Seq_QC_Plot_Reads_in_Cells <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -910,7 +981,7 @@ Seq_QC_Plot_Reads_in_Cells <- function( #' Plot the fraction of reads confidently mapped to transcriptome #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -938,38 +1009,48 @@ Seq_QC_Plot_Reads_in_Cells <- function( Seq_QC_Plot_Transcriptome <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Transcriptome(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -980,7 +1061,7 @@ Seq_QC_Plot_Transcriptome <- function( metrics_dataframe[,"Reads_Mapped_Confidently_to_Transcriptome"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Confidently_to_Transcriptome"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Confidently_to_Transcriptome"]]), color = .data[["samples_plotting"]]) + @@ -992,13 +1073,13 @@ Seq_QC_Plot_Transcriptome <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Confidently_to_Transcriptome"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Confidently_to_Transcriptome"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Transcriptome") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1018,10 +1099,10 @@ Seq_QC_Plot_Transcriptome <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1040,7 +1121,7 @@ Seq_QC_Plot_Transcriptome <- function( #' Plot the fraction of reads confidently mapped to genome #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1068,38 +1149,48 @@ Seq_QC_Plot_Transcriptome <- function( Seq_QC_Plot_Genome <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Genome(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -1110,7 +1201,7 @@ Seq_QC_Plot_Genome <- function( metrics_dataframe[,"Reads_Mapped_Confidently_to_Genome"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Confidently_to_Genome"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Confidently_to_Genome"]]), color = .data[["samples_plotting"]]) + @@ -1122,13 +1213,13 @@ Seq_QC_Plot_Genome <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Confidently_to_Genome"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Confidently_to_Genome"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Genome") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1148,10 +1239,10 @@ Seq_QC_Plot_Genome <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1170,7 +1261,7 @@ Seq_QC_Plot_Genome <- function( #' Plot the fraction of reads confidently mapped to intergenic regions #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1198,38 +1289,48 @@ Seq_QC_Plot_Genome <- function( Seq_QC_Plot_Intergenic <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Intergenic(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -1240,7 +1341,7 @@ Seq_QC_Plot_Intergenic <- function( metrics_dataframe[,"Reads_Mapped_Confidently_to_Intergenic_Regions"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Confidently_to_Intergenic_Regions"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Confidently_to_Intergenic_Regions"]]), color = .data[["samples_plotting"]]) + @@ -1252,13 +1353,13 @@ Seq_QC_Plot_Intergenic <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Confidently_to_Intergenic_Regions"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Confidently_to_Intergenic_Regions"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Intergenic Regions") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1278,10 +1379,10 @@ Seq_QC_Plot_Intergenic <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1300,7 +1401,7 @@ Seq_QC_Plot_Intergenic <- function( #' Plot the fraction of reads confidently mapped to intronic regions #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1328,38 +1429,48 @@ Seq_QC_Plot_Intergenic <- function( Seq_QC_Plot_Intronic <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Intronic(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -1370,7 +1481,7 @@ Seq_QC_Plot_Intronic <- function( metrics_dataframe[,"Reads_Mapped_Confidently_to_Intronic_Regions"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Confidently_to_Intronic_Regions"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Confidently_to_Intronic_Regions"]]), color = .data[["samples_plotting"]]) + @@ -1382,13 +1493,13 @@ Seq_QC_Plot_Intronic <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Confidently_to_Intronic_Regions"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Confidently_to_Intronic_Regions"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Intronic Regions") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1408,10 +1519,10 @@ Seq_QC_Plot_Intronic <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1430,7 +1541,7 @@ Seq_QC_Plot_Intronic <- function( #' Plot the fraction of reads confidently mapped to Exonic regions #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1458,38 +1569,48 @@ Seq_QC_Plot_Intronic <- function( Seq_QC_Plot_Exonic <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Exonic(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") + } + + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -1500,7 +1621,7 @@ Seq_QC_Plot_Exonic <- function( metrics_dataframe[,"Reads_Mapped_Confidently_to_Exonic_Regions"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Confidently_to_Exonic_Regions"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Confidently_to_Exonic_Regions"]]), color = .data[["samples_plotting"]]) + @@ -1512,13 +1633,13 @@ Seq_QC_Plot_Exonic <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Confidently_to_Exonic_Regions"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Confidently_to_Exonic_Regions"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Exonic Regions") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1538,10 +1659,10 @@ Seq_QC_Plot_Exonic <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1560,7 +1681,7 @@ Seq_QC_Plot_Exonic <- function( #' Plot the fraction of reads mapped Antisense to Gene #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1588,38 +1709,48 @@ Seq_QC_Plot_Exonic <- function( Seq_QC_Plot_Antisense <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, significance = FALSE, ... ) { - if (!plot_by %in% colnames(x = metrics_dataframe)) { - cli_abort(message = "{.val {plot_by}} is not a column in the provided {.code metrics_dataframe}.") + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Antisense(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + + if (!group.by %in% colnames(x = metrics_dataframe)) { + cli_abort(message = "{.val {group.by}} is not a column in the provided {.code metrics_dataframe}.") } - # Change plot_by to character vector to make significance functions show all comparisons - if (inherits(x = metrics_dataframe[[plot_by]], what = "factor")) { + # Change group.by to character vector to make significance functions show all comparisons + if (inherits(x = metrics_dataframe[[group.by]], what = "factor")) { stats_dataframe <- metrics_dataframe - stats_dataframe[[plot_by]] <- as.character(stats_dataframe[[plot_by]]) + stats_dataframe[[group.by]] <- as.character(stats_dataframe[[group.by]]) } else { stats_dataframe <- metrics_dataframe } # Create color palette if null and check valid if provided - length_plotby <- length(x = unique(x = metrics_dataframe[[plot_by]])) + length_plotby <- length(x = unique(x = metrics_dataframe[[group.by]])) - if (is.null(x = colors_use) && !plot_by == "sample_id") { + if (is.null(x = colors_use) && !group.by == "sample_id") { if (length_plotby <= 8) { colors_use <- Dark2_Pal() } else { colors_use <- DiscretePalette_scCustomize(num_colors = length_plotby, palette = "polychrome") } } else { - if (length(x = colors_use) < length_plotby && !plot_by == "sample_id") { + if (length(x = colors_use) < length_plotby && !group.by == "sample_id") { cli_abort(message = c("Not enough colors provided.", - "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {plot_by}} column: {.field {length_plotby}}.") + "i" = "The number of colors supplied: {.field {length(x = colors_use)}}, is less than the number of groups in {.val {group.by}} column: {.field {length_plotby}}.") ) } else { colors_use <- colors_use @@ -1630,7 +1761,7 @@ Seq_QC_Plot_Antisense <- function( metrics_dataframe[,"Reads_Mapped_Antisense_to_Gene"] <- as.numeric(gsub("%", "", metrics_dataframe[,"Reads_Mapped_Antisense_to_Gene"])) - if (plot_by == "sample_id") { + if (group.by == "sample_id") { metrics_dataframe$samples_plotting <- "Samples" plot <- ggplot(metrics_dataframe, aes(x = .data[["samples_plotting"]], y = .data[["Reads_Mapped_Antisense_to_Gene"]]), color = .data[["samples_plotting"]]) + @@ -1642,13 +1773,13 @@ Seq_QC_Plot_Antisense <- function( scale_y_continuous(labels = label_percent(accuracy = 0.01, scale = 1)) + theme_ggprism_mod() } else { - plot <- ggplot(metrics_dataframe, aes(x=.data[[plot_by]], y = .data[["Reads_Mapped_Antisense_to_Gene"]], fill = .data[[plot_by]])) + + plot <- ggplot(metrics_dataframe, aes(x=.data[[group.by]], y = .data[["Reads_Mapped_Antisense_to_Gene"]], fill = .data[[group.by]])) + geom_boxplot(fill = "white") + geom_dotplot(binaxis ='y', stackdir = 'center', dotsize = dot_size) + scale_fill_manual(values = colors_use) + ggtitle("Percent of Reads Confidently Mapped to Antisense to Gene") + ylab('Percent of Reads') + - xlab(plot_by) + + xlab(group.by) + scale_y_continuous(labels = label_percent(accuracy = 1, scale = 1)) + theme_ggprism_mod() } @@ -1668,10 +1799,10 @@ Seq_QC_Plot_Antisense <- function( )) } - if (length(x = unique(x = stats_dataframe[[plot_by]])) < 2) { - cli_abort(message = "Cannot calculate statistics when {.val {plot_by}} column contains less than 2 groups.") + if (length(x = unique(x = stats_dataframe[[group.by]])) < 2) { + cli_abort(message = "Cannot calculate statistics when {.val {group.by}} column contains less than 2 groups.") } - groups <- unique(x = stats_dataframe[[plot_by]]) + groups <- unique(x = stats_dataframe[[group.by]]) comparisons <- combn(groups, 2) comparisons <- data.frame(comparisons, stringsAsFactors = FALSE) @@ -1695,7 +1826,7 @@ Seq_QC_Plot_Antisense <- function( #' Plot a combined plot of the basic QC metrics from sequencing output. #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1723,7 +1854,8 @@ Seq_QC_Plot_Antisense <- function( Seq_QC_Plot_Basic_Combined <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -1731,6 +1863,15 @@ Seq_QC_Plot_Basic_Combined <- function( significance = FALSE, ... ) { + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Basic_Combined(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + # Create rotated axis value if (isTRUE(x = x_lab_rotate)) { axis_angle <- 45 @@ -1739,36 +1880,36 @@ Seq_QC_Plot_Basic_Combined <- function( } # Create Plots & modify for plotting together - p1 <- Seq_QC_Plot_Number_Cells(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p1 <- Seq_QC_Plot_Number_Cells(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p1 <- p1 + labs(title = str_wrap(p1$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p2 <- Seq_QC_Plot_Reads_per_Cell(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p2 <- Seq_QC_Plot_Reads_per_Cell(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p2 <- p2 + labs(title = str_wrap(p2$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p3 <- Seq_QC_Plot_Genes(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p3 <- Seq_QC_Plot_Genes(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p3 <- p3 + labs(title = str_wrap(p3$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p4 <- Seq_QC_Plot_UMIs(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p4 <- Seq_QC_Plot_UMIs(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p4 <- p4 + labs(title = str_wrap(p4$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p5 <- Seq_QC_Plot_Total_Genes(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p5 <- Seq_QC_Plot_Total_Genes(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p5 <- p5 + labs(title = str_wrap(p5$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p6 <- Seq_QC_Plot_Saturation(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p6 <- Seq_QC_Plot_Saturation(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p6 <- p6 + labs(title = str_wrap(p6$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p7 <- Seq_QC_Plot_Reads_in_Cells(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p7 <- Seq_QC_Plot_Reads_in_Cells(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p7 <- p7 + labs(title = str_wrap(p7$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p8 <- Seq_QC_Plot_Transcriptome(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p8 <- Seq_QC_Plot_Transcriptome(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p8 <- p8 + labs(title = str_wrap(p8$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) @@ -1787,7 +1928,7 @@ Seq_QC_Plot_Basic_Combined <- function( #' Plot a combined plot of the Alignment QC metrics from sequencing output. #' #' @param metrics_dataframe data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}). -#' @param plot_by Grouping factor for the plot. Default is to plot as single group with single point per sample. +#' @param group.by Grouping factor for the plot. Default is to plot as single group with single point per sample. #' @param colors_use colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if #' less than 8 groups and `DiscretePalette_scCustomize(palette = "polychrome")` if more than 8. #' @param dot_size size of the dots plotted if `plot_by` is not `sample_id` Default is 1. @@ -1815,7 +1956,8 @@ Seq_QC_Plot_Basic_Combined <- function( Seq_QC_Plot_Alignment_Combined <- function( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -1823,6 +1965,15 @@ Seq_QC_Plot_Alignment_Combined <- function( significance = FALSE, ... ) { + # check deprecation + if (is_present(plot_by)) { + deprecate_warn(when = "3.5.0", + what = "Seq_QC_Plot_Alignment_Combined(plot_by)", + details = c("i" = "The {.code plot_by} parameter is soft-deprecated. Please update code to use `group.by` instead.") + ) + group.by <- plot_by + } + # Create rotated axis value if (isTRUE(x = x_lab_rotate)) { axis_angle <- 45 @@ -1831,28 +1982,28 @@ Seq_QC_Plot_Alignment_Combined <- function( } # Create Plots & modify for plotting together - p1 <- Seq_QC_Plot_Genome(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size, ...) + p1 <- Seq_QC_Plot_Genome(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size, ...) p1 <- p1 + labs(title = str_wrap(p1$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p2 <- Seq_QC_Plot_Intergenic(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p2 <- Seq_QC_Plot_Intergenic(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p2 <- p2 + labs(title = str_wrap(p2$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p3 <- Seq_QC_Plot_Transcriptome(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p3 <- Seq_QC_Plot_Transcriptome(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p3 <- p3 + labs(title = str_wrap(p3$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p4 <- Seq_QC_Plot_Exonic(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p4 <- Seq_QC_Plot_Exonic(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p4 <- p4 + labs(title = str_wrap(p4$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p5 <- Seq_QC_Plot_Intronic(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p5 <- Seq_QC_Plot_Intronic(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p5 <- p5 + labs(title = str_wrap(p5$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) - p6 <- Seq_QC_Plot_Antisense(metrics_dataframe = metrics_dataframe, plot_by = plot_by, colors_use = colors_use, significance = significance, dot_size = dot_size,) + p6 <- Seq_QC_Plot_Antisense(metrics_dataframe = metrics_dataframe, group.by = group.by, colors_use = colors_use, significance = significance, dot_size = dot_size,) p6 <- p6 + labs(title = str_wrap(p6$labels$title, 18)) + theme_ggprism_mod(base_size = 10, axis_text_angle = axis_angle) From 3a62f81a6815aaf91f1b2990687c268f95feff22 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:31:51 -0500 Subject: [PATCH 46/74] Update changelog --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 4d6c1f8ce..681b2c311 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,7 @@ ## Changed +- **BREAKING CHANGE** The `plot_by` parameter has been soft-deprecated in `Seq_QC_Plot_*` family of functions. Please use `group.by` instead. Using `plot_by` will warn user but still work until scCustomize v3.5.0. - Add checks for appropriate `...` usage. ## Fixes From 221f796a07d5f3797d6de91e550f440b044f5871 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:32:19 -0500 Subject: [PATCH 47/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8917ce518..97d1b473c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0005 -Date: 2026-01-23 +Version: 3.2.4.0006 +Date: 2026-02-26 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From f2050f0cc0f3dc47820d02d0049ababbc4ab2b1a Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:32:46 -0500 Subject: [PATCH 48/74] Update docs --- man/Add_Cell_QC_Metrics.Rd | 4 ++++ man/Add_MALAT1_Threshold.Rd | 4 ++++ man/Seq_QC_Plot_Alignment_Combined.Rd | 5 +++-- man/Seq_QC_Plot_Antisense.Rd | 5 +++-- man/Seq_QC_Plot_Basic_Combined.Rd | 5 +++-- man/Seq_QC_Plot_Exonic.Rd | 5 +++-- man/Seq_QC_Plot_Genes.Rd | 5 +++-- man/Seq_QC_Plot_Genome.Rd | 5 +++-- man/Seq_QC_Plot_Intergenic.Rd | 5 +++-- man/Seq_QC_Plot_Intronic.Rd | 5 +++-- man/Seq_QC_Plot_Number_Cells.Rd | 5 +++-- man/Seq_QC_Plot_Reads_in_Cells.Rd | 5 +++-- man/Seq_QC_Plot_Reads_per_Cell.Rd | 7 +++++-- man/Seq_QC_Plot_Saturation.Rd | 5 +++-- man/Seq_QC_Plot_Total_Genes.Rd | 5 +++-- man/Seq_QC_Plot_Transcriptome.Rd | 5 +++-- man/Seq_QC_Plot_UMIs.Rd | 5 +++-- man/exAM_Scoring.Rd | 4 ++++ 18 files changed, 59 insertions(+), 30 deletions(-) diff --git a/man/Add_Cell_QC_Metrics.Rd b/man/Add_Cell_QC_Metrics.Rd index a05a37e52..28f905a93 100644 --- a/man/Add_Cell_QC_Metrics.Rd +++ b/man/Add_Cell_QC_Metrics.Rd @@ -56,6 +56,7 @@ Add_Cell_QC_Metrics(object, ...) add_hemo = TRUE, add_lncRNA = TRUE, add_cell_cycle = TRUE, + assay_suffix = FALSE, mito_name = "percent_mito", ribo_name = "percent_ribo", mito_ribo_name = "percent_mito_ribo", @@ -179,6 +180,9 @@ function will abort if column with name provided to \code{meta_col_name} is pres \item{add_cell_cycle}{logical, whether to addcell cycle scores and phase based on \code{\link[Seurat]{CellCycleScoring}}. Only applicable if \code{species = "human"}. (Default is TRUE).} +\item{assay_suffix}{logical, whether to add assay suffix to the QC column name added to meta.data. +Default is FALSE.} + \item{ieg_module_name}{name to use for new meta data column for module score of IEGs. Default is "ieg_score".} } \value{ diff --git a/man/Add_MALAT1_Threshold.Rd b/man/Add_MALAT1_Threshold.Rd index 169d3468f..7f7293e11 100644 --- a/man/Add_MALAT1_Threshold.Rd +++ b/man/Add_MALAT1_Threshold.Rd @@ -12,6 +12,7 @@ Add_MALAT1_Threshold(object, ...) species, sample_col = NULL, malat1_threshold_name = NULL, + assay_suffix = FALSE, ensembl_ids = FALSE, assay = NULL, overwrite = FALSE, @@ -45,6 +46,9 @@ Add_MALAT1_Threshold(object, ...) \item{malat1_threshold_name}{name to use for the new meta.data column containing percent IEG gene counts. Default is set dependent on species gene symbol.} +\item{assay_suffix}{logical, whether to add assay suffix to the QC column name added to meta.data. +Default is FALSE.} + \item{ensembl_ids}{logical, whether feature names in the object are gene names or ensembl IDs (default is FALSE; set TRUE if feature names are ensembl IDs).} diff --git a/man/Seq_QC_Plot_Alignment_Combined.Rd b/man/Seq_QC_Plot_Alignment_Combined.Rd index f4989dc18..6b804b297 100644 --- a/man/Seq_QC_Plot_Alignment_Combined.Rd +++ b/man/Seq_QC_Plot_Alignment_Combined.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Alignment_Combined( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -18,7 +19,7 @@ Seq_QC_Plot_Alignment_Combined( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Antisense.Rd b/man/Seq_QC_Plot_Antisense.Rd index 493df5b16..ab02d3fe8 100644 --- a/man/Seq_QC_Plot_Antisense.Rd +++ b/man/Seq_QC_Plot_Antisense.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Antisense( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Antisense( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Basic_Combined.Rd b/man/Seq_QC_Plot_Basic_Combined.Rd index 6e3968aed..51e54dfac 100644 --- a/man/Seq_QC_Plot_Basic_Combined.Rd +++ b/man/Seq_QC_Plot_Basic_Combined.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Basic_Combined( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -18,7 +19,7 @@ Seq_QC_Plot_Basic_Combined( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Exonic.Rd b/man/Seq_QC_Plot_Exonic.Rd index c03a0ce04..4b22d111f 100644 --- a/man/Seq_QC_Plot_Exonic.Rd +++ b/man/Seq_QC_Plot_Exonic.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Exonic( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Exonic( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Genes.Rd b/man/Seq_QC_Plot_Genes.Rd index b90312610..904edcf66 100644 --- a/man/Seq_QC_Plot_Genes.Rd +++ b/man/Seq_QC_Plot_Genes.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Genes( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Genes( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Genome.Rd b/man/Seq_QC_Plot_Genome.Rd index 355f97b81..87a55f93c 100644 --- a/man/Seq_QC_Plot_Genome.Rd +++ b/man/Seq_QC_Plot_Genome.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Genome( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Genome( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Intergenic.Rd b/man/Seq_QC_Plot_Intergenic.Rd index 147c76daa..ff7847a65 100644 --- a/man/Seq_QC_Plot_Intergenic.Rd +++ b/man/Seq_QC_Plot_Intergenic.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Intergenic( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Intergenic( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Intronic.Rd b/man/Seq_QC_Plot_Intronic.Rd index 193a8003f..161962252 100644 --- a/man/Seq_QC_Plot_Intronic.Rd +++ b/man/Seq_QC_Plot_Intronic.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Intronic( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Intronic( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Number_Cells.Rd b/man/Seq_QC_Plot_Number_Cells.Rd index 35ea5bf88..f927a2991 100644 --- a/man/Seq_QC_Plot_Number_Cells.Rd +++ b/man/Seq_QC_Plot_Number_Cells.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Number_Cells( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Number_Cells( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Reads_in_Cells.Rd b/man/Seq_QC_Plot_Reads_in_Cells.Rd index 7be4f5002..fb28f4057 100644 --- a/man/Seq_QC_Plot_Reads_in_Cells.Rd +++ b/man/Seq_QC_Plot_Reads_in_Cells.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Reads_in_Cells( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Reads_in_Cells( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Reads_per_Cell.Rd b/man/Seq_QC_Plot_Reads_per_Cell.Rd index 61f47787c..5c3bce88d 100644 --- a/man/Seq_QC_Plot_Reads_per_Cell.Rd +++ b/man/Seq_QC_Plot_Reads_per_Cell.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Reads_per_Cell( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,9 @@ Seq_QC_Plot_Reads_per_Cell( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} + +\item{plot_by}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} soft-deprecated. See \code{group.by}.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Saturation.Rd b/man/Seq_QC_Plot_Saturation.Rd index 43427e9af..18fcc4aa4 100644 --- a/man/Seq_QC_Plot_Saturation.Rd +++ b/man/Seq_QC_Plot_Saturation.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Saturation( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Saturation( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Total_Genes.Rd b/man/Seq_QC_Plot_Total_Genes.Rd index 90038a8c3..b8397c160 100644 --- a/man/Seq_QC_Plot_Total_Genes.Rd +++ b/man/Seq_QC_Plot_Total_Genes.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Total_Genes( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Total_Genes( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_Transcriptome.Rd b/man/Seq_QC_Plot_Transcriptome.Rd index 0fe36c1a6..7b955fdfd 100644 --- a/man/Seq_QC_Plot_Transcriptome.Rd +++ b/man/Seq_QC_Plot_Transcriptome.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_Transcriptome( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_Transcriptome( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/Seq_QC_Plot_UMIs.Rd b/man/Seq_QC_Plot_UMIs.Rd index 5ee01a979..a8714e7b3 100644 --- a/man/Seq_QC_Plot_UMIs.Rd +++ b/man/Seq_QC_Plot_UMIs.Rd @@ -6,7 +6,8 @@ \usage{ Seq_QC_Plot_UMIs( metrics_dataframe, - plot_by = "sample_id", + group.by = "sample_id", + plot_by = deprecated(), colors_use = NULL, dot_size = 1, x_lab_rotate = FALSE, @@ -17,7 +18,7 @@ Seq_QC_Plot_UMIs( \arguments{ \item{metrics_dataframe}{data.frame contain Cell Ranger QC Metrics (see \code{\link{Read_Metrics_10X}}).} -\item{plot_by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} +\item{group.by}{Grouping factor for the plot. Default is to plot as single group with single point per sample.} \item{colors_use}{colors to use for plot if plotting by group. Defaults to RColorBrewer Dark2 palette if less than 8 groups and \code{DiscretePalette_scCustomize(palette = "polychrome")} if more than 8.} diff --git a/man/exAM_Scoring.Rd b/man/exAM_Scoring.Rd index 50c9dac14..689928d35 100644 --- a/man/exAM_Scoring.Rd +++ b/man/exAM_Scoring.Rd @@ -8,6 +8,7 @@ exAM_Scoring( seurat_object, species, exam_module_name = NULL, + assay_suffix = FALSE, method = "Seurat", ensembl_ids = FALSE, assay = NULL, @@ -23,6 +24,9 @@ exAM_Scoring( \item{exam_module_name}{name to use for the new meta.data column containing module scores.} +\item{assay_suffix}{logical, whether to add assay suffix to the QC column name added to meta.data. +Default is FALSE.} + \item{method}{method to use for module scoring, currently only "Seurat" is supported but more to be added. .} \item{ensembl_ids}{logical, whether feature names in the object are gene names or From 4eb7b75a6e2eaefd928d854eaf5c4cbbf6c5b676 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:56:08 -0500 Subject: [PATCH 49/74] make linewidth addition conditional to avoid empty aesthetic warnings in ggplot 4.0.0+ --- R/Plotting_QC_Seurat.R | 61 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/R/Plotting_QC_Seurat.R b/R/Plotting_QC_Seurat.R index 01c75ee5f..e589f19ef 100644 --- a/R/Plotting_QC_Seurat.R +++ b/R/Plotting_QC_Seurat.R @@ -77,12 +77,20 @@ QC_Plots_Genes <- function( nFeature <- paste0("nFeature_", assay) plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nFeature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } + # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -172,12 +180,20 @@ QC_Plots_UMIs <- function( nCount <- paste0("nCount_", assay) plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nCount, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } + # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -263,12 +279,20 @@ QC_Plots_Mito <- function( Is_Seurat(seurat_object = seurat_object) plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = mito_name, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } + # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -358,12 +382,20 @@ QC_Plots_Feature <- function( plot_title <- paste0(feature, " per Cell/Nucleus") } plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = feature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } + # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -660,8 +692,16 @@ QC_Histogram <- function( plot <- ggplot(data = data_to_plot, aes(x = .data[[all_found_features[x]]])) + geom_histogram(color = "black", fill = colors_use, bins = bins) + theme_cowplot() + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + ggtitle(plot_titles[x]) + + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } }) # wrap and return plots @@ -699,8 +739,17 @@ QC_Histogram <- function( plot <- ggplot(data = sub_data, aes(x = .data[[all_found_features]])) + geom_histogram(color = "black", fill = colors_use[x], bins = bins) + theme_cowplot() + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + ggtitle(meta_sample_list[x]) + + # cutoff linewidth adjustment + if (!is.null(x = cutoff_line_width)) { + plot <- plot + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + } else { + plot <- plot + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + } + }) # wrap and return plots From 0acbd6fd9d8d28975d4bf4028e25fe55dd4ff831 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:58:00 -0500 Subject: [PATCH 50/74] Update changelog --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 681b2c311..b32a88703 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,8 @@ - Add checks for appropriate `...` usage. ## Fixes - +- Fixed setting of `linewidth` parameter to be conditional in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. + From e5c76bd8b270fab2fe1acffc39d81a5edc098bd3 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 08:58:07 -0500 Subject: [PATCH 51/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 97d1b473c..00aefe6ae 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.0006 -Date: 2026-02-26 +Version: 3.2.4.9007 +Date: 2026-03-03 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From 3f1b4024c2849d02368e4b8f7ce09113f680612f Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:08:39 -0500 Subject: [PATCH 52/74] revert conditional and set a default close to ggplot2 default value to avoid issues in complexity --- R/Plotting_QC_Seurat.R | 196 ++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 63 deletions(-) diff --git a/R/Plotting_QC_Seurat.R b/R/Plotting_QC_Seurat.R index e589f19ef..443a9bbd8 100644 --- a/R/Plotting_QC_Seurat.R +++ b/R/Plotting_QC_Seurat.R @@ -76,21 +76,19 @@ QC_Plots_Genes <- function( nFeature <- paste0("nFeature_", assay) + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nFeature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } - # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -179,21 +177,19 @@ QC_Plots_UMIs <- function( nCount <- paste0("nCount_", assay) + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nCount, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } - # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -278,21 +274,19 @@ QC_Plots_Mito <- function( # Check Seurat Is_Seurat(seurat_object = seurat_object) + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = mito_name, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } - # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -381,21 +375,20 @@ QC_Plots_Feature <- function( if (is.null(x = plot_title)) { plot_title <- paste0(feature, " per Cell/Nucleus") } + + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = feature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + theme(plot.subtitle = element_text(hjust = 0.5), legend.position = "none") - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } - # Rotate x axis label if (isFALSE(x = x_lab_rotate)) { plot <- plot + UnRotate_X() @@ -477,7 +470,27 @@ QC_Plots_Complexity <- function( color_seed = 123, ... ) { - plot <- QC_Plots_Feature(seurat_object = seurat_object, feature = feature, group.by = group.by, x_axis_label = x_axis_label, y_axis_label = y_axis_label, plot_title = plot_title, low_cutoff = low_cutoff, high_cutoff = high_cutoff, pt.size = pt.size, colors_use = colors_use, x_lab_rotate = x_lab_rotate, y_axis_log = y_axis_log, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, cutoff_line_width = cutoff_line_width, ...) + # plot + plot <- QC_Plots_Feature(seurat_object = seurat_object, + feature = feature, + group.by = group.by, + x_axis_label = x_axis_label, + y_axis_label = y_axis_label, + plot_title = plot_title, + low_cutoff = low_cutoff, + high_cutoff = high_cutoff, + pt.size = pt.size, + colors_use = colors_use, + x_lab_rotate = x_lab_rotate, + y_axis_log = y_axis_log, + raster = raster, + ggplot_default_colors = ggplot_default_colors, + color_seed = color_seed, + plot_median = plot_median, + median_size = median_size, + plot_boxplot = plot_boxplot, + cutoff_line_width = cutoff_line_width, + ...) return(plot) } @@ -568,11 +581,57 @@ QC_Plots_Combined_Vln <- function( } # Create Individual Plots - feature_plot <- QC_Plots_Genes(seurat_object = seurat_object, group.by = group.by, low_cutoff = feature_cutoffs[1], high_cutoff = feature_cutoffs[2], pt.size = pt.size, colors_use = colors_use, x_lab_rotate = x_lab_rotate, y_axis_log = y_axis_log, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, cutoff_line_width = cutoff_line_width, ...) - - UMI_plot <- QC_Plots_UMIs(seurat_object = seurat_object, group.by = group.by, low_cutoff = UMI_cutoffs[1], high_cutoff = UMI_cutoffs[2], pt.size = pt.size, colors_use = colors_use, x_lab_rotate = x_lab_rotate, y_axis_log = y_axis_log, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, cutoff_line_width = cutoff_line_width, ...) - - mito_plot <- QC_Plots_Mito(seurat_object = seurat_object, group.by = group.by, mito_name = mito_name, low_cutoff = mito_cutoffs[1], high_cutoff = mito_cutoffs[2], pt.size = pt.size, colors_use = colors_use, x_lab_rotate = x_lab_rotate, y_axis_log = y_axis_log, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, cutoff_line_width = cutoff_line_width, ...) + feature_plot <- QC_Plots_Genes(seurat_object = seurat_object, + group.by = group.by, + low_cutoff = feature_cutoffs[1], + high_cutoff = feature_cutoffs[2], + pt.size = pt.size, + colors_use = colors_use, + x_lab_rotate = x_lab_rotate, + y_axis_log = y_axis_log, + raster = raster, + ggplot_default_colors = ggplot_default_colors, + color_seed = color_seed, + plot_median = plot_median, + median_size = median_size, + plot_boxplot = plot_boxplot, + cutoff_line_width = cutoff_line_width, + ...) + + UMI_plot <- QC_Plots_UMIs(seurat_object = seurat_object, + group.by = group.by, + low_cutoff = UMI_cutoffs[1], + high_cutoff = UMI_cutoffs[2], + pt.size = pt.size, + colors_use = colors_use, + x_lab_rotate = x_lab_rotate, + y_axis_log = y_axis_log, + raster = raster, + ggplot_default_colors = ggplot_default_colors, + color_seed = color_seed, + plot_median = plot_median, + median_size = median_size, + plot_boxplot = plot_boxplot, + cutoff_line_width = cutoff_line_width, + ...) + + mito_plot <- QC_Plots_Mito(seurat_object = seurat_object, + group.by = group.by, + mito_name = mito_name, + low_cutoff = mito_cutoffs[1], + high_cutoff = mito_cutoffs[2], + pt.size = pt.size, + colors_use = colors_use, + x_lab_rotate = x_lab_rotate, + y_axis_log = y_axis_log, + raster = raster, + ggplot_default_colors = ggplot_default_colors, + color_seed = color_seed, + plot_median = plot_median, + median_size = median_size, + plot_boxplot = plot_boxplot, + cutoff_line_width = cutoff_line_width, + ...) # wrap plots plots <- wrap_plots(feature_plot, UMI_plot, mito_plot, ncol = 3) @@ -685,6 +744,12 @@ QC_Histogram <- function( plot_titles <- all_found_features } + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + # Plot if (is.null(x = split.by)) { plot_list <- lapply(1:length(x = all_found_features), function(x) { @@ -692,16 +757,8 @@ QC_Histogram <- function( plot <- ggplot(data = data_to_plot, aes(x = .data[[all_found_features[x]]])) + geom_histogram(color = "black", fill = colors_use, bins = bins) + theme_cowplot() + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + ggtitle(plot_titles[x]) - - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } }) # wrap and return plots @@ -739,17 +796,8 @@ QC_Histogram <- function( plot <- ggplot(data = sub_data, aes(x = .data[[all_found_features]])) + geom_histogram(color = "black", fill = colors_use[x], bins = bins) + theme_cowplot() + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") + ggtitle(meta_sample_list[x]) - - # cutoff linewidth adjustment - if (!is.null(x = cutoff_line_width)) { - plot <- plot + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) - } else { - plot <- plot + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red") - } - }) # wrap and return plots @@ -893,6 +941,12 @@ QC_Plot_UMIvsGene <- function( } } + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + if (isFALSE(x = ident_legend) && isFALSE(x = combination)) { cli_warn(message = "{.code ident_legend} parameter ignored as {.code combination = FALSE}") } @@ -1130,12 +1184,20 @@ QC_Plot_GenevsFeature <- function( } } + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + # Plot - FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nFeature, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + + plot <- FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nFeature, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + geom_hline(yintercept = c(low_cutoff_gene, high_cutoff_gene), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + + return(plot) } @@ -1242,10 +1304,18 @@ QC_Plot_UMIvsFeature <- function( } } + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.635 is as close to default as I can approximate + if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_line_width <- 0.635 + } + # Plot - FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nCount, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + + plot <- FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nCount, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + geom_hline(yintercept = c(low_cutoff_UMI, high_cutoff_UMI), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + xlab(x_axis_label) + ylab(y_axis_label) + + return(plot) } From b27e7263840de38c247023c3409a9c30e45f40c2 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:09:25 -0500 Subject: [PATCH 53/74] Update changelog --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b32a88703..d1e0795cb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,7 @@ - Add checks for appropriate `...` usage. ## Fixes -- Fixed setting of `linewidth` parameter to be conditional in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. +- Fixed setting default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. From 1c45a55fd4592a8082e3b2dde26c3db43e25b199 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Tue, 3 Mar 2026 10:09:38 -0500 Subject: [PATCH 54/74] bump version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 00aefe6ae..3b7f3d5b8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.9007 +Version: 3.2.4.9008 Date: 2026-03-03 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), From c2306cbe499cd50b62a9b35d2b2ed18f88b6ee59 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:49:32 -0500 Subject: [PATCH 55/74] fix sample_col parameter to avoid deprecation warning --- R/LIGER_Utilities.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/LIGER_Utilities.R b/R/LIGER_Utilities.R index 408ee8479..a922df152 100644 --- a/R/LIGER_Utilities.R +++ b/R/LIGER_Utilities.R @@ -581,7 +581,7 @@ Dataset_Size_LIGER <- function( found_meta <- Meta_Present(object = liger_object, meta_col_names = meta_data_column, print_msg = FALSE)[[1]] - sample_meta <- Extract_Sample_Meta(object = liger_object, sample_name = "dataset", variables_include = found_meta) + sample_meta <- Extract_Sample_Meta(object = liger_object, sample_col = "dataset", variables_include = found_meta) # join data dataset_cells_df <- right_join(x = dataset_cells_df, y = sample_meta, by = join_by("dataset")) From 963d8ebd9ab19d383bf210e078ba925955155d20 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:53:17 -0500 Subject: [PATCH 56/74] Fix manual entries for Extract_Sample_Meta --- R/Utilities_Objects.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/Utilities_Objects.R b/R/Utilities_Objects.R index c8cac1c03..986f0a930 100644 --- a/R/Utilities_Objects.R +++ b/R/Utilities_Objects.R @@ -497,7 +497,7 @@ Add_Sample_Meta <- function( #' @param include_all logical, whether or not to include all object meta data columns in output data.frame. #' Default is FALSE. #' -#' @return Returns a data.frame with one row per `sample_name`. +#' @return Returns a data.frame with one row per `sample_col`. #' #' @importFrom dplyr any_of grouped_df select slice #' @importFrom magrittr "%>%" @@ -510,14 +510,14 @@ Add_Sample_Meta <- function( #' library(Seurat) #' pbmc_small[["batch"]] <- sample(c("batch1", "batch2"), size = ncol(pbmc_small), replace = TRUE) #' -#' sample_meta <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident") +#' sample_meta <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident") #' #' # Only return specific columns from meta data (orig.ident and batch) -#' sample_meta2 <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident", +#' sample_meta2 <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident", #' variables_include = "batch") #' #' # Return all columns from meta data -#' sample_meta3 <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident", +#' sample_meta3 <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident", #' include_all = TRUE) #' From 2208ba50122b71887120cc4238603de56466c54a Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:53:24 -0500 Subject: [PATCH 57/74] Update changelog --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index d1e0795cb..f4fc398ac 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,7 @@ ## Fixes - Fixed setting default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. +- Fixed warning in `Dataset_Size_LIGER` due to deprecation in `Extract_Sample_Meta`. From 341153b19ac99b9ade73619f8af69117f11d6cf7 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:54:56 -0500 Subject: [PATCH 58/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3b7f3d5b8..5b784855d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.9008 -Date: 2026-03-03 +Version: 3.2.4.9009 +Date: 2026-03-04 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From 7cf0b2a1c9a6af1c769ef38bf57792478162bbca Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:55:15 -0500 Subject: [PATCH 59/74] Update docs --- man/Extract_Sample_Meta.Rd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/Extract_Sample_Meta.Rd b/man/Extract_Sample_Meta.Rd index d49dc3b3f..e45324f4e 100644 --- a/man/Extract_Sample_Meta.Rd +++ b/man/Extract_Sample_Meta.Rd @@ -38,7 +38,7 @@ function can be joined with output of \code{\link[scCustomize]{Median_Stats}}. Default is FALSE.} } \value{ -Returns a data.frame with one row per \code{sample_name}. +Returns a data.frame with one row per \code{sample_col}. } \description{ Returns a by identity meta.data data.frame with one row per sample. Useful for downstream @@ -48,14 +48,14 @@ quick view of sample breakdown, meta data table creation, and/or use in pseudobu library(Seurat) pbmc_small[["batch"]] <- sample(c("batch1", "batch2"), size = ncol(pbmc_small), replace = TRUE) -sample_meta <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident") +sample_meta <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident") # Only return specific columns from meta data (orig.ident and batch) -sample_meta2 <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident", +sample_meta2 <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident", variables_include = "batch") # Return all columns from meta data -sample_meta3 <- Extract_Sample_Meta(object = pbmc_small, sample_name = "orig.ident", +sample_meta3 <- Extract_Sample_Meta(object = pbmc_small, sample_col = "orig.ident", include_all = TRUE) } From 8c1eaedd5e0495aadaa4c365a60ef4ede34d83c5 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:27:36 -0500 Subject: [PATCH 60/74] deprecate old line_width to linewidth --- R/Plotting_QC_Seurat.R | 278 +++++++++++++++++++++++++++++------------ R/Plotting_Seurat.R | 23 +++- 2 files changed, 219 insertions(+), 82 deletions(-) diff --git a/R/Plotting_QC_Seurat.R b/R/Plotting_QC_Seurat.R index 443a9bbd8..7d7f4fc86 100644 --- a/R/Plotting_QC_Seurat.R +++ b/R/Plotting_QC_Seurat.R @@ -14,7 +14,9 @@ #' @param x_axis_label Label for x axis. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting. #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -54,7 +56,8 @@ QC_Plots_Genes <- function( y_axis_label = "Features", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, plot_boxplot = FALSE, @@ -68,6 +71,15 @@ QC_Plots_Genes <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_Genes(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -77,13 +89,13 @@ QC_Plots_Genes <- function( nFeature <- paste0("nFeature_", assay) # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nFeature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + @@ -115,7 +127,9 @@ QC_Plots_Genes <- function( #' @param x_axis_label Label for x axis. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting. #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -155,7 +169,8 @@ QC_Plots_UMIs <- function( y_axis_label = "UMIs", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -169,6 +184,15 @@ QC_Plots_UMIs <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_UMIs(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -178,13 +202,13 @@ QC_Plots_UMIs <- function( nCount <- paste0("nCount_", assay) # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = nCount, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + @@ -218,7 +242,9 @@ QC_Plots_UMIs <- function( #' @param x_axis_label Label for x axis. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting. #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -258,7 +284,8 @@ QC_Plots_Mito <- function( y_axis_label = "% Mitochondrial Gene Counts", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -271,17 +298,26 @@ QC_Plots_Mito <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_Mito(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = mito_name, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + @@ -314,7 +350,9 @@ QC_Plots_Mito <- function( #' @param plot_title Plot Title. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting. #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -356,7 +394,8 @@ QC_Plots_Feature <- function( plot_title = NULL, low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -369,6 +408,15 @@ QC_Plots_Feature <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_Feature(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -377,13 +425,13 @@ QC_Plots_Feature <- function( } # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } plot <- VlnPlot_scCustom(seurat_object = seurat_object, features = feature, group.by = group.by, colors_use = colors_use, pt.size = pt.size, raster = raster, ggplot_default_colors = ggplot_default_colors, color_seed = color_seed, plot_median = plot_median, plot_boxplot = plot_boxplot, median_size = median_size, ...) + - geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle(plot_title) + @@ -416,7 +464,9 @@ QC_Plots_Feature <- function( #' @param plot_title Plot Title. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -457,7 +507,8 @@ QC_Plots_Complexity <- function( plot_title = "Cell Complexity", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, plot_boxplot = FALSE, @@ -470,6 +521,15 @@ QC_Plots_Complexity <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_Complexity(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # plot plot <- QC_Plots_Feature(seurat_object = seurat_object, feature = feature, @@ -489,7 +549,7 @@ QC_Plots_Complexity <- function( plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, - cutoff_line_width = cutoff_line_width, + cutoff_linewidth = cutoff_linewidth, ...) return(plot) @@ -508,7 +568,9 @@ QC_Plots_Complexity <- function( #' @param mito_cutoffs Numeric vector of length 1 or 2 to plot lines for potential low/high threshold for filtering. #' @param mito_name The column name containing percent mitochondrial counts information. Default value is #' "percent_mito" which is default value created when using `Add_Mito_Ribo()`. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param pt.size Point size for plotting #' @param plot_median logical, whether to plot median for each ident on the plot (Default is FALSE). #' @param median_size Shape size for the median is plotted. @@ -547,7 +609,8 @@ QC_Plots_Combined_Vln <- function( UMI_cutoffs = NULL, mito_cutoffs = NULL, mito_name = "percent_mito", - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -560,6 +623,15 @@ QC_Plots_Combined_Vln <- function( color_seed = 123, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plots_Combined_Vln(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -595,7 +667,7 @@ QC_Plots_Combined_Vln <- function( plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, - cutoff_line_width = cutoff_line_width, + cutoff_linewidth = cutoff_linewidth, ...) UMI_plot <- QC_Plots_UMIs(seurat_object = seurat_object, @@ -612,7 +684,7 @@ QC_Plots_Combined_Vln <- function( plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, - cutoff_line_width = cutoff_line_width, + cutoff_linewidth = cutoff_linewidth, ...) mito_plot <- QC_Plots_Mito(seurat_object = seurat_object, @@ -630,7 +702,7 @@ QC_Plots_Combined_Vln <- function( plot_median = plot_median, median_size = median_size, plot_boxplot = plot_boxplot, - cutoff_line_width = cutoff_line_width, + cutoff_linewidth = cutoff_linewidth, ...) # wrap plots @@ -653,7 +725,9 @@ QC_Plots_Combined_Vln <- function( #' @param features Feature from meta.data, assay features, or feature name shortcut to plot. #' @param low_cutoff Plot line a potential low threshold for filtering. #' @param high_cutoff Plot line a potential high threshold for filtering. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param split.by Feature to split plots by (i.e. "orig.ident"). #' @param bins number of bins to plot default is 250. #' @param colors_use color to fill histogram bars, default is "dodgerblue". @@ -684,18 +758,28 @@ QC_Plots_Combined_Vln <- function( QC_Histogram <- function( seurat_object, - features, - low_cutoff = NULL, - high_cutoff = NULL, - cutoff_line_width = NULL, - split.by = NULL, - bins = 250, - colors_use = "dodgerblue", - num_columns = NULL, - plot_title = NULL, - assay = NULL, - print_defaults = FALSE + features, + low_cutoff = NULL, + high_cutoff = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, + split.by = NULL, + bins = 250, + colors_use = "dodgerblue", + num_columns = NULL, + plot_title = NULL, + assay = NULL, + print_defaults = FALSE ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Histogram(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -745,9 +829,9 @@ QC_Histogram <- function( } # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } # Plot @@ -757,7 +841,7 @@ QC_Histogram <- function( plot <- ggplot(data = data_to_plot, aes(x = .data[[all_found_features[x]]])) + geom_histogram(color = "black", fill = colors_use, bins = bins) + theme_cowplot() + - geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + + geom_vline(xintercept = c(low_cutoff, high_cutoff), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + ggtitle(plot_titles[x]) }) @@ -823,7 +907,9 @@ QC_Histogram <- function( #' @param high_cutoff_gene Plot line a potential high threshold for filtering genes per cell. #' @param low_cutoff_UMI Plot line a potential low threshold for filtering UMIs per cell. #' @param high_cutoff_UMI Plot line a potential high threshold for filtering UMIs per cell. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param colors_use vector of colors to use for plotting by identity. #' @param meta_gradient_name Name of continuous meta data variable to color points in plot by. #' (MUST be continuous variable i.e. "percent_mito"). @@ -881,7 +967,8 @@ QC_Plot_UMIvsGene <- function( high_cutoff_gene = Inf, low_cutoff_UMI = -Inf, high_cutoff_UMI = Inf, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, meta_gradient_name = NULL, meta_gradient_color = viridis_plasma_dark_high, @@ -900,6 +987,15 @@ QC_Plot_UMIvsGene <- function( shuffle_seed = 1, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plot_UMIvsGene(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -942,9 +1038,9 @@ QC_Plot_UMIvsGene <- function( } # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } if (isFALSE(x = ident_legend) && isFALSE(x = combination)) { @@ -1009,8 +1105,8 @@ QC_Plot_UMIvsGene <- function( scale_color_gradientn(colors = meta_gradient_color, limits = c(meta_gradient_low_cutoff, NA), na.value = meta_gradient_na_color) + theme_cowplot() + theme(plot.title = element_text(hjust = 0.5)) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle("Genes vs. UMIs per Cell/Nucleus", subtitle = c(paste0("Correlation of full dataset is: ", plot_cor_full, ".", "\nCorrelation of filtered dataset would be: ", plot_cor_filtered, ". ", "\nThe low cutoff for plotting ", meta_gradient_name, " is: ", meta_cutoff_reported))) @@ -1021,8 +1117,8 @@ QC_Plot_UMIvsGene <- function( scale_color_gradientn(colors = meta_gradient_color, limits = c(meta_gradient_low_cutoff, NA), na.value = meta_gradient_na_color) + theme_cowplot() + theme(plot.title = element_text(hjust = 0.5)) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle("Genes vs. UMIs per Cell/Nucleus", subtitle = c(paste0("Correlation of full dataset is: ", plot_cor_full, ".", "\nCorrelation of filtered dataset would be: ", plot_cor_filtered, ". ", "\nThe low cutoff for plotting ", meta_gradient_name, " is: ", meta_cutoff_reported))) @@ -1031,8 +1127,8 @@ QC_Plot_UMIvsGene <- function( # Plot by identity if (is.null(x = meta_gradient_name) && isFALSE(x = combination)) { p1 <- FeatureScatter(object = seurat_object, feature1 = nCount, feature2 = nFeature, cells = cells, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle("Genes vs. UMIs per Cell/Nucleus", subtitle = c(paste0("Correlation of full dataset is: ", plot_cor_full, ".", "\nCorrelation of filtered dataset would be: ", plot_cor_filtered, "."))) @@ -1042,8 +1138,8 @@ QC_Plot_UMIvsGene <- function( if (isTRUE(x = combination)) { # Plot by identity p1 <- FeatureScatter(object = seurat_object, feature1 = nCount, feature2 = nFeature, cells = cells, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) + ggtitle("") @@ -1058,8 +1154,8 @@ QC_Plot_UMIvsGene <- function( scale_color_gradientn(colors = meta_gradient_color, limits = c(meta_gradient_low_cutoff, NA), na.value = meta_gradient_na_color) + theme_cowplot() + theme(plot.title = element_text(hjust = 0.5)) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) } else { @@ -1068,8 +1164,8 @@ QC_Plot_UMIvsGene <- function( scale_color_gradientn(colors = meta_gradient_color, limits = c(meta_gradient_low_cutoff, NA), na.value = meta_gradient_na_color) + theme_cowplot() + theme(plot.title = element_text(hjust = 0.5)) + - geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(if(is.finite(x = low_cutoff_gene)) {low_cutoff_gene}, if(is.finite(x = high_cutoff_gene)) {high_cutoff_gene}), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(if(is.finite(x = low_cutoff_UMI)) {low_cutoff_UMI}, if(is.finite(x = high_cutoff_UMI)) {high_cutoff_UMI}), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) } @@ -1093,7 +1189,9 @@ QC_Plot_UMIvsGene <- function( #' @param high_cutoff_gene Plot line a potential high threshold for filtering genes per cell. #' @param low_cutoff_feature Plot line a potential low threshold for filtering feature1 per cell. #' @param high_cutoff_feature Plot line a potential high threshold for filtering feature1 per cell. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param colors_use vector of colors to use for plotting by identity. #' @param pt.size Adjust point size for plotting. #' @param group.by Name of one or more metadata columns to group (color) cells by (for example, orig.ident). @@ -1134,7 +1232,8 @@ QC_Plot_GenevsFeature <- function( high_cutoff_gene = NULL, low_cutoff_feature = NULL, high_cutoff_feature = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, pt.size = 1, group.by = NULL, @@ -1146,6 +1245,15 @@ QC_Plot_GenevsFeature <- function( shuffle_seed = 1, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plot_GenevsFeature(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -1185,15 +1293,15 @@ QC_Plot_GenevsFeature <- function( } # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } # Plot plot <- FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nFeature, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + - geom_hline(yintercept = c(low_cutoff_gene, high_cutoff_gene), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff_gene, high_cutoff_gene), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) @@ -1213,7 +1321,9 @@ QC_Plot_GenevsFeature <- function( #' @param high_cutoff_UMI Plot line a potential high threshold for filtering UMI per cell. #' @param low_cutoff_feature Plot line a potential low threshold for filtering feature1 per cell. #' @param high_cutoff_feature Plot line a potential high threshold for filtering feature1 per cell. -#' @param cutoff_line_width numerical value for thickness of cutoff lines, default is NULL. +#' @param cutoff_line_width `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. +#' @param cutoff_linewidth numerical value for thickness of cutoff lines, +#' default is NULL, uses ggplot2 defaults. #' @param colors_use vector of colors to use for plotting by identity. #' @param pt.size Adjust point size for plotting. #' @param group.by Name of one or more metadata columns to group (color) cells by (for example, orig.ident). @@ -1254,7 +1364,8 @@ QC_Plot_UMIvsFeature <- function( high_cutoff_UMI = NULL, low_cutoff_feature = NULL, high_cutoff_feature = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, pt.size = 1, group.by = NULL, @@ -1266,6 +1377,15 @@ QC_Plot_UMIvsFeature <- function( shuffle_seed = 1, ... ) { + # check deprecation + if (is_present(cutoff_line_width)) { + deprecate_warn(when = "3.5.0", + what = "QC_Plot_UMIvsFeature(cutoff_line_width)", + details = c("i" = "The {.code cutoff_line_width} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- cutoff_line_width + } + # Check Seurat Is_Seurat(seurat_object = seurat_object) @@ -1305,15 +1425,15 @@ QC_Plot_UMIvsFeature <- function( } # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings - # 0.635 is as close to default as I can approximate - if (is.null(x = cutoff_line_width) && packageVersion(pkg = "ggplot2") >= "4.0.0") { - cutoff_line_width <- 0.635 + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 } # Plot plot <- FeatureScatter(object = seurat_object, feature1 = feature1, feature2 = nCount, pt.size = pt.size, shuffle = TRUE, raster = raster, raster.dpi = raster.dpi, cols = colors_use, group.by = group.by, seed = shuffle_seed, ...) + - geom_hline(yintercept = c(low_cutoff_UMI, high_cutoff_UMI), linetype = "dashed", color = "red", linewidth = cutoff_line_width) + - geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_line_width) + + geom_hline(yintercept = c(low_cutoff_UMI, high_cutoff_UMI), linetype = "dashed", color = "red", linewidth = cutoff_linewidth) + + geom_vline(xintercept = c(low_cutoff_feature, high_cutoff_feature), linetype = "dashed", color = "blue", linewidth = cutoff_linewidth) + xlab(x_axis_label) + ylab(y_axis_label) diff --git a/R/Plotting_Seurat.R b/R/Plotting_Seurat.R index 597a6fcdc..6d1a5c421 100644 --- a/R/Plotting_Seurat.R +++ b/R/Plotting_Seurat.R @@ -2666,9 +2666,10 @@ FeatureScatter_scCustom <- function( #' @param ndims The number of dims to plot. Default is NULL and will plot all dims #' @param reduction The reduction to use, default is "pca" #' @param calc_cutoffs logical, whether or not to calculate the cutoffs, default is TRUE. -#' @param plot_cutoffs lgoical, whether to plot the cutoffs as vertical lines on plot, default is TRUE. +#' @param plot_cutoffs logical, whether to plot the cutoffs as vertical lines on plot, default is TRUE. #' @param line_colors colors for the cutoff lines, default is c("dodgerblue", "firebrick"). -#' @param linewidth widith of the cutoff lines, default is 0.5. +#' @param cutoff_linewidth width of the cutoff lines, default is NULL, uses ggplot2 default. +#' @param linewidth `r lifecycle::badge("soft-deprecated")`. See `cutoff_linewidth`. #' #' @references Modified from following: \url{https://hbctraining.github.io/scRNA-seq/lessons/elbow_plot_metric.html}. #' @@ -2691,8 +2692,18 @@ ElbowPlot_scCustom <- function( calc_cutoffs = TRUE, plot_cutoffs = TRUE, line_colors = c("dodgerblue", "firebrick"), - linewidth = 0.5 + cutoff_linewidth = NULL, + linewidth = deprecated() ) { + # check deprecation + if (is_present(linewidth)) { + deprecate_warn(when = "3.5.0", + what = "ElbowPlot_scCustom(linewidth)", + details = c("i" = "The {.code linewidth} parameter is soft-deprecated. Please update code to use `cutoff_linewidth` instead.") + ) + cutoff_linewidth <- linewidth + } + # check seurat Is_Seurat(seurat_object = seurat_object) @@ -2734,6 +2745,12 @@ ElbowPlot_scCustom <- function( cli_abort(message = "The number of values provided to {.code line_colors} must be either 1 or 2.") } + # set default `linewidth` when ggplot2 >= 4.0.0 to avoid empty aesthetic warnings + # 0.6365 is as close to default as I can approximate + if (is.null(x = cutoff_linewidth) && packageVersion(pkg = "ggplot2") >= "4.0.0") { + cutoff_linewidth <- 0.6365 + } + # Create plot plot <- ElbowPlot(seurat_object, ndims = ndims, reduction = reduction) From 2c7c24efb533401420a4182ab4e2f909aee7ab46 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:29:24 -0500 Subject: [PATCH 61/74] Update changelog --- NEWS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index f4fc398ac..46c270b0e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,7 +7,9 @@ ## Changed - **BREAKING CHANGE** The `plot_by` parameter has been soft-deprecated in `Seq_QC_Plot_*` family of functions. Please use `group.by` instead. Using `plot_by` will warn user but still work until scCustomize v3.5.0. -- Add checks for appropriate `...` usage. +- **BREAKING CHANGE** The parameter `cutoff_line_width` has been soft-deprecated in `QC_Plot_*` family of functions. Please use `cutoff_linewidth` instead. Using `cutoff_line_width` will warn user but still work until scCustomize v3.5.0. +- **BREAKING CHANGE** The parameter `linewidth` has been soft-deprecated in `ElbowPlot_scCustom` function. Please use `cutoff_linewidth` instead. Using `linewidth` will warn user but still work until scCustomize v3.5.0. +- Add checks for appropriate `...` usage. ## Fixes - Fixed setting default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. From 92d5a6d0af37220c2911218071f5a20a0469e1ef Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:29:40 -0500 Subject: [PATCH 62/74] Update docs --- man/ElbowPlot_scCustom.Rd | 9 ++++++--- man/QC_Histogram.Rd | 8 ++++++-- man/QC_Plot_GenevsFeature.Rd | 8 ++++++-- man/QC_Plot_UMIvsFeature.Rd | 8 ++++++-- man/QC_Plot_UMIvsGene.Rd | 8 ++++++-- man/QC_Plots_Combined_Vln.Rd | 8 ++++++-- man/QC_Plots_Complexity.Rd | 8 ++++++-- man/QC_Plots_Feature.Rd | 8 ++++++-- man/QC_Plots_Genes.Rd | 8 ++++++-- man/QC_Plots_Mito.Rd | 8 ++++++-- man/QC_Plots_UMIs.Rd | 8 ++++++-- 11 files changed, 66 insertions(+), 23 deletions(-) diff --git a/man/ElbowPlot_scCustom.Rd b/man/ElbowPlot_scCustom.Rd index 22791ddce..24cb194c1 100644 --- a/man/ElbowPlot_scCustom.Rd +++ b/man/ElbowPlot_scCustom.Rd @@ -11,7 +11,8 @@ ElbowPlot_scCustom( calc_cutoffs = TRUE, plot_cutoffs = TRUE, line_colors = c("dodgerblue", "firebrick"), - linewidth = 0.5 + cutoff_linewidth = NULL, + linewidth = deprecated() ) } \arguments{ @@ -23,11 +24,13 @@ ElbowPlot_scCustom( \item{calc_cutoffs}{logical, whether or not to calculate the cutoffs, default is TRUE.} -\item{plot_cutoffs}{lgoical, whether to plot the cutoffs as vertical lines on plot, default is TRUE.} +\item{plot_cutoffs}{logical, whether to plot the cutoffs as vertical lines on plot, default is TRUE.} \item{line_colors}{colors for the cutoff lines, default is c("dodgerblue", "firebrick").} -\item{linewidth}{widith of the cutoff lines, default is 0.5.} +\item{cutoff_linewidth}{width of the cutoff lines, default is NULL, uses ggplot2 default.} + +\item{linewidth}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} } \value{ ggplot2 object diff --git a/man/QC_Histogram.Rd b/man/QC_Histogram.Rd index a677b3654..088c57535 100644 --- a/man/QC_Histogram.Rd +++ b/man/QC_Histogram.Rd @@ -9,7 +9,8 @@ QC_Histogram( features, low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, split.by = NULL, bins = 250, colors_use = "dodgerblue", @@ -28,7 +29,10 @@ QC_Histogram( \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{split.by}{Feature to split plots by (i.e. "orig.ident").} diff --git a/man/QC_Plot_GenevsFeature.Rd b/man/QC_Plot_GenevsFeature.Rd index f968becb3..8cd6bb133 100644 --- a/man/QC_Plot_GenevsFeature.Rd +++ b/man/QC_Plot_GenevsFeature.Rd @@ -13,7 +13,8 @@ QC_Plot_GenevsFeature( high_cutoff_gene = NULL, low_cutoff_feature = NULL, high_cutoff_feature = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, pt.size = 1, group.by = NULL, @@ -43,7 +44,10 @@ QC_Plot_GenevsFeature( \item{high_cutoff_feature}{Plot line a potential high threshold for filtering feature1 per cell.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{colors_use}{vector of colors to use for plotting by identity.} diff --git a/man/QC_Plot_UMIvsFeature.Rd b/man/QC_Plot_UMIvsFeature.Rd index 49b41dc6b..ba3f5fb19 100644 --- a/man/QC_Plot_UMIvsFeature.Rd +++ b/man/QC_Plot_UMIvsFeature.Rd @@ -13,7 +13,8 @@ QC_Plot_UMIvsFeature( high_cutoff_UMI = NULL, low_cutoff_feature = NULL, high_cutoff_feature = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, pt.size = 1, group.by = NULL, @@ -43,7 +44,10 @@ QC_Plot_UMIvsFeature( \item{high_cutoff_feature}{Plot line a potential high threshold for filtering feature1 per cell.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{colors_use}{vector of colors to use for plotting by identity.} diff --git a/man/QC_Plot_UMIvsGene.Rd b/man/QC_Plot_UMIvsGene.Rd index 1394f5d4b..9ac9f7d4e 100644 --- a/man/QC_Plot_UMIvsGene.Rd +++ b/man/QC_Plot_UMIvsGene.Rd @@ -12,7 +12,8 @@ QC_Plot_UMIvsGene( high_cutoff_gene = Inf, low_cutoff_UMI = -Inf, high_cutoff_UMI = Inf, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, colors_use = NULL, meta_gradient_name = NULL, meta_gradient_color = viridis_plasma_dark_high, @@ -47,7 +48,10 @@ QC_Plot_UMIvsGene( \item{high_cutoff_UMI}{Plot line a potential high threshold for filtering UMIs per cell.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{colors_use}{vector of colors to use for plotting by identity.} diff --git a/man/QC_Plots_Combined_Vln.Rd b/man/QC_Plots_Combined_Vln.Rd index 1e3f09b40..baa41dde6 100644 --- a/man/QC_Plots_Combined_Vln.Rd +++ b/man/QC_Plots_Combined_Vln.Rd @@ -11,7 +11,8 @@ QC_Plots_Combined_Vln( UMI_cutoffs = NULL, mito_cutoffs = NULL, mito_name = "percent_mito", - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -40,7 +41,10 @@ default is the current active.ident of the object.} \item{mito_name}{The column name containing percent mitochondrial counts information. Default value is "percent_mito" which is default value created when using \code{Add_Mito_Ribo()}.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting} diff --git a/man/QC_Plots_Complexity.Rd b/man/QC_Plots_Complexity.Rd index 769ac3e1d..a7c278dec 100644 --- a/man/QC_Plots_Complexity.Rd +++ b/man/QC_Plots_Complexity.Rd @@ -13,7 +13,8 @@ QC_Plots_Complexity( plot_title = "Cell Complexity", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, plot_boxplot = FALSE, @@ -45,7 +46,10 @@ default is the current active.ident of the object.} \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting} diff --git a/man/QC_Plots_Feature.Rd b/man/QC_Plots_Feature.Rd index d48e76ccc..49e7192ef 100644 --- a/man/QC_Plots_Feature.Rd +++ b/man/QC_Plots_Feature.Rd @@ -13,7 +13,8 @@ QC_Plots_Feature( plot_title = NULL, low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -45,7 +46,10 @@ default is the current active.ident of the object.} \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting.} diff --git a/man/QC_Plots_Genes.Rd b/man/QC_Plots_Genes.Rd index 7eca2a978..4f3afe155 100644 --- a/man/QC_Plots_Genes.Rd +++ b/man/QC_Plots_Genes.Rd @@ -12,7 +12,8 @@ QC_Plots_Genes( y_axis_label = "Features", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, plot_boxplot = FALSE, @@ -43,7 +44,10 @@ default is the current active.ident of the object.} \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting.} diff --git a/man/QC_Plots_Mito.Rd b/man/QC_Plots_Mito.Rd index be3399d02..eed70b4a6 100644 --- a/man/QC_Plots_Mito.Rd +++ b/man/QC_Plots_Mito.Rd @@ -13,7 +13,8 @@ QC_Plots_Mito( y_axis_label = "\% Mitochondrial Gene Counts", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -46,7 +47,10 @@ default is the current active.ident of the object.} \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting.} diff --git a/man/QC_Plots_UMIs.Rd b/man/QC_Plots_UMIs.Rd index 7b05a56a6..3f7a011cb 100644 --- a/man/QC_Plots_UMIs.Rd +++ b/man/QC_Plots_UMIs.Rd @@ -12,7 +12,8 @@ QC_Plots_UMIs( y_axis_label = "UMIs", low_cutoff = NULL, high_cutoff = NULL, - cutoff_line_width = NULL, + cutoff_line_width = deprecated(), + cutoff_linewidth = NULL, pt.size = NULL, plot_median = FALSE, median_size = 15, @@ -43,7 +44,10 @@ default is the current active.ident of the object.} \item{high_cutoff}{Plot line a potential high threshold for filtering.} -\item{cutoff_line_width}{numerical value for thickness of cutoff lines, default is NULL.} +\item{cutoff_line_width}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#soft-deprecated}{\figure{lifecycle-soft-deprecated.svg}{options: alt='[Soft-deprecated]'}}}{\strong{[Soft-deprecated]}}. See \code{cutoff_linewidth}.} + +\item{cutoff_linewidth}{numerical value for thickness of cutoff lines, +default is NULL, uses ggplot2 defaults.} \item{pt.size}{Point size for plotting.} From 3894c0fa05f1b03023a033ee8ff523e38bb49bb3 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:29:52 -0500 Subject: [PATCH 63/74] bump version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5b784855d..efe511345 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.9009 +Version: 3.2.4.9010 Date: 2026-03-04 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), From d4bbebdea5b2ae3b9608e226e59a117aba5a0c9f Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:16:33 -0500 Subject: [PATCH 64/74] typo fix --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 46c270b0e..77d7b6835 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,8 +11,9 @@ - **BREAKING CHANGE** The parameter `linewidth` has been soft-deprecated in `ElbowPlot_scCustom` function. Please use `cutoff_linewidth` instead. Using `linewidth` will warn user but still work until scCustomize v3.5.0. - Add checks for appropriate `...` usage. + ## Fixes -- Fixed setting default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. +- Fixed default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. - Fixed warning in `Dataset_Size_LIGER` due to deprecation in `Extract_Sample_Meta`. From e4ebfbb86ab88236539d573633be9c6ab8fc90d0 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:17:01 -0500 Subject: [PATCH 65/74] version and year updates --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 77d7b6835..bc886f0a5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# scCustomize 3.X.X (202X-XX-XX) +# scCustomize 3.3.0 (2026-XX-XX) ## Added - Added `reorder`, `reorder_decreasing`, and `plot_median` parameters to `Plot_Cells_per_Sample` when grouping by sample. - Added `Read_Velocity` function to enable reading of velocyto loom output files without needing to install velocyto.R (which currently suffers from significant installation issues unrelated to reading loom files). From 6aec142870bc0498741cf4e7b53b0b4437fa2b0a Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:17:47 -0500 Subject: [PATCH 66/74] styling --- R/Utilities.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Utilities.R b/R/Utilities.R index 996148818..3b21d7bed 100644 --- a/R/Utilities.R +++ b/R/Utilities.R @@ -1132,7 +1132,7 @@ Add_Pct_Diff <- function( overwrite = FALSE ) { # Check if percent difference exists already - if ("pct_diff" %in% colnames(marker_dataframe)) { + if ("pct_diff" %in% colnames(x = marker_dataframe)) { df_name <- deparse(expr = substitute(expr = marker_dataframe)) if (isFALSE(x = overwrite)) { cli_abort(message = c("{.val pct_diff} column already present in {.code marker_dataframe}: {.val {df_name}}.", From 0ca7ba02bdce575f157ea20f7ebab8f872e61ad3 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:21:47 -0500 Subject: [PATCH 67/74] styling --- R/Utilities_Objects.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/Utilities_Objects.R b/R/Utilities_Objects.R index 986f0a930..0eedd6b9a 100644 --- a/R/Utilities_Objects.R +++ b/R/Utilities_Objects.R @@ -892,7 +892,7 @@ Random_Cells_Downsample <- function( rownames() %>% sample(size = num_cells[x]) }) - names(random_cells) <- idents_all + names(x = random_cells) <- idents_all } else { # set seed and select random cells per ident prev_seed <- get_seed() From 42ae77b7f81da0d80e3db189c2123eba5753a966 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:23:08 -0500 Subject: [PATCH 68/74] styling --- R/Utilities_Objects.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/Utilities_Objects.R b/R/Utilities_Objects.R index 0eedd6b9a..9d00c84f8 100644 --- a/R/Utilities_Objects.R +++ b/R/Utilities_Objects.R @@ -1089,7 +1089,12 @@ Store_Palette_Seurat <- function( # Check Seurat Is_Seurat(seurat_object = seurat_object) - seurat_object <- Store_Misc_Info_Seurat(seurat_object = seurat_object, data_to_store = palette, data_name = palette_name, list_as_list = list_as_list, overwrite = overwrite, verbose = verbose) + seurat_object <- Store_Misc_Info_Seurat(seurat_object = seurat_object, + data_to_store = palette, + data_name = palette_name, + list_as_list = list_as_list, + overwrite = overwrite, + verbose = verbose) return(seurat_object) } @@ -1168,7 +1173,7 @@ Add_Alt_Feature_ID <- function( # if providing features_tsv if (!is.null(x = features_tsv_file)) { features_table <- data.table::fread(file = features_tsv_file, header = FALSE, data.table = FALSE) - colnames(features_table) <- c("Ensembl_ID", "Symbol", "Modality") + colnames(x = features_table) <- c("Ensembl_ID", "Symbol", "Modality") features_table$Symbol <- make.unique(features_table$Symbol) From 62663a73d9ce8aa2384de9423e44706bd89c929b Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:45:01 -0400 Subject: [PATCH 69/74] fix issue with split plots and group levels if only one group present in split --- R/Plotting_Seurat.R | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/R/Plotting_Seurat.R b/R/Plotting_Seurat.R index 6d1a5c421..2ae4f5f5e 100644 --- a/R/Plotting_Seurat.R +++ b/R/Plotting_Seurat.R @@ -2188,7 +2188,14 @@ DimPlot_scCustom <- function( if (is.null(x = group.by)) { levels_overall <- levels(x = Idents(object = seurat_object)) } else { - levels_overall <- levels(x = seurat_object@meta.data[[group.by]]) + if (inherits(x = seurat_object@meta.data[[group.by]], what = "factor")) { + levels_overall <- levels(x = seurat_object@meta.data[[group.by]]) + } else { + cli_inform(message = c("The variable {.field {group.by}} is not a factor. Converting for plot.", + "i" = "To avoid this warning set meta.data column to factor before plotting.")) + seurat_object@meta.data[[group.by]] <- factor(seurat_object@meta.data[[group.by]]) + levels_overall <- levels(x = seurat_object@meta.data[[group.by]]) + } } if (length(x = levels_overall) > length(x = colors_use)) { @@ -2197,7 +2204,9 @@ DimPlot_scCustom <- function( colors_overall <- colors_use - names(x = colors_overall) <- levels_overall + if (is.null(x = names(x = colors_overall))) { + names(x = colors_overall) <- levels_overall + } # plot plots <- lapply(1:length(x = split_by_list), function(x) { From 23ee3f5b4500dbc2d989c3b4a8c01838ab3cc88f Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:48:12 -0400 Subject: [PATCH 70/74] Update changelog --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index bc886f0a5..6b853792b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,6 +15,7 @@ ## Fixes - Fixed default setting of `linewidth` parameter in `geom_hline` and `geom_vline` calls to avoid unnecessary empty aesthetic warnings in ggplot2 4.0.0+. - Fixed warning in `Dataset_Size_LIGER` due to deprecation in `Extract_Sample_Meta`. +- Fixed issue with group colors in in `DimPlot_scCustom` when split only contains one group Thanks @zrlewis, ([#262](https://github.com/samuel-marsh/scCustomize/issues/262)). From 02a7fc88209f1206982083f6a93a4bbe055453ef Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:48:18 -0400 Subject: [PATCH 71/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index efe511345..038b54171 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.9010 -Date: 2026-03-04 +Version: 3.2.4.9011 +Date: 2026-03-25 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From ca70891b0f97fcb17bd12871f8e95cc442b6de55 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 2 Apr 2026 08:06:14 -0400 Subject: [PATCH 72/74] Update changelog --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6b853792b..ac7e5395b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ - **BREAKING CHANGE** The `plot_by` parameter has been soft-deprecated in `Seq_QC_Plot_*` family of functions. Please use `group.by` instead. Using `plot_by` will warn user but still work until scCustomize v3.5.0. - **BREAKING CHANGE** The parameter `cutoff_line_width` has been soft-deprecated in `QC_Plot_*` family of functions. Please use `cutoff_linewidth` instead. Using `cutoff_line_width` will warn user but still work until scCustomize v3.5.0. - **BREAKING CHANGE** The parameter `linewidth` has been soft-deprecated in `ElbowPlot_scCustom` function. Please use `cutoff_linewidth` instead. Using `linewidth` will warn user but still work until scCustomize v3.5.0. -- Add checks for appropriate `...` usage. +- Add some checks for appropriate `...` usage. ## Fixes From 0531750a922ae0fc323854b8778793cdd771db68 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 2 Apr 2026 08:06:46 -0400 Subject: [PATCH 73/74] bump version and date --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 038b54171..c652887a5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,8 +2,8 @@ Package: scCustomize Type: Package Title: Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing Description: Collection of functions created and/or curated to aid in the visualization and analysis of single-cell data using 'R'. 'scCustomize' aims to provide 1) Customized visualizations for aid in ease of use and to create more aesthetic and functional visuals. 2) Improve speed/reproducibility of common tasks/pieces of code in scRNA-seq analysis with a single or group of functions. For citation please use: Marsh SE (2021) "Custom Visualizations & Functions for Streamlined Analyses of Single Cell Sequencing" RRID:SCR_024675. -Version: 3.2.4.9011 -Date: 2026-03-25 +Version: 3.2.4.9012 +Date: 2026-04-02 Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), From 867425bc789d779c73a843b30db0fcecada24874 Mon Sep 17 00:00:00 2001 From: Samuel Marsh <38284410+samuel-marsh@users.noreply.github.com> Date: Thu, 2 Apr 2026 08:11:35 -0400 Subject: [PATCH 74/74] standardize format --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c652887a5..09fed5401 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -8,8 +8,8 @@ Authors@R: c( person(given = "Samuel", family = "Marsh", email = "sccustomize@gmail.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-3012-6945")), person(given = "Ming", family = "Tang", role = c("ctb"), email = "tangming2005@gmail.com"), person(given = 'Velina', family = 'Kozareva', role = c('ctb')), - person("Lucas", "Graybuck", email = "lucasg@alleninstitute.org", role = c('ctb')), - person("Zoe", "Clarke", email = "zoe.clarke@utoronto.ca", role = c('ctb'), comment = c(ORCID = "0000-0003-0336-8049")) + person(given ="Lucas", family = "Graybuck", email = "lucasg@alleninstitute.org", role = c('ctb')), + person(given ="Zoe", family = "Clarke", email = "zoe.clarke@utoronto.ca", role = c('ctb'), comment = c(ORCID = "0000-0003-0336-8049")) ) URL: https://github.com/samuel-marsh/scCustomize, https://samuel-marsh.github.io/scCustomize/, https://doi.org/10.5281/zenodo.5706431 BugReports: https://github.com/samuel-marsh/scCustomize/issues