# How to Interpret GO Enrichment Results in R

Interpreting GO enrichment results in R means reading the statistical and annotation columns returned by packages such as clusterProfiler after testing whether Gene Ontology (GO) terms are overrepresented in your gene list, and then translating those rows into biological statements you can defend. It covers verifying the test setup, evaluating p values and adjusted p values, judging effect sizes such as fold enrichment, collapsing redundant terms, and visualizing how the significant terms relate to each other.

This step matters because enrichment analysis is usually the point where a differential expression table becomes a biological story. A list of 800 differentially expressed genes tells a reviewer little on its own, but a coherent finding that ribosome and glycolysis pathways are altered, as reported in an epithelial ovarian cancer RNA-seq study that used Bioconductor tools for exactly this purpose, is a testable hypothesis about tumor metabolism [1]. The interpretation layer is also where most errors creep in, since the raw output of an enrichment test is a long, redundant table that is easy to overread [2].

## What GO Enrichment Output Actually Means

GO is a controlled vocabulary that describes gene products across three independent ontologies: Biological Process (BP), Molecular Function (MF), and Cellular Component (CC). Terms are connected in a directed acyclic graph, so a specific term such as "mitochondrial ATP synthesis coupled proton transport" inherits meaning from all of its parent terms.

Enrichment analysis asks one question: does any GO term appear in my gene list more often than expected by chance, given the annotation rate in a defined background set? Overrepresentation analysis (ORA) answers this for an unranked list, typically with a hypergeometric or Fisher exact test. Gene set enrichment analysis (GSEA) answers a related question for a ranked list, such as all genes ordered by log2 fold change, and can detect coordinated modest shifts that ORA misses [3], [4].

When you run `enrichGO()` or `gseGO()` from clusterProfiler, the main R/Bioconductor workhorse for this task, the result object converts to a data frame with a standard set of columns. Learning to read those columns is the core skill.

| Column | What it contains | How to read it |
|--|--|--|
| ID / Description | GO accession (for example GO:0006412) and term name | Your unit of interpretation, one row per term |
| ONTOLOGY | BP, MF, or CC | Always report which ontology a term came from |
| GeneRatio | Fraction like 12/200 | Genes in your list annotated to the term over total genes in your list |
| BgRatio | Fraction like 250/18000 | Genes in the background annotated to the term over total background size |
| pvalue | Hypergeometric or permutation p value | Raw significance, uncorrected for many tests |
| p.adjust | Multiple-testing corrected value, usually Benjamini-Hochberg | The number you should filter and report on |
| qvalue | Storey-style q value estimate | An alternative false discovery rate estimate, similar role to p.adjust |
| Count | Number of your genes annotated to the term | Should agree with the numerator of GeneRatio |
| Fold Enrichment / odds ratio | Effect size | How many times more frequent the term is in your list than in the background |
| geneID | The actual genes driving the term | Read this before believing the term, it exposes annotation artifacts |

A common point of confusion is that GeneRatio and Count are not independent signals. If GeneRatio reads 12/200 and Count reads 12, those describe the same 12 genes. The ratio form is useful because it lets you compute fold enrichment directly: divide 12/200 (0.060) by 250/18000 (0.0139) to get roughly 4.3, meaning the term is about four times more common in your list than in the background [3].

## How the Statistics Work Under the Hood

ORA uses the hypergeometric distribution, equivalently a Fisher exact test on a 2 by 2 [contingency table](/blog/data-analysis/what-is-a-contingency-table). For a term with 250 background genes out of 18,000, drawing 200 query genes, the test asks how often you would see 12 or more of those 250 genes land in your draw if the draw were random. Small p values indicate that the observed overlap is unlikely under random sampling.

Two consequences of this design shape everything about interpretation:

1. The test is repeated once per GO term, often thousands of times per ontology. With thousands of simultaneous tests, uncorrected p values below 0.05 appear constantly by chance, which is why every serious workflow applies a false discovery rate correction such as Benjamini-Hochberg before calling terms significant, [4].
2. The result depends entirely on the background universe. If your universe is all genes on the array but your list came only from genes that passed an expression filter, the annotation rates in the background no longer match the sampling process, and the test is biased. Specifying the universe explicitly is one of the highest-leverage decisions in the whole workflow, [5].

