{"spec_id":"scatter-connected-temporal","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nscatter-connected-temporal: Connected Scatter Plot with Temporal Path\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-09\n\"\"\"\n\nimport os\n\nimport matplotlib.colors as mcolors\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.patches import FancyArrowPatch\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome tokens\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# Data — Phillips curve: unemployment vs inflation over 30 years\nnp.random.seed(42)\nyears = np.arange(1994, 2024)\nn = len(years)\n\nunemployment = np.zeros(n)\ninflation = np.zeros(n)\nunemployment[0] = 6.5\ninflation[0] = 2.8\n\nfor i in range(1, n):\n    cycle = np.sin(2 * np.pi * i / 10)\n    unemployment[i] = unemployment[i - 1] + cycle * 0.4 + np.random.normal(0, 0.3)\n    inflation[i] = inflation[i - 1] - 0.3 * (unemployment[i] - unemployment[i - 1]) + np.random.normal(0, 0.2)\n\nunemployment = np.clip(unemployment, 3.0, 10.0)\ninflation = np.clip(inflation, 0.5, 6.0)\n\n# Imprint sequential colormap (brand green → blue) for temporal progression\nimprint_seq = mcolors.LinearSegmentedColormap.from_list(\"imprint_seq\", [\"#009E73\", \"#4467A3\"])\nnorm = mcolors.Normalize(vmin=0, vmax=n - 1)\n\n# Canvas: 3200×1800 px (landscape 16:9) — figsize=(8,4.5) × dpi=400\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw path segments with Imprint sequential color gradient\nfor i in range(n - 1):\n    ax.plot(\n        unemployment[i : i + 2],\n        inflation[i : i + 2],\n        color=imprint_seq(norm(i)),\n        linewidth=2.0,\n        solid_capstyle=\"round\",\n        zorder=2,\n    )\n\n# Directional arrows at key intervals along the path\narrow_indices = [4, 11, 18, 25]\nfor idx in arrow_indices:\n    arrow = FancyArrowPatch(\n        (unemployment[idx], inflation[idx]),\n        (unemployment[idx + 1], inflation[idx + 1]),\n        arrowstyle=\"-|>\",\n        mutation_scale=10,\n        color=imprint_seq(norm(idx)),\n        linewidth=1.2,\n        zorder=3,\n    )\n    ax.add_patch(arrow)\n\n# Scatter points — edge uses PAGE_BG so markers separate cleanly on both themes\nax.scatter(\n    unemployment,\n    inflation,\n    c=np.arange(n),\n    cmap=imprint_seq,\n    norm=norm,\n    s=130,\n    edgecolors=PAGE_BG,\n    linewidth=0.8,\n    zorder=5,\n)\n\n# Annotate key time points\nlabel_indices = [0, 9, 19, n - 1]\noffsets = [(10, -14), (-14, 12), (10, 12), (-14, -14)]\nfor idx, (dx, dy) in zip(label_indices, offsets, strict=True):\n    ax.annotate(\n        str(years[idx]),\n        (unemployment[idx], inflation[idx]),\n        textcoords=\"offset points\",\n        xytext=(dx, dy),\n        fontsize=9,\n        fontweight=\"bold\",\n        color=imprint_seq(norm(idx)),\n        arrowprops={\"arrowstyle\": \"-\", \"color\": imprint_seq(norm(idx)), \"alpha\": 0.5, \"linewidth\": 0.6},\n    )\n\n# Highlight the unemployment peak — most economically significant inflection point\npeak_idx = int(np.argmax(unemployment))\nax.annotate(\n    f\"Unemployment\\npeak · {years[peak_idx]}\",\n    (unemployment[peak_idx], inflation[peak_idx]),\n    xytext=(0.68, 0.28),\n    textcoords=\"axes fraction\",\n    fontsize=8,\n    color=INK,\n    bbox={\n        \"facecolor\": ELEVATED_BG,\n        \"edgecolor\": INK_SOFT,\n        \"alpha\": 0.88,\n        \"boxstyle\": \"round,pad=0.35\",\n        \"linewidth\": 0.6,\n    },\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_SOFT, \"linewidth\": 0.7},\n)\n\n# Colorbar for temporal progression\nsm = plt.cm.ScalarMappable(cmap=imprint_seq, norm=mcolors.Normalize(vmin=years[0], vmax=years[-1]))\nsm.set_array([])\ncbar = fig.colorbar(sm, ax=ax, pad=0.02, aspect=28, shrink=0.75)\ncbar.set_label(\"Year\", fontsize=10, color=INK)\ncbar.ax.tick_params(labelsize=8, colors=INK_SOFT)\ncbar.outline.set_visible(False)\n\n# Title — length 59 chars < 67, so fontsize stays at 12\ntitle = \"scatter-connected-temporal · python · matplotlib · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", pad=8, color=INK)\nax.set_xlabel(\"Unemployment Rate (%)\", fontsize=10, color=INK)\nax.set_ylabel(\"Inflation Rate (%)\", fontsize=10, 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)\nax.spines[\"left\"].set_linewidth(0.6)\nax.spines[\"bottom\"].set_linewidth(0.6)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Grid — both axes for scatter context\nax.yaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax.xaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax.set_axisbelow(True)\n\nfig.subplots_adjust(left=0.10, right=0.86, top=0.91, bottom=0.13)\n# bbox_inches MUST stay default (None) — \"tight\" silently trims canvas\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}