"""Appendix A3 — Feature-group cumulative F1 contribution (waterfall). Each bar is the validation-F1 gain from adding that group on top of the previous stage (incremental ablation). The gains sum to 0.0283, exactly LightGCN 0.9386 -> 0.9669. """ from pathlib import Path import matplotlib.pyplot as plt from style import apply, save, PALETTE as C, COL1 # noqa: E402 KEY = "figA3_feature_importance" TITLE = "Appendix Figure A3. Feature-group contribution" GROUPS = [ ("graph / meta-path stacking", 0.0174, C[0]), ("7-block random-walk ensemble", 0.0028, C[1]), ("DeepWalk + Node2Vec", 0.0022, C[2]), ("higher-order propagation", 0.0020, C[3]), ("BPR-MF", 0.0017, C[4]), ("neg / topk / variant scores", 0.0011, C[5]), ("rich content (18-d)", 0.0006, C[6]), ("content mean-cos", 0.0005, C[7]), ] def make(root, out): apply() names = [g[0] for g in GROUPS]; gains = [g[1] for g in GROUPS]; cols = [g[2] for g in GROUPS] total = sum(gains) fig, ax = plt.subplots(figsize=(COL1 * 1.5, 3.4)) ax.barh(range(len(GROUPS)), gains, color=cols) ax.set_yticks(range(len(GROUPS))); ax.set_yticklabels(names, fontsize=7.5) ax.invert_yaxis() for i, g in enumerate(gains): ax.text(g + 0.0003, i, f"+{g:.4f}", va="center", fontsize=7) ax.set_xlabel("$\\Delta$F1 (cumulative, seed=202)"); ax.set_xlim(0, 0.020) ax.set_title(f"Feature-group contribution ($\\Sigma\\Delta$={total:.4f})", fontsize=9) save(fig, KEY, out) return dict(key=KEY, title=TITLE, status="ok", files=[f"{KEY}.pdf", f"{KEY}.png", f"{KEY}.svg"], sources=["post95/extra/content_rich/node2vec/randomwalk/high_order incremental ablations"], caption=( "Cumulative F1 contribution by feature group (incremental ablation on seed=202). The graph/" "meta-path stacking with the LightGBM stacker dominates (+0.0174); the random-walk blocks, " "DeepWalk/Node2Vec and higher-order propagation form the second tier. Gains sum to 0.0283, " "exactly the LightGCN 0.9386 -> 0.9669 improvement, confirming internal consistency.")) if __name__ == "__main__": from style import ensure_dirs r = make(Path("."), ensure_dirs(Path("."))) print(r["key"], r["status"])