{"spec_id":"stem-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nstem-basic: Basic Stem Plot\nLibrary: bokeh 3.9.2 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-07-25\n\"\"\"\n\nimport os\nimport sys\nimport time\nfrom pathlib import Path\n\n\nsys.path = [p for p in sys.path if not p.endswith(\"/python\")]\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Label, Range1d\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\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\"\nAMBER = \"#DDCC77\"\n\n# Data - Discrete signal samples (damped oscillation)\nnp.random.seed(42)\nn_points = 30\nx = np.arange(n_points)\ny = np.exp(-0.1 * x) * np.sin(0.5 * x) * 2 + np.random.randn(n_points) * 0.1\n\nbaseline = 0\npeak_idx = int(np.argmax(np.abs(y)))\n\n# Create data sources\nsource = ColumnDataSource(data={\"x\": x, \"y\": y})\nstem_source = ColumnDataSource(data={\"x0\": x, \"y0\": np.full_like(x, baseline, dtype=float), \"x1\": x, \"y1\": y})\n\n# Create figure\nW, H = 3200, 1800\np = figure(\n    width=W,\n    height=H,\n    title=\"stem-basic · python · bokeh · anyplot.ai\",\n    x_axis_label=\"Sample Index\",\n    y_axis_label=\"Amplitude\",\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\n# Tighten the y-range to the data extent (avoid wasted canvas space)\ny_pad = (y.max() - y.min()) * 0.15\np.y_range = Range1d(start=y.min() - y_pad, end=y.max() + y_pad)\n\n# Draw stems (vertical lines from baseline to data points)\np.segment(x0=\"x0\", y0=\"y0\", x1=\"x1\", y1=\"y1\", source=stem_source, line_width=4, color=BRAND, alpha=0.85)\n\n# Draw markers at data points\nmarker_renderer = p.scatter(x=\"x\", y=\"y\", source=source, size=25, color=BRAND, alpha=1.0)\n\n# Hover tooltip on markers, showcasing bokeh's interactivity in the HTML output\np.add_tools(HoverTool(renderers=[marker_renderer], tooltips=[(\"Sample\", \"@x\"), (\"Amplitude\", \"@y{0.00}\")]))\n\n# Highlight the peak-amplitude sample as a focal point\np.scatter(x=[x[peak_idx]], y=[y[peak_idx]], size=34, color=AMBER, line_color=INK, line_width=2, level=\"overlay\")\npeak_label = Label(\n    x=x[peak_idx],\n    y=y[peak_idx],\n    x_offset=18,\n    y_offset=14,\n    text=\"Peak amplitude\",\n    text_font_size=\"24pt\",\n    text_color=INK_SOFT,\n)\np.add_layout(peak_label)\n\n# Draw baseline\np.line(x=[x.min() - 0.5, x.max() + 0.5], y=[baseline, baseline], line_width=3, color=INK_SOFT, alpha=0.6)\n\n# Sizing\np.title.text_font_size = \"50pt\"\np.xaxis.axis_label_text_font_size = \"42pt\"\np.yaxis.axis_label_text_font_size = \"42pt\"\np.xaxis.major_label_text_font_size = \"34pt\"\np.yaxis.major_label_text_font_size = \"34pt\"\n\n# Theme-adaptive chrome\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = None\n\np.title.text_color = INK\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_color = INK\np.xgrid.grid_line_alpha = 0.10\np.ygrid.grid_line_alpha = 0.10\n\n# Save outputs: HTML via bokeh, PNG via headless Chrome screenshot.\n# bokeh.io.export_png probes /usr/bin/chromedriver first, which can be an\n# unusable snap shim in CI — screenshotting the saved HTML with Selenium\n# sidesteps that entirely (see prompts/library/bokeh.md).\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.set_window_size(W, H)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}