GSEA takes a different route. It ranks all measured genes from most up-regulated to most down-regulated, walks down the ranked list, and computes an enrichment score for each gene set based on where that set's members cluster in the ranking. Significance comes from gene set permutation. The output adds columns such as NES (normalized enrichment score), where a positive NES means the set sits toward the top of the ranked list and a negative NES toward the bottom, plus the leading edge genes that contribute most of the score [3], [4].

## A Step-by-Step clusterProfiler Workflow

This section walks through how to interpret GO enrichment results in R using a concrete human RNA-seq example. The same logic extends to other organisms through the appropriate OrgDb annotation package.

### Step 1. Install and load the packages

```r
BiocManager::install(c("clusterProfiler", "org.Hs.eg.db"))
library(clusterProfiler)
library(org.Hs.eg.db)
```

clusterProfiler, topGO, and DOSE are the three Bioconductor packages most often used for this family of analyses, with clusterProfiler handling both ORA and GSEA against GO, KEGG, and MSigDB gene sets [3].

### Step 2. Prepare the gene list and the universe

```r
de <- read.csv("degs.csv")
sig_genes <- de$ENTREZID[which(de$padj < 0.05)]
universe  <- de$ENTREZID[which(de$baseMean > 10)]
```

Two rules apply here. First, clusterProfiler expects identifier types it can match to the annotation database, and ENTREZID is the safest key for OrgDb work. If your table holds SYMBOL or Ensembl IDs, convert them first with `bitr()` and keep a record of what failed to map, because unmapped genes are silently dropped from the test. Second, pass the universe argument whenever your gene list was selected from a filtered set, so the background reflects the genes that actually had a chance to enter your list [4].

### Step 3. Run the enrichment test

```r
ego <- enrichGO(
  gene          = sig_genes,
  universe      = universe,
  OrgDb         = org.Hs.eg.db,
  keyType       = "ENTREZID",
  ont           = "BP",
  pAdjustMethod = "BH",
  pvalueCutoff  = 0.05,
  qvalueCutoff  = 0.2,
  readable      = TRUE
)
```

The `ont` argument selects the ontology, and running BP, MF, and CC separately is standard practice because they describe different aspects of biology. The `readable = TRUE` flag converts Entrez IDs back to gene symbols in the geneID column, which makes manual inspection far easier.

### Step 4. Collapse redundant terms

GO's hierarchical structure means a significant parent term often appears alongside several significant children, producing long tables where neighboring rows say nearly the same thing. Redundancy of this kind is a recognized problem across enrichment tools, and several methods exist specifically to remove it [2], [6].

```r
ego_sim <- simplify(ego, cutoff = 0.7, by = "p.adjust", select_fun = min)
```

`simplify()` uses semantic similarity between terms and keeps the best-supported representative within each redundant cluster. An alternative strategy, implemented in the gogadget package, filters the table and then clusters redundant terms hierarchically before presenting them as a heatmap [7].

### Step 5. Inspect the table

```r
res <- as.data.frame(ego_sim)
head(res[order(res$p.adjust), c("Description", "GeneRatio", "BgRatio",
                                "p.adjust", "Count", "FoldEnrichment")])
```

Read the top rows in this order. Look first at p.adjust, then at FoldEnrichment or the ratio of GeneRatio to BgRatio, then at Count to see how many genes support the term, and finally at geneID to confirm the genes make sense for the claimed process [3].

### Step 6. Visualize

```r
dotplot(ego_sim, showCategory = 15, split = NULL) +
  ggplot2::labs(title = "GO BP, adjusted p < 0.05")
emapplot(ego_sim, showCategory = 30)
```

The dot plot encodes adjusted p value as color and Count or GeneRatio as dot size. The enrichment map style plot arranges terms as a network where edges reflect shared genes, so functional themes appear as clusters rather than as flat ranked rows [4], [8].

## Reading a Result Row by Row

![Worked calculation dividing GeneRatio 18/300 by BgRatio 220/16000 to get fold enrichment of about 4.4 for ribosomal small subunit biogenesis.](/article-images/illustrations/interpret-go-enrichment-results-r-fold-enrichment-worked.webp)

Suppose one row of your table reads:

