Repository navigation
Add gene set enrichment analysis of ranked genes, as fgsea - #54
Merged
Merged
Conversation
gsea ports fgsea 1.32.2 (MIT licence; notice in THIRD_PARTY_NOTICES.md): enrichment scores and leading edges, normalized scores, and p-values by fgsea's multilevel method or by permutations of the genes, from seeds that a numpy generator draws. Given the seeds fgsea draws, it returns fgsea's table. plot_enrichment and plot_gsea_table draw fgsea's enrichment plot and table. Checked against fgsea 1.32.2 (validation/gsea.py: 24 checks, none failed).
parashardhapola
force-pushed
the
46-gsea
branch
from
October 26, 2026 17:59
3c99fd1 to
d812f2e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Gene set enrichment analysis (GSEA) is the pathway method most users know: it ranks
every gene by its change and asks whether a set's genes gather at the top or at the
bottom of the ranking (Subramanian et al. 2005). This adds preranked GSEA as fgsea
1.32.2 computes it (Korotkevich et al. 2021): enrichment scores and leading edges,
normalized scores, and p-values by fgsea's default multilevel method or by
permutations, from a differential-expression result, any table of results or a mapping
of gene id to statistic, with the two figures fgsea users expect. fgsea is MIT
licensed, so its algorithms are ported with their order of operations: given the seeds
fgsea draws,
gseareturns fgsea's table, and with its own seeds it agrees withinMonte Carlo error.
Changes
cytepool.gsea.gsea(statistics, gene_sets, *, group, contrast, id_column, statistic_column, min_size, max_size, exponent, score_type, method, n_permutations, sample_size, eps, rng, n_threads):one row per set tested, in the order of
gene_sets, withgene_set,n_genes(theset's genes among the ranked genes),
enrichment_score,normalized_enrichment_score,p_value,adjusted_p_value(Benjamini-Hochberg),log2_error(fgsea'slog2err) andleading_edge(genes separated by", ", fromthe most extreme statistic inwards). Statistics: a
DifferentialExpressionResultwitha group and contrast, a
Tableor pandas DataFrame (id_column,statistic_column,default
"t"), a pandas Series or a mapping. Sets: aGeneSetsor a mapping. fgsea'soptions under their own names:
exponent(gseaParam),score_type("std","pos","neg"),min_size/max_size,method("multilevel",fgseaMultilevel;"permutation",fgseaSimple),n_permutations(nPermSimpleornperm),sample_size,eps.cytepool.plotting.plot_enrichment(statistics, gene_set, *, group, contrast, id_column, statistic_column, exponent, ax):fgsea's
plotEnrichment, the running sum with a tick at each gene of the set anddashed lines at its extremes.
cytepool.plotting.plot_gsea_table(statistics, gene_sets, table, *, group, contrast, id_column, statistic_column, top, exponent, ax):fgsea's
plotGseaTablefor the top sets up and down (as fgsea's guide chooses them),each with its genes as bars at their ranks, its NES, p-value and adjusted p-value.
cytepool._ranked_genes(private): reading statistics into a ranking, matching setsto it, and the running sum; it also now holds the group-and-contrast and collection
checks that
over_representationhad, with the same messages.cytepool._options.random_generator(private): the check ofrng, moved from therotation tests, which use it unchanged.
rows in "Choosing a method", the overview, citing and glossary, fgsea moved among the
tools CytePool is validated against, four new references (Korotkevich 2021, Mootha
2003, Matsumoto 1998, Durstenfeld 1964; DOIs checked against Crossref, the preprint
version against bioRxiv) on the Bibliography page, the Validation page (section,
summary row, totals, the R environment) and the README status row.
THIRD_PARTY_NOTICES.md: what is adapted from fgsea, file by file, with its licence.Design notes
calcGseaStat; random sets followcalcGseaStatCumulative(the scores of everyprefix of one random sample of the largest set size, from upper convex hulls in
square-root blocks, O(K^1.5) per permutation, including fgsea's floor of 1e-5/1024
under the smallest nonzero statistic); the multilevel method follows
EsRuler(duplication of the upper half, the Markov chain with chunked walks and a remembered
gene,
getPvaluewith digamma-based level probabilities and the sign correction); theR-level rules follow
fgseaSimpleImplandfgseaMultilevel(NES, the(1 + b)/(1 + n)p-values, NaN for fewer than 10 random scores of a set's sign, the choice of the
multilevel method by comparing a beta-quantile interval of the permutation estimate
with
multilevelError,eps). The order of floating-point operations is kept, sothat decisions in the Markov chain fall the same way: the p-values agree to 1e-13.
rng(a numpy Generator, a seed, or None for a fresh one) drawsdistinct seeds from 1 to 10^9: two for the multilevel method, as fgsea's two
sample.int(10^9, 1), or one per chunk ofmax(1000, ceiling(n / 128))permutations,as
fgseaSimple. Each starts MT19937 seeded as C++'sstd::mt19937(checked againstthe C++ standard's 10,000th output and against numpy's MT19937), with fgsea's
rejection sampler for integers. Permutation chunks and multilevel set sizes run in
parallel with seeds of their own, so results do not depend on the number of threads.
extended-precision
cumsumandsumdo (found by probing), so that ties between stepsof the walk, which decide the leading edge, fall as in fgsea.
sort(decreasing = TRUE)keeps them (found by probing); a warning gives the share oftied nonzero statistics, as fgsea's does. fgsea's other warnings are kept: all
statistics positive with
"std", sets with too few random scores of their sign,p-values likely overestimated, p-values below
eps.refuses them, so users drop them first); infinite statistics, ids on two rows, fewer
than two ranked genes and all-zero statistics are refused. A set of every ranked gene
is never tested, as in fgsea. No set in the size range gives an empty table, as in
fgsea; no gene of any set among the ranked genes is an error naming examples of both.
when no set goes to the multilevel method, fgsea adjusts over the sets before it
removes those with 1 to 9 random scores of their sign, so its values come out
smaller or larger. (2) fgsea tests a set alone with R's
sample.int; CytePool usesfgsea's sampler for one set as for many (agreement within Monte Carlo error).
(3) Random samples of at least half the genes are shuffled by Fisher-Yates with
fgsea's integers, where fgsea calls
std::shuffle, whose algorithm is left to eachC++ library. The table keeps the order of
gene_sets; fgsea sorts by name.the defaults: fgsea 2.9 s,
gsea4.1 s on one thread after compilation; 10,000permutations: fgsea 1.9 s,
gsea1.5 s on 2 threads.collapsePathways,fora, label permutation (fgseaLabel) andGESECA.
Validation
validation/gsea.pyagainst fgsea 1.32.2 (R 4.4.3), on 4,000 simulated genes (6% up,5% down, 60 tied at 1.25, 30 at zero) and 90 sets of 1 to 1,000 genes (a fifth outside
the ranking; enriched up, down, both or none; every gene, one gene, the tied block, a
set listing genes twice, two identical sets). The R script records the seeds fgsea
draws where it hands them to its compiled code (
traceof fgsea's MIT-licensedfgseaSimpleImplandmultilevelImpl), andgseadraws the same throughrng.Tiers: exact (1e-10) for scores, leading edges, figures and, given fgsea's seeds,
everything; calibrated (4 standard errors of the difference of the means of 20 runs)
with seeds of their own. 7 min 3 s, 323 MiB peak, with 2 threads.
Tests
tests/test_gsea.py, 54 tests, against fixtures that the validation writes fromfgsea (300 genes, 14 sets): every exact case replayed with fgsea's seeds (all columns
to 1e-10), fgsea's choice of the multilevel method for every count (1,000 and 2,000
permutations), the single set within Monte Carlo error, and
plot_enrichmentagainstplotEnrichmentData. Independent of the code: enrichment scores and leading edgesderived by hand (up, down, equal deviations with each score type, the tie rule); MT19937
against the C++ standard and numpy; the hull scores of every prefix against a walk of
each prefix; invariants (negated statistics negate the scores; tied genes outside a set
and the order of genes or of sets leave results unchanged; seeds reproduce results and
threads do not change them); sizes after matching, duplicates and genes outside the
ranking; a result, its table, a mapping and a pandas Series rank alike; adjusted p-values
are BH of the reported p-values; warnings and messages;
plot_gsea_table's selection,bars and number format. The masked-input inventory gains
gsea,plot_enrichmentandplot_gsea_table(each with a variant for a differential-expression result). Themodule runs in about 2 to 5 s warm and 73 s cold (compiling the routines and limma-voom
for one test). Full suite, on top of the branches before it: 4,244 passed, 18 skipped;
also with bounds checks, and with
NUMBA_CPU_NAME=generic,OPENBLAS_CORETYPE=Sandybridgeand 4 threads.