Skip to content

Gene ID Mapping

Gene identifiers are the sample-name problem’s harder cousin. The same gene appears as a symbol (TP53), an Entrez ID (7157), an Ensembl ID (ENSG00000141510), an old alias (HER2 for ERBB2), a retired symbol (SEPT2, renamed SEPTIN2 in 2020), or an Excel date (2-Sep, which is what a spreadsheet made of SEPT2). Reconciling these is a Bioconductor job, so this guide is R only. Python users reach for mygene / MyGene.info, a network API, which is the closest equivalent.

Every code block runs in a container in the companion repository. HGNChelper and org.Hs.eg.db run offline; biomaRt queries Ensembl live.

checkGeneSymbols marks each symbol approved or not and suggests the current one. It corrects case, resolves aliases, updates retired symbols, and reverses the Excel date corruption. A /// in the suggestion means the corruption is ambiguous and a human has to choose.

library(HGNChelper)
ids <- read.csv("../fixtures/gene_ids.csv")
checkGeneSymbols(ids$symbol)
x Approved Suggested.Symbol
1 TP53 TRUE TP53
2 EGFR TRUE EGFR
3 HER2 FALSE ERBB2
4 1-Mar FALSE MARCHF1 /// MTARC1
5 2-Sep FALSE SEPTIN2 /// SEPTIN6
6 SEPT2 FALSE SEPTIN2 /// SEPTIN6

The Excel corruption is not a curiosity. Ziemann and colleagues found in 2016 that roughly a fifth of papers with Excel gene lists in their supplements contained these errors, when a spreadsheet silently turned SEPT2 and MARCH1 into dates. It is the reason HGNC formally renamed the SEPT and MARCH gene families to SEPTIN and MARCHF in 2020. HGNChelper carries a dated internal map, so the fix is reproducible offline.

Once the symbols are clean, org.Hs.eg.db maps between identifier systems from a local annotation database. No network, pinned to the image’s Bioconductor release, so the same input always gives the same IDs.

library(AnnotationDbi)
library(org.Hs.eg.db)
clean <- c("TP53", "EGFR", "ERBB2")
data.frame(
symbol = clean,
entrez = mapIds(org.Hs.eg.db, keys = clean, column = "ENTREZID", keytype = "SYMBOL"),
ensembl = mapIds(org.Hs.eg.db, keys = clean, column = "ENSEMBL", keytype = "SYMBOL"),
row.names = NULL
)
symbol entrez ensembl
1 TP53 7157 ENSG00000141510
2 EGFR 1956 ENSG00000146648
3 ERBB2 2064 ENSG00000141736

biomaRt does the same mapping from Ensembl directly, which you want when you need an attribute the local database does not carry. The catch is reproducibility: the current Ensembl release moves and the servers go down. Pin the query to a dated archive host, and it returns the same answer every time.

library(biomaRt)
mart <- useEnsembl(biomart = "genes", dataset = "hsapiens_gene_ensembl",
host = "https://oct2024.archive.ensembl.org")
getBM(attributes = c("hgnc_symbol", "entrezgene_id", "ensembl_gene_id"),
filters = "hgnc_symbol", values = clean, mart = mart)
hgnc_symbol entrezgene_id ensembl_gene_id
1 EGFR 1956 ENSG00000146648
2 ERBB2 2064 ENSG00000141736
3 TP53 7157 ENSG00000141510

The offline and online results agree on every ID. Use org.Hs.eg.db for a fast, reproducible mapping and biomaRt when you need something only Ensembl has, and pin the archive so a rerun next year still matches the page.