{"spec_id":"histogram-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nhistogram-basic: Basic Histogram\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 94/100 | Updated: 2026-05-28\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\n\n\n# Theme\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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]  # #009E73 — first series (bars)\nBLUE = IMPRINT_PALETTE[2]  # #4467A3 — mean reference line\nOCHRE = IMPRINT_PALETTE[3]  # #BD8233 — median reference line (colorblind-safe pair)\n\n# Data — exam score distribution with three realistic student groups\nnp.random.seed(42)\nscores_main = np.random.normal(loc=72, scale=10, size=180)\nscores_high = np.random.normal(loc=88, scale=5, size=40)\nscores_low = np.random.normal(loc=45, scale=4, size=15)\nvalues = np.concatenate([scores_main, scores_high, scores_low])\nvalues = np.clip(values, 0, 100)\n\nmean_val = np.mean(values)\nmedian_val = np.median(values)\n\n# Plot\nfig = go.Figure()\nfig.add_trace(\n    go.Histogram(\n        x=values,\n        nbinsx=20,\n        marker={\"color\": BRAND, \"opacity\": 0.85, \"line\": {\"color\": PAGE_BG, \"width\": 1}},\n        hovertemplate=\"Score: %{x:.0f}<br>Count: %{y}<extra></extra>\",\n    )\n)\n\n# Median vertical line (ochre, dashed)\nfig.add_shape(\n    type=\"line\",\n    x0=median_val,\n    x1=median_val,\n    y0=0,\n    y1=0.88,\n    yref=\"paper\",\n    line={\"color\": OCHRE, \"width\": 2.5, \"dash\": \"dash\"},\n)\n\n# Mean vertical line (blue, dotted)\nfig.add_shape(\n    type=\"line\", x0=mean_val, x1=mean_val, y0=0, y1=0.88, yref=\"paper\", line={\"color\": BLUE, \"width\": 2.5, \"dash\": \"dot\"}\n)\n\n# Legend for mean/median reference lines\nfig.add_annotation(\n    x=0.02,\n    y=0.97,\n    xref=\"paper\",\n    yref=\"paper\",\n    text=(\n        f'<span style=\"color:{OCHRE}\">▬ ▬</span> Median: {median_val:.1f}'\n        f'<br><span style=\"color:{BLUE}\">·····</span> Mean: {mean_val:.1f}'\n    ),\n    showarrow=False,\n    font={\"size\": 15, \"color\": INK},\n    align=\"left\",\n    bgcolor=ELEVATED_BG,\n    bordercolor=INK_SOFT,\n    borderwidth=1,\n    borderpad=6,\n)\n\n# Cluster annotations\nfig.add_annotation(\n    x=72,\n    y=44,\n    text=\"Main cluster<br><i>77% of students</i>\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1.2,\n    arrowcolor=INK_SOFT,\n    ax=-90,\n    ay=-55,\n    font={\"size\": 15, \"color\": INK},\n    bgcolor=ELEVATED_BG,\n    bordercolor=INK_SOFT,\n    borderwidth=1,\n    borderpad=5,\n)\n\nfig.add_annotation(\n    x=88,\n    y=26,\n    text=\"High achievers<br><i>17% of students</i>\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1.2,\n    arrowcolor=INK_SOFT,\n    ax=60,\n    ay=-50,\n    font={\"size\": 15, \"color\": INK},\n    bgcolor=ELEVATED_BG,\n    bordercolor=INK_SOFT,\n    borderwidth=1,\n    borderpad=5,\n)\n\nfig.add_annotation(\n    x=45,\n    y=6,\n    text=\"Struggling<br><i>6% of students</i>\",\n    showarrow=True,\n    arrowhead=2,\n    arrowsize=1.2,\n    arrowcolor=INK_SOFT,\n    ax=-60,\n    ay=-55,\n    font={\"size\": 15, \"color\": INK},\n    bgcolor=ELEVATED_BG,\n    bordercolor=INK_SOFT,\n    borderwidth=1,\n    borderpad=5,\n)\n\n# Layout\ntitle = \"histogram-basic · python · plotly · anyplot.ai\"\ntitle_fontsize = 16  # len=46, under 67-char baseline — no scaling needed\n\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n    title={\"text\": title, \"font\": {\"size\": title_fontsize, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\", \"y\": 0.96},\n    xaxis={\n        \"title\": {\"text\": \"Score (points)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"showline\": True,\n        \"linecolor\": INK_SOFT,\n        \"linewidth\": 1,\n        \"range\": [30, 105],\n    },\n    yaxis={\n        \"title\": {\"text\": \"Frequency (count)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": True,\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"griddash\": \"dot\",\n        \"zeroline\": False,\n        \"showline\": False,\n        \"rangemode\": \"tozero\",\n    },\n    template=\"plotly_white\",\n    bargap=0,\n    margin={\"l\": 80, \"r\": 40, \"t\": 80, \"b\": 60},\n)\n\n# Save — 3200×1800 px 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"}