Open-source project
zqfang/GSEApy avatar
zqfang/GSEApy

GSEApy: gene set enrichment analysis that stays inside your Python workflow

Gene Set Enrichment Analysis in Python

716 stars147 forksPythonBSD-3-Clause

At a glance

What is it?
GSEApy is a Python package with a Rust core that re-implements the Broad GSEA workflow, including a Rust port of the fgsea multilevel algorithm, plus wrappers for ssGSEA, GSVA and Enrichr. It suits analysts who want enrichment results and publication figures from pandas without a detour through R.
Who is it for?
GSEApy fits a team that lives in pandas, needs publication figures and wants the Broad file formats without the desktop tool, and it is the sensible pick when p-value resolution below 1/permutation_num matters because the multilevel method is available as method='multilevel'. Skip it if you need the full GSEA feature set, since the project points to the Broad documentation for that, or if you cannot install a Rust toolchain for a source build.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Enrichment analysis for people who already live in pandas

The author states the motivation directly: he wanted to explore data with Pandas and could not find a convenient tool to do gene set enrichment analysis inside Python. The project answers that gap with a Python and Rust implementation of GSEA and a wrapper for Enrichr. It handles RNA-seq, ChIP-seq and microarray data, supports convenient Gene Ontology enrichment, and produces publication quality figures in Python.

The workflow arguments matter more than the feature list. GSEApy runs inside an interactive console without switching to R, it is described as friendly to both wet and dry lab users, and it produces or reproduces publishable figures. Batch jobs are straightforward, and the tool drops into a bash shell or a data analysis pipeline such as snakemake. Documentation lives at gseapy.readthedocs.io, the project homepage is gseapy.rtfd.io, and the package is published on PyPI and Bioconda.

The project is a single-author effort by Zhuoqing Fang, with a peer reviewed paper in Bioinformatics (2022, doi 10.1093/bioinformatics/btac757) that you will need if you use this in published work.

Seven sub-commands, each with its own input contract

GSEApy exposes seven modules, and the choice is driven by the shape of your data rather than by preference. The gsea module produces GSEA results from a txt file (FPKM, expected counts, TPM and similar), a cls file, and a gene set file in gmt format. The prerank module takes a pre-ranked gene list with correlation values in .rnk format plus a gmt file, and it is described as an API to the GSEA pre-rank tools. The ssgsea module performs single sample GSEA and accepts a pd.Series indexed by gene name, or a pd.DataFrame that may be a GCT file, alongside a GMT file. The gsva module implements the GSVA method by Hanzelmann and colleagues with the same input shape as ssGSEA. The replot module reproduces GSEA desktop results, and its only input is the location of GSEA Desktop output. The enrichr module calls the Enrichr API, and the biomart module converts gene identifiers through the BioMart API. Per-module options are documented with 'gseapy COMMAND -h'. All file formats are identical to the GSEA desktop version.

A Rust extension sits under the Python API

The performance work is not a wrapper around an existing binary. setup.py declares the Rust extension explicitly: project metadata lives in pyproject.toml and that file exists solely for the Rust extension built through setuptools_rust. The extension is named gseapy.gse, is built from Cargo.toml, links through the PyO3 binding, and switches on debug builds when the environment contains DEBUG.

python
from setuptools_rust import Binding, RustExtension
python
        RustExtension(
            "gseapy.gse",
            "Cargo.toml",
            debug="DEBUG" in os.environ,
            binding=Binding.PyO3,
        )

Cargo.toml confirms the split. The crate is named gse, built as a cdylib, and depends on pyo3 0.29.0 with the extension-module feature, rayon 1.10 for parallel work, rand 0.9 with the small_rng feature, csv, itertools and statrs. The Python layer keeps numpy, scipy, pandas, matplotlib and requests, so the environment you already have for tables and plots is the one you need.

One point of confusion is worth stating: Cargo.toml carries version 1.3.0 for the crate while the Python package version in pyproject.toml is 1.3.1, and the published releases are v1.3.1, v1.3.0 and v1.2.1.

