{"spec_id":"box-horizontal","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nbox-horizontal: Horizontal Box Plot\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-12\n\"\"\"\n\nimport sys\nfrom pathlib import Path\n\n\n# Remove script directory from sys.path to avoid circular import of bokeh\n_script_dir = str(Path(__file__).parent)\nsys.path[:] = [p for p in sys.path if p != _script_dir and p != \"\"]\n\nimport os\nimport time\n\nimport bokeh.io\nimport bokeh.models\nimport bokeh.plotting\nimport numpy as np\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\noutput_file = bokeh.io.output_file\nsave = bokeh.io.save\nFactorRange = bokeh.models.FactorRange\nfigure = bokeh.plotting.figure\n\n# Change to script directory for output files\nos.chdir(Path(__file__).parent)\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\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\"\n\n# Okabe-Ito palette\nBRAND = \"#009E73\"\nALT_COLOR = \"#C475FD\"\n\n# Data - Response times (ms) by service type\nnp.random.seed(42)\n\ncategories = [\"Cache Layer\", \"API Gateway\", \"Authentication\", \"Database Query\", \"File Storage\"]\n\n# Generate different distributions for each service (sorted by median)\ndata = {\n    \"Cache Layer\": np.random.normal(15, 5, 80),\n    \"API Gateway\": np.random.normal(45, 12, 80),\n    \"Authentication\": np.concatenate([np.random.normal(65, 15, 75), [130, 145, 150]]),\n    \"Database Query\": np.concatenate([np.random.normal(120, 30, 80), [220, 240]]),\n    \"File Storage\": np.random.normal(200, 50, 80),\n}\n\n# Calculate box plot statistics for each category\nstats = {}\noutliers_x = []\noutliers_y = []\n\nfor cat in categories:\n    values = data[cat]\n    q1 = np.percentile(values, 25)\n    q2 = np.percentile(values, 50)\n    q3 = np.percentile(values, 75)\n    iqr = q3 - q1\n    upper_fence = q3 + 1.5 * iqr\n    lower_fence = q1 - 1.5 * iqr\n\n    # Whiskers extend to furthest data point within fence\n    mask = (values >= lower_fence) & (values <= upper_fence)\n    upper = values[mask].max() if mask.any() else q3\n    lower = values[mask].min() if mask.any() else q1\n\n    # Find outliers\n    outlier_mask = (values < lower_fence) | (values > upper_fence)\n    for o in values[outlier_mask]:\n        outliers_x.append(o)\n        outliers_y.append(cat)\n\n    stats[cat] = {\"q1\": q1, \"q2\": q2, \"q3\": q3, \"upper\": upper, \"lower\": lower}\n\n# Plot\np = figure(\n    width=4800,\n    height=2700,\n    y_range=FactorRange(*categories),\n    x_axis_label=\"Response Time (ms)\",\n    y_axis_label=\"Service Type\",\n    title=\"box-horizontal · bokeh · anyplot.ai\",\n)\n\n# Box and whisker dimensions\nbox_height = 0.6\ncap_height = 0.3\n\nfor cat in categories:\n    s = stats[cat]\n\n    # Draw box (IQR)\n    p.hbar(\n        y=[cat],\n        left=[s[\"q1\"]],\n        right=[s[\"q3\"]],\n        height=box_height,\n        fill_color=BRAND,\n        fill_alpha=0.7,\n        line_color=INK_SOFT,\n        line_width=2,\n    )\n\n    # Draw median line\n    p.hbar(\n        y=[cat],\n        left=[s[\"q2\"] - 1],\n        right=[s[\"q2\"] + 1],\n        height=box_height,\n        fill_color=ALT_COLOR,\n        line_color=ALT_COLOR,\n        line_width=0,\n    )\n\n    # Draw whiskers (horizontal lines from box to whisker ends)\n    p.hbar(y=[cat], left=[s[\"lower\"]], right=[s[\"q1\"]], height=0.02, fill_color=INK_SOFT, line_color=INK_SOFT)\n    p.hbar(y=[cat], left=[s[\"q3\"]], right=[s[\"upper\"]], height=0.02, fill_color=INK_SOFT, line_color=INK_SOFT)\n\n    # Draw whisker caps (vertical lines at whisker ends)\n    p.hbar(\n        y=[cat],\n        left=[s[\"lower\"] - 0.5],\n        right=[s[\"lower\"] + 0.5],\n        height=cap_height,\n        fill_color=INK_SOFT,\n        line_color=INK_SOFT,\n    )\n    p.hbar(\n        y=[cat],\n        left=[s[\"upper\"] - 0.5],\n        right=[s[\"upper\"] + 0.5],\n        height=cap_height,\n        fill_color=INK_SOFT,\n        line_color=INK_SOFT,\n    )\n\n# Draw outliers\nif outliers_x:\n    p.scatter(\n        x=outliers_x, y=outliers_y, size=15, fill_color=ALT_COLOR, line_color=INK_SOFT, line_width=2, marker=\"circle\"\n    )\n\n# Style\np.title.text_font_size = \"28pt\"\np.title.text_color = INK\n\np.xaxis.axis_label_text_font_size = \"22pt\"\np.yaxis.axis_label_text_font_size = \"22pt\"\np.xaxis.axis_label_text_color = INK\np.yaxis.axis_label_text_color = INK\n\np.xaxis.major_label_text_font_size = \"18pt\"\np.yaxis.major_label_text_font_size = \"18pt\"\np.xaxis.major_label_text_color = INK_SOFT\np.yaxis.major_label_text_color = INK_SOFT\n\n# Grid styling\np.xgrid.grid_line_alpha = 0.10\np.xgrid.grid_line_color = INK\np.ygrid.grid_line_alpha = 0.0\n\n# Background and borders\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\np.outline_line_color = INK_SOFT\n\n# Axis styling\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\n# Save HTML\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\n# Screenshot with headless Chrome\nW, H = 4800, 2700\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()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}