1111# ' @param group.by Name of grouping variable to use. If `NULL`, use the active
1212# ' cell identities.
1313# ' @param assay Name of assay to use. If `NULL`, use the default assay.
14- # ' @param var.features Subset to only variable features for ontology term
15- # ' enrichment.
14+ # ' @param var.features Restrict the analysis to variable features. When `TRUE`
15+ # ' (the default), differential testing is performed only on the variable
16+ # ' features of the assay, so the ranked list scored by [fgsea::fgsea()] (the
17+ # ' enrichment universe) and the term gene sets are evaluated within the same
18+ # ' variable-feature space. Requires variable features to be set for the assay
19+ # ' (e.g. with [FindTopFeatures()]).
1620# ' @param scoreType `scoreType` parameter for [fgsea::fgseaSimple()].
1721# ' Options are "std", "pos", "neg" (two-tailed or one-tailed tests).
1822# ' @param direction Which direction of enrichment to retain. `"up"` (default)
@@ -55,12 +59,25 @@ EnrichedTerms <- function(
5559
5660 assay <- assay %|| % DefaultAssay(object = object )
5761
62+ marker.args <- list (... )
5863 if (var.features ) {
64+ var.feat <- VariableFeatures(object = object [[assay ]])
65+ if (length(x = var.feat ) == 0 ) {
66+ stop(
67+ " var.features = TRUE but no variable features are set for assay '" ,
68+ assay , " '. Compute variable features first (for example with " ,
69+ " FindTopFeatures()) or set var.features = FALSE."
70+ )
71+ }
72+ # Restrict the analysis to variable features
73+ marker.args $ features <- if (is.null(x = marker.args $ features )) {
74+ var.feat
75+ } else {
76+ intersect(x = marker.args $ features , y = var.feat )
77+ }
5978 terms <- lapply(
6079 X = terms ,
61- FUN = function (x ) {
62- x [x %in% VariableFeatures(object = object [[assay ]])]
63- }
80+ FUN = function (x ) x [x %in% var.feat ]
6481 )
6582 }
6683
@@ -80,17 +97,19 @@ EnrichedTerms <- function(
8097 )
8198 }
8299 for (i in seq_along(along.with = cellgroups )) {
83- mk <- Seurat :: FindMarkers(
84- object = object ,
85- assay = assay ,
86- ident.1 = cellgroups [[i ]],
87- group.by = group.by ,
88- ...
100+ mk <- do.call(
101+ what = Seurat :: FindMarkers ,
102+ args = c(
103+ list (
104+ object = object ,
105+ assay = assay ,
106+ ident.1 = cellgroups [[i ]],
107+ group.by = group.by
108+ ),
109+ marker.args
110+ )
89111 )
90- mk $ rank_score <- sign(mk $ avg_log2FC ) * - log10(mk $ p_val )
91- ranked_list <- setNames(object = mk $ rank_score , nm = rownames(x = mk ))
92- max_non_infinite <- max(ranked_list [! is.infinite(x = ranked_list )])
93- ranked_list [is.infinite(x = ranked_list )] <- max_non_infinite
112+ ranked_list <- RankFeatures(markers = mk )
94113
95114 # run fgsea
96115 fgsea_results <- fgsea :: fgsea(
@@ -127,3 +146,26 @@ EnrichedTerms <- function(
127146 }
128147 return (pred )
129148}
149+
150+ # Build a named ranked statistic vector for fgsea from a FindMarkers result.
151+ # The score for each feature is sign(avg_log2FC) * -log10(p_val), so that
152+ # strongly up-regulated features get large positive scores and strongly
153+ # down-regulated features get large negative scores. A p-value of exactly zero
154+ # produces an infinite score (+Inf when up-regulated, -Inf when down-regulated);
155+ # each infinite score is capped at the most extreme finite score of the same
156+ # sign. Capping the two signs separately keeps up-regulated features at the top
157+ # of the ranking and down-regulated features at the bottom
158+ # @param markers A data frame of differential test results with columns
159+ # `avg_log2FC` and `p_val` and feature names as row names (as returned by
160+ # [Seurat::FindMarkers()]).
161+ # @return A named numeric vector of ranking statistics.
162+ RankFeatures <- function (markers ) {
163+ rank_score <- sign(x = markers $ avg_log2FC ) * - log10(x = markers $ p_val )
164+ ranked_list <- setNames(object = rank_score , nm = rownames(x = markers ))
165+ finite_scores <- ranked_list [is.finite(x = ranked_list )]
166+ pos_cap <- if (length(x = finite_scores ) > 0 ) max(finite_scores ) else 1
167+ neg_cap <- if (length(x = finite_scores ) > 0 ) min(finite_scores ) else - 1
168+ ranked_list [ranked_list == Inf ] <- pos_cap
169+ ranked_list [ranked_list == - Inf ] <- neg_cap
170+ return (ranked_list )
171+ }
0 commit comments