SPB Git

spb/wp27_uqo

Public
5.9 KB · 138 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 ax, k in zip(axes, keys):60        d = eff[k]61        x = d[x_col].astype(float).to_numpy()62        ax.axhline(0, color=INK2, lw=0.7)63        ax.fill_between(x, d.ci_lo, d.ci_hi, color=SERIES[0], alpha=0.18, lw=0)64        ax.plot(x, d["diff"], color=SERIES[0], marker="o", ms=4)65        ax.set_title(ylabels.get(k, k))66        ax.set_xlabel("LLM share of trader slots")67    for ax in axes[len(keys):]:68        ax.axis("off")69    return fig707172def cell_bars(df: pd.DataFrame, outcome: str, cells: list[str], labels: dict[str, str] | None = None, ylabel: str = "",73              control: str | None = None) -> plt.Figure:74    g = df[df.cell.isin(cells)].groupby("cell")[outcome]75    m, se = g.mean().reindex(cells), (g.std(ddof=1) / np.sqrt(g.size())).reindex(cells)76    fig, ax = plt.subplots(figsize=(0.55 * len(cells) + 1.5, 2.3))77    cols = [SERIES[0] if c != control else INK2 for c in cells]78    ax.bar(range(len(cells)), m, yerr=1.96 * se, color=cols, width=0.62, error_kw={"lw": 0.8, "capsize": 2})79    ax.set_xticks(range(len(cells)))80    ax.set_xticklabels([(labels or {}).get(c, c) for c in cells], rotation=30, ha="right")81    ax.set_ylabel(ylabel or outcome)82    ax.grid(axis="x", visible=False)83    return fig848586def local_projection_plot(lp: dict[str, pd.DataFrame], title: str = "") -> plt.Figure:87    fig, ax = plt.subplots(figsize=(3.6, 2.4))88    for (label, d), c in zip(lp.items(), SERIES):89        ax.plot(d.h, 100 * d.beta, color=c, label=label)90        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)91    ax.axhline(0, color=INK2, lw=0.7)92    ax.set_xlabel("ticks after the fundamental shock")93    ax.set_ylabel("treatment effect on log(P/V) gap, %")94    ax.legend()95    if title:96        ax.set_title(title)97    return fig9899100def scatter_mechanism(df: pd.DataFrame, x: str, y: str, hue: str, xlabel: str, ylabel: str) -> plt.Figure:101    fig, ax = plt.subplots(figsize=(3.4, 2.6))102    for (k, d), c in zip(df.groupby(hue), SERIES):103        ax.scatter(d[x], d[y], s=14, color=c, label=str(k), alpha=0.85, edgecolor="white", linewidth=0.4)104    ax.set_xlabel(xlabel)105    ax.set_ylabel(ylabel)106    ax.legend(title=hue)107    return fig108109110def wealth_and_inventory(agents: pd.DataFrame, ticks: pd.DataFrame | None = None) -> plt.Figure:111    fig, axes = plt.subplots(1, 2, figsize=(6.0, 2.4))112    ax = axes[0]113    for (pol, d), c in zip(agents.groupby("policy"), SERIES):114        ax.hist(d["pnl_pct"], bins=25, alpha=0.6, color=c, label=pol)115    ax.set_xlabel("P&L, % of initial wealth")116    ax.set_ylabel("traders")117    ax.legend()118    ax = axes[1]119    if ticks is not None:120        d = ticks[ticks.asset == 0]121        ax.plot(d.t, d.depth_bid5, color=SERIES[0], label="bid depth (5 levels)")122        ax.plot(d.t, d.depth_ask5, color=SERIES[1], label="ask depth")123        ax.set_xlabel("tick")124        ax.set_ylabel("lots")125        ax.legend()126    return fig127128129def cost_quality(df: pd.DataFrame, xcol: str, ycol: str, label_col: str, xlabel: str, ylabel: str) -> plt.Figure:130    fig, ax = plt.subplots(figsize=(3.4, 2.6))131    for (k, d), c in zip(df.groupby(label_col), SERIES):132        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,133                    fmt="o", color=c, ms=5, capsize=2, label=str(k))134    ax.set_xlabel(xlabel)135    ax.set_ylabel(ylabel)136    ax.legend()137    return fig138
Files · b6c1d09b402a43e723c06ff0c9a6073a51ea3fa7
Menu

Theme