Complementary simulation study

Mixed setting with global program and cell-type-specific program

[1]:
import os, warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
import scanpy as sc
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import MaxNLocator

try:
    import libpysal, esda
    from libpysal.weights import KNN
    HAVE_ESDA = True
except Exception:
    HAVE_ESDA = False
    print("esda/libpysal not found -> spatial coherence falls back to |corr|")

mpl.rcParams.update({
    "pdf.fonttype": 42, "ps.fonttype": 42, "axes.linewidth": 0.8,
    "font.size": 13, "axes.titlesize": 14, "axes.labelsize": 13,
    "xtick.labelsize": 11, "ytick.labelsize": 11,
    "axes.edgecolor": "#333333", "figure.facecolor": "white", "axes.facecolor": "white",
    "axes.spines.top": False, "axes.spines.right": False,
})
TITLE_FS = 15; CBAR_FS = 9; BAR_TICK_FS = 10
[2]:
RESULT_PATH = "/import/home2/share/yqzeng/MacSGP/results/simulation/shared_new"
FIG_DIR  = "/import/home2/share/yqzeng/MacSGP/revision_2/vis/2.2/figs/mixed_panel"
IND_DIR  = os.path.join(FIG_DIR, "individual")
os.makedirs(IND_DIR, exist_ok=True)

SCN = "S2_mixed"
BASELINES = ["nmf", "mofa", "stamp", "spicemix"]
METHODS = ["macsgp"] + BASELINES
PROP_THRESH = 0.10
CMAP = "bwr"       # factor maps
PROP_CMAP = "YlOrBr"    # proportion (yellow)

PALETTE = {"nmf": "#CCB974", "mofa": "#55A868", "spicemix": "#C44E52", "stamp": "#8172B3", "macsgp": "#4C72B0"}
LABEL   = {"nmf": "NMF", "mofa": "MOFA", "spicemix": "SpiceMix", "stamp": "STAMP", "macsgp": "MacSGP"}
ORDER   = ["nmf", "mofa", "spicemix", "stamp", "macsgp"]

def _sub(m): return {"nmf":"NMF","mofa":"MOFA","stamp":"stamp","spicemix":"spicemix"}[m]
def factor_path(m):  return os.path.join(RESULT_PATH, _sub(m), f"factors_{SCN}.csv")
def loading_path(m): return os.path.join(RESULT_PATH, _sub(m), f"loadings_{SCN}.csv")
[3]:
def _corr(a, b):
    a=np.asarray(a,float).ravel(); b=np.asarray(b,float).ravel()
    return 0.0 if a.std()==0 or b.std()==0 else float(np.corrcoef(a,b)[0,1])

def spatial_coherence(f_true, f_rec, coords, mask, k=9):
    a=np.asarray(f_true).ravel()[mask]; b=np.asarray(f_rec).ravel()[mask]
    if a.std()==0 or b.std()==0: return 0.0
    if HAVE_ESDA:
        w=KNN.from_array(coords[mask], k=k); w.transform='r'
        return abs(esda.Moran_BV(a,b,w,permutations=0).I)
    return abs(_corr(a,b))

def macsgp_components(adata, celltypes):
    out={}
    if 'factor' in adata.obsm:
        for ct in celltypes:
            out[f"{ct}.SGP0"]=(adata.obsm['factor'][ct].values.ravel(), adata.varm['loading'][ct].values.ravel())
    for ct in celltypes:
        if ct in adata.obsm:
            F=np.asarray(adata.obsm[ct]); L=np.asarray(adata.varm[ct])
            for j in range(F.shape[1]):
                if np.linalg.norm(L[:,j])>1e-8:
                    out[f"{ct}.SGP{j}"]=(F[:,j], L[:,j])
    return out

def clip99(v):
    v=np.asarray(v,float).ravel()
    lo,hi=np.percentile(v,[1,99])
    if hi<=lo: hi=lo+1e-9
    return np.clip(v,lo,hi), lo, hi
