"""Ubuntu plots from explicit evaluation records; never invent missing measurements.""" from pathlib import Path import numpy as np import pandas as pd import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib import font_manager GOLD = "#C68B25" INK = "#302B25" def configure_fonts(font_dir=None): if font_dir is None: font_dir = Path(__file__).resolve().parent/"fonts" for path in Path(font_dir).glob("*.ttf"): font_manager.fontManager.addfont(str(path)) available = {f.name for f in font_manager.fontManager.ttflist} if "Ubuntu" not in available: raise ValueError("Ubuntu is unavailable; provide --font-dir pointing to the included fonts") plt.rcParams.update({"font.family": "Ubuntu", "font.size": 10, "mathtext.fontset": "cm", "pdf.fonttype": 42, "ps.fonttype": 42, "axes.spines.top": False, "axes.spines.right": False, "axes.labelcolor": INK, "text.color": INK, "savefig.bbox": "tight", "figure.dpi": 160}) def plot_summary(summary, output, font_dir=None, title="Retrospective endpoint evaluation"): configure_fonts(font_dir) frame = pd.read_csv(summary) if not isinstance(summary, pd.DataFrame) else summary output = Path(output); output.mkdir(parents=True, exist_ok=True) for metric in frame.metric.unique(): group = frame[frame.metric == metric].sort_values("mean", ascending=metric != "success") fig, ax = plt.subplots(figsize=(6.6, max(2.8, .38*len(group)))) y = np.arange(len(group)) colors = [GOLD if m == "remedi" else "#8C8982" for m in group.method] ax.barh(y, group["mean"], color=colors, height=.65) ax.errorbar(group["mean"], y, xerr=np.vstack([np.maximum(0, group["mean"]-group.ci_low), np.maximum(0, group.ci_high-group["mean"])]), fmt="none", ecolor=INK, capsize=2, linewidth=.8) ax.set_yticks(y, group.method); ax.invert_yaxis() ax.set_xlabel(metric.replace("_", " ")); ax.set_title(title, loc="left", fontsize=11) fig.savefig(output/f"{metric}.pdf"); fig.savefig(output/f"{metric}.png"); plt.close(fig) def plot_curve(csv, output, x, y, hue="method", lower=None, upper=None, font_dir=None): configure_fonts(font_dir) frame = pd.read_csv(csv) for field in [x, y, hue]: if field not in frame: raise ValueError(f"Missing plot column {field}") fig, ax = plt.subplots(figsize=(5.2, 3.4)) palette = [GOLD, "#74716B", "#9E4A35", "#457679", "#756589"] for i, (name, group) in enumerate(frame.groupby(hue, sort=True)): group = group.sort_values(x) ax.plot(group[x], group[y], marker="o", markersize=3.5, label=name, color=GOLD if name == "remedi" else palette[(i+1) % len(palette)]) if lower and upper: ax.fill_between(group[x], group[lower], group[upper], alpha=.13) ax.set_xlabel(x.replace("_", " ")); ax.set_ylabel(y.replace("_", " ")); ax.legend(frameon=False) output = Path(output); output.parent.mkdir(parents=True, exist_ok=True) fig.savefig(output); plt.close(fig)