Installing gseapy: conda, pip, uv, or a Rust toolchain first

The installation section gives three equivalent routes, and any of them gets a working install on a machine with a prebuilt wheel:

bash
   # if you have conda/mamba
   $ conda install -c bioconda gseapy
   # or pip
   $ pip install gseapy
   # or uv
   $ uv add gseapy

If the pip install fails, the documented fallback is to install the Rust toolchain first and then build from source. For gseapy above version 0.11.0 a Rust compiler is needed, along with setuptools-rust at build time.

bash
   # install rust toolchain
   curl https://sh.rustup.rs -sSf | sh -s -- -y
   export PATH="$PATH:$HOME/.cargo/bin"

That path means a machine without a matching wheel has to carry a Rust compiler, and the README also notes Python 3.8+ as the requirement. The package metadata is stricter: requires-python is set to >=3.9. Runtime dependencies are numpy>=1.13.0, scipy, pandas, matplotlib>=2.2 and requests, and the classifiers mark the project as Development Status :: 4 - Beta with support declared for MacOS X, Windows and POSIX.

The prerank multilevel path is where the Rust port pays off

The headline feature is described as a faithful Rust port of the fgsea multilevel algorithm that resolves p-values far below 1/permutation_num. In other words, the permutation count no longer puts a floor under how small a p-value can get, which matters when a gene set separates so cleanly that the standard method saturates. The prerank module supports it through method='multilevel', and the command line form is shown with an explicit permutation sample size and tolerance:

bash
  # An example to run Prerank with the fgsea multilevel p-value method
  $ gseapy prerank -r gsea_data.rnk -g gene_sets.gmt --method multilevel --sample-size 101 --eps 1e-50 -o test

The faithfulness claim has a specific technical anchor. Cargo.toml includes rand_mt 6.0.3 with default-features disabled, commented as the fgsea multilevel core with a bit-for-bit boost::mt19937 match, plus the special crate for digamma and trigamma, and statrs 0.17 for statistics. Reproducing the random number stream is what makes results comparable with the reference implementation, not merely similar.

The plain prerank run takes the same inputs without the method flag:

bash
  # An example to run Prerank using gseapy prerank module
  $ gseapy prerank -r gsea_data.rnk -g gene_sets.gmt -o test

Running the modules and reading the figures

The command line examples in the README are the fastest way to see the input contract of each module. The gsea module takes the expression table, the class file, the gene set file and an output directory:

bash
  # An example to run GSEA using gseapy gsea module
  $ gseapy gsea -d exptable.txt -c test.cls -g gene_sets.gmt -o test

Single sample and GSVA runs share the same shape, one taking a plain expression file:

bash
  # An example to run ssGSEA using gseapy ssgsea module
  $ gseapy ssgsea -d expression.txt -g gene_sets.gmt -o test
bash
  # An example to run GSVA using gseapy ssgsea module
  $ gseapy gsva -d expression.txt -g gene_sets.gmt -o test

Enrichr is the odd one out because it is a network call rather than a local file analysis, and the input is a gene list plus a library name:

bash
  # An example to use enrichr api
  $ gseapy enrichr -i gene_list.txt -g KEGG_2016 -o test

The replot module is worth understanding separately. Its only input is the directory of GSEA Desktop output, which makes it a bridge rather than an analyser: run the desktop tool if you want, then regenerate the figures in Python.

bash
  # An example to reproduce figures using replot module.
  $ gseapy replot -i ./Gsea.reports -o test

The README states that using the same data for GSEAPreranked and GSEApy reproduces similar results, and shows a side by side comparison of the two outputs.

Where GSEApy stops and the Broad GSEA documentation takes over

The README is explicit about scope. The full GSEA is, in its words, far too extensive to describe, and it points to the Broad GSEA documentation for the complete picture, with all file formats identical to the desktop version. So GSEApy is a faithful re-implementation of the mainstream path, not a superset of it. If your analysis needs a GSEA feature that has no matching sub-command, you need the original tool.

