| """dahlin wt vs kit-w41 lineage composition and log2fc."""
|
| from __future__ import annotations
|
| import warnings, sys
|
| warnings.filterwarnings("ignore")
|
| from pathlib import Path
|
| import numpy as np, pandas as pd
|
| import matplotlib; matplotlib.use("Agg")
|
| import matplotlib.pyplot as plt
|
|
|
| sys.path.insert(0, str(Path(__file__).parent))
|
| from palette import apply_style, color_for, GENOTYPE_COLORS
|
| apply_style()
|
|
|
|
|
| SUPTITLE_FS = 15
|
| TITLE_FS = 14
|
| LABEL_FS = 12
|
| TICK_FS = 11
|
| LEGEND_FS = 10
|
| ANNOT_FS = 10
|
| ANNOT_EM_FS = 11
|
| CBAR_FS = 12
|
| PANEL_FS = 12
|
| plt.rcParams.update({
|
| "figure.titlesize": SUPTITLE_FS,
|
| "axes.titlesize": TITLE_FS,
|
| "axes.labelsize": LABEL_FS,
|
| "xtick.labelsize": TICK_FS,
|
| "ytick.labelsize": TICK_FS,
|
| "legend.fontsize": LEGEND_FS,
|
| "legend.title_fontsize": LEGEND_FS + 1,
|
| })
|
|
|
| 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))
|
| CSV = ROOT / "discovery/hematopoiesis/marker/dahlin_predictions.csv"
|
| OUT = ROOT / "figures/biology/biology_05_dahlin_composition.pdf"
|
|
|
| GT_MAP = {"SIGAB1":"WT","SIGAC1":"WT","SIGAD1":"WT","SIGAF1":"WT",
|
| "SIGAG1":"WT","SIGAH1":"WT","SIGAG8":"Kit_W41","SIGAH8":"Kit_W41"}
|
|
|
| LINEAGE_ORDER = ["LT-HSC", "MPP", "lymphoid", "erythroid", "megakaryocyte",
|
| "myeloid", "monocyte", "macrophage", "basophil-mast"]
|
|
|
|
|
| def main():
|
| df = pd.read_csv(CSV)
|
| df["sample"] = df["cell_id"].str.split("_").str[0]
|
| df["genotype"] = df["sample"].map(GT_MAP).fillna("other")
|
|
|
| df = df[df["genotype"].isin(["WT", "Kit_W41"])]
|
| tab = pd.crosstab(df["pred_label"], df["genotype"]).reindex(LINEAGE_ORDER).fillna(0)
|
| print("[fig5] counts:")
|
| print(tab)
|
|
|
| frac = tab.div(tab.sum(axis=0), axis=1)
|
| log2fc = np.log2((frac["Kit_W41"] + 1e-4) / (frac["WT"] + 1e-4))
|
|
|
| fig, axes = plt.subplots(1, 2, figsize=(17, 7.2), gridspec_kw={"width_ratios": [1.1, 1]})
|
|
|
| ax = axes[0]
|
| genos = ["WT", "Kit_W41"]
|
| bottoms = np.zeros(len(genos))
|
| for lin in LINEAGE_ORDER:
|
| vals = np.array([frac[g].loc[lin] for g in genos])
|
| ax.bar(genos, vals, bottom=bottoms,
|
| color=color_for(lin), edgecolor="white",
|
| linewidth=0.7, label=lin)
|
| for j, v in enumerate(vals):
|
| if v > 0.035:
|
| ax.text(j, bottoms[j] + v / 2, f"{v*100:.1f}%",
|
| ha="center", va="center", fontsize=ANNOT_FS, color="white",
|
| fontweight="bold")
|
| bottoms += vals
|
| ax.set_ylabel("Within-genotype fraction")
|
| ax.set_ylim(0, 1)
|
| ax.set_title(f"Lineage composition — WT (n={int(tab['WT'].sum())}) vs "
|
| f"Kit-W41 (n={int(tab['Kit_W41'].sum())})\n"
|
| f"9 largest lineages shown; 91 cells in 4 minor classes omitted "
|
| f"(pro-B 66, T-cell 14, endothelial 6, naive-B 5)", fontsize=TITLE_FS)
|
| ax.legend(loc="center left", bbox_to_anchor=(1.02, 0.5),
|
| frameon=False, ncol=1, title="Lineage")
|
| ax.text(-0.12, 1.05, "(a)", transform=ax.transAxes,
|
| fontsize=PANEL_FS, fontweight="bold", va="bottom", ha="right")
|
|
|
| ax = axes[1]
|
| lins = LINEAGE_ORDER
|
| vals = [log2fc.loc[l] for l in lins]
|
| colors = [GENOTYPE_COLORS["Kit_W41"] if v > 0 else GENOTYPE_COLORS["WT"] for v in vals]
|
| y = np.arange(len(lins))
|
| ax.barh(y, vals, color=colors, edgecolor="black", linewidth=0.6)
|
| ax.axvline(0, color="black", lw=0.8)
|
|
|
|
|
|
|
|
|
| for i, (l, v) in enumerate(zip(lins, vals)):
|
| n_kit = int(tab["Kit_W41"].loc[l]); n_wt = int(tab["WT"].loc[l])
|
| ax.annotate(
|
| f"{v:+.2f} (Kit={n_kit}, WT={n_wt})",
|
| xy=(v, i), xycoords="data",
|
| xytext=(6, 0), textcoords="offset points",
|
| ha="left", va="center", fontsize=ANNOT_EM_FS, clip_on=False,
|
| )
|
| ax.set_yticks(y); ax.set_yticklabels(lins)
|
| ax.tick_params(axis="y", pad=6)
|
| ax.invert_yaxis()
|
| ax.set_xlabel("log2(Kit-W41 fraction / WT fraction)")
|
| ax.set_title("Compositional shift — Kit-W41 expands erythroid, contracts lymphoid")
|
| max_abs = max(abs(min(vals)), abs(max(vals)))
|
|
|
| ax.set_xlim(-max_abs * 1.15, max_abs * 2.8)
|
| ax.text(-0.14, 1.05, "(b)", transform=ax.transAxes,
|
| fontsize=PANEL_FS, fontweight="bold", va="bottom", ha="right")
|
|
|
| plt.suptitle("Dahlin — Kit-W41 alters lineage output balance",
|
| fontsize=SUPTITLE_FS, y=1.02, fontweight="bold")
|
| plt.tight_layout()
|
| plt.savefig(OUT, bbox_inches="tight", dpi=180)
|
| plt.close()
|
| print(f"[fig5] wrote {OUT}")
|
|
|
|
|
| if __name__ == "__main__":
|
| main()
|
|
|