Lesson 12 of 13 · 12 min
Functional and GO Enrichment
A ranked DE gene list tells you what changed, but not why it matters. Over-representation analysis (ORA) asks whether a biological theme appears among your significant genes more often than expected by chance. That expectation is set by a background gene universe, so choosing the universe correctly matters as much as the DE list itself.
Convert to Entrez IDs
clusterProfiler links genes to GO terms and KEGG pathways through Entrez gene IDs, so symbols or Ensembl IDs must be converted first. The bitr helper maps IDs against the org.Hs.eg.db annotation package; it drops anything it cannot map and warns you what fraction of the input failed. Convert your background the same way, since both lists must live in the same ID space.
library(clusterProfiler)
library(org.Hs.eg.db)
# de_genes : SYMBOLs of significant genes (e.g. padj < 0.05 & |log2FC| > 1)
# all_genes : every gene that passed DESeq2 independent filtering (the universe)
de_map <- bitr(de_genes, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
bg_map <- bitr(all_genes, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = org.Hs.eg.db)
de_entrez <- unique(de_map$ENTREZID)
bg_entrez <- unique(bg_map$ENTREZID)GO and KEGG enrichment
ego <- enrichGO(gene = de_entrez,
universe = bg_entrez,
OrgDb = org.Hs.eg.db,
keyType = "ENTREZID",
ont = "BP", # biological process
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
qvalueCutoff = 0.10,
minGSSize = 10, # drop tiny gene sets
maxGSSize = 500, # drop over-generic sets
readable = TRUE) # map Entrez back to symbols
ekegg <- enrichKEGG(gene = de_entrez,
universe = bg_entrez,
organism = "hsa",
keyType = "kegg", # KEGG hsa IDs == Entrez IDs
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
minGSSize = 10,
maxGSSize = 500)
ekegg <- setReadable(ekegg, OrgDb = org.Hs.eg.db, keyType = "ENTREZID")> head(as.data.frame(ego), 5)[, c("ID", "Description", "GeneRatio", "p.adjust", "Count")]
ID Description GeneRatio p.adjust Count
GO:0006954 GO:0006954 inflammatory response 45/612 3.10e-12 45
GO:0002250 GO:0002250 adaptive immune response 38/612 8.24e-10 38
GO:0009611 GO:0009611 response to wounding 41/612 2.71e-08 41
GO:0030198 GO:0030198 extracellular matrix organization 29/612 6.05e-07 29
GO:0071345 GO:0071345 cellular response to cytokine stimulus 52/612 1.18e-06 52
clusterProfiler adjusts each term's raw hypergeometric p-value for multiple testing with Benjamini-Hochberg, reported as p.adjust. The minGSSize and maxGSSize filters remove tiny sets that are statistically unstable and huge ones too generic to interpret. GeneRatio (k/n) versus BgRatio tells you the fold enrichment behind each adjusted p-value.
Warning: a proper background universe is every gene that had a real chance of being called DE, typically the genes that passed DESeq2 independent filtering, not the whole genome. Omitting universe makes all annotated genes the background, which overstates significance and produces misleadingly long term lists.
GSEA threshold-free alternative
# Rank ALL tested genes by a signed statistic (log2FC or the Wald stat log2FC/lfcSE).
# res must already carry an Entrez column (mapped with bitr/mapIds beforehand).
# Names must be Entrez IDs; the vector must be sorted decreasing with unique names.
gene_list <- res$log2FoldChange
names(gene_list) <- res$entrez
gene_list <- gene_list[!is.na(names(gene_list)) & !duplicated(names(gene_list))]
gene_list <- sort(gene_list, decreasing = TRUE)
gse <- gseGO(geneList = gene_list,
OrgDb = org.Hs.eg.db,
keyType = "ENTREZID",
ont = "BP",
minGSSize = 10,
maxGSSize = 500,
pAdjustMethod = "BH",
pvalueCutoff = 0.05,
eps = 0) # allow very small p-values (fgsea)
# Unlike ORA, GSEA needs no significance cutoff: it uses the full ranking and
# reports a signed NES, catching coordinated shifts ORA would miss.Try it yourself: from your own DESeq2 results build de_entrez and bg_entrez as above, run enrichGO(ont = "BP"), then summarize the top themes with dotplot(ego, showCategory = 15). Re-run once with universe = bg_entrez and once with the universe omitted, and note how many more terms clear p.adjust < 0.05 with the wrong background.