[4]:
# ---- load S2_mixed ----
adata = sc.read_h5ad(os.path.join(RESULT_PATH, f"adata_result_macsgp_{SCN}.h5ad"))
coords = np.asarray(adata.obsm['spatial'])
celltypes = list(adata.obsm['proportion'].columns)
prop0 = np.asarray(adata.obsm['proportion']['celltype_0']).ravel()
prop1 = np.asarray(adata.obsm['proportion']['celltype_1']).ravel()

programs={}
gf=np.asarray(adata.obsm['global_factor']); gl=np.asarray(adata.varm['global_loading'])
for r in range(gf.shape[1]):
    programs[f"shared{r}"]=dict(f=gf[:,r], l=gl[:,r], mask=np.ones(adata.n_obs,bool))
sf=adata.obsm['simulated_factor']; sl=np.asarray(adata.varm['simulated_loading'])
for i,ct in enumerate(sf.columns):
    programs[f"specific[{ct}]"]=dict(f=np.asarray(sf[ct]).ravel(), l=sl[:,i], mask=prop0>PROP_THRESH)

macsgp=macsgp_components(adata, celltypes)
base={}
for m in BASELINES:
    fdf=pd.read_csv(factor_path(m), index_col=0).reindex(adata.obs_names)
    ldf=pd.read_csv(loading_path(m), index_col=0).reindex(adata.var_names, fill_value=0.0)
    base[m]=(fdf, ldf)
print("programs:", list(programs), "| macsgp:", list(macsgp))
programs: ['shared0', 'specific[celltype_0]'] | macsgp: ['celltype_0.SGP0', 'celltype_0.SGP1', 'celltype_1.SGP0']
[5]:
SEL = {
    "shared0":              {"macsgp":"celltype_0.SGP0","nmf":0,"mofa":0,"stamp":1,"spicemix":3},
    "specific[celltype_0]": {"macsgp":"celltype_0.SGP1","nmf":1,"mofa":1,"stamp":2,"spicemix":4},
}
def get_component(method, selector):
    if method=="macsgp": return macsgp[selector]
    fdf,ldf=base[method]; j=int(selector)
    return fdf.iloc[:,j].values.ravel(), ldf.iloc[:,j].values.ravel()

rows=[]
for prog,pinfo in programs.items():
    f_true,l_true,mask=pinfo['f'],pinfo['l'],pinfo['mask']
    for m in [x for x in METHODS if x in SEL.get(prog,{})]:
        f_rec,l_rec=get_component(m, SEL[prog][m])
        rows.append({"program":prog,"method":m,
                     "spatial_coherence":round(spatial_coherence(f_true,f_rec,coords,mask),3),
                     "program_coherence":round(abs(_corr(l_rec,l_true)),3)})
metrics=pd.DataFrame(rows); metrics.to_csv(os.path.join(FIG_DIR,"metrics_selected.csv"), index=False)
def _val(prog, method, metric):
    r=metrics[(metrics.program==prog)&(metrics.method==method)]
    return abs(float(r[metric].iloc[0])) if len(r) else 0.0
print(metrics.to_string(index=False))
             program   method  spatial_coherence  program_coherence
             shared0   macsgp              0.939              0.971
             shared0      nmf              0.898              0.837
             shared0     mofa              0.955              0.951
             shared0    stamp              0.760              0.573
             shared0 spicemix              0.745              0.572
specific[celltype_0]   macsgp              0.921              0.845
specific[celltype_0]      nmf              0.733              0.479
specific[celltype_0]     mofa              0.065              0.040
specific[celltype_0]    stamp              0.741              0.433
specific[celltype_0] spicemix              0.825              0.778
[ ]:
g_true = programs["shared0"]["f"]; s_true = programs["specific[celltype_0]"]["f"]
def base_fac(m, prog): return get_component(m, SEL[prog][m])[0]

