{"spec_id":"stem-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nstem-basic: Basic Stem Plot\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\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\"  # Imprint palette position 1\n\n# Data — acoustic impulse response samples\nnp.random.seed(42)\nn_samples = 30\nsample_index = np.arange(n_samples)\namplitude = np.exp(-sample_index / 8) * np.cos(sample_index * 0.9) + np.random.randn(n_samples) * 0.03\n\ndf = pd.DataFrame({\"n\": sample_index, \"amplitude\": amplitude, \"baseline\": 0.0})\n\n# Decay envelope — highlights the exponential decay story\nn_env = np.linspace(0, n_samples - 1, 200)\nenv_df = pd.DataFrame({\"n\": n_env, \"upper\": np.exp(-n_env / 8), \"lower\": -np.exp(-n_env / 8)})\n\n# Shaded decay region (subtle background emphasis)\nenvelope_area = (\n    alt.Chart(env_df)\n    .mark_area(color=BRAND, opacity=0.07)\n    .encode(x=alt.X(\"n:Q\"), y=alt.Y(\"upper:Q\"), y2=alt.Y2(\"lower:Q\"))\n)\n\n# Dashed bounds of the decay envelope\nenvelope_upper = (\n    alt.Chart(env_df)\n    .mark_line(color=INK_SOFT, strokeWidth=1.2, strokeDash=[4, 3], opacity=0.45)\n    .encode(x=alt.X(\"n:Q\"), y=alt.Y(\"upper:Q\"))\n)\n\nenvelope_lower = (\n    alt.Chart(env_df)\n    .mark_line(color=INK_SOFT, strokeWidth=1.2, strokeDash=[4, 3], opacity=0.45)\n    .encode(x=alt.X(\"n:Q\"), y=alt.Y(\"lower:Q\"))\n)\n\n# Baseline rule at y=0\nbaseline_rule = alt.Chart(pd.DataFrame({\"y\": [0]})).mark_rule(color=INK_SOFT, strokeWidth=1.2).encode(y=alt.Y(\"y:Q\"))\n\n# Stems: vertical rules from baseline to each data point\nstems = (\n    alt.Chart(df)\n    .mark_rule(color=BRAND, strokeWidth=1.6, opacity=0.85)\n    .encode(\n        x=alt.X(\"n:Q\", title=\"Sample Index (n)\", axis=alt.Axis(labelFontSize=11, titleFontSize=14)),\n        y=alt.Y(\"baseline:Q\"),\n        y2=alt.Y2(\"amplitude:Q\"),\n    )\n)\n\n# Hover selection — nearest-point highlight, an Altair-distinctive interactive param\n# that has no static-PNG equivalent (default state == static render, unaffected).\nhover = alt.selection_point(fields=[\"n\"], on=\"pointerover\", nearest=True, empty=False)\n\n# Markers at the tip of each stem\nmarkers = (\n    alt.Chart(df)\n    .mark_circle(color=BRAND, stroke=PAGE_BG, strokeWidth=1.5)\n    .encode(\n        x=alt.X(\"n:Q\"),\n        y=alt.Y(\"amplitude:Q\", title=\"Amplitude (a.u.)\", axis=alt.Axis(labelFontSize=11, titleFontSize=14)),\n        size=alt.condition(hover, alt.value(160), alt.value(70)),\n        tooltip=[\n            alt.Tooltip(\"n:Q\", title=\"Sample (n)\"),\n            alt.Tooltip(\"amplitude:Q\", title=\"Amplitude (a.u.)\", format=\".3f\"),\n        ],\n    )\n    .add_params(hover)\n)\n\n# Compose and apply theme-adaptive chrome\nchart = (\n    (envelope_area + envelope_upper + envelope_lower + baseline_rule + stems + markers)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(\n            \"stem-basic · python · altair · anyplot.ai\", fontSize=18, fontWeight=\"bold\", anchor=\"middle\", color=INK\n        ),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        grid=False,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=11,\n        titleFontSize=14,\n    )\n    .configure_title(color=INK, fontSize=18, fontWeight=\"bold\")\n)\n\n# Save — hard target: 3200 x 1800 (landscape). See prompts/library/altair.md \"Canvas\".\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\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}x{_h}, exceeds target {TW}x{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"}