{"spec_id":"histogram-kde","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nhistogram-kde: Histogram with KDE Overlay\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 87/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom scipy import stats\n\n\n# Theme tokens (see prompts/default-style-guide.md)\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.12)\" if THEME == \"light\" else \"rgba(240,239,232,0.12)\"\n\nBRAND = \"#009E73\"  # Imprint palette position 1 — first series\nKDE_COLOR = \"#C475FD\"  # Imprint palette position 2\n# Fill is a touch stronger on dark so it doesn't read as pure background there\nKDE_FILL = \"rgba(196,117,253,0.12)\" if THEME == \"light\" else \"rgba(196,117,253,0.22)\"\n\n# Data - Stock returns simulation with realistic distribution\nnp.random.seed(42)\n# Mix of normal returns with slight negative skew (typical for stock returns)\nreturns = np.concatenate(\n    [\n        np.random.normal(0.05, 0.8, 400),  # Main distribution\n        np.random.normal(-1.5, 0.5, 80),  # Left tail (market drops)\n        np.random.normal(1.2, 0.4, 70),  # Right tail (gains)\n    ]\n)\n# Shuffle to mix\nnp.random.shuffle(returns)\n\n# Calculate KDE (plotly has no built-in KDE, so scipy provides the smooth curve)\nkde = stats.gaussian_kde(returns)\nx_kde = np.linspace(returns.min() - 0.5, returns.max() + 0.5, 300)\ny_kde = kde(x_kde)\n\n# Create figure\nfig = go.Figure()\n\n# Native histogram trace (density-normalized) instead of manual np.histogram\n# binning, edge matches page background for subtle bar separation\nfig.add_trace(\n    go.Histogram(\n        x=returns,\n        histnorm=\"probability density\",\n        nbinsx=35,\n        marker={\"color\": BRAND, \"opacity\": 0.5, \"line\": {\"color\": PAGE_BG, \"width\": 1}},\n        name=\"Histogram\",\n        hovertemplate=\"Return: %{x:.2f}%<br>Density: %{y:.3f}<extra></extra>\",\n    )\n)\n\n# Add KDE curve with a soft fill to reveal the density shape at a glance\nfig.add_trace(\n    go.Scatter(\n        x=x_kde,\n        y=y_kde,\n        mode=\"lines\",\n        line={\"color\": KDE_COLOR, \"width\": 3.5},\n        fill=\"tozeroy\",\n        fillcolor=KDE_FILL,\n        name=\"KDE\",\n        hovertemplate=\"Return: %{x:.2f}%<br>Density: %{y:.3f}<extra></extra>\",\n    )\n)\n\n# Update layout for 3200x1800 px canvas\nfig.update_layout(\n    autosize=False,\n    title={\n        \"text\": \"histogram-kde · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Daily Return (%)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"showgrid\": False,\n        \"zeroline\": True,\n        \"zerolinecolor\": GRID,\n        \"zerolinewidth\": 1,\n        \"linecolor\": INK_SOFT,\n        \"showspikes\": True,\n        \"spikecolor\": INK_SOFT,\n        \"spikethickness\": 1,\n        \"spikemode\": \"across\",\n        \"spikesnap\": \"cursor\",\n    },\n    yaxis={\n        \"title\": {\"text\": \"Density\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"gridcolor\": GRID,\n        \"gridwidth\": 1,\n        \"linecolor\": INK_SOFT,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    legend={\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"x\": 0.98,\n        \"y\": 0.98,\n        \"xanchor\": \"right\",\n        \"yanchor\": \"top\",\n        \"bgcolor\": ELEVATED_BG,\n        \"borderwidth\": 0,\n    },\n    margin={\"l\": 90, \"r\": 50, \"t\": 90, \"b\": 70},\n    bargap=0.05,\n    hovermode=\"x unified\",\n)\n\n# Callouts naming the two tail components so the skew/tail story the\n# spec calls out is explicit, not just visible in the shape\nfig.add_annotation(\n    x=-1.5,\n    y=float(kde(-1.5)[0]),\n    text=\"Left tail: market drops\",\n    showarrow=True,\n    arrowhead=2,\n    arrowcolor=INK_SOFT,\n    ax=-10,\n    ay=-45,\n    font={\"size\": 10, \"color\": INK_SOFT},\n)\nfig.add_annotation(\n    x=1.2,\n    y=float(kde(1.2)[0]),\n    text=\"Right tail: gains\",\n    showarrow=True,\n    arrowhead=2,\n    arrowcolor=INK_SOFT,\n    ax=25,\n    ay=-45,\n    font={\"size\": 10, \"color\": INK_SOFT},\n)\n\n# Save as PNG and HTML\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}