PANELS = [
 (0,0,"spatial", dict(v=prop0,  sign=None,   cmap=PROP_CMAP, title="",   tag="ct1_prop")),
 (0,1,"spatial", dict(v=g_true, sign=None,   cmap=CMAP, title="",     tag="global_true")),
 (0,2,"spatial", dict(v=s_true, sign=None,   cmap=CMAP, title="",   tag="specific_true")),
 (0,3,"spatial", dict(v=macsgp["celltype_0.SGP0"][0], sign=g_true, cmap=CMAP, title="", tag="macsgp_ct1_sgp0")),
 (0,4,"spatial", dict(v=macsgp["celltype_0.SGP1"][0], sign=s_true, cmap=CMAP, title="", tag="macsgp_ct1_sgp1")),
 (0,5,"spatial", dict(v=macsgp["celltype_1.SGP0"][0], sign=prop1,  cmap=CMAP, title="", tag="macsgp_ct2_sgp0")),
 (1,0,"spatial", dict(v=base_fac("nmf","shared0"),      sign=g_true, cmap=CMAP, title="",      tag="global_nmf")),
 (1,1,"spatial", dict(v=base_fac("mofa","shared0"),     sign=g_true, cmap=CMAP, title="",     tag="global_mofa")),
 (1,3,"spatial", dict(v=base_fac("stamp","shared0"),    sign=g_true, cmap=CMAP, title="",    tag="global_stamp")),
 (1,2,"spatial", dict(v=base_fac("spicemix","shared0"), sign=g_true, cmap=CMAP, title="", tag="global_spicemix")),
 (1,4,"bar",     dict(prog="shared0", metric="spatial_coherence", title="", tag="global_bar_spatial")),
 (1,5,"bar",     dict(prog="shared0", metric="program_coherence", title="", tag="global_bar_program")),
 (2,0,"spatial", dict(v=base_fac("nmf","specific[celltype_0]"),      sign=s_true, cmap=CMAP, title="",      tag="specific_nmf")),
 (2,1,"spatial", dict(v=base_fac("mofa","specific[celltype_0]"),     sign=s_true, cmap=CMAP, title="",     tag="specific_mofa")),
 (2,3,"spatial", dict(v=base_fac("stamp","specific[celltype_0]"),    sign=s_true, cmap=CMAP, title="",    tag="specific_stamp")),
 (2,2,"spatial", dict(v=base_fac("spicemix","specific[celltype_0]"), sign=s_true, cmap=CMAP, title="", tag="specific_spicemix")),
 (2,4,"bar",     dict(prog="specific[celltype_0]", metric="spatial_coherence", title="", tag="specific_bar_spatial")),
 (2,5,"bar",     dict(prog="specific[celltype_0]", metric="program_coherence", title="", tag="specific_bar_program")),
]

def add_cbar(ax, sm, ticks, label):
    cax = ax.inset_axes([0.25, -0.15, 0.5, 0.045])   # half-width, centered, below the panel
    cb = ax.figure.colorbar(sm, cax=cax, orientation="horizontal")
    if ticks:
        cb.locator = MaxNLocator(nbins=3); cb.update_ticks(); cax.tick_params(labelsize=CBAR_FS)
    else:
        cb.set_ticks([])
    if label:
        cb.set_label(label, fontsize=CBAR_FS, labelpad=2)
    return cb

def draw_spatial(ax, v, sign, cmap, normalize, cbar_ticks, cbar_label, title=None, spot=6):
    vv=np.asarray(v,float).ravel()
    if sign is not None and _corr(vv,sign)<0: vv=-vv
    vc,lo,hi=clip99(vv)
    if normalize:
        vc=(vc-lo)/(hi-lo+1e-12); lo,hi=0.0,1.0
    sm=ax.scatter(coords[:,0],coords[:,1],c=vc,cmap=cmap,s=spot,vmin=lo,vmax=hi,linewidths=0)
    ax.set_aspect("equal"); ax.set_xticks([]); ax.set_yticks([])
    for sp in ax.spines.values(): sp.set_visible(False)
    if title: ax.set_title(title, fontsize=TITLE_FS)
    add_cbar(ax, sm, cbar_ticks, cbar_label)
    return sm

