Extract Baxter 2018 curated pigmentation gene list

Result: data/processed/baxter2018_650_pigmentation_genes.csv derived from the committed raw supplement. Fully offline and deterministic.

Raw input (NOT committed — Wiley subscription supplement, git-ignored): data/raw/baxter2018/pcmr12743-sup-0007-tables7.xlsx, sheet “650 Pigmentation Genes” (Table S7).

Normalization decision (from docs/specs/baxter2018.spec.md): the sheet has 656 gene rows (rows carrying a Gene stable ID; pandas reads 659 rows with 3 trailing blanks). Of these, 635 carry a human gene symbol — the usable human set. The human-symbol filter is applied downstream (in Notebook 2, and in later analysis where the human set is needed), not destructively here: this notebook preserves the full Table S7 and reports the counts so they are auditable.

Key terms — so this notebook stands on its own (you shouldn’t need the other notebooks to read this one).

  • Baxter 2018 / the “650 Pigmentation Genes” list — a published, manually curated catalog of genes with a role in pigmentation (Baxter et al. 2018, Pigment Cell & Melanoma Research; the raw input is that paper’s Table S7 supplement). It is this project’s broad reference set of pigmentation-relevant genes — much wider than the textbook melanin-making core. The sheet is nicknamed “650,” but it actually holds 656 gene-ID rows, of which 635 carry a human gene symbol — this notebook reports both counts so the mismatch is auditable, not a bug.
  • Gene stable ID vs. gene symbol — a “gene row” is any row bearing an Ensembl stable ID (permanent identifier like ENSG…); a subset of those also carry a standard human gene symbol (HGNC name like TYR). The 656→635 drop is rows that lack a usable human symbol (non-human orthologs or unannotated IDs).
  • Notebook 2 (NB2) — a downstream notebook in this pipeline that resolves gene sets to symbols; it (not this notebook) applies the 635-human-symbol filter. That is why this step deliberately keeps the full 656-row Table S7 intact.
  • Non-destructive checkpoint — the processed CSV written here preserves every input row instead of pre-filtering, so the raw→processed conversion loses nothing; the counts are asserted in code, making the step deterministic, offline, and reproducible.
Show code
import pandas as pd
RAW = "data/raw/baxter2018/pcmr12743-sup-0007-tables7.xlsx"
s7 = pd.read_excel(RAW, sheet_name="650 Pigmentation Genes")
print("Table S7:", s7.shape[0], "rows,", s7.shape[1], "columns")
print("columns:", list(s7.columns))

Step 1 — Compute and assert the canonical counts (656 gene-IDs, 635 human symbols)

Show code
n_rows      = len(s7)
n_gene_ids  = int(s7["Gene stable ID"].notna().sum())
n_human     = int(s7["Human gene symbol"].notna().sum())
print(f"pandas rows: {n_rows}  |  gene-ID rows: {n_gene_ids}  |  human-symbol rows: {n_human}")
assert n_rows == 659,     f"expected 659 pandas rows, got {n_rows}"
assert n_gene_ids == 656, f"expected 656 gene-ID rows, got {n_gene_ids}"
assert n_human == 635,    f"expected 635 human-symbol rows, got {n_human}"
# payoff-locus coverage (HERC2 deliberately absent, consistent with Raghunath):
for g in ["OCA2","MC1R","TYR","SLC24A5","SLC45A2","HERC2"]:
    print(f"  {g}: {'present' if (s7['Human gene symbol']==g).any() else 'ABSENT'}")

Step 2 — Write the checkpoint (full Table S7, non-destructive)

We commit the full sheet as the processed table; the 635-human-symbol subset is derived where it is used, so the raw→processed step here loses nothing.

Show code
s7.to_csv("data/processed/baxter2018_650_pigmentation_genes.csv", index=False)
print("wrote", len(s7), "rows ->", "data/processed/baxter2018_650_pigmentation_genes.csv")
s7.head()
Back to top