Skip to content
Back to skills

Bio Core Pathways

ASecurity

Gene Ontology and pathway enrichment — GO structure, hypergeometric/Fisher test, ORA vs GSEA, Benjamini-Hochberg FDR, KEGG/Reactome API patterns.

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aipythongoreactexpresstestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add Pavel-Kravchenko/Bioinformatics --skill bio-core-pathways --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bio Core Pathways?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bio Core Pathways
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/pavel-kravchenko-bio-core-pathways/badge)](https://www.skillsdirectory.com/skills/pavel-kravchenko-bio-core-pathways)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bio-core-pathways
description: Gene Ontology and pathway enrichment — GO structure, hypergeometric/Fisher test, ORA vs GSEA, Benjamini-Hochberg FDR, KEGG/Reactome API patterns.
tool_type: python
primary_tool: NumPy
---

# Gene Ontology and Pathway Analysis

## Pitfalls

- **KEGG is dynamic**: Pathways are manually curated and updated. A gene list from 2020 may classify differently in 2024. Always note the KEGG release date.
- **ORA vs GSEA**: ORA requires a binary gene list (arbitrary threshold). GSEA uses a continuous ranking (fold change, -log10 p-value) and is more powerful — no threshold needed. For GSEA, the "leading edge" subset is the core signal.
- **True path rule**: GO annotations must be propagated to all ancestor terms before enrichment testing. A gene annotated to "apoptosis" is implicitly also annotated to "cell death" and "biological process." Failing to propagate underestimates term coverage.
- **Background gene set matters**: Use the genes actually tested (expressed in your assay), not the whole genome. Using the wrong background inflates or deflates enrichment.
- **Bonferroni is overly conservative**: For correlated GO terms (a gene's annotation propagates to many ancestors), Benjamini-Hochberg FDR is preferred.
- **IEA evidence codes are low-quality**: Electronically inferred annotations (IEA) are unreviewed. Filter them out for high-confidence analysis; keep only IDA, IMP, IPI, IGI, IEP (experimental) or ISS, IBA (computational, moderate confidence).

## GO Structure

Three orthogonal ontologies, each a Directed Acyclic Graph (DAG):

| Ontology | Abbreviation | Meaning |
|---|---|---|
| Molecular Function | MF | Biochemical activity of the gene product |
| Biological Process | BP | Pathway or larger biological process |
| Cellular Component | CC | Where in the cell the product is active |

## Evidence Code Quality

| Tier | Codes | Quality |
|---|---|---|
| Experimental | EXP, IDA, IPI, IMP, IGI, IEP | High |
| Computational (curated) | ISS, ISO, ISA, ISM, IBA | Medium |
| Traceable/Non-traceable | TAS, NAS | Low |
| Electronic | IEA | Lowest — filter for high-confidence work |

## Hypergeometric Test (ORA)

```
Contingency table:
                  | In GO term | Not in term | Total
------------------+------------+-------------+------
In gene list      |     k      |    n - k    |   n
Not in gene list  |   K - k    |  N-K-n+k    | N - n
Total             |     K      |    N - K    |   N

N = background size (expressed genes)
K = genes annotated to this GO term in background
n = genes in your list
k = overlap
P(X >= k) = hypergeom.sf(k-1, N, K, n)
```

```python
from scipy import stats

def hypergeom_enrichment(k, n, K, N):
    """One-tailed p-value: probability of seeing k or more by chance."""
    return stats.hypergeom.sf(k - 1, N, K, n)

fold_enrichment = (k / n) / (K / N)
```

## GO Enrichment Implementation

```python
from scipy import stats
import numpy as np

def go_enrichment(gene_list, term_to_genes, background_size=20000):
    """
    gene_list: list of gene symbols
    term_to_genes: dict of GO_ID -> set of annotated genes (propagated)
    Returns list of dicts sorted by p-value with BH FDR.
    """
    gene_set = set(g.upper() for g in gene_list)
    n = len(gene_set)
    N = background_size

    results = []
    for term_id, term_genes in term_to_genes.items():
        k = len(gene_set & {g.upper() for g in term_genes})
        if k == 0:
            continue
        K = len(term_genes)
        p = stats.hypergeom.sf(k - 1, N, K, n)
        results.append({'go_id': term_id, 'k': k, 'K': K,
                        'fold_enrichment': (k/n) / (K/N), 'p_value': p})

    results.sort(key=lambda r: r['p_value'])

    # Benjamini-Hochberg FDR
    m = len(results)
    for i, r in enumerate(results):
        r['fdr'] = min(r['p_value'] * m / (i + 1), 1.0)
    # Enforce monotonicity
    running_min = 1.0
    for r in reversed(results):
        running_min = min(running_min, r['fdr'])
        r['fdr'] = running_min

    return results
```

## Multiple Testing Correction

```python
def bh_correction(p_values):
    """Benjamini-Hochberg FDR correction."""
    m = len(p_values)
    indexed = sorted(enumerate(p_values), key=lambda x: x[1])
    adjusted = [0.0] * m
    for rank, (orig_idx, p) in enumerate(indexed, 1):
        adjusted[orig_idx] = min(p * m / rank, 1.0)
    # Enforce monotonicity
    running_min = 1.0
    for _, (orig_idx, _) in reversed(list(enumerate(indexed))):
        running_min = min(running_min, adjusted[orig_idx])
        adjusted[orig_idx] = running_min
    return adjusted

# Or use statsmodels:
from statsmodels.stats.multitest import multipletests
_, adj_pvals, _, _ = multipletests(p_values, method='fdr_bh')
```

## KEGG REST API

```python
import urllib.request

def kegg_get(endpoint):
    url = f"https://rest.kegg.jp/{endpoint}"
    with urllib.request.urlopen(url, timeout=10) as r:
        return r.read().decode('utf-8')

# List all human pathways
pathways = kegg_get("list/pathway/hsa")

# Get genes in a specific pathway
genes_in_pathway = kegg_get("link/hsa/hsa05210")  # colorectal cancer

# Find pathways containing a gene
pathway_for_gene = kegg_get("link/pathway/hsa:7157")  # TP53
```

## Tool Decision Table

| Scenario | Tool |
|---|---|
| Gene list, binary significant/not | ORA (hypergeometric / Fisher exact) |
| Ranked gene list (all tested genes) | GSEA (fgsea, clusterProfiler) |
| Human pathways with reactions | Reactome (ReactomePA) |
| Community-curated, open-access | WikiPathways |
| Organism-specific metabolism | KEGG |
| All GO terms + FDR | goatools, clusterProfiler, g:Profiler |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…