def draw_bar(ax, prog, metric, title=None):
    vals=[_val(prog,m,metric) for m in ORDER]
    ax.bar(range(len(ORDER)), vals, color=[PALETTE[m] for m in ORDER])
    ax.set_xticks(range(len(ORDER))); ax.set_xticklabels([LABEL[m] for m in ORDER], rotation=45, ha="right", fontsize=BAR_TICK_FS)
    ax.set_ylim(0,1); ax.tick_params(axis="y", labelsize=CBAR_FS); ax.set_box_aspect(1)
    ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False)
    if title: ax.set_title(title, fontsize=TITLE_FS)

def render(ax, kind, p):
    if kind=="spatial":
        is_prop = (p["tag"]=="ct1_prop")
        draw_spatial(ax, p["v"], p["sign"], p["cmap"], normalize=(not is_prop),
                     cbar_ticks=is_prop, cbar_label=(None if is_prop else "Normalized score"),
                     title=p.get("title"))
    else:
        draw_bar(ax, p["prog"], p["metric"], p.get("title"))
[7]:
fig, axes = plt.subplots(3, 6, figsize=(6*2.15, 3*2.95))
for r,c,kind,p in PANELS:
    render(axes[r][c], kind, p)
fig.subplots_adjust(left=0.02, right=0.99, top=0.93, bottom=0.03, wspace=0.22, hspace=0.60)
fig.savefig(os.path.join(FIG_DIR,"mixed_panel_composite.pdf"), bbox_inches="tight")
fig.savefig(os.path.join(FIG_DIR,"mixed_panel_composite.png"), dpi=200, bbox_inches="tight")
plt.show(); print("composite saved")
../../_images/analysis_simulation_simulation_complementary_8_0.png
composite saved
[8]:
for r,c,kind,p in PANELS:
    stem=os.path.join(IND_DIR, f"r{r+1}c{c+1}_{p['tag']}")
    if kind=="spatial":
        f,ax=plt.subplots(figsize=(2.8,3.1))
        is_prop=(p["tag"]=="ct1_prop")
        draw_spatial(ax, p["v"], p["sign"], p["cmap"], normalize=(not is_prop),
                     cbar_ticks=is_prop, cbar_label=(None if is_prop else "Normalized scores"),
                     title=None, spot=18)
        f.savefig(stem+".pdf", bbox_inches="tight", transparent=True)
        f.savefig(stem+".png", dpi=200, bbox_inches="tight", transparent=True); plt.close(f)
    else:
        f,ax=plt.subplots(figsize=(3.0,3.2))
        draw_bar(ax, p["prog"], p["metric"], title=p["title"])
        f.savefig(stem+".pdf", bbox_inches="tight"); f.savefig(stem+".png", dpi=200, bbox_inches="tight"); plt.close(f)
print("individual panels ->", IND_DIR)
individual panels -> /import/home2/share/yqzeng/MacSGP/revision_2/vis/2.2/figs/mixed_panel/individual

Negative control setting with only global program

[ ]:
import os, warnings
warnings.filterwarnings("ignore")
import numpy as np
import pandas as pd
import scanpy as sc
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import MaxNLocator

try:
    import libpysal, esda
    from libpysal.weights import KNN
    HAVE_ESDA = True
except Exception:
    HAVE_ESDA = False
    print("esda/libpysal not found -> spatial coherence falls back to |corr|")

mpl.rcParams.update({
    "pdf.fonttype": 42, "ps.fonttype": 42, "axes.linewidth": 0.8,
    "font.size": 13, "axes.titlesize": 14, "axes.labelsize": 13,
    "xtick.labelsize": 11, "ytick.labelsize": 11,
    "axes.edgecolor": "#333333", "figure.facecolor": "white", "axes.facecolor": "white",
    "axes.spines.top": False, "axes.spines.right": False,
})
TITLE_FS = 15; CBAR_FS = 9; BAR_TICK_FS = 10
[ ]:
RESULT_PATH = "/import/home2/share/yqzeng/MacSGP/results/simulation/shared_new"
FIG_DIR  = "/import/home2/share/yqzeng/MacSGP/revision_2/vis/2.2/figs/neg_panel"
IND_DIR  = os.path.join(FIG_DIR, "individual")
os.makedirs(IND_DIR, exist_ok=True)

