{"spec_id":"horizon-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nhorizon-basic: Horizon Chart\nLibrary: matplotlib 3.11.1 | Python 3.13.15\nQuality: 89/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport matplotlib.colors as mcolors\nimport matplotlib.dates as mdates\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom scipy.ndimage import gaussian_filter1d\n\n\n# Theme tokens\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\"\n\n# Data - 8 server metrics over 24 hours, sampled every 5 minutes for a smooth\n# mountain-range fold, with pronounced daily cycles + localized spikes.\nnp.random.seed(42)\nhours = pd.date_range(\"2024-01-15\", periods=288, freq=\"5min\")\n\nseries_names = [\"CPU Load\", \"Memory\", \"Network I/O\", \"Disk I/O\", \"Requests/s\", \"Latency\", \"Queue Depth\", \"Threads\"]\nn_series = len(series_names)\nn_points = len(hours)\n\ndata = {}\nfor i, name in enumerate(series_names):\n    base = np.sin(np.linspace(0, 2 * np.pi, n_points) + i * np.pi / 4) * 0.5\n    # Smooth the per-point noise so the 5-min sampling reads as a fluid\n    # mountain-range fold rather than a jittery, needle-thin silhouette.\n    noise = gaussian_filter1d(np.random.randn(n_points), sigma=4) * 0.45\n    spikes = np.zeros(n_points)\n    if i % 2 == 0:\n        t = np.arange(n_points)\n        width = n_points / 48  # ~30-minute-wide bump at 5-minute resolution\n        for center in np.random.choice(n_points, 4, replace=False):\n            magnitude = np.random.uniform(0.7, 1.2) * (1 if np.random.random() > 0.3 else -1)\n            spikes += magnitude * np.exp(-0.5 * ((t - center) / width) ** 2)\n    data[name] = np.clip(base + noise + spikes, -1.5, 1.5)\n\n# Horizon chart parameters - 3 mirrored bands, intensity increases with magnitude.\n# Imprint green for positive load, Imprint red for negative (matches the\n# domain convention of \"elevated load\" reading as the warmer/alarming hue).\nn_bands = 3\npos_base = mcolors.to_rgb(\"#009E73\")\nneg_base = mcolors.to_rgb(\"#AE3030\")\n# Custom normalizer maps band index -> fill alpha, replacing a hand-tuned\n# arithmetic ladder with a reusable, principled intensity ramp.\nband_norm = mcolors.Normalize(vmin=-0.6, vmax=n_bands - 0.4)\n\n\ndef band_alpha(band_idx):\n    return 0.25 + 0.60 * band_norm(band_idx)\n\n\n# Canvas - landscape 3200x1800 (figsize x dpi), hard contract, never deviate\nfig, axes = plt.subplots(n_series, 1, figsize=(8, 4.5), dpi=400, sharex=True, facecolor=PAGE_BG)\nfig.subplots_adjust(hspace=0.10, top=0.865, bottom=0.115, left=0.065, right=0.85)\n\nfor idx, (name, values) in enumerate(data.items()):\n    ax = axes[idx]\n    ax.set_facecolor(PAGE_BG)\n\n    max_abs = max(abs(values.min()), abs(values.max()), 0.01)\n    normalized = values / max_abs\n    band_edges = np.linspace(0, 1, n_bands + 1)\n\n    for band_idx in range(n_bands):\n        lower, upper = band_edges[band_idx], band_edges[band_idx + 1]\n        alpha = band_alpha(band_idx)\n\n        pos_folded = np.clip(np.clip(normalized, 0, None) - lower, 0, upper - lower)\n        ax.fill_between(hours, 0, pos_folded, color=(*pos_base, alpha), linewidth=0)\n\n        neg_folded = np.clip(np.clip(-normalized, 0, None) - lower, 0, upper - lower)\n        ax.fill_between(hours, 0, neg_folded, color=(*neg_base, alpha), linewidth=0)\n\n    ax.set_ylim(0, 1 / n_bands + 0.05)\n    ax.set_xlim(hours[0], hours[-1])\n    ax.set_yticks([])\n\n    # Series label with a background-matched stroke so it stays legible\n    # even where it sits directly above a high-intensity band.\n    ax.text(\n        1.015,\n        0.5,\n        name,\n        transform=ax.transAxes,\n        fontsize=9,\n        fontweight=\"bold\",\n        va=\"center\",\n        ha=\"left\",\n        color=INK,\n        path_effects=[pe.withStroke(linewidth=2, foreground=PAGE_BG)],\n    )\n\n    ax.spines[\"top\"].set_visible(False)\n    ax.spines[\"right\"].set_visible(False)\n    ax.spines[\"left\"].set_visible(False)\n\n    if idx < n_series - 1:\n        ax.spines[\"bottom\"].set_visible(False)\n        ax.tick_params(axis=\"x\", length=0)\n    else:\n        ax.spines[\"bottom\"].set_color(INK_SOFT)\n        ax.tick_params(axis=\"x\", labelsize=8, colors=INK_SOFT, labelcolor=INK_SOFT)\n\n# Configure x-axis on bottom subplot - plain hour-of-day ticks since every\n# point falls on the same day, matching the \"Time (Hour of Day)\" axis label.\naxes[-1].xaxis.set_major_locator(mdates.HourLocator(byhour=range(0, 24, 3)))\naxes[-1].xaxis.set_major_formatter(mdates.DateFormatter(\"%H:%M\"))\naxes[-1].set_xlabel(\"Time (Hour of Day)\", fontsize=10, color=INK)\n\n# Title\nfig.suptitle(\"horizon-basic · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", y=0.978, color=INK)\n\n# Legend for bands with theme-aware colors\nlegend_elements = [\n    plt.Rectangle((0, 0), 1, 1, facecolor=(*pos_base, band_alpha(i)), label=lbl)\n    for i, lbl in enumerate([\"Low +\", \"Mid +\", \"High +\"])\n] + [\n    plt.Rectangle((0, 0), 1, 1, facecolor=(*neg_base, band_alpha(i)), label=lbl)\n    for i, lbl in enumerate([\"Low −\", \"Mid −\", \"High −\"])\n]\nleg = fig.legend(\n    handles=legend_elements,\n    loc=\"upper center\",\n    ncol=6,\n    fontsize=7.5,\n    frameon=True,\n    bbox_to_anchor=(0.46, 0.928),\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\nplt.setp(leg.get_texts(), color=INK_SOFT)\n\n# bbox_inches MUST stay default (None) - \"tight\" silently crops the 3200x1800 canvas\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}