{"spec_id":"step-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nstep-basic: Basic Step Plot\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\nimport sys\n\n\n# Workaround: this file is named altair.py, which shadows the altair module\n_cwd = os.getcwd()\nif _cwd in sys.path:\n    sys.path.remove(_cwd)\nif \"\" in sys.path:\n    sys.path.remove(\"\")\n\nimport altair as alt\n\nsys.path.insert(0, _cwd)\n\nimport pandas as pd\nfrom PIL import Image\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"\n\n# Data — monthly cumulative software subscription revenue\nmonths = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\ncumulative_revenue = [12, 25, 31, 48, 52, 67, 89, 95, 108, 124, 145, 168]\n\ndf = pd.DataFrame({\"Month\": months, \"Cumulative Revenue\": cumulative_revenue})\n\n# Largest month-over-month jump — used to give the chart a focal point (size/opacity\n# accent only, no text callout; brand green stays the only data color per the palette rules)\ndeltas = [b - a for a, b in zip(cumulative_revenue, cumulative_revenue[1:], strict=False)]\npeak_idx = deltas.index(max(deltas)) + 1\npeak_month = months[peak_idx]\npeak_df = df[df[\"Month\"] == peak_month]\n\n# Step line\nline = (\n    alt.Chart(df)\n    .mark_line(interpolate=\"step-after\", strokeWidth=3, color=BRAND)\n    .encode(\n        x=alt.X(\"Month:N\", title=\"Month\", sort=months, axis=alt.Axis(labelAngle=0)),\n        y=alt.Y(\"Cumulative Revenue:Q\", title=\"Cumulative Revenue (thousands $)\"),\n    )\n)\n\n# Thicker overlay on the steepest step to emphasize the largest jump\nhighlight_line = (\n    alt.Chart(df.iloc[peak_idx - 1 : peak_idx + 1])\n    .mark_line(interpolate=\"step-after\", strokeWidth=6, color=BRAND)\n    .encode(x=alt.X(\"Month:N\", sort=months), y=\"Cumulative Revenue:Q\")\n)\n\n# Markers at each data point\npoints = (\n    alt.Chart(df)\n    .mark_point(size=90, color=BRAND, filled=True, opacity=1.0)\n    .encode(x=alt.X(\"Month:N\", sort=months), y=\"Cumulative Revenue:Q\")\n)\n\n# Soft halo + enlarged marker at the peak jump's endpoint — the chart's focal point\npeak_halo = (\n    alt.Chart(peak_df)\n    .mark_point(size=320, color=BRAND, filled=True, opacity=0.20)\n    .encode(x=alt.X(\"Month:N\", sort=months), y=\"Cumulative Revenue:Q\")\n)\npeak_marker = (\n    alt.Chart(peak_df)\n    .mark_point(size=160, color=BRAND, filled=True, opacity=1.0)\n    .encode(x=alt.X(\"Month:N\", sort=months), y=\"Cumulative Revenue:Q\")\n)\n\n# Compose and style — see prompts/library/altair.md \"Canvas\" for the inner-view sizing rationale\nchart = (\n    (line + highlight_line + points + peak_halo + peak_marker)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(\"step-basic · python · altair · anyplot.ai\", fontSize=16, color=INK),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.10,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=10,\n        titleFontSize=12,\n    )\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\n# Pad-to-target — vl-convert pads the inner view with title/axis/legend extents,\n# so the saved PNG rarely lands exactly on the canonical size (see altair.md \"Canvas\")\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}