SCN = "S3_neg"
BASELINES = ["nmf", "mofa", "stamp", "spicemix"]
METHODS = ["macsgp"] + BASELINES
PROP_THRESH = 0.10
CMAP = "bwr"            # factor maps
PROP_CMAP = "YlOrBr"    # proportion (yellow)

PALETTE = {"nmf": "#CCB974", "mofa": "#55A868", "spicemix": "#C44E52", "stamp": "#8172B3", "macsgp": "#4C72B0"}
LABEL   = {"nmf": "NMF", "mofa": "MOFA", "spicemix": "SpiceMix", "stamp": "STAMP", "macsgp": "MacSGP"}
ORDER   = ["nmf", "mofa", "spicemix", "stamp", "macsgp"]

def _sub(m): return {"nmf":"NMF","mofa":"MOFA","stamp":"stamp","spicemix":"spicemix"}[m]
def factor_path(m):  return os.path.join(RESULT_PATH, _sub(m), f"factors_{SCN}.csv")
def loading_path(m): return os.path.join(RESULT_PATH, _sub(m), f"loadings_{SCN}.csv")
[ ]:
def _corr(a, b):
    a=np.asarray(a,float).ravel(); b=np.asarray(b,float).ravel()
    return 0.0 if a.std()==0 or b.std()==0 else float(np.corrcoef(a,b)[0,1])

def spatial_coherence(f_true, f_rec, coords, mask, k=9):
    a=np.asarray(f_true).ravel()[mask]; b=np.asarray(f_rec).ravel()[mask]
    if a.std()==0 or b.std()==0: return 0.0
    if HAVE_ESDA:
        w=KNN.from_array(coords[mask], k=k); w.transform='r'
        return abs(esda.Moran_BV(a,b,w,permutations=0).I)
    return abs(_corr(a,b))

def macsgp_components(adata, celltypes):
    out={}
    if 'factor' in adata.obsm:
        for ct in celltypes:
            out[f"{ct}.SGP0"]=(adata.obsm['factor'][ct].values.ravel(), adata.varm['loading'][ct].values.ravel())
    for ct in celltypes:
        if ct in adata.obsm:
            F=np.asarray(adata.obsm[ct]); L=np.asarray(adata.varm[ct])
            for j in range(F.shape[1]):
                if np.linalg.norm(L[:,j])>1e-8:
                    out[f"{ct}.SGP{j}"]=(F[:,j], L[:,j])
    return out

def clip99(v):
    v=np.asarray(v,float).ravel()
    lo,hi=np.percentile(v,[1,99])
    if hi<=lo: hi=lo+1e-9
    return np.clip(v,lo,hi), lo, hi
[ ]:
# ---- load S3_neg ----
adata = sc.read_h5ad(os.path.join(RESULT_PATH, f"adata_result_macsgp_{SCN}.h5ad"))
coords = np.asarray(adata.obsm['spatial'])
celltypes = list(adata.obsm['proportion'].columns)
prop0 = np.asarray(adata.obsm['proportion']['celltype_0']).ravel()
prop1 = np.asarray(adata.obsm['proportion']['celltype_1']).ravel()

programs={}
gf=np.asarray(adata.obsm['global_factor']); gl=np.asarray(adata.varm['global_loading'])
for r in range(gf.shape[1]):
    programs[f"shared{r}"]=dict(f=gf[:,r], l=gl[:,r], mask=np.ones(adata.n_obs,bool))

