GOATOOLS: Gene Ontology enrichment in Python, from the obo file to the p-value table
Python library to handle Gene Ontology (GO) terms
At a glance
- What is it?
- GOATOOLS is a BSD licensed Python library for Gene Ontology work that parses obo ontology files, reads association files in several formats, runs Fisher's exact test for over and under-representation and applies multiple testing corrections from Bonferroni through several false discovery rate procedures. It is aimed at bioinformatics teams who want enrichment analysis without hand-assembling statistics and ontology code.
- Who is it for?
- GOATOOLS fits a Python bioinformatics team that needs enrichment analysis over the full Gene Ontology, needs the multiple correction routines in the same library as the ontology parser, and wants the association file formats that current annotation releases actually ship in.
- Can I use it commercially?
- Yes. BSD-2-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 received new commits within the last day.
- 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Enrichment analysis that starts from an obo file, not a spreadsheet
GOATOOLS exists because Gene Ontology analysis is two problems wearing one coat. The first is ontology handling: the GO is a directed acyclic graph of terms with parent relationships, and any analysis has to walk that graph correctly to know what counts as an annotation. The second is statistics: an enrichment test produces many p-values at once, and those have to be corrected before you can trust any of them.
The library handles both. It processes over- and under-representation of GO terms based on Fisher's exact test, and it applies numerous multiple correction routines, including locally implemented versions of Bonferroni, Sidak, Holm and false discovery rate. It also reads the obo-formatted file from the Gene Ontology website, exposing the ontology as a directed acyclic graph that allows traversal from leaf to root.
The audience is Python users doing genomics or bioinformatics work. The README describes the package as a Python library to handle Gene Ontology terms, and the published paper describes it as a library for Gene Ontology analyses, with a peer reviewed paper in Scientific Reports. The authors list includes people associated with the Gene Ontology project itself, which is consistent with a library that follows the ontology's own file formats rather than approximating them.
One framing note helps set expectations. This is a library of components, not a single command. The README's Contents section reads as a capability list: enrichment, ontology parsing, association file readers, ancestor and descendant lookups, list comparison, plotting, text output of hierarchies and GOslim mapping. You assemble the pieces yourself, which is why the cookbook approach and the run.sh example cases exist.
Graph traversal, descendants count and information content
The graph half of the library is not decorative. Three operations on the DAG carry most of the analytical weight.
The first is the ancestor and descendant query. The README documents getting parents or ancestors for a GO term with or without optional relationships, and getting children or descendants on the same terms, plus a notebook for printing details about a GO ID's parents. The 'with or without optional relationships' part matters more than it sounds: the GO graph records relationships such as is_a and part_of, and a tool that flattens the graph with the wrong relationship set will pull in annotations you did not intend to count. Leaving the relationship set optional means the caller decides, and has to know what they are deciding.
The second is descendants count, and the third is information content. Both are described as things the package can print for a list of GO terms, with a notebook linked for the details. Descendants count answers how large a subtree hangs below a term, which is the denominator intuition that makes a term's enrichment interpretable. Information content weights a term by how rare it is in the ontology, so a broad term like a root or a high-level biological process carries less weight than a narrow one. Both are properties of the ontology itself rather than of your gene list, which is why they are computed here from the obo file you downloaded rather than looked up in a service.
The practical upshot is that the enrichment numbers in your table are traceable to a specific ontology file. If a term's descendant count is off, you can check it directly. If the result looks strange, the first thing to inspect is the graph, and this library gives you the means to do that.
Four association file formats, because the ecosystem ships several
The second half of the ingestion problem is mapping genes to terms. The README lists four readers, and the choice among them is a practical matter of which file your upstream pipeline produced.
GAF is the GO Annotation File format, GPAD is the Gene Product Association Data format, and there is support for NCBI's gene2go file and for an id2gos format, with an example linked from the repository's data directory. In other words, the library accepts the flat association formats that different archives and pipelines emit, rather than requiring you to normalise everything into one shape before you start.
The format distinction is not cosmetic. A GAF file and a GPAD file carry the same core relationship between a gene product and a GO term, but they encode it with different headers and different column conventions, and the annotation evidence codes differ in what they permit. Silently reading one as the other is a common source of results that look plausible and are wrong, so a library that names the formats it supports and refuses the others is doing you a favour.
The GO Association files are read through the same object model that the ontology is read into, which is what allows an enrichment run to work from a term list plus an association file. The library also handles protein products with multiple associations to GO terms, which is the normal case rather than an edge case, and it can map those to GOslim terms.
That last capability deserves its own note, because the alternative is a separate tool. The README describes mapping GO terms, or protein products with multiple associations to GO terms, to GOslim terms, and positions the feature as analogous to the map2slim.pl script that geneontology.org distributes. GOslim is a condensed subset of the ontology, useful when you want to talk about biological process at a level of detail that survives across species rather than drowning in thousands of narrow terms.
Fisher's exact test with corrections from Bonferroni to two-stage FDR
The statistical core is Fisher's exact test, and the correction menu is the widest part of the library.
Locally implemented routines cover Bonferroni, Sidak, Holm and false discovery rate. On top of that, the README lists corrections taken from statsmodels: FDR Benjamini/Hochberg, FDR Benjamini/Yekutieli, Holm-Sidak, Simes-Hochberg, Hommel, FDR 2-stage Benjamini-Hochberg, FDR 2-stage Benjamini-Krieger-Yekutieli, FDR adaptive Gavrilov-Benjamini-Sarkar, Bonferroni, Sidak and Holm.
The distinction between those two groups is worth understanding before you pick. A locally implemented routine ships inside GOATOOLS, and statsmodels is an optional dependency, listed as such in the Dependencies section alongside scipy.stats.fisher_exact. So the library can do the whole job with only SciPy present, and gains a wider set of procedures when statsmodels is installed.
For a genomic study the choice of procedure is a scientific decision, not a technical one, and the library deliberately does not make it for you. Bonferroni controls the family-wise error rate and is conservative. The false discovery rate procedures control the expected proportion of false positives among the rejected hypotheses, which is often the more useful quantity when you are scanning hundreds of terms. The two-stage and adaptive variants exist because the plain procedures can be badly behaved in some regimes.
The README also flags a simulation result as one of the findings of the project's own work: larger study sizes result in higher GOEA sensitivity, meaning fewer truly significant observations go unreported. The simulation code for that work is in a separate repository, goatools_simulation, and the manuscript figures are linked. The practical reading is that a small gene list will miss real signal, and a larger list will not rescue an underpowered comparison either.
Installing with pip, and fetching the ontology file you will need
Installation is straightforward, and there is a conda-forge channel as well as PyPI:
pip install goatoolsconda install -c conda-forge goatoolsThe development version is installed straight from the repository:
pip install git+git://github.com/tanghaibao/goatools.gitThe documentation asks for a Python version of at least 3.7.
The step that catches people out is the second half of setup, and it is not a package install at all. You need an .obo file of the current Gene Ontology, which the README shows as a download:
wget http://current.geneontology.org/ontology/go-basic.oboIf you want the condensed GOslim version instead, for example the generic GOslim terms, the corresponding file is:
wget http://current.geneontology.org/ontology/subsets/goslim_generic.oboThe README points to the GO download page and the GO subset guide for these files, and the library also references the GO annotation file formats documentation for the association files.
Note what this means for reproducibility. The file you download is not versioned by the library, and it is the file that determines the descendants counts, the information content and the ancestor sets behind your results. A results table and the .obo file that produced it belong together. Two runs against different ontology releases are not comparable, and the library gives you no way to notice that other than discipline on your side.
Two optional extras are worth noting for the plotting features. The README lists Graphviz for graph visualisation, plus pygraphviz as a Python binding for communicating with Graphviz, and pydot as a Python interface to Graphviz's Dot language. You need one of those two options installed if you intend to plot the ontology lineage. The dependencies section notes that when installing via PyPI or conda-forge, all dependencies are downloaded automatically.
The README also mentions run.sh, which contains example cases using the installed goatools CLI, and a docs directory with the find_enrichment example documentation for finding GO enrichment of genes under study.
Grouping, comparison, and the version number that comes from git tags
Beyond the core analysis, the library adds three conveniences that change how results are read.
GO Grouping is described as a way to visualise the major findings in a gene ontology enrichment analysis more easily, with a detailed description in the manuscript. A raw enrichment table can be long and repetitive, with related terms scattered across rows, so grouping the hits changes the shape of the output rather than the statistics behind it. The README points at a figure from the paper as an example of what a GOATOOLS output looks like.
Comparison is the second convenience: `goatools compare_gos` compares two or more lists of GO IDs, and can be used with or without grouping. This is aimed at the ordinary case where you have run enrichment on two conditions and want to know what is shared and what is specific.
The third is a GOslim mapping, already covered, which is what makes a result readable across datasets.
Now the version question, which matters more than it sounds. The repository has no GitHub releases at all. The version number is instead derived from git by setuptools_scm, and the pyproject.toml in the repository root shows the configuration, including where the derived version is written:
[tool.setuptools_scm]
write_to = "goatools/version.py"
git_describe_command = "git describe --dirty --tags --long --match v* --first-parent"
version_scheme = "no-guess-dev"The describe command only matches tags shaped like v followed by a version, so the tags in the repository are the release mechanism even though GitHub's releases page is empty. The practical consequence for you is that the version you depend on is chosen by whatever the package index resolves at install time, and pinning a version in your environment or lock file is the only way to keep an analysis repeatable across time. The build system also relies on setuptools_scm, and the project keeps a CHANGELOG.md at the root, which is where a version bump is documented.
The repository is not archived, and the last push was on 2026-10-18, two days before this review, so the code is being changed. That is good for bug fixes and a reason to pin versions rather than to track them. The licence is BSD, which the README states and the repository's LICENSE file holds, so redistribution of the library inside a commercial tool carries none of the obligations that AGPL or a custom licence would impose.
Editorial conclusion
GOATOOLS fits a Python bioinformatics team that needs enrichment analysis over the full Gene Ontology, needs the multiple correction routines in the same library as the ontology parser, and wants the association file formats that current annotation releases actually ship in. It is the wrong choice for a team whose only requirement is one p-value with one correction, since that is a few lines of scipy.stats.fisher_exact and statsmodels, and it is the wrong choice for a reproducible release pipeline that expects published versions, because the repository has no GitHub releases and the version number is derived from git tags by setuptools_scm. Before you adopt it, pin the version your environment resolves, read the CHANGELOG.md at the repository root, and confirm that the go-basic.obo or GOslim file you download matches the annotation release your genes come from.
Frequently asked questions
How do I install GOATOOLS and the Gene Ontology file it needs?
Install the package with `pip install goatools` or `conda install -c conda-forge goatools`, and download the ontology file separately, for example `wget http://current.geneontology.org/ontology/go-basic.obo`. The README asks for Python 3.7 or newer.
Which multiple testing corrections does GOATOOLS support?
Locally implemented routines cover Bonferroni, Sidak, Holm and false discovery rate, and the README also lists procedures taken from the optional statsmodels dependency, including FDR Benjamini/Hochberg, FDR Benjamini/Yekutieli, Holm-Sidak, Simes-Hochberg, Hommel, two-stage FDR variants and FDR adaptive Gavrilov-Benjamini-Sarkar.
Which gene association file formats can GOATOOLS read?
The README lists GAF, GPAD, NCBI's gene2go file and an id2gos format, alongside reading the obo-formatted ontology file from the Gene Ontology website.
Does GOATOOLS publish release versions on GitHub?
The repository has no GitHub releases. The version is derived from git tags by setuptools_scm, and the pyproject.toml configures that with a describe command matching tags of the form v followed by a version, so the tags rather than the releases page are the release mechanism.
Do I need Graphviz to use GOATOOLS?
Graphviz, or one of the Python interfaces to it, is only needed for the plotting features. The Dependencies section lists Graphviz for graph visualisation, with pygraphviz and pydot as the two alternative Python options.
Official sources
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.
[](https://hysenlabs.com/projects/tanghaibao-goatools)Was this useful?