{"spec_id":"histogram-kde","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nhistogram-kde: Histogram with KDE Overlay\nLibrary: letsplot 4.11.0 | Python 3.13.14\nQuality: 92/100 | Updated: 2026-08-05\n\"\"\"\n\nimport os\nfrom pathlib import Path\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Theme tokens (Imprint palette — see prompts/default-style-guide.md)\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\"\nRULE = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\nBRAND = \"#009E73\"  # Imprint palette position 1\nAMBER = \"#DDCC77\"  # semantic warning anchor — tail-risk callout\n\n# Data - simulated stock daily returns: a calm regime plus a stress regime\n# (large drawdowns) and a short rally, producing visible left-skew and a\n# heavy left tail that the KDE overlay reveals more clearly than raw bins.\nnp.random.seed(42)\nreturns = np.concatenate(\n    [np.random.normal(0.001, 0.015, 400), np.random.normal(-0.02, 0.03, 50), np.random.normal(0.02, 0.025, 50)]\n)\nreturns = returns * 100\nmean_return = float(np.mean(returns))\n\ndf = pd.DataFrame({\"Daily Return (%)\": returns})\n\n# Illustrative \"stress day\" cutoff: returns beyond this are the fat left tail\ntail_cutoff = -5.0\ntail_band = pd.DataFrame({\"xmin\": [returns.min() - 1], \"xmax\": [tail_cutoff]})\n\nanyplot_theme = (\n    theme_minimal()\n    # custom overrides must be added after theme_minimal() — lets-plot resolves\n    # theme layers in order, and an earlier fill gets clobbered by a later\n    # base theme's own default otherwise\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_line(color=RULE, size=0.5),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=12, color=INK),\n        axis_text=element_text(size=10, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        plot_title=element_text(size=16, color=INK),\n        legend_position=\"none\",  # single series — legend would be redundant\n    )\n)\n\nplot = (\n    ggplot(df, aes(x=\"Daily Return (%)\"))\n    # Tail-risk callout band drawn first so bars/KDE sit on top of it\n    + geom_rect(\n        aes(xmin=\"xmin\", xmax=\"xmax\"),\n        data=tail_band,\n        ymin=0,\n        ymax=0.28,\n        fill=AMBER,\n        color=\"rgba(221,204,119,0)\",\n        alpha=0.12,\n        inherit_aes=False,\n    )\n    + geom_histogram(\n        aes(y=\"..density..\"),\n        bins=35,\n        fill=BRAND,\n        alpha=0.55,\n        color=BRAND,\n        size=0.5,\n        tooltips=layer_tooltips()\n        .format(\"^x\", \".1f\")\n        .format(\"..density..\", \".3f\")\n        .line(\"Return|^x%\")\n        .line(\"Density|@..density..\")\n        .line(\"Count|@..count..\"),\n    )\n    + geom_area(\n        stat=\"density\", color=INK_SOFT, fill=INK_SOFT, alpha=0.12, size=1.5\n    )\n    + geom_vline(\n        xintercept=mean_return, color=INK, linetype=\"dashed\", size=0.8\n    )\n    + geom_text(\n        x=mean_return + 0.4, y=0.27, label=f\"mean {mean_return:+.1f}%\", color=INK, size=3.2, hjust=0\n    )\n    + geom_text(\n        x=tail_cutoff - 0.3, y=0.27, label=\"stress tail\", color=INK_SOFT, size=3.2, hjust=1\n    )\n    + labs(\n        x=\"Daily Return (%)\", y=\"Density\", title=\"histogram-kde · letsplot · anyplot.ai\"\n    )\n    + ggsize(800, 450)\n    + anyplot_theme\n)\n\n# Save\noutput_dir = Path(__file__).parent\nggsave(plot, str(output_dir / f\"plot-{THEME}.png\"), scale=4)\nggsave(plot, str(output_dir / f\"plot-{THEME}.html\"))\n"}