macsgp=macsgp_components(adata, celltypes)
base={}
for m in BASELINES:
    fdf=pd.read_csv(factor_path(m), index_col=0).reindex(adata.obs_names)
    ldf=pd.read_csv(loading_path(m), index_col=0).reindex(adata.var_names, fill_value=0.0)
    base[m]=(fdf, ldf)
print("programs:", list(programs), "| macsgp:", list(macsgp))
programs: ['shared0'] | macsgp: ['celltype_0.SGP0', 'celltype_1.SGP0']
[ ]:
SEL = {"shared0": {"macsgp":"celltype_0.SGP0","nmf":0,"mofa":0,"stamp":4,"spicemix":0}}
def get_component(method, selector):
    if method=="macsgp": return macsgp[selector]
    fdf,ldf=base[method]; j=int(selector)
    return fdf.iloc[:,j].values.ravel(), ldf.iloc[:,j].values.ravel()

rows=[]
for prog,pinfo in programs.items():
    f_true,l_true,mask=pinfo['f'],pinfo['l'],pinfo['mask']
    for m in [x for x in METHODS if x in SEL.get(prog,{})]:
        f_rec,l_rec=get_component(m, SEL[prog][m])
        rows.append({"program":prog,"method":m,
                     "spatial_coherence":round(spatial_coherence(f_true,f_rec,coords,mask),3),
                     "program_coherence":round(abs(_corr(l_rec,l_true)),3)})
metrics=pd.DataFrame(rows); metrics.to_csv(os.path.join(FIG_DIR,"metrics_selected.csv"), index=False)
def _val(prog, method, metric):
    r=metrics[(metrics.program==prog)&(metrics.method==method)]
    return abs(float(r[metric].iloc[0])) if len(r) else 0.0
print(metrics.to_string(index=False))
program   method  spatial_coherence  program_coherence
shared0   macsgp              0.931              0.978
shared0      nmf              0.900              0.851
shared0     mofa              0.956              0.960
shared0    stamp              0.852              0.939
shared0 spicemix              0.716              0.589
[ ]:
g_true = programs["shared0"]["f"]
def base_fac(m, prog): return get_component(m, SEL[prog][m])[0]

PANELS = [
 (0,0,"spatial", dict(v=prop0,  sign=None,  cmap=PROP_CMAP, title="", tag="ct1_prop")),
 (0,1,"spatial", dict(v=g_true, sign=None,  cmap=CMAP, title="", tag="global_true")),
 (0,2,"spatial", dict(v=macsgp["celltype_0.SGP0"][0], sign=g_true, cmap=CMAP, title="", tag="macsgp_ct1_sgp0")),
 (0,3,"spatial", dict(v=macsgp["celltype_1.SGP0"][0], sign=prop1,  cmap=CMAP, title="", tag="macsgp_ct2_sgp0")),
 (0,4,"bar",     dict(prog="shared0", metric="spatial_coherence", title="", tag="global_bar_spatial")),
 (1,0,"spatial", dict(v=base_fac("nmf","shared0"),      sign=g_true, cmap=CMAP, title="", tag="global_nmf")),
 (1,1,"spatial", dict(v=base_fac("mofa","shared0"),     sign=g_true, cmap=CMAP, title="", tag="global_mofa")),
 (1,3,"spatial", dict(v=base_fac("stamp","shared0"),    sign=g_true, cmap=CMAP, title="", tag="global_stamp")),
 (1,2,"spatial", dict(v=base_fac("spicemix","shared0"), sign=g_true, cmap=CMAP, title="", tag="global_spicemix")),
 (1,4,"bar",     dict(prog="shared0", metric="program_coherence", title="", tag="global_bar_program")),
]

def add_cbar(ax, sm, ticks, label):
    cax = ax.inset_axes([0.25, -0.15, 0.5, 0.045])   # half-width, centered, below the panel
    cb = ax.figure.colorbar(sm, cax=cax, orientation="horizontal")
    if ticks:
        cb.locator = MaxNLocator(nbins=3); cb.update_ticks(); cax.tick_params(labelsize=CBAR_FS)
    else:
        cb.set_ticks([])
    if label:
        cb.set_label(label, fontsize=CBAR_FS, labelpad=2)
    return cb

