Extract Bajpai 2023 CRISPR-screen hits

Result: the 169-gene hit list (data/processed/bajpai2023_crispr_hits.csv) derived, in the open, from the committed raw supplement — no pre-baked CSV. Fully offline and deterministic.

Raw input (committed, CC BY): data/raw/bajpai2023/science.ade6289_table_s1.xlsx, sheet “Low SSC FACS enriched genes” — the full 4,956-gene genome-wide screen.

The one hit-calling decision (from docs/specs/bajpai2023.spec.md): q_value < 0.10 on the combined casTLE score → 169 hits, reproducing the paper’s “169 functionally diverse genes.” All hits have positive casTLE effect (enriched in the low-melanin gate) ⇒ uniform direction perturbation reduces pigmentation.

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

  • Bajpai 2023 CRISPR screen — a genome-wide CRISPR knockout screen (Bajpai et al. 2023, Science) that perturbs each of ~5,000 genes in human melanocytes and measures the effect on melanin production; this notebook re-derives the paper’s hit list from the committed raw supplement.
  • casTLE score / casTLE effect — the screen’s per-gene effect size, from the casTLE statistical framework. A positive casTLE effect means knocking the gene out reduces melanin, so the gene is a positive regulator of pigmentation. All hits here are positive ⇒ a single uniform direction, “perturbation reduces pigmentation.”
  • Low SSC / FACS gate (“low-melanin gate”) — cells were sorted by flow cytometry (FACS). Melanin granules scatter light, so cells with low side-scatter (SSC) are the low-melanin cells; genes enriched in that low-melanin fraction are ones whose knockout lowers pigment.
  • q-value (q < 0.10) — a false-discovery-rate–adjusted p-value; the one hit-calling threshold applied here (≤10% expected false positives), which yields the paper’s 169 hits.
Show code
import pandas as pd
RAW = "data/raw/bajpai2023/science.ade6289_table_s1.xlsx"
screen = pd.read_excel(RAW, sheet_name="Low SSC FACS enriched genes")
print("full screen:", screen.shape[0], "genes,", screen.shape[1], "columns")
assert screen.shape[0] == 4956, f"expected 4956-gene screen, got {screen.shape[0]}"

Step 1 — Apply the hit threshold (q < 0.10) — the single documented assumption

Show code
THRESHOLD = 0.10
hits = screen[screen["q_value"] < THRESHOLD].copy()
print(f"hits at q < {THRESHOLD}: {len(hits)}")
assert len(hits) == 169, f"expected 169 hits (paper's count), got {len(hits)}"
# reference points recorded in the spec:
for t in (0.05, 0.10, 0.15):
    print(f"  q < {t}: {(screen['q_value'] < t).sum()}")

Step 2 — Confirm uniform direction and record it

Show code
assert (hits["Combined_casTLE_Effect"] > 0).all(), "not all hits have positive casTLE effect"
hits["direction_note"] = "perturbation_reduces_pigmentation (enriched in low-SSC/low-melanin gate)"
# The paper reports recovering "previously known pigmentation genes" alongside novel hits (named examples
# in its text include MYO5A/ATP7A/RAB1A, which fall in its looser extended set). Among the strict q<0.10
# list, confirm canonical melanogenesis genes are present:
known = ["TYR","DCT","SLC45A2","OCA2"]
print("canonical pigmentation genes in the 169-hit list:", {g: g in set(hits["Symbol"]) for g in known})

Step 3 — Select the documented columns and write the checkpoint

Show code
KEEP = ['GeneID','Symbol','GeneInfo','Localization','Process','Function',
        'Combined_casTLE_Effect','Combined_casTLE_Score','Minimum_Effect_Estimate',
        'Maximum_Effect_Estimate','p_value','q_value','direction_note']
out = hits[KEEP].reset_index(drop=True)
out.to_csv("data/processed/bajpai2023_crispr_hits.csv", index=False)
print("wrote", len(out), "hit genes ->", "data/processed/bajpai2023_crispr_hits.csv")
out.head()
Back to top