{"spec_id":"line-impurity-comparison","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nline-impurity-comparison: Gini Impurity vs Entropy Comparison\nLibrary: letsplot 4.10.1 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-29\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot.export import ggsave\n\n\nLetsPlot.setup_html()\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Data\np = np.linspace(0, 1, 200)\n\n# Gini impurity: 2p(1 - p)\ngini = 2 * p * (1 - p)\n\n# Entropy: -p log2(p) - (1-p) log2(1-p), normalized to [0, 1]\nwith np.errstate(divide=\"ignore\", invalid=\"ignore\"):\n    entropy = -p * np.log2(p) - (1 - p) * np.log2(1 - p)\nentropy = np.nan_to_num(entropy, nan=0.0)\n\n# Main curves data — Gini first so it gets Imprint position 1 (#009E73)\ndf = pd.DataFrame(\n    {\n        \"p\": np.tile(p, 2),\n        \"impurity\": np.concatenate([gini, entropy]),\n        \"metric\": [\"Gini: 2p(1−p)\"] * len(p) + [\"Entropy (normalized)\"] * len(p),\n    }\n)\n\n# Shaded region between curves\nribbon_df = pd.DataFrame({\"p\": p, \"gini\": gini, \"entropy\": entropy})\n\n# Open circle markers at maxima (unified, no unused columns)\nmarkers_df = pd.DataFrame(\n    {\"p\": [0.5, 0.5], \"impurity\": [1.0, 0.5], \"metric\": [\"Entropy (normalized)\", \"Gini: 2p(1−p)\"]}\n)\n\n# Arrow pointing to peak area\narrow_df = pd.DataFrame({\"x\": [0.5], \"y\": [0.93], \"xend\": [0.5], \"yend\": [0.86]})\n\n# Annotations\nmain_label_df = pd.DataFrame({\"p\": [0.5], \"impurity\": [0.97], \"label\": [\"Maximum uncertainty at p = 0.5\"]})\nregion_label_df = pd.DataFrame(\n    {\"p\": [0.72], \"impurity\": [0.6], \"label\": [\"Shaded region:\\ndifference between metrics\"]}\n)\n# Shifted inward (0.07 / 0.93) to avoid left/right edge clipping\nboundary_df = pd.DataFrame(\n    {\"p\": [0.07, 0.93], \"impurity\": [0.06, 0.06], \"label\": [\"p → 0: both → 0\", \"p → 1: both → 0\"], \"hjust\": [0.0, 1.0]}\n)\n\n# Plot\nplot = (\n    ggplot()\n    # Shaded ribbon between curves\n    + geom_ribbon(\n        data=ribbon_df,\n        mapping=aes(x=\"p\", ymin=\"gini\", ymax=\"entropy\"),\n        fill=IMPRINT_PALETTE[2],\n        alpha=0.12,\n        tooltips=\"none\",\n    )\n    # Main curves with interactive tooltips\n    + geom_line(\n        data=df,\n        mapping=aes(x=\"p\", y=\"impurity\", color=\"metric\"),\n        size=1.5,\n        tooltips=layer_tooltips()\n        .format(\"@p\", \".2f\")\n        .format(\"@impurity\", \".3f\")\n        .line(\"@metric\")\n        .line(\"p = @p\")\n        .line(\"impurity = @impurity\"),\n    )\n    # Vertical guide at p=0.5\n    + geom_vline(xintercept=0.5, color=INK_SOFT, size=0.5, linetype=\"dashed\")\n    # Arrow from annotation to entropy maximum\n    + geom_segment(\n        aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\"),\n        data=arrow_df,\n        color=INK_SOFT,\n        size=0.6,\n        arrow=arrow(angle=25, length=8, type=\"closed\"),\n    )\n    # Open circle markers at maxima — unified, border color from metric scale\n    + geom_point(\n        aes(x=\"p\", y=\"impurity\", color=\"metric\"),\n        data=markers_df,\n        size=6,\n        shape=21,\n        fill=PAGE_BG,\n        stroke=2,\n        tooltips=\"none\",\n    )\n    # Annotation: maximum uncertainty label\n    + geom_text(\n        data=main_label_df,\n        mapping=aes(x=\"p\", y=\"impurity\", label=\"label\"),\n        size=4,\n        color=INK,\n        fontface=\"bold italic\",\n    )\n    # Annotation: shaded region explanation\n    + geom_text(\n        data=region_label_df,\n        mapping=aes(x=\"p\", y=\"impurity\", label=\"label\"),\n        size=3.5,\n        color=INK_SOFT,\n        fontface=\"italic\",\n    )\n    # Annotations: boundary behavior at p→0 and p→1\n    + geom_text(\n        data=boundary_df,\n        mapping=aes(x=\"p\", y=\"impurity\", label=\"label\", hjust=\"hjust\"),\n        size=3,\n        color=INK_MUTED,\n        fontface=\"italic\",\n    )\n    # Scales — Imprint palette canonical order\n    + scale_color_manual(values=[IMPRINT_PALETTE[0], IMPRINT_PALETTE[1]])\n    + scale_x_continuous(breaks=list(np.arange(0, 1.1, 0.1)), limits=[0, 1])\n    + scale_y_continuous(breaks=list(np.arange(0, 1.2, 0.2)), limits=[-0.06, 1.05])\n    # Labels\n    + labs(\n        x=\"Probability of class 1 (p)\",\n        y=\"Impurity measure (normalized)\",\n        title=\"line-impurity-comparison · python · letsplot · anyplot.ai\",\n        subtitle=\"Both criteria peak at maximum uncertainty (p = 0.5), explaining why Gini and entropy yield similar tree structures\",\n        color=\"\",\n    )\n    # Canvas: 800×450 × scale=4 → 3200×1800 px\n    + ggsize(800, 450)\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        axis_text=element_text(size=10, color=INK_SOFT),\n        axis_title=element_text(size=12, color=INK),\n        plot_title=element_text(size=16, face=\"bold\", color=INK),\n        plot_subtitle=element_text(size=10, color=INK_SOFT, face=\"italic\"),\n        legend_text=element_text(size=10, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"bottom\",\n        panel_grid_major=element_line(color=INK_SOFT, size=0.2),\n        panel_grid_minor=element_blank(),\n        axis_line=element_blank(),\n        axis_ticks=element_blank(),\n        plot_margin=[20, 20, 10, 10],\n    )\n)\n\n# Save — PNG + HTML (interactive)\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}