diff --git a/DESCRIPTION b/DESCRIPTION index 2cdac3190..bdf90336f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,5 +1,5 @@ Package: Seurat -Version: 5.1.0.9017 +Version: 5.1.0.9018 Title: Tools for Single Cell Genomics Description: A toolkit for quality control, analysis, and exploration of single cell RNA sequencing data. 'Seurat' aims to enable users to identify and interpret sources of heterogeneity from single cell transcriptomic measurements, and to integrate diverse types of single cell data. See Satija R, Farrell J, Gennert D, et al (2015) , Macosko E, Basu A, Satija R, et al (2015) , Stuart T, Butler A, et al (2019) , and Hao, Hao, et al (2020) for more details. Authors@R: c( diff --git a/NEWS.md b/NEWS.md index f385e8135..abfd8dbd8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,8 @@ # Unreleased ## Changes +- Added `group.by` parameter to `FindAllMarkers`, allowing users to regroup their data using a non-default identity class prior to performing differential expression ([#9550](https://github.com/satijalab/seurat/pull/9550)) +#' performing differential expression (see example); \code{"ident"} to use Idents - Added `image.type` parameter to `Read10X_Image` enabling `VisiumV1` instances to be populated instead of instances of the default `VisiumV2` class ([#9556](https://github.com/satijalab/seurat/pull/9556)) - Fixed `IntegrateLayers` to respect the `dims.to.integrate` parameter. - Added `stroke.size` parameter to `DimPlot` ([#8180](https://github.com/satijalab/seurat/pull/8180)) diff --git a/R/differential_expression.R b/R/differential_expression.R index 515491545..6e45e8cfe 100644 --- a/R/differential_expression.R +++ b/R/differential_expression.R @@ -46,6 +46,7 @@ FindAllMarkers <- function( object, assay = NULL, features = NULL, + group.by = NULL, logfc.threshold = 0.1, test.use = 'wilcox', slot = 'data', @@ -75,11 +76,25 @@ FindAllMarkers <- function( return.thresh <- 0.7 } if (is.null(x = node)) { + if (!is.null(x = group.by) && !identical(x = group.by, y = "ident")) { + if (length(x = group.by) == 1 && ! group.by %in% colnames(x = object@meta.data)) { + stop("'", group.by, "' not found in object metadata") + } + Idents(object = object) <- group.by + } idents.all <- sort(x = unique(x = Idents(object = object))) } else { if (!PackageCheck('ape', error = FALSE)) { stop(cluster.ape, call. = FALSE) } + if (!is.null(group.by)) { + warning( + paste0( + "The `group.by` parameter for `FindAllMarkers` ", + "is ignored when `node` is set." + ) + ) + } tree <- Tool(object = object, slot = 'BuildClusterTree') if (is.null(x = tree)) { stop("Please run 'BuildClusterTree' before finding markers on nodes") @@ -896,7 +911,7 @@ FindMarkers.DimReduc <- function( #' use all other cells for comparison; if an object of class \code{phylo} or #' 'clustertree' is passed to \code{ident.1}, must pass a node to find markers for #' @param group.by Regroup cells into a different identity class prior to -#' performing differential expression (see example) +#' performing differential expression (see example); \code{"ident"} to use Idents #' @param subset.ident Subset a particular identity class prior to regrouping. #' Only relevant if group.by is set (see example) #' @param assay Assay to use in differential expression testing @@ -919,10 +934,13 @@ FindMarkers.Seurat <- function( reduction = NULL, ... ) { - if (!is.null(x = group.by)) { + if (!is.null(x = group.by) && !identical(x = group.by, y = "ident")) { if (!is.null(x = subset.ident)) { object <- subset(x = object, idents = subset.ident) } + if (length(x = group.by) == 1 && ! group.by %in% colnames(x = object@meta.data)) { + stop("'", group.by, "' not found in object metadata") + } Idents(object = object) <- group.by } if (!is.null(x = assay) && !is.null(x = reduction)) { diff --git a/man/FindAllMarkers.Rd b/man/FindAllMarkers.Rd index a2f9f764e..35183b632 100644 --- a/man/FindAllMarkers.Rd +++ b/man/FindAllMarkers.Rd @@ -9,6 +9,7 @@ FindAllMarkers( object, assay = NULL, features = NULL, + group.by = NULL, logfc.threshold = 0.1, test.use = "wilcox", slot = "data", @@ -37,6 +38,9 @@ FindAllMarkers( \item{features}{Genes to test. Default is to use all genes} +\item{group.by}{Regroup cells into a different identity class prior to +performing differential expression (see example); \code{"ident"} to use Idents} + \item{logfc.threshold}{Limit testing to genes which show, on average, at least X-fold difference (log-scale) between the two groups of cells. Default is 0.1 Increasing logfc.threshold speeds up the function, but can miss weaker signals. diff --git a/man/FindMarkers.Rd b/man/FindMarkers.Rd index 373d58190..4a437fc8f 100644 --- a/man/FindMarkers.Rd +++ b/man/FindMarkers.Rd @@ -231,7 +231,7 @@ use all other cells for comparison; if an object of class \code{phylo} or 'clustertree' is passed to \code{ident.1}, must pass a node to find markers for} \item{group.by}{Regroup cells into a different identity class prior to -performing differential expression (see example)} +performing differential expression (see example); \code{"ident"} to use Idents} \item{subset.ident}{Subset a particular identity class prior to regrouping. Only relevant if group.by is set (see example)} diff --git a/tests/testthat/test_differential_expression.R b/tests/testthat/test_differential_expression.R index f0c685670..060d485e1 100644 --- a/tests/testthat/test_differential_expression.R +++ b/tests/testthat/test_differential_expression.R @@ -366,12 +366,17 @@ test_that("BPCells FindMarkers gives same results", { # Tests for FindAllMarkers # ------------------------------------------------------------------------------- -results <- suppressMessages(suppressWarnings(FindAllMarkers(object = pbmc_small,pseudocount.use=1))) -results.clr <- suppressMessages(suppressWarnings(FindAllMarkers(object = clr.obj, pseudocount.use=1))) -results.sct <- suppressMessages(suppressWarnings(FindAllMarkers(object = sct.obj, pseudocount.use=1, vst.flavor = "v1"))) -results.pseudo <- suppressMessages(suppressWarnings(FindAllMarkers(object = pbmc_small, pseudocount.use = 0.1))) test_that("FindAllMarkers works as expected", { + pbmc_copy <- pbmc_small + Idents(pbmc_copy) <- "orig.ident" + + results <- suppressMessages(suppressWarnings(FindAllMarkers(object = pbmc_small, pseudocount.use = 1))) + results.clr <- suppressMessages(suppressWarnings(FindAllMarkers(object = clr.obj, pseudocount.use = 1))) + results.sct <- suppressMessages(suppressWarnings(FindAllMarkers(object = sct.obj, pseudocount.use = 1, vst.flavor = "v1"))) + results.pseudo <- suppressMessages(suppressWarnings(FindAllMarkers(object = pbmc_small, pseudocount.use = 0.1))) + results.gb <- suppressMessages(suppressWarnings(FindAllMarkers(object = pbmc_copy, pseudocount.use = 1, group.by = "RNA_snn_res.1"))) + expect_equal(colnames(x = results), c("p_val", "avg_log2FC", "pct.1", "pct.2", "p_val_adj", "cluster", "gene")) expect_equal(results[1, "p_val"], 9.572778e-13, tolerance = 1e-18) expect_equal(results[1, "avg_log2FC"], -6.030507, tolerance = 1e-6) @@ -407,6 +412,10 @@ test_that("FindAllMarkers works as expected", { expect_equal(results.pseudo[1, "p_val_adj"], 2.201739e-10, tolerance = 1e-15) expect_equal(nrow(x = results.pseudo), 222) expect_equal(rownames(results.pseudo)[1], "HLA-DPB1") + + # Setting `group.by` the group by parameter is equivalent + # to setting the object's `Idents` before running `FindAllMarkers`. + expect_equal(results.gb, results) })