"""Plot measured evaluation summaries and collect comparable seed-level results.""" from pathlib import Path import argparse, json, csv import numpy as np import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from matplotlib import font_manager ROOT = Path(__file__).resolve().parents[1] for f in (ROOT / "assets/fonts").glob("*.ttf"): font_manager.fontManager.addfont(str(f)) plt.rcParams.update( {"font.family": "Ubuntu", "mathtext.fontset": "cm", "pdf.fonttype": 42} ) def plot(paths, output): records = [json.loads(Path(p).read_text()) for p in paths] rows = [] for d in records: if d.get("protocol") != "split-first-v1": raise ValueError("Use corrected evaluation outputs") for search, metrics in d["summary"]["inverse"].items(): rows.append( { "method": d["method"], "search": search, "seed": d["seed"], **{k: v["mean"] for k, v in metrics.items()}, } ) if not rows: raise ValueError("No inverse results for this catalog and target split") out = Path(output) out.mkdir(parents=True, exist_ok=True) with open(out / "summary.csv", "w") as f: writer = csv.DictWriter(f, fieldnames=list(rows[0])) writer.writeheader() writer.writerows(rows) fig, axes = plt.subplots(1, 2, figsize=(10, 4), layout="constrained") labels = [ r["method"] + "\n" + r["search"] + "\nseed " + str(r["seed"]) for r in rows ] for ax, k, title in zip( axes, ["top5", "measured_regret"], [ "Exact Top-5 recovery (higher is better)", "Measured regret (lower is better)", ], ): ax.bar(np.arange(len(rows)), [r[k] for r in rows], color="#62AEDD", width=0.6) ax.set_xticks(np.arange(len(rows)), labels, fontsize=8) ax.set_title(title, fontsize=11) ax.spines[["top", "right"]].set_visible(False) ax.grid(axis="y", alpha=0.2) ax.set_axisbelow(True) for ext in ["pdf", "png"]: fig.savefig(out / f"evaluation.{ext}", dpi=220) plt.close(fig) if __name__ == "__main__": p = argparse.ArgumentParser() p.add_argument("results", nargs="+") p.add_argument("--output", default="plots") a = p.parse_args() plot(a.results, a.output)