{"spec_id":"stem-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nstem-basic: Basic Stem Plot\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 92/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens (Imprint palette)\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\"\nGRID = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\nBRAND = \"#009E73\"  # Imprint palette position 1\nBRAND_RGB = tuple(int(BRAND[i : i + 2], 16) for i in (1, 3, 5))\n\n# Data - Discrete signal samples (damped oscillation)\nnp.random.seed(42)\nx = np.arange(0, 30)\ny = np.exp(-x / 10) * np.cos(x * 0.8) + np.random.randn(30) * 0.05\n\n# Alpha gradient - fades each stem/marker by its own amplitude magnitude, so the\n# decaying tail recedes visually without drawing separate envelope curves.\nmagnitude = np.abs(y)\nalpha = 0.35 + 0.65 * (magnitude / magnitude.max())\npeak_idx = int(np.argmax(magnitude))\n\n# Decay envelope - the exp(-x/10) bound that shapes the oscillation, drawn as a\n# faint dotted band to reinforce the damped-signal story (Plotly shape overlay).\nenvelope = np.exp(-x / 10)\n\n# Plot\nfig = go.Figure()\n\n# Baseline at y=0\nfig.add_trace(\n    go.Scatter(\n        x=[x.min() - 0.5, x.max() + 0.5],\n        y=[0, 0],\n        mode=\"lines\",\n        line={\"color\": INK_SOFT, \"width\": 2},\n        showlegend=False,\n        hoverinfo=\"skip\",\n    )\n)\n\n# Envelope band tracing the damped-oscillation bound\nfor sign in (1, -1):\n    fig.add_trace(\n        go.Scatter(\n            x=x,\n            y=sign * envelope,\n            mode=\"lines\",\n            line={\"color\": INK_SOFT, \"width\": 1.5, \"dash\": \"dot\"},\n            opacity=0.4,\n            showlegend=False,\n            hoverinfo=\"skip\",\n        )\n    )\n\n# Stems (vertical lines from baseline to data points), alpha-faded by magnitude\nfor xi, yi, ai in zip(x, y, alpha, strict=True):\n    fig.add_trace(\n        go.Scatter(\n            x=[xi, xi],\n            y=[0, yi],\n            mode=\"lines\",\n            line={\"color\": f\"rgba({BRAND_RGB[0]},{BRAND_RGB[1]},{BRAND_RGB[2]},{ai:.3f})\", \"width\": 3},\n            showlegend=False,\n            hoverinfo=\"skip\",\n        )\n    )\n\n# Markers at the top of each stem, same alpha gradient\nmarker_colors = [f\"rgba({BRAND_RGB[0]},{BRAND_RGB[1]},{BRAND_RGB[2]},{ai:.3f})\" for ai in alpha]\nfig.add_trace(\n    go.Scatter(\n        x=x,\n        y=y,\n        mode=\"markers\",\n        marker={\"color\": marker_colors, \"size\": 16, \"line\": {\"color\": PAGE_BG, \"width\": 2}},\n        showlegend=False,\n        hovertemplate=\"Sample: %{x}<br>Amplitude: %{y:.3f}<extra></extra>\",\n    )\n)\n\n# Style\nfig.update_layout(\n    autosize=False,\n    width=800,\n    height=450,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\n        \"text\": \"stem-basic · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 20, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Sample Index (n)\", \"font\": {\"size\": 17, \"color\": INK}},\n        \"tickfont\": {\"size\": 13, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"zeroline\": False,\n        \"linecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Amplitude (a.u.)\", \"font\": {\"size\": 17, \"color\": INK}},\n        \"tickfont\": {\"size\": 13, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"zeroline\": False,\n        \"linecolor\": INK_SOFT,\n    },\n    margin={\"l\": 70, \"r\": 35, \"t\": 75, \"b\": 60},\n    showlegend=False,\n)\n\n# Annotation calling out the dominant peak sample\nfig.add_annotation(\n    x=x[peak_idx],\n    y=y[peak_idx],\n    text=\"Peak amplitude\",\n    showarrow=True,\n    arrowhead=2,\n    arrowcolor=INK_SOFT,\n    ax=40,\n    ay=-35,\n    font={\"size\": 13, \"color\": INK},\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}