| Field | Value |
|--|--|
| Description | ribosomal small subunit biogenesis |
| GeneRatio | 18/300 |
| BgRatio | 220/16000 |
| p.adjust | 3.1e-06 |
| Count | 18 |
| FoldEnrichment | 4.4 |

The interpretation, spoken plainly: 18 of your 300 significant genes are annotated to ribosomal small subunit biogenesis. Across the 16,000 gene background, only 220 of 16,000 carry this annotation, so you would expect about 4 genes in a random draw of 300 (300 times 220 divided by 16,000 is roughly 4). Observing 18 is about 4.4-fold more than expected, and after correcting for the thousands of terms tested, the probability of seeing this much enrichment by chance remains near 3 in a million. The finding is statistically strong, well supported by gene count, and biologically specific enough to name the ribosome assembly machinery in your results.

Contrast that with a weaker row: GeneRatio 5/300, BgRatio 900/16000, p.adjust 0.048. Fold enrichment here is roughly 0.3, meaning the term is actually underrepresented in your list despite crossing the 0.05 threshold. That row is significant only because the term is depleted, or because of borderline multiple-testing behavior, and it does not belong in your headline claims. This is exactly the kind of row that reading p.adjust alone would let slip through, and it is why fold enrichment belongs next to p.adjust in every table you make.

A useful check on any biological claim is whether the direction of change of the driving genes is coherent. If the eight genes behind a "cell cycle" term all go up, the term supports a statement about proliferation. If four go up and four go down, the term may simply reflect shared annotation, and the honest statement is that cell cycle genes are represented, not activated.

## Overrepresentation, GSEA, and Fold-Change-Aware Variants

![Decision path asking whether the gene list is ranked and whether direction matters, leading to ORA or GSEA with the matching biological wording.](/article-images/illustrations/interpret-go-enrichment-results-r-ora-or-gsea-choice.webp)

The choice of method changes what your output means, and confusing the two is a frequent source of misinterpretation.

ORA treats the gene list as a bag of names and ignores how strongly each gene changed. It is the right tool when you have a natural cutoff, such as all genes with adjusted p value below 0.05, and it returns terms that are numerically overrepresented in that set.

GSEA uses the full ranking and can detect sets where many genes shift mildly in the same direction, none of which would survive an individual significance cutoff. Its results are directional, so positive and negative NES values let you say a process is up-regulated or down-regulated rather than merely present,.

Between these poles, newer methods extract additional signal from the fold changes themselves. Fold-Change-Specific Enrichment Analysis (FSEA) identifies GO terms shared by groups of genes responding with a similar magnitude, for example strongly versus weakly affected genes, and can detect terms that classical ORA and GSEA both miss because those terms are not enriched relative to the whole genome background but are tightly regulated within a specific fold-change interval [9]. If your biological question involves response magnitude rather than presence or absence of change, this family of methods is worth a look.

Reporting direction is also where visualization choice matters. Standard bar and dot plots summarize significance, but they hide whether the underlying genes went up or down. Network-based visualizations that carry node-level properties such as expression directionality were developed precisely to close that gap, letting a reader see which functional modules are driven by up-regulated versus down-regulated genes [8].

## Dealing with Redundant and Overlapping Terms

Long lists of overlapping GO terms are the norm, not a bug in your run. The GO graph's parent-child structure guarantees that a strongly enriched specific term will usually pull its parents along with it, and tools that do not address this produce output that is difficult to digest [6], [10]. Four practical remedies are in wide use:

1. Semantic similarity reduction with `simplify()` in clusterProfiler, which merges terms with similar meaning and keeps the most significant representative.
2. Network visualization, where each term is a node and edges represent gene overlap, so clusters of related terms become visually obvious. The Enrichment Map approach formalized this idea, grouping gene sets into network clusters so the major functional themes of an experiment can be read at a glance [11], and the Reimand and colleagues protocol pairs it with Cytoscape as a standard interpretation step [4].
3. Fully automated network summaries in R. The aPEAR package clusters pathway gene sets by similarity and assigns each cluster a readable name, producing an objective overview without manual parameter tuning [12].
4. Modular or comparative enrichment, where terms and genes are organized into functionally coherent modules before reporting. GeneCodis introduced modular enrichment analysis for this purpose, and its later releases extended the framework to regulatory elements such as transcription factors and miRNAs [2], [13].

