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()