1"""Generate paper figures from saved results (no proprietary data). Usage: make_figures.py --exps pilot,main,ablations"""2from __future__ import annotations34import argparse5import json6import sys7from pathlib import Path89import numpy as np10import pandas as pd1112ROOT = Path(__file__).resolve().parents[1]13sys.path.insert(0, str(ROOT / "src"))14from chronos_x.eval import figures as F # noqa: E4021516OUT = ROOT / "paper" / "figures"171819def main() -> None:20 ap = argparse.ArgumentParser(); ap.add_argument("--exps", default="pilot"); ap.add_argument("--split", default="test")21 a = ap.parse_args()22 made = []23 for exp in a.exps.split(","):24 d = ROOT / "results" / "metrics" / exp25 for suffix in ("", "_heldout"):26 f = d / f"leaderboard_{a.split}{suffix}.csv"27 if not f.exists():28 continue29 lb = pd.read_csv(f)30 F.fig_scores_by_horizon(lb, OUT, f"fig_{exp}_crps{suffix}", "crps_scaled"); made.append(f"fig_{exp}_crps{suffix}")31 F.fig_scores_by_horizon(lb, OUT, f"fig_{exp}_wis{suffix}", "wis"); made.append(f"fig_{exp}_wis{suffix}")32 F.fig_calibration(lb, OUT, f"fig_{exp}_calibration{suffix}"); made.append(f"fig_{exp}_calibration{suffix}")33 # PIT histograms at H=60 for up to four models34 per = {}35 for m in ("naive_rw", "garch_t", "lightgbm", "mrpt_small", "mrpt_base"):36 p = d / f"per_origin_{m}_{a.split}{suffix}.npz"37 if p.exists():38 per[m] = np.load(p)["pit_60"]39 if per:40 F.fig_pit(dict(list(per.items())[:4]), OUT, f"fig_{exp}_pit{suffix}"); made.append(f"fig_{exp}_pit{suffix}")41 fc = d / f"compare_crps_vs_naive_rw_{a.split}{suffix}.csv"42 if fc.exists():43 F.fig_ablation_forest(pd.read_csv(fc), OUT, f"fig_{exp}_forest{suffix}", "random walk"); made.append(f"fig_{exp}_forest{suffix}")44 logs = {m: d / m / "train_log.jsonl" for m in ("mrpt_small", "mrpt_small_1m_only", "mrpt_base", "tcn") if (d / m / "train_log.jsonl").exists()}45 if logs:46 F.fig_training_curves(logs, OUT, f"fig_{exp}_training"); made.append(f"fig_{exp}_training")47 print("figures:", made)484950if __name__ == "__main__":51 main()52