A pragmatic rule for figures: show the network view to establish themes, then show a small dot plot of one representative term per theme. Reviewers rarely want fifty rows, and the network is what justifies keeping only the representatives.

## Checking Your Inputs Before Trusting the Output

Most bad GO enrichment results are produced upstream of the enrichment test itself. Four checks catch the majority of problems.

**Universe definition.** The background must describe the set of genes that could have appeared in your list. For RNA-seq, a defensible default is all genes that passed a minimal expression filter and were tested for differential expression, not the whole genome. When multiple analysis pipelines are compared end to end, enrichment results can differ substantially between pipelines, and skipping the expression filter has been identified as the choice with the highest impact on the correspondence between pipelines, especially in low-power settings [5]. The practical takeaway is to fix your preprocessing choices, record them, and keep them constant across the comparisons you intend to make.

**Annotation recency and completeness.** GO annotations are updated continuously, so results shift over time even with identical input lists. Tools that query live databases through an API, such as gprofiler2's R interface, let you timestamp an analysis by recording the database version used, which is one reason programmatic access through R packages has become standard for reproducible pipelines [14].

**Identifier mapping losses.** Converting from gene symbols or Ensembl IDs to Entrez IDs always loses some genes. If 15 percent of your list fails to map, and the failures are biased toward poorly characterized genes, the enrichment test is quietly running on a different list than the one you think you have. Report the mapping success rate.

**Species resources.** For well-annotated organisms, OrgDb-based workflows are straightforward. For organisms with sparse native annotation, platforms built from BLAST, InterProScan, and orthology-based annotation transfer, then exposed through clusterProfiler-compatible GMT libraries, show how the same interpretive logic applies once adequate gene-to-term mappings exist [15]. If your species lacks an OrgDb, building or sourcing a GMT file and using clusterProfiler's `enricher()` function with that file follows the same statistical logic and returns the same table structure.

## Common Mistakes and Limitations

These are the errors seen most often when researchers interpret GO enrichment results in R, listed so you can check your own analysis against them.

**Filtering on raw p values.** With thousands of GO terms tested at once, uncorrected p values generate large numbers of false positives. Filter on p.adjust or the q value column, and state the correction method in your methods section,.

**Ignoring the universe argument.** Running `enrichGO()` without a universe when your list came from a filtered table produces inflated enrichment for well-annotated genes. This single omission can manufacture or destroy findings, and its downstream impact has been measured directly in pipeline comparison work [5].

**Treating GO enrichment as pathway analysis.** GO BP terms describe processes, not curated signaling pathways. Term names such as "signal transduction" are broad and tell you less than the term's geneID column suggests. For pathway-level claims, pair GO results with pathway databases such as KEGG, which is the pattern used in most published RNA-seq analyses [3], [1].

**Confusing presence with direction.** A significant BP term in ORA says genes in your list share an annotation. It says nothing about whether the process went up or down. Only ranked-list methods such as GSEA, or post-hoc inspection of the driving genes' log2 fold changes, support directional language [3], [4].

**Overreading tiny gene counts.** A term supported by two or three genes can reach significance when the term is rare in the background. Count below roughly five is a reasonable soft flag for treating a hit as hypothesis-generating rather than as evidence, and reading the geneID list is the fastest way to judge whether the story is real.

**Reporting every significant term.** A typical run returns hundreds of significant terms after `simplify()`. Interpreting all of them dilutes the analysis and increases false discovery in your narrative, even if each individual p.adjust value is defensible. Cluster terms by theme first, then interpret themes [2], [11], [12].

**Forgetting that annotation is incomplete and biased.** Well-studied genes and processes carry more annotations, so they enrich more readily, an effect related to gene length and discovery-history bias. Results for poorly annotated genes and novel transcripts will be systematically underrepresented, and absence of enrichment is not evidence of absence of biology.

**Assuming results are stable across tool choices.** Different tools apply different statistical tests, background definitions, and annotation versions. If clusterProfiler, gprofiler2, and topGO agree on your top themes, confidence rises. If they disagree, the disagreement itself is information about which part of your result depends on method choice [5],,. Tools such as EnrichmentBrowser were built to run set-based and network-based methods side by side for exactly this reason [16].

