{"spec_id":"box-notched","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nbox-notched: Notched Box Plot\nLibrary: plotly 6.9.0 | Python 3.13.15\nQuality: 95/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme tokens (Imprint style guide — theme-adaptive chrome)\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.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint categorical palette (first series always #009E73)\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data - clinical trial pain-reduction scores (VAS points) across a\n# placebo-controlled dose-escalation study with a standard-care comparator arm.\nrng = np.random.default_rng(42)\n\narms = [\"Placebo\", \"Low Dose\", \"Medium Dose\", \"High Dose\", \"Standard Care\"]\narm_params = {\n    \"Placebo\": (72, 8, 9),\n    \"Low Dose\": (78, 22, 11),\n    \"Medium Dose\": (85, 35, 13),\n    \"High Dose\": (64, 48, 14),\n    \"Standard Care\": (70, 30, 12),\n}\noutcome_data = {arm: rng.normal(mean, sd, n) for arm, (n, mean, sd) in arm_params.items()}\n\n# Add outliers - placebo non-responders (pain worsened) and dose super-responders\noutcome_data[\"Placebo\"] = np.append(outcome_data[\"Placebo\"], [-25, -18])\noutcome_data[\"High Dose\"] = np.append(outcome_data[\"High Dose\"], [92, 88])\noutcome_data[\"Standard Care\"] = np.append(outcome_data[\"Standard Care\"], [-10])\n\n\n# Plot\nfig = go.Figure()\n\nfor i, arm in enumerate(arms):\n    values = outcome_data[arm]\n    fig.add_trace(\n        go.Box(\n            y=values,\n            x=[arm] * len(values),\n            name=arm,\n            boxpoints=\"outliers\",\n            notched=True,\n            notchwidth=0.4,\n            whiskerwidth=0.6,\n            quartilemethod=\"linear\",\n            marker=dict(color=IMPRINT_PALETTE[i], size=10, opacity=0.85, line=dict(color=INK, width=1)),\n            line=dict(width=2),\n            fillcolor=IMPRINT_PALETTE[i],\n            opacity=0.82,\n            hovertemplate=f\"<b>{arm}</b><br>Pain reduction: %{{y:.1f}} pts<extra></extra>\",\n        )\n    )\n\n# Visual hypothesis test: annotate the Placebo vs. High Dose comparison with a\n# significance bracket whenever the notches (95% CI around the median) don't\n# overlap - the core reason a notched box plot exists.\nplacebo_q1, placebo_median, placebo_q3 = np.percentile(outcome_data[\"Placebo\"], [25, 50, 75])\nplacebo_half = 1.57 * (placebo_q3 - placebo_q1) / np.sqrt(len(outcome_data[\"Placebo\"]))\nhigh_dose_q1, high_dose_median, high_dose_q3 = np.percentile(outcome_data[\"High Dose\"], [25, 50, 75])\nhigh_dose_half = 1.57 * (high_dose_q3 - high_dose_q1) / np.sqrt(len(outcome_data[\"High Dose\"]))\nnotches_overlap = (placebo_median + placebo_half) >= (high_dose_median - high_dose_half)\n\nif not notches_overlap:\n    data_span = max(v.max() for v in outcome_data.values()) - min(v.min() for v in outcome_data.values())\n    bracket_y = max(outcome_data[\"Placebo\"].max(), outcome_data[\"High Dose\"].max()) + 0.06 * data_span\n    tick = 0.02 * data_span\n    placebo_x, high_dose_x = arms.index(\"Placebo\"), arms.index(\"High Dose\")\n    for x0, x1 in [(placebo_x, placebo_x), (high_dose_x, high_dose_x), (placebo_x, high_dose_x)]:\n        fig.add_shape(\n            type=\"line\",\n            xref=\"x\",\n            yref=\"y\",\n            x0=x0,\n            x1=x1,\n            y0=bracket_y - tick if x0 == x1 else bracket_y,\n            y1=bracket_y,\n            line=dict(color=INK_SOFT, width=1.5),\n        )\n    fig.add_annotation(\n        x=(placebo_x + high_dose_x) / 2,\n        y=bracket_y,\n        xref=\"x\",\n        yref=\"y\",\n        yshift=14,\n        text=\"Notches don't overlap → medians differ (p < 0.05)\",\n        showarrow=False,\n        font=dict(size=11, color=INK_SOFT),\n    )\n\n# Two-line tick labels surface sample size per arm, tying the chart back to the\n# spec's \"notch reliability improves with n > 20\" note.\ntick_text = [f\"{arm}<br>n={len(outcome_data[arm])}\" for arm in arms]\n\n# Style\nfig.update_layout(\n    autosize=False,\n    width=800,\n    height=450,\n    title=dict(\n        text=\"box-notched · python · plotly · anyplot.ai\", font=dict(size=16, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    xaxis=dict(\n        title=dict(text=\"Treatment Arm\", font=dict(size=12, color=INK)),\n        tickmode=\"array\",\n        tickvals=list(range(len(arms))),\n        ticktext=tick_text,\n        tickfont=dict(size=10, color=INK_SOFT),\n        showgrid=False,\n        linecolor=INK_SOFT,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Pain Reduction (VAS points)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        gridcolor=GRID,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    showlegend=False,\n    margin=dict(l=80, r=40, t=80, b=60),\n)\n\n# Save - hard target 3200x1800 (landscape)\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}