{"spec_id":"line-win-probability","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nline-win-probability: Win Probability Chart\nLibrary: matplotlib 3.11.0 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-06-21\n\"\"\"\n\nimport os as _os\nimport sys\n\n\n# Remove this script's directory from sys.path so this file (matplotlib.py) does\n# not shadow the matplotlib package when invoked from inside this directory.\n_this_dir = _os.path.dirname(_os.path.abspath(__file__))\nsys.path = [p for p in sys.path if _os.path.abspath(p) != _this_dir]\ndel _this_dir\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nfrom matplotlib.collections import LineCollection\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — semantic mapping: Eagles → brand green, Cowboys → blue\nEAGLES_COLOR = \"#009E73\"  # Imprint position 1 — home team\nCOWBOYS_COLOR = \"#4467A3\"  # Imprint position 3 — away team\n\n# Data — simulated NFL game: Eagles vs Cowboys\nnp.random.seed(42)\n\nn_plays = 120\nplays = np.arange(n_plays + 1)\nwin_prob = np.full(n_plays + 1, 0.50)\n\nscoring_events = [\n    (8, 0.12, \"PHI Field Goal (3-0)\"),\n    (22, -0.10, \"DAL Touchdown (7-3)\"),\n    (35, 0.15, \"PHI Touchdown (10-7)\"),\n    (48, 0.08, \"PHI Field Goal (13-7)\"),\n    (58, -0.18, \"DAL Touchdown (14-13)\"),\n    (72, 0.14, \"PHI Touchdown (20-14)\"),\n    (85, -0.06, \"DAL Field Goal (20-17)\"),\n    (95, 0.12, \"PHI Touchdown (27-17)\"),\n    (110, -0.05, \"DAL Field Goal (27-20)\"),\n]\n\nprob = 0.50\nnoise = np.random.normal(0, 0.012, n_plays + 1)\nevent_indices = {e[0]: (e[1], e[2]) for e in scoring_events}\n\nfor i in range(1, n_plays + 1):\n    if i in event_indices:\n        prob += event_indices[i][0]\n    prob += noise[i]\n    prob = np.clip(prob, 0.02, 0.98)\n    win_prob[i] = prob\n\n# Force convergence to Eagles win\nfor i in range(105, n_plays + 1):\n    t = (i - 105) / (n_plays - 105)\n    win_prob[i] = win_prob[105] * (1 - t**2) + 1.0 * t**2\n\nquarter_boundaries = [0, 30, 60, 90, n_plays]\nquarter_labels = [\"Q1\", \"Q2\", \"Q3\", \"Q4\"]\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Fill above/below 50% — balanced alphas so neither team's fill dominates visually\nax.fill_between(plays, win_prob, 0.5, where=(win_prob >= 0.5), color=EAGLES_COLOR, alpha=0.30, interpolate=True)\nax.fill_between(plays, win_prob, 0.5, where=(win_prob < 0.5), color=COWBOYS_COLOR, alpha=0.30, interpolate=True)\n\n# Win probability line via LineCollection — idiomatic and efficient\npoints = np.array([plays, win_prob]).T.reshape(-1, 1, 2)\nsegments = np.concatenate([points[:-1], points[1:]], axis=1)\nmidpoints = (win_prob[:-1] + win_prob[1:]) / 2\nseg_colors = [EAGLES_COLOR if m >= 0.5 else COWBOYS_COLOR for m in midpoints]\nlc = LineCollection(segments, colors=seg_colors, linewidths=2.5, zorder=3, capstyle=\"round\")\nax.add_collection(lc)\n\n# 50% baseline\nax.axhline(y=0.5, color=INK_MUTED, linewidth=1.2, linestyle=\"--\", alpha=0.6, zorder=2)\n\n# Quarter dividers and labels\nfor qb in quarter_boundaries[1:-1]:\n    ax.axvline(x=qb, color=INK_MUTED, linewidth=0.8, linestyle=\":\", alpha=0.4)\n\nfor i, label in enumerate(quarter_labels):\n    mid = (quarter_boundaries[i] + quarter_boundaries[i + 1]) / 2\n    ax.text(mid, 0.13, label, ha=\"center\", va=\"center\", fontsize=8, color=INK_MUTED, fontweight=\"medium\")\n\n# Annotate key scoring events\nannotation_events = [\n    (8, \"FG 3-0\"),\n    (22, \"TD 7-3\"),\n    (35, \"TD 10-7\"),\n    (58, \"TD 14-13\"),\n    (72, \"TD 20-14\"),\n    (95, \"TD 27-17\"),\n]\n\nfor play_idx, label in annotation_events:\n    wp = win_prob[play_idx]\n    offset_y = 0.07 if wp >= 0.5 else -0.07\n    txt = ax.annotate(\n        label,\n        xy=(play_idx, wp),\n        xytext=(play_idx, wp + offset_y),\n        fontsize=8,\n        fontweight=\"bold\",\n        ha=\"center\",\n        va=\"center\",\n        color=INK,\n        arrowprops={\"arrowstyle\": \"-\", \"color\": INK_MUTED, \"linewidth\": 0.8},\n        zorder=4,\n    )\n    txt.set_path_effects([pe.withStroke(linewidth=2.5, foreground=PAGE_BG)])\n\n# Scoring event dots on the curve\nfor play_idx, _ in annotation_events:\n    color = EAGLES_COLOR if win_prob[play_idx] >= 0.5 else COWBOYS_COLOR\n    ax.plot(\n        play_idx,\n        win_prob[play_idx],\n        \"o\",\n        color=color,\n        markersize=5,\n        zorder=5,\n        markeredgecolor=PAGE_BG,\n        markeredgewidth=0.8,\n    )\n\n# Axes\nax.set_xlim(0, n_plays)\nax.set_ylim(0.10, 1.02)\nax.set_yticks([0.25, 0.5, 0.75, 1.0])\nax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f\"{x:.0%}\"))\n\n# Grid — subtle y-axis only\nax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK)\nax.set_axisbelow(True)\n\n# Labels and scaled title\ntitle = \"Eagles 27 – Cowboys 20 · line-win-probability · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title)))\nax.set_xlabel(\"Play Number\", fontsize=10, color=INK)\nax.set_ylabel(\"Win Probability\", fontsize=10, color=INK)\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\n\n# Spines\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\n# Legend\neagles_patch = mpatches.Patch(color=EAGLES_COLOR, alpha=0.5, label=\"Eagles\")\ncowboys_patch = mpatches.Patch(color=COWBOYS_COLOR, alpha=0.5, label=\"Cowboys\")\nleg = ax.legend(handles=[eagles_patch, cowboys_patch], fontsize=8, loc=\"upper left\", framealpha=0.9, edgecolor=INK_SOFT)\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.09, right=0.97, top=0.91, bottom=0.12)\n\n# Save — no bbox_inches=\"tight\" (would trim canvas from 3200×1800)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}