## Quick Review

1. ORA tests whether GO terms appear more often in your gene list than expected given the background annotation rate, using a hypergeometric or Fisher exact test.
2. Filter and report on p.adjust or qvalue, never on raw pvalue, and name the correction method.
3. Fold enrichment is the effect size. GeneRatio divided by BgRatio tells you how many-fold overrepresented a term is.
4. Always pass the universe argument when your gene list came from a filtered dataset.
5. Read the geneID column before believing a term. Small counts and incoherent directions are red flags.
6. Reduce redundancy with `simplify()`, network plots, or clustering before interpreting, then report one representative term per functional theme [3], [7], [11].
7. ORA answers "which terms are overrepresented in my list," GSEA answers "which gene sets sit at the extremes of my ranked list," and each supports different biological language [3], [4].

## Frequently Asked Questions

### What does interpret GO enrichment results in R mean?

It means reading the statistical output of Gene Ontology enrichment tests run in R, such as the GeneRatio, BgRatio, p.adjust, Count, and geneID columns returned by clusterProfiler, and converting those rows into defensible biological statements. Interpretation spans checking your universe and identifier mapping, judging significance and effect size together, collapsing redundant terms, and visualizing term relationships before writing claims.

### What p value cutoff should I use for GO enrichment?

Use an adjusted p value cutoff, most commonly 0.05 after Benjamini-Hochberg correction, and report the cutoff and method explicitly. Cutoffs in the 0.05 to 0.1 range for q values are common exploratory starting points, but any terms near the boundary should be presented as hypothesis-generating rather than as established findings, and effect size should always be shown alongside them.

### Why do so many GO terms overlap, and how do I reduce redundancy?

GO terms form a hierarchy, so a significant specific term usually makes its parent terms significant too, producing tables where many rows describe the same biology. Reduce this with semantic similarity reduction via `simplify()`, hierarchical clustering of redundant terms as in gogadget, or network visualizations such as Enrichment Map and aPEAR that group overlapping terms into readable clusters.

### Should I use ORA or GSEA for my GO analysis?

Use ORA when you have a biologically natural gene list, such as genes passing an adjusted p value cutoff, and GSEA when you have a full ranked list and want to detect coordinated modest shifts and assign direction. Many analyses report both, since ORA highlights strong hits and GSEA captures weak-but-consistent movement that ORA cannot see.

### Why do my GO results change when I change the universe?

The hypergeometric test computes expected term frequencies from the background, so changing the background changes both the expectation and the p values. A universe that is too broad or too narrow relative to how your gene list was selected biases the test, and pipeline comparisons show that filtering decisions upstream are among the biggest drivers of differences in downstream enrichment results.

### How do I report GO enrichment results in a paper?

Report the tool and version, the annotation database and release date, the ontology tested, the statistical test, the multiple-testing correction, the universe definition, and the identifier mapping rate. In figures, show adjusted p values and an effect size such as fold enrichment or gene count, and support each headline term with a brief note on which genes drive it and their direction of change.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What does interpret GO enrichment results in R mean?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "It means reading the statistical output of Gene Ontology enrichment tests run in R, such as the GeneRatio, BgRatio, p.adjust, Count, and geneID columns returned by clusterProfiler, and converting those rows into defensible biological statements. Interpretation spans checking your universe and identifier mapping, judging significance and effect size together, collapsing redundant terms, and visualizing term relationships before writing claims."
      }
    },
    {
      "@type": "Question",
      "name": "What p value cutoff should I use for GO enrichment?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use an adjusted p value cutoff, most commonly 0.05 after Benjamini-Hochberg correction, and report the cutoff and method explicitly. Cutoffs in the 0.05 to 0.1 range for q values are common exploratory starting points, but any terms near the boundary should be presented as hypothesis-generating rather than as established findings, and effect size should always be shown alongside them."
      }
    },
    {
      "@type": "Question",
      "name": "Why do so many GO terms overlap, and how do I reduce redundancy?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "GO terms form a hierarchy, so a significant specific term usually makes its parent terms significant too, producing tables where many rows describe the same biology. Reduce this with semantic similarity reduction via simplify(), hierarchical clustering of redundant terms as in gogadget, or network visualizations such as Enrichment Map and aPEAR that group overlapping terms into readable clusters."
      }
    },
    {
      "@type": "Question",
      "name": "Should I use ORA or GSEA for my GO analysis?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use ORA when you have a biologically natural gene list, such as genes passing an adjusted p value cutoff, and GSEA when you have a full ranked list and want to detect coordinated modest shifts and assign direction. Many analyses report both, since ORA highlights strong hits and GSEA captures weak-but-consistent movement that ORA cannot see."
      }
    },
    {
      "@type": "Question",
      "name": "Why do my GO results change when I change the universe?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The hypergeometric test computes expected term frequencies from the background, so changing the background changes both the expectation and the p values. A universe that is too broad or too narrow relative to how your gene list was selected biases the test, and pipeline comparisons show that filtering decisions upstream are among the biggest drivers of differences in downstream enrichment results."
      }
    },
    {
      "@type": "Question",
      "name": "How do I report GO enrichment results in a paper?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Report the tool and version, the annotation database and release date, the ontology tested, the statistical test, the multiple-testing correction, the universe definition, and the identifier mapping rate. In figures, show adjusted p values and an effect size such as fold enrichment or gene count, and support each headline term with a brief note on which genes drive it and their direction of change."
      }
    }
  ]
}
</script>

