SPB Git

spb/wp27_uqo

Public
6.1 KB · 144 lines python
1"""Publication figures (matplotlib, vector PDF). Palette validated with the dataviz checker (light surface)."""2from __future__ import annotations34from pathlib import Path56import matplotlib7matplotlib.use("Agg")8import matplotlib.pyplot as plt  # noqa: E4029import numpy as np  # noqa: E40210import pandas as pd  # noqa: E4021112SERIES = ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#6250d6", "#008300", "#e34948"]13INK, INK2, GRID = "#0b0b0b", "#52514e", "#e6e5e1"1415plt.rcParams.update({16    "font.family": "serif", "font.size": 9, "axes.labelsize": 9, "axes.titlesize": 9.5, "legend.fontsize": 8,17    "xtick.labelsize": 8, "ytick.labelsize": 8, "axes.edgecolor": INK2, "axes.linewidth": 0.6, "axes.grid": True,18    "grid.color": GRID, "grid.linewidth": 0.5, "axes.spines.top": False, "axes.spines.right": False,19    "lines.linewidth": 1.6, "figure.dpi": 150, "savefig.bbox": "tight", "pdf.fonttype": 42, "text.color": INK,20    "axes.labelcolor": INK, "xtick.color": INK2, "ytick.color": INK2, "legend.frameon": False,21})222324def save(fig, path: str | Path) -> None:25    path = Path(path)26    path.parent.mkdir(parents=True, exist_ok=True)27    fig.savefig(path)28    fig.savefig(path.with_suffix(".png"), dpi=200)29    plt.close(fig)303132def price_paths(runs: list[tuple[str, pd.DataFrame]], shock_t: int | None = None, title: str = "") -> plt.Figure:33    """Mid vs latent value for several runs (one panel per run)."""34    n = len(runs)35    fig, axes = plt.subplots(1, n, figsize=(2.9 * n, 2.4), sharey=True)36    axes = np.atleast_1d(axes)37    for ax, (label, tk), c in zip(axes, runs, SERIES):38        d = tk[tk.asset == 0]39        ax.plot(d.t, d.full_value / 100, color=INK2, lw=1.0, ls="--", label="latent value")40        ax.plot(d.t, d.public_value / 100, color=INK2, lw=0.7, ls=":", label="public value")41        ax.plot(d.t, d.mid / 100, color=c, lw=1.2, label="mid")42        if shock_t:43            ax.axvline(shock_t, color=GRID, lw=1.0)44        ax.set_title(label)45        ax.set_xlabel("tick")46    axes[0].set_ylabel("price")47    axes[0].legend(loc="upper left")48    if title:49        fig.suptitle(title, fontsize=9.5)50    return fig515253def dose_response(eff: dict[str, pd.DataFrame], ylabels: dict[str, str], x_col: str = "level", ncols: int = 3) -> plt.Figure:54    """eff: outcome -> DataFrame with columns level, diff, ci_lo, ci_hi (paired effects vs control)."""55    keys = list(eff)56    nrows = int(np.ceil(len(keys) / ncols))57    fig, axes = plt.subplots(nrows, ncols, figsize=(2.6 * ncols, 2.1 * nrows))58    axes = np.atleast_1d(axes).ravel()59    for i, (ax, k) in enumerate(zip(axes, keys)):60        d = eff[k]61        if "ci_lo" not in d or d["diff"].isna().all():62            ax.set_title(ylabels.get(k, k) + " (n/a)")63            continue64        x = d[x_col].astype(float).to_numpy()65        sc = 1e4 if k == "msd_log" else 1.066        ax.axhline(0, color=INK2, lw=0.7)67        ax.fill_between(x, sc * d.ci_lo, sc * d.ci_hi, color=SERIES[0], alpha=0.18, lw=0)68        ax.plot(x, sc * d["diff"], color=SERIES[0], marker="o", ms=4)69        ax.set_title(ylabels.get(k, k))70        if i >= len(keys) - ncols:71            ax.set_xlabel("LLM share of trader slots")72    for ax in axes[len(keys):]:73        ax.axis("off")74    fig.tight_layout()75    return fig767778def cell_bars(df: pd.DataFrame, outcome: str, cells: list[str], labels: dict[str, str] | None = None, ylabel: str = "",79              control: str | None = None) -> plt.Figure:80    g = df[df.cell.isin(cells)].groupby("cell")[outcome]81    m, se = g.mean().reindex(cells), (g.std(ddof=1) / np.sqrt(g.size())).reindex(cells)82    fig, ax = plt.subplots(figsize=(0.55 * len(cells) + 1.5, 2.3))83    cols = [SERIES[0] if c != control else INK2 for c in cells]84    ax.bar(range(len(cells)), m, yerr=1.96 * se, color=cols, width=0.62, error_kw={"lw": 0.8, "capsize": 2})85    ax.set_xticks(range(len(cells)))86    ax.set_xticklabels([(labels or {}).get(c, c) for c in cells], rotation=30, ha="right")87    ax.set_ylabel(ylabel or outcome)88    ax.grid(axis="x", visible=False)89    return fig909192def local_projection_plot(lp: dict[str, pd.DataFrame], title: str = "") -> plt.Figure:93    fig, ax = plt.subplots(figsize=(3.6, 2.4))94    for (label, d), c in zip(lp.items(), SERIES):95        ax.plot(d.h, 100 * d.beta, color=c, label=label)96        ax.fill_between(d.h, 100 * (d.beta - 1.96 * d.se), 100 * (d.beta + 1.96 * d.se), color=c, alpha=0.15, lw=0)97    ax.axhline(0, color=INK2, lw=0.7)98    ax.set_xlabel("ticks after the fundamental shock")99    ax.set_ylabel("treatment effect on log(P/V) gap, %")100    ax.legend()101    if title:102        ax.set_title(title)103    return fig104105106def scatter_mechanism(df: pd.DataFrame, x: str, y: str, hue: str, xlabel: str, ylabel: str) -> plt.Figure:107    fig, ax = plt.subplots(figsize=(3.4, 2.6))108    for (k, d), c in zip(df.groupby(hue), SERIES):109        ax.scatter(d[x], d[y], s=14, color=c, label=str(k), alpha=0.85, edgecolor="white", linewidth=0.4)110    ax.set_xlabel(xlabel)111    ax.set_ylabel(ylabel)112    ax.legend(title=hue)113    return fig114115116def wealth_and_inventory(agents: pd.DataFrame, ticks: pd.DataFrame | None = None) -> plt.Figure:117    fig, axes = plt.subplots(1, 2, figsize=(6.0, 2.4))118    ax = axes[0]119    for (pol, d), c in zip(agents.groupby("policy"), SERIES):120        ax.hist(d["pnl_pct"], bins=25, alpha=0.6, color=c, label=pol)121    ax.set_xlabel("P&L, % of initial wealth")122    ax.set_ylabel("traders")123    ax.legend()124    ax = axes[1]125    if ticks is not None:126        d = ticks[ticks.asset == 0]127        ax.plot(d.t, d.depth_bid5, color=SERIES[0], label="bid depth (5 levels)")128        ax.plot(d.t, d.depth_ask5, color=SERIES[1], label="ask depth")129        ax.set_xlabel("tick")130        ax.set_ylabel("lots")131        ax.legend()132    return fig133134135def cost_quality(df: pd.DataFrame, xcol: str, ycol: str, label_col: str, xlabel: str, ylabel: str) -> plt.Figure:136    fig, ax = plt.subplots(figsize=(3.4, 2.6))137    for (k, d), c in zip(df.groupby(label_col), SERIES):138        ax.errorbar(d[xcol].mean(), d[ycol].mean(), yerr=1.96 * d[ycol].std(ddof=1) / np.sqrt(len(d)) if len(d) > 1 else 0,139                    fmt="o", color=c, ms=5, capsize=2, label=str(k))140    ax.set_xlabel(xlabel)141    ax.set_ylabel(ylabel)142    ax.legend()143    return fig144
Files · d09ab0c71293588caf435dcaebf6e4bc03a129ff
Menu

Theme