Source code for oadr_cpep.plot

"""
Plotting for oadr-cpep. All graphics live here (matplotlib for PNG/SVG, plotly
for the self-contained interactive HTML) so the compute modules stay free of
rendering code.
"""
from __future__ import annotations

import os

import numpy as np


[docs] def scatter(y, pred, title, base): """One observed-vs-predicted scatter -> <base>.png, .svg, and interactive .html.""" import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt lo = float(min(y.min(), np.nanmin(pred))) hi = float(max(y.max(), np.nanmax(pred))) fig, ax = plt.subplots(figsize=(5.4, 4.8), constrained_layout=True) ax.scatter(y, pred, c="#1f77b4", s=55, edgecolor="white") ax.plot([lo, hi], [lo, hi], "k--", alpha=0.4) ax.set_title(title, fontweight="bold") ax.set_xlabel("Observed log(C-peptide AUC)") ax.set_ylabel("Predicted") ax.grid(alpha=0.25) fig.savefig(base + ".png", dpi=220) fig.savefig(base + ".svg") plt.close(fig) try: import plotly.graph_objects as go pf = go.Figure() pf.add_trace(go.Scatter(x=y, y=pred, mode="markers", showlegend=False, marker=dict(size=8, line=dict(width=1, color="white")))) pf.add_trace(go.Scatter(x=[lo, hi], y=[lo, hi], mode="lines", showlegend=False, line=dict(dash="dash", color="black"))) pf.update_layout(title_text=title, xaxis_title="Observed", yaxis_title="Predicted", width=560, height=480) pf.write_html(base + ".html") except Exception: # plotly optional; PNG/SVG still produced pass
[docs] def solo_vs_federated(site, panel, y, results, base, sites_label=""): """Per-method solo-vs-federated grid (rows = methods, cols = solo|federated) -> <base>.png, .svg, and interactive .html. ``results`` is the list built by apply_coefficients (each has method, solo, fed, r2_solo, ci_solo, r2_fed, ci_fed).""" import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt nm = len(results) allp = [y] + [r["solo"] for r in results] + [r["fed"] for r in results] lo = float(min(np.nanmin(a) for a in allp)) hi = float(max(np.nanmax(a) for a in allp)) prov = f" [of {sites_label}]" if sites_label else "" suptitle = f"{site} panel {panel} — solo vs federated{prov}" fig, axes = plt.subplots(nm, 2, figsize=(10, 4.4 * nm), constrained_layout=True, squeeze=False) for row, r in enumerate(results): for col, (pred, lab, r2v, ci) in enumerate([ (r["solo"], f"{site} alone", r["r2_solo"], r["ci_solo"]), (r["fed"], f"{site} + federated", r["r2_fed"], r["ci_fed"])]): ax = axes[row][col] ax.scatter(y, pred, c="#1f77b4", s=55, edgecolor="white") ax.plot([lo, hi], [lo, hi], "k--", alpha=0.4) ax.set_title(f"{r['method'].upper()}{lab}\nR²={r2v:+.2f} [{ci[0]:+.2f}, {ci[1]:+.2f}]", fontweight="bold", fontsize=10) ax.set_xlabel("Observed"); ax.set_ylabel("Predicted"); ax.grid(alpha=0.25) fig.suptitle(suptitle, fontsize=13, fontweight="bold") fig.savefig(base + ".png", dpi=220) fig.savefig(base + ".svg") plt.close(fig) try: import plotly.graph_objects as go from plotly.subplots import make_subplots titles = [] for r in results: titles += [f"{r['method'].upper()} — alone R²={r['r2_solo']:+.2f}", f"{r['method'].upper()} — +federated R²={r['r2_fed']:+.2f}"] pfig = make_subplots(rows=nm, cols=2, subplot_titles=titles) for row, r in enumerate(results, start=1): for col, pred in enumerate([r["solo"], r["fed"]], start=1): pfig.add_trace(go.Scatter(x=y, y=pred, mode="markers", showlegend=False, marker=dict(size=7, line=dict(width=1, color="white"))), row=row, col=col) pfig.add_trace(go.Scatter(x=[lo, hi], y=[lo, hi], mode="lines", showlegend=False, line=dict(dash="dash", color="black")), row=row, col=col) pfig.update_layout(title_text=suptitle, width=900, height=380 * nm) pfig.write_html(base + ".html") except Exception: pass