File size: 6,250 Bytes
141bacd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | """within-class wilcoxon DE WT vs Kit_W41 + module scoring; checks dahlin paper claims (Myc/ISR)."""
from __future__ import annotations
from pathlib import Path
import warnings, json, sys
warnings.filterwarnings("ignore")
import numpy as np, pandas as pd, anndata as ad, scanpy as sc, scipy.sparse as sp
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])))
DAHLIN = Path(str(PANDA_ROOT / "data/corpus/hematopoiesis/held_out_unlabeled/dahlin_extract"))
PRED = Path(str(PANDA_ROOT / "discovery/hematopoiesis/marker/66_dahlin_predictions.csv"))
OUT = Path(str(PANDA_ROOT / "discovery/hematopoiesis/marker"))
def load_dahlin_all():
parts = []
for f in sorted(DAHLIN.glob("*.txt.gz")):
gsm = f.name.split("_")[0]
sample = f.name.split("_")[1].split(".")[0]
df = pd.read_csv(f, sep="\t", compression="gzip", index_col=0)
X = sp.csr_matrix(df.values.T.astype(np.float32))
obs = pd.DataFrame(index=[f"{sample}_{bc}" for bc in df.columns.astype(str)])
obs["sample"] = sample
var = pd.DataFrame(index=df.index.astype(str))
var["ensmusg"] = var.index.values
a = ad.AnnData(X=X, obs=obs, var=var)
parts.append(a)
return ad.concat(parts, join="outer", label="_batch")
def main():
print("[dahlin-mech] loading Dahlin …", flush=True)
a = load_dahlin_all()
import mygene
mg = mygene.MyGeneInfo()
ids = a.var_names.astype(str).tolist()
print(f"[dahlin-mech] mapping {len(ids)} genes …", flush=True)
res = mg.querymany(ids, scopes="ensembl.gene", fields="symbol", species="mouse",
verbose=False)
id2sym = {r["query"]: r["symbol"] for r in res if "symbol" in r}
syms = pd.Series(a.var_names.astype(str)).map(id2sym).values
keep = pd.notna(syms)
a = a[:, keep].copy(); a.var_names = syms[keep]
a.var_names_make_unique()
print(f"[dahlin-mech] converted, shape now {a.shape}", flush=True)
preds = pd.read_csv(PRED, index_col=0)
a.obs = a.obs.join(preds[["pred_label", "pred_conf", "genotype"]], how="left")
print(f"[dahlin-mech] joined preds; genotype counts {a.obs['genotype'].value_counts().to_dict()}",
flush=True)
a_de = a.copy()
sc.pp.normalize_total(a_de, target_sum=1e4)
sc.pp.log1p(a_de)
rows = []
lg_class_min = 300
class_counts = a.obs["pred_label"].value_counts()
for cls in class_counts[class_counts >= lg_class_min].index:
mask = a.obs["pred_label"] == cls
sub = a_de[mask].copy()
if len(sub.obs["genotype"].unique()) < 2:
continue
gc = sub.obs["genotype"].value_counts()
if gc.min() < 30:
continue
try:
sc.tl.rank_genes_groups(sub, "genotype", method="wilcoxon", n_genes=40,
use_raw=False)
for grp in ["Kit_W41", "WT"]:
if grp not in sub.uns["rank_genes_groups"]["names"].dtype.names:
continue
names = list(sub.uns["rank_genes_groups"]["names"][grp][:15])
lfcs = list(sub.uns["rank_genes_groups"]["logfoldchanges"][grp][:15])
padjs = list(sub.uns["rank_genes_groups"]["pvals_adj"][grp][:15])
for g, l, p in zip(names, lfcs, padjs):
rows.append({"class": cls, "up_in": grp, "gene": g,
"logfc": round(float(l), 3),
"padj": float(p)})
except Exception as exc:
print(f"[dahlin-mech] DE failed for {cls}: {exc}")
pd.DataFrame(rows).to_csv(OUT / "67_dahlin_within_class_de.csv", index=False)
MODULES = {
"MYC_targets": ["Myc", "Mycn", "Nme1", "Odc1", "Ncl", "Ppat", "Cad",
"Ldha", "Nop58"],
"Integrated_stress": ["Atf4", "Ddit3", "Chac1", "Trib3", "Asns", "Gdf15",
"Sesn2", "Slc7a11"],
"Apoptosis_pro": ["Bax", "Bak1", "Bad", "Bid", "Bbc3", "Pmaip1", "Casp3",
"Casp9", "Trp53"],
"Apoptosis_anti": ["Bcl2", "Bcl2l1", "Mcl1", "Bcl2l2"],
"Erythroid_dev": ["Klf1", "Gata1", "Gata2", "Epor", "Hba-a1", "Hbb-b1"],
"Cell_cycle": ["Ccnd1", "Ccnd2", "Ccne1", "Ccna2", "Ccnb1", "Cdk1",
"Mki67", "Top2a", "Pcna", "Mcm2", "Mcm3", "Mcm4"],
"Kit_signaling": ["Kit", "Kitl", "Sos1", "Rasgrf1", "Grb2", "Pik3ca"],
}
for name, genes in MODULES.items():
present = [g for g in genes if g in a_de.var_names]
if not present: continue
sc.tl.score_genes(a_de, gene_list=present, score_name=f"pw_{name}",
random_state=0, use_raw=False)
print("\n[dahlin-mech] Kit_W41 vs WT module score contrast per class:", flush=True)
mod_rows = []
for cls in class_counts[class_counts >= lg_class_min].index:
mask = a_de.obs["pred_label"] == cls
for name in MODULES.keys():
col = f"pw_{name}"
if col not in a_de.obs: continue
s = a_de.obs.loc[mask, col].astype(float).values
g = a_de.obs.loc[mask, "genotype"].astype(str).values
cv = s[g == "Kit_W41"]; wv = s[g == "WT"]
if len(cv) < 20 or len(wv) < 20: continue
try:
_, p = mannwhitneyu(cv, wv, alternative="two-sided")
except Exception: p = 1.0
delta = cv.mean() - wv.mean()
star = "***" if p < 1e-6 else "**" if p < 1e-3 else "*" if p < 0.05 else ""
print(f" {cls:15s} {name:20s} delta={delta:+.4f} p={p:.2e} {star}")
mod_rows.append({"class": cls, "module": name,
"delta_Kit_minus_WT": round(delta, 4),
"MannU_p": p})
pd.DataFrame(mod_rows).to_csv(OUT / "67_dahlin_module_scores.csv", index=False)
print(f"\n[dahlin-mech] complete. Outputs in {OUT}/")
if __name__ == "__main__":
main()
|