{"spec_id":"density-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\ndensity-basic: Basic Density Plot\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-30\n\"\"\"\n\nimport os\nimport sys\n\n\n# This file is named bokeh.py — remove its directory from sys.path so imports\n# resolve to the installed bokeh package rather than this script itself.\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _script_dir]\n\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import BoxAnnotation, ColumnDataSource, HoverTool, Label, NumeralTickFormatter, Span\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Imprint palette — theme-adaptive chrome\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — first categorical series is always #009E73\nBRAND = \"#009E73\"\n\n# Data — response times (ms) for a web service showing bimodal distribution\nnp.random.seed(42)\nresponse_times = np.concatenate(\n    [\n        np.random.normal(150, 30, 300),  # Fast responses\n        np.random.normal(280, 40, 100),  # Slower responses (bimodal tail)\n    ]\n)\n\n# Kernel density estimation — Silverman's rule for bandwidth\nn = len(response_times)\nstd = np.std(response_times)\niqr = np.percentile(response_times, 75) - np.percentile(response_times, 25)\nbandwidth = 0.9 * min(std, iqr / 1.34) * n ** (-0.2)\n\n# Evaluate KDE on a fine grid\nx_grid = np.linspace(response_times.min() - 40, response_times.max() + 40, 500)\ndensity = np.zeros_like(x_grid)\nfor xi in response_times:\n    density += np.exp(-0.5 * ((x_grid - xi) / bandwidth) ** 2)\ndensity /= n * bandwidth * np.sqrt(2 * np.pi)\n\n# Locate the two mode peaks for data storytelling\npeak1_idx = np.argmax(density[:250])\npeak2_idx = 250 + np.argmax(density[250:])\npeak1_x, peak1_y = x_grid[peak1_idx], density[peak1_idx]\npeak2_x, peak2_y = x_grid[peak2_idx], density[peak2_idx]\n\n# ColumnDataSource for density curve (enables HoverTool)\nsource = ColumnDataSource(data={\"x\": x_grid, \"density\": density})\n\n# Rug plot — individual observations as vertical segments at y=0\nrug_y0 = -0.00055\nrug_y1 = rug_y0 + 0.00075\nrug_source = ColumnDataSource(\n    data={\"x\": response_times, \"y0\": np.full_like(response_times, rug_y0), \"y1\": np.full_like(response_times, rug_y1)}\n)\n\n# Figure — canonical 3200×1800 landscape canvas\np = figure(\n    width=3200,\n    height=1800,\n    title=\"density-basic · python · bokeh · anyplot.ai\",\n    x_axis_label=\"Response Time (ms)\",\n    y_axis_label=\"Density\",\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# BoxAnnotation highlights each modal region — idiomatic Bokeh way to shade bands\nbox1 = BoxAnnotation(left=peak1_x - 70, right=peak1_x + 70, fill_color=BRAND, fill_alpha=0.07, line_color=None)\nbox2 = BoxAnnotation(left=peak2_x - 65, right=peak2_x + 65, fill_color=BRAND, fill_alpha=0.07, line_color=None)\np.add_layout(box1)\np.add_layout(box2)\n\n# Span — vertical dashed reference lines at each peak (Bokeh-idiomatic)\nfor px in (peak1_x, peak2_x):\n    p.add_layout(\n        Span(location=px, dimension=\"height\", line_color=BRAND, line_width=2, line_dash=\"dashed\", line_alpha=0.45)\n    )\n\n# Fill under the density curve\np.varea(x=\"x\", y1=0, y2=\"density\", source=source, fill_color=BRAND, fill_alpha=0.18)\n\n# Density curve (primary glyph; also the HoverTool target)\ndensity_line = p.line(x=\"x\", y=\"density\", source=source, line_color=BRAND, line_width=5, line_alpha=0.9)\n\n# Peak annotations\nfor px, py, label in ((peak1_x, peak1_y, \"Fast Responses\"), (peak2_x, peak2_y, \"Slower Responses\")):\n    p.add_layout(\n        Label(\n            x=px,\n            y=py,\n            text=label,\n            text_font_size=\"34pt\",\n            text_color=INK,\n            text_font_style=\"bold\",\n            text_align=\"center\",\n            y_offset=20,\n        )\n    )\n\n# HoverTool — vline mode follows cursor along the curve\nhover = HoverTool(\n    renderers=[density_line],\n    tooltips=[(\"Response Time\", \"@x{0.0} ms\"), (\"Density\", \"@density{0.00000}\")],\n    mode=\"vline\",\n    line_policy=\"nearest\",\n)\np.add_tools(hover)\n\n# Rug plot — reduced alpha to ease visual crowding in the primary cluster\np.segment(x0=\"x\", y0=\"y0\", x1=\"x\", y1=\"y1\", source=rug_source, line_color=BRAND, line_width=2, line_alpha=0.38)\n\n# Text sizing — canonical native-pixel values for 3200×1800\np.title.text_font_size = \"50pt\"\np.title.text_color = INK\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\"\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\n\n# Y-axis numeric format\np.yaxis.formatter = NumeralTickFormatter(format=\"0.0000\")\n\n# Axis chrome — theme-adaptive\np.xaxis.axis_line_color = INK_SOFT\np.yaxis.axis_line_color = INK_SOFT\np.xaxis.axis_line_width = 1\np.yaxis.axis_line_width = 1\np.xaxis.minor_tick_line_color = None\np.yaxis.minor_tick_line_color = None\np.xaxis.major_tick_line_color = INK_SOFT\np.yaxis.major_tick_line_color = INK_SOFT\n\n# Grid — y-axis only, subtle\np.xgrid.grid_line_color = None\np.ygrid.grid_line_color = INK\np.ygrid.grid_line_alpha = 0.15\np.ygrid.grid_line_width = 1\n\n# Background and border\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = None\n\n# Save interactive HTML (required catalog artifact)\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome — Selenium 4 / Selenium Manager\nW, H = 3200, 1800\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)\n# CDP override is authoritative — --window-size alone loses ~139 px to Chrome chrome\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n\n# Normalize to exact 3200×1800 — guards against ±1–2 px rounding in headless Chrome\nfrom PIL import Image as _Image\n\n\n_img = _Image.open(f\"plot-{THEME}.png\")\nif _img.size != (W, H):\n    _norm = _Image.new(\"RGB\", (W, H), PAGE_BG)\n    _norm.paste(_img, ((W - _img.size[0]) // 2, (H - _img.size[1]) // 2))\n    _norm.save(f\"plot-{THEME}.png\")\n"}