Two failure modes are practical rather than theoretical. The enrichr module depends on a remote service, Enrichr, that the README describes as open source and freely available at http://amp.pharm.mssm.edu/Enrichr, so the module is only as available as that endpoint and your network path to it. The biomart module has the same property through the BioMart API. In both cases the result is not reproducible from a local file if the service is down.

A second constraint is the build itself. Anyone on a platform without a prebuilt wheel needs a Rust compiler on the machine, and a scientific environment that is frozen and air-gapped has to plan for that. The project also has no dependency lock for pip users in the same way uv.lock serves uv users.

Metadata and documentation disagree on two details worth checking

Two small inconsistencies are visible in the project files and could matter during review or setup. The first is licensing. pyproject.toml declares license as BSD-3-Clause and points license-files at LICENSE, and the repository has a LICENSE file, while the README still carries a badge that reads MIT. The metadata and the badge do not agree, so a downstream project that copied the badge into its own documentation may have recorded the wrong identifier. You should not treat the badge as authoritative.

The second is the Python floor. The README lists Python 3.8+ under dependencies, but pyproject.toml sets requires-python to >=3.9, which is what a package manager will actually enforce when it resolves the dependency. A 3.8 environment will be rejected at install time even though the README suggests it is supported.

Maintenance is a matter of record rather than promise. The last push was on 2026-09-02, and tagged releases have appeared at a reasonable cadence in 2026 with v1.3.1 on 2026-07-25, v1.3.0 on 2026-06-21 and v1.2.1 on 2026-04-27. The project is still classified as beta in its own metadata, and it is a single maintainer project, so treat that as a factor in your adoption decision.

Editorial conclusion

GSEApy fits a team that lives in pandas, needs publication figures and wants the Broad file formats without the desktop tool, and it is the sensible pick when p-value resolution below 1/permutation_num matters because the multilevel method is available as method='multilevel'. Skip it if you need the full GSEA feature set, since the project points to the Broad documentation for that, or if you cannot install a Rust toolchain for a source build. Verify first that your Python is 3.9 or newer, because the package metadata requires it while the README still says Python 3.8+, and note that the metadata declares BSD-3-Clause even though the README badge shows MIT.

Frequently asked questions

How do I install GSEApy and run my first GSEA analysis?

Install it with conda install -c bioconda gseapy, pip install gseapy, or uv add gseapy; if the install fails you need a Rust toolchain and a source build. Then run the gsea module with an expression table, a cls file and a gmt gene set file, for example gseapy gsea -d exptable.txt -c test.cls -g gene_sets.gmt -o test, and check the output directory named by -o.

What is the difference between GSEApy prerank and the ssGSEA and GSVA modules?

prerank takes a pre-ranked gene list with correlation values in .rnk format plus a gmt file, and can use the fgsea multilevel p-value method with method='multilevel'. ssGSEA performs single sample GSEA on a per-sample expression series or DataFrame, and gsva implements the GSVA method with the same input shape as ssGSEA.

Does GSEApy need Rust to install?

Only when there is no prebuilt wheel for your platform, since the package is built as a Rust extension through setuptools_rust and setup.py registers the gseapy.gse extension with the PyO3 binding. The README says a Rust compiler is needed for gseapy above 0.11.0, and documents installing the toolchain with rustup before retrying pip or uv.

Can I reproduce GSEA desktop figures with GSEApy?

Yes. The replot module takes only the location of GSEA Desktop output results and regenerates the figures, for example gseapy replot -i ./Gsea.reports -o test. The README also states that the same data run through GSEAPreranked and GSEApy reproduces similar results.

What license is GSEApy released under?

The package metadata in pyproject.toml declares license = "BSD-3-Clause" with the license file in the LICENSE entry, and the repository contains a LICENSE file. The README badge still says MIT, so rely on the metadata and the LICENSE file rather than the badge when you record the license in your own project.

Official sources

  1. License: BSD-3-Clause
  2. Project website
  3. README
  4. Releases
  5. zqfang/GSEApy on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zqfang-gseapy.svg)](https://hysenlabs.com/projects/zqfang-gseapy)

Was this useful?