def draw_spatial(ax, v, sign, cmap, normalize, cbar_ticks, cbar_label, title=None, spot=6):
    vv=np.asarray(v,float).ravel()
    if sign is not None and _corr(vv,sign)<0: vv=-vv
    vc,lo,hi=clip99(vv)
    if normalize:
        vc=(vc-lo)/(hi-lo+1e-12); lo,hi=0.0,1.0
    sm=ax.scatter(coords[:,0],coords[:,1],c=vc,cmap=cmap,s=spot,vmin=lo,vmax=hi,linewidths=0)
    ax.set_aspect("equal"); ax.set_xticks([]); ax.set_yticks([])
    for sp in ax.spines.values(): sp.set_visible(False)
    if title: ax.set_title(title, fontsize=TITLE_FS)
    add_cbar(ax, sm, cbar_ticks, cbar_label)
    return sm

def draw_bar(ax, prog, metric, title=None):
    vals=[_val(prog,m,metric) for m in ORDER]
    ax.bar(range(len(ORDER)), vals, color=[PALETTE[m] for m in ORDER])
    ax.set_xticks(range(len(ORDER))); ax.set_xticklabels([LABEL[m] for m in ORDER], rotation=45, ha="right", fontsize=BAR_TICK_FS)
    ax.set_ylim(0,1); ax.tick_params(axis="y", labelsize=CBAR_FS); ax.set_box_aspect(1)
    ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False)
    if title: ax.set_title(title, fontsize=TITLE_FS)

def render(ax, kind, p):
    if kind=="spatial":
        is_prop = (p["tag"]=="ct1_prop")
        draw_spatial(ax, p["v"], p["sign"], p["cmap"], normalize=(not is_prop),
                     cbar_ticks=is_prop, cbar_label=(None if is_prop else "Normalized score"),
                     title=p.get("title"))
    else:
        draw_bar(ax, p["prog"], p["metric"], p.get("title"))
[ ]:
fig, axes = plt.subplots(2, 5, figsize=(5*2.15, 2*2.95))
for r,c,kind,p in PANELS:
    render(axes[r][c], kind, p)
fig.subplots_adjust(left=0.02, right=0.99, top=0.93, bottom=0.03, wspace=0.22, hspace=0.60)
fig.savefig(os.path.join(FIG_DIR,"neg_panel_composite.pdf"), bbox_inches="tight")
fig.savefig(os.path.join(FIG_DIR,"neg_panel_composite.png"), dpi=200, bbox_inches="tight")
plt.show(); print("composite saved")
../../_images/analysis_simulation_simulation_complementary_17_0.png
composite saved
[ ]:
for r,c,kind,p in PANELS:
    stem=os.path.join(IND_DIR, f"r{r+1}c{c+1}_{p['tag']}")
    if kind=="spatial":
        f,ax=plt.subplots(figsize=(2.8,3.1))
        is_prop=(p["tag"]=="ct1_prop")
        draw_spatial(ax, p["v"], p["sign"], p["cmap"], normalize=(not is_prop),
                     cbar_ticks=is_prop, cbar_label=(None if is_prop else "Normalized score"),
                     title=None, spot=18)
        f.savefig(stem+".pdf", bbox_inches="tight", transparent=True)
        f.savefig(stem+".png", dpi=200, bbox_inches="tight", transparent=True); plt.close(f)
    else:
        f,ax=plt.subplots(figsize=(3.0,3.2))
        draw_bar(ax, p["prog"], p["metric"], title=p["title"])
        f.savefig(stem+".pdf", bbox_inches="tight"); f.savefig(stem+".png", dpi=200, bbox_inches="tight"); plt.close(f)
print("individual panels ->", IND_DIR)
individual panels -> /import/home2/share/yqzeng/MacSGP/revision_2/vis/2.2/figs/neg_panel/individual