## Sources

1. [Gene expression profiles and pathway enrichment analysis to identification of differentially expressed gene and signaling pathways in epithelial ovarian cancer based on high-throughput RNA-seq data.](https://pubmed.ncbi.nlm.nih.gov/34839022/)
2. [GeneCodis3: a non-redundant and modular enrichment analysis tool for functional genomics](https://pubmed.ncbi.nlm.nih.gov/22573175/)
3. [Functional Insights Through Gene Ontology, Disease Ontology, and KEGG Pathway Enrichment.](https://pubmed.ncbi.nlm.nih.gov/40455152/)
4. [Pathway enrichment analysis and visualization of omics data using g:Profiler, GSEA, Cytoscape and EnrichmentMap](https://pubmed.ncbi.nlm.nih.gov/30664679/)
5. [Assessing the impact of transcriptomics data analysis pipelines on downstream functional enrichment results](https://pubmed.ncbi.nlm.nih.gov/38943333/)
6. [NetGen: a novel network-based probabilistic generative model for gene set functional enrichment analysis.](https://pubmed.ncbi.nlm.nih.gov/28950861/)
7. [Gogadget: An R Package for Interpretation and Visualization of GO Enrichment Results.](https://pubmed.ncbi.nlm.nih.gov/28000438/)
8. [Enhancing Interpretation of Functional Enrichment Analysis Results with Advanced Network Visualizations.](https://pubmed.ncbi.nlm.nih.gov/42905494/)
9. [Fold-Change-Specific Enrichment Analysis (FSEA): Quantification of Transcriptional Response Magnitude for Functional Gene Groups.](https://pubmed.ncbi.nlm.nih.gov/32316383/)
10. [NeVOmics: An Enrichment Tool for Gene Ontology and Functional Network Analysis and Visualization of Data from OMICs Technologies](https://pubmed.ncbi.nlm.nih.gov/30477135/)
11. [Enrichment Map: A Network-Based Method for Gene-Set Enrichment Visualization and Interpretation](https://pubmed.ncbi.nlm.nih.gov/21085593/)
12. [aPEAR: an R package for autonomous visualization of pathway enrichment networks](https://pubmed.ncbi.nlm.nih.gov/37935424/)
13. [GeneCodis 4: Expanding the modular enrichment analysis to regulatory elements](https://doi.org/10.1101/2021.04.15.439962)
14. [gprofiler2 -- an R package for gene list functional enrichment analysis and namespace conversion toolset g:Profiler](https://pubmed.ncbi.nlm.nih.gov/33564394/)
15. [ShEnrich: a functional annotation database and enrichment platform for penaeid shrimp.](https://pubmed.ncbi.nlm.nih.gov/42963765/)
16. [Bioconductor’s EnrichmentBrowser: seamless navigation through combined results of set- & network-based enrichment analysis](https://pubmed.ncbi.nlm.nih.gov/26791995/)