PANDA / scripts /analysis /109_veres_mature_beta.py
bryan7264's picture
Correction pass: gate-matched Dahlin, retracted unsupported claims, complete HF-placode DEG set, restyled figures
141bacd verified
Raw
History Blame Contribute Delete
4.11 kB
"""split veres beta-predicted cells into mature (MAFA/UCN3 hi) vs SC-beta by INS x MAT quadrants."""
from __future__ import annotations
import json
import warnings
from pathlib import Path
import anndata as ad
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore")
import os as _os
from pathlib import Path as _Path
PANDA_ROOT = _Path(_os.environ.get("PANDA_ROOT", str(_Path(__file__).resolve().parents[2])))
ROOT = Path(str(PANDA_ROOT))
OUT = ROOT / "discovery/pancreas/marker"
OUT.mkdir(parents=True, exist_ok=True)
VERES = ROOT / "data/corpus/pancreas/held_out_labeled/veres_GSE114412_test.h5ad"
PRED = ROOT / "discovery/pancreas/marker/veres_predictions.csv"
def z(x):
x = np.asarray(x, dtype=float)
s = x.std()
return (x - x.mean()) / (s if s > 0 else 1.0)
def main():
print("[load]", flush=True)
a = ad.read_h5ad(VERES)
pred = pd.read_csv(PRED)
pred_map = dict(zip(pred["cell_id"].astype(str), pred["pred_label"]))
a.obs["pred_label"] = pd.Categorical(
[pred_map.get(c, "unknown") for c in a.obs_names])
print("[filter] pred_label == beta", flush=True)
sub = a[a.obs["pred_label"].astype(str) == "beta"].copy()
print(f"[filter] {sub.n_obs} beta-predicted cells", flush=True)
# veres X is already log-normalised (range 0..9)
def col(g):
if g not in sub.var_names:
return np.zeros(sub.n_obs)
j = sub.var_names.get_loc(g)
x = sub.X[:, j]
if hasattr(x, "toarray"):
x = x.toarray()
return np.asarray(x).ravel()
ins1 = col("Ins1"); ins2 = col("Ins2"); iapp = col("Iapp")
mafa = col("Mafa"); ucn3 = col("Ucn3")
insulin = ins1 + ins2
mature = z(mafa) + z(ucn3)
# split thresholds are the median within the veres beta-predicted set
ins_thr = float(np.median(insulin))
mat_thr = float(np.median(mature))
ins_hi = insulin > ins_thr
mat_hi = mature > mat_thr
quadrant = np.array(
["INS+/MAT+" if (ih and mh) else
"INS+/MAT-" if (ih and not mh) else
"INS-/MAT+" if (not ih and mh) else
"INS-/MAT-"
for ih, mh in zip(ins_hi, mat_hi)])
sub.obs["INS_level"] = insulin
sub.obs["MAT_score"] = mature
sub.obs["quadrant"] = pd.Categorical(quadrant)
df = sub.obs[["quadrant", "INS_level", "MAT_score"]].copy()
df["Ins1"] = ins1; df["Ins2"] = ins2; df["Iapp"] = iapp
df["Mafa"] = mafa; df["Ucn3"] = ucn3
df["paper_label"] = sub.obs["paper_label"].astype(str).values
df.reset_index().rename(columns={"index": "cell_id"}).to_csv(
OUT / "109_veres_mature_beta_scores.csv", index=False)
counts = df["quadrant"].value_counts().to_dict()
paper_by_quad = df.groupby(["quadrant", "paper_label"], observed=True) \
.size().unstack(fill_value=0)
paper_by_quad.to_csv(OUT / "109_veres_mature_beta_paper_x_quadrant.csv")
n_total = int(sub.n_obs)
n_mature = int((df["quadrant"] == "INS+/MAT+").sum())
n_scbeta = int((df["quadrant"] == "INS+/MAT-").sum())
fraction_mature = n_mature / n_total if n_total else 0.0
means = df.groupby("quadrant", observed=True)[
["Mafa", "Ucn3", "Ins1", "Ins2", "Iapp"]].mean().round(3).to_dict()
summary = {
"n_beta_predicted": n_total,
"ins_threshold": ins_thr,
"mat_threshold": mat_thr,
"quadrant_counts": counts,
"n_mature_INS+MAT+": n_mature,
"n_SCbeta_INS+MAT-": n_scbeta,
"fraction_mature": round(fraction_mature, 4),
"mean_expression_per_quadrant": means,
"paper_label_x_quadrant": {
q: paper_by_quad.loc[q].to_dict()
for q in paper_by_quad.index
} if len(paper_by_quad) else {},
}
with open(OUT / "109_veres_mature_beta_summary.json", "w") as f:
json.dump(summary, f, indent=2)
print("\n[done]")
print(json.dumps(summary, indent=2))
if __name__ == "__main__":
main()