"""melanocyte deep dive on aldrich: within-melanocyte cKO vs WT DE and pathway scoring.""" from __future__ import annotations from pathlib import Path import warnings warnings.filterwarnings("ignore") import numpy as np import pandas as pd import anndata as ad import scanpy as sc from scipy.stats import mannwhitneyu import os as _os from pathlib import Path as _Path PANDA_ROOT = _Path(_os.environ.get("PANDA_ROOT", str(_Path(__file__).resolve().parents[2]))) TARGET = Path(str(PANDA_ROOT / "data/processed/skin/adata_processed.h5ad")) PROJ = Path(str(PANDA_ROOT / "discovery/pan_skin/marker/50_aldrich_projections.h5ad")) OUT = Path(str(PANDA_ROOT / "discovery/pan_skin/marker")) PATHWAYS = { "MITF_regulon": ["Mitf", "Dct", "Tyr", "Pmel", "Mlana", "Tyrp1", "Slc24a5", "Slc45a2", "Silv", "Sox10", "Pax3", "Kit", "Ednrb"], "Wnt_signaling": ["Wnt3", "Wnt5a", "Wnt7a", "Wnt10b", "Ctnnb1", "Lef1", "Tcf4", "Tcf7", "Axin2", "Dkk1", "Sfrp1", "Fzd7", "Lrp5", "Lrp6"], "BMP_TGF_signaling": ["Bmp2", "Bmp4", "Bmp5", "Bmp7", "Bmpr1a", "Bmpr1b", "Bmpr2", "Smad1", "Smad3", "Smad5", "Tgfb1", "Tgfb2", "Tgfbr1", "Tgfbr2", "Id1", "Id2", "Id3"], "FGF_signaling": ["Fgf1", "Fgf2", "Fgf7", "Fgf9", "Fgf10", "Fgf20", "Fgfr1", "Fgfr2", "Fgfr3", "Fgfr4", "Etv1", "Etv4", "Etv5", "Spry2", "Spry4", "Dusp6"], "Notch_signaling": ["Notch1", "Notch2", "Notch3", "Jag1", "Jag2", "Dll1", "Dll3", "Dll4", "Hes1", "Hes5", "Hey1", "Hey2", "Rbpj", "Maml1"], "Hedgehog": ["Shh", "Ihh", "Ptch1", "Ptch2", "Smo", "Gli1", "Gli2", "Gli3", "Hhip", "Sufu"], "EMT": ["Zeb1", "Zeb2", "Snai1", "Snai2", "Twist1", "Twist2", "Vim", "Cdh2", "Fn1", "Mmp2", "Mmp9", "Prrx1"], "Cell_cycle": ["Ccnd1", "Ccnd2", "Ccne1", "Ccna2", "Ccnb1", "Cdk1", "Cdk2", "Cdk4", "Cdk6", "Mki67", "Top2a", "Pcna", "Mcm2", "Mcm3"], "Neural_crest": ["Sox10", "Sox9", "Sox2", "Pax3", "Foxd3", "Nes", "Ngfr", "Ednrb", "Kit", "Tfap2a"], "Apoptosis": ["Bax", "Bak1", "Bad", "Bid", "Bcl2", "Bcl2l1", "Casp3", "Casp9", "Casp8", "Fas", "Fasl", "Trp53", "Cdkn1a"], } def pathway_scoring(sub, pathway_dict): for name, genes in pathway_dict.items(): present = [g for g in genes if g in sub.var_names] if not present: sub.obs[f"{name}"] = 0.0 continue sc.tl.score_genes(sub, gene_list=present, score_name=f"{name}", random_state=0, use_raw=False) return sub def main(): a = ad.read_h5ad(TARGET) p = ad.read_h5ad(PROJ) a.obs["pred_label"] = p.obs["pred_bbse_label"].values mask = a.obs["pred_label"] == "melanocyte" print(f"[mel] melanocyte cells: {int(mask.sum())} " f"({int((mask & (a.obs['genotype']=='En1-cKO')).sum())} cKO, " f"{int((mask & (a.obs['genotype']=='WT')).sum())} WT)", flush=True) if mask.sum() < 30: print("[mel] too few melanocyte cells") return sub = a[mask].copy() sub = pathway_scoring(sub, PATHWAYS) print("\n[mel] pathway score cKO vs WT (positive = up in cKO):") rows = [] for name in PATHWAYS.keys(): s = sub.obs[name].astype(float).values g = sub.obs["genotype"].values c_scores = s[g == "En1-cKO"] w_scores = s[g == "WT"] stat, pval = mannwhitneyu(c_scores, w_scores, alternative="two-sided") delta = c_scores.mean() - w_scores.mean() rows.append({"pathway": name, "delta_cKO_minus_WT": round(delta, 4), "MannU_p": pval, "cKO_mean": round(c_scores.mean(), 4), "WT_mean": round(w_scores.mean(), 4)}) star = "***" if pval < 0.001 else "**" if pval < 0.01 else "*" if pval < 0.05 else "" print(f" {name:20s} delta={delta:+.4f} p={pval:.2e} {star}") pd.DataFrame(rows).sort_values("MannU_p").to_csv(OUT / "56_melanocyte_pathways.csv", index=False) print("\n[mel] within-melanocyte cKO vs WT DE:") sc.tl.rank_genes_groups(sub, "genotype", method="wilcoxon", n_genes=50, use_raw=False) de = pd.DataFrame({ "cKO_up_gene": sub.uns["rank_genes_groups"]["names"]["En1-cKO"][:20], "cKO_up_lfc": sub.uns["rank_genes_groups"]["logfoldchanges"]["En1-cKO"][:20], "WT_up_gene": sub.uns["rank_genes_groups"]["names"]["WT"][:20], "WT_up_lfc": sub.uns["rank_genes_groups"]["logfoldchanges"]["WT"][:20], }) print(de.to_string(index=False)) de.to_csv(OUT / "56_melanocyte_wilcoxon_cko_vs_wt.csv", index=False) lines = ["# Melanocyte deep dive — Aldrich En1-cKO vs WT", "", f"PANDA-MLP-predicted melanocyte cells: **{int(mask.sum())} total** " f"({int((mask & (a.obs['genotype']=='En1-cKO')).sum())} cKO, " f"{int((mask & (a.obs['genotype']=='WT')).sum())} WT).", "", "The v3b+v3c cross-model replication showed ~2× melanocyte enrichment in cKO " "(log2fc +1.04, Fisher p ≈ 4×10⁻⁶). Here we probe what's happening WITHIN the " "melanocyte compartment.", "", "## Pathway score comparisons (MannU cKO vs WT)", ""] dfp = pd.DataFrame(rows).sort_values("MannU_p") lines.append(dfp.to_markdown(index=False)) lines += ["", "## Within-melanocyte Wilcoxon DE (top 20 each direction)", ""] de_show = de.copy() de_show["cKO_up_lfc"] = de_show["cKO_up_lfc"].round(3) de_show["WT_up_lfc"] = de_show["WT_up_lfc"].round(3) lines.append(de_show.to_markdown(index=False)) (OUT / "56_melanocyte_deep_dive.md").write_text("\n".join(lines)) print(f"[mel] wrote {OUT}/56_melanocyte_deep_dive.md") if __name__ == "__main__": main()