{"spec_id":"line-impurity-comparison","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nline-impurity-comparison: Gini Impurity vs Entropy Comparison\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-05-29\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\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\n# Imprint palette — first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Data\np = np.linspace(0, 1, 500)\n\ngini = 2 * p * (1 - p)\ngini_normalized = gini / gini.max()\n\n# Entropy with edge-case handling (0 * log2(0) → 0 by convention)\nentropy = np.zeros_like(p)\nmask = (p > 0) & (p < 1)\nentropy[mask] = -p[mask] * np.log2(p[mask]) - (1 - p[mask]) * np.log2(1 - p[mask])\nentropy_normalized = entropy / entropy.max()\n\n# Long-format DataFrame — enables seaborn's idiomatic hue+style palette mapping\nGINI_LABEL = \"Gini: 2p(1−p)\"\nENTROPY_LABEL = \"Entropy: −p log₂p − (1−p) log₂(1−p)\"\n\ndf = pd.DataFrame(\n    {\n        \"p\": np.concatenate([p, p]),\n        \"Impurity\": np.concatenate([gini_normalized, entropy_normalized]),\n        \"Criterion\": [GINI_LABEL] * len(p) + [ENTROPY_LABEL] * len(p),\n    }\n)\n\n# Canvas: landscape 3200×1800 px (8 × 400 = 3200, 4.5 × 400 = 1800)\n# Do NOT pass bbox_inches='tight' to savefig — see prompts/library/seaborn.md \"Canvas\"\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK_MUTED,\n        \"grid.alpha\": 0.12,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400)\n\ncolor_gini = IMPRINT_PALETTE[0]  # #009E73 — first series (Gini)\ncolor_entropy = IMPRINT_PALETTE[1]  # #C475FD — second series (Entropy)\n\n# Shaded region between the two curves to highlight their divergence\nax.fill_between(p, gini_normalized, entropy_normalized, alpha=0.08, color=color_gini, zorder=1)\n\n# Seaborn hue+style mapping — idiomatic multi-series approach using seaborn's\n# palette dispatch and automatic dash-cycle differentiation\nsns.lineplot(\n    data=df,\n    x=\"p\",\n    y=\"Impurity\",\n    hue=\"Criterion\",\n    style=\"Criterion\",\n    palette={GINI_LABEL: color_gini, ENTROPY_LABEL: color_entropy},\n    linewidth=2.5,\n    ax=ax,\n)\n\n# Subtle y-axis-only grid for readability (spec recommends a light grid)\nax.yaxis.grid(True, alpha=0.12, linewidth=0.8)\n\n# Annotation: max divergence between the two curves\ndivergence = np.abs(entropy_normalized - gini_normalized)\nmax_div_idx = np.argmax(divergence)\nmid_y = (gini_normalized[max_div_idx] + entropy_normalized[max_div_idx]) / 2\n\nax.annotate(\n    \"Max divergence\",\n    xy=(p[max_div_idx], mid_y),\n    xytext=(p[max_div_idx] + 0.07, mid_y + 0.09),\n    fontsize=9,\n    fontstyle=\"italic\",\n    color=INK_SOFT,\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_SOFT, \"lw\": 1.2},\n)\n\n# Annotation: both curves peak at p = 0.5\nax.plot(0.5, 1.0, \"o\", color=INK, markersize=5, zorder=5)\nax.annotate(\n    \"Max impurity\\np = 0.5\",\n    xy=(0.5, 1.0),\n    xytext=(0.34, 0.77),\n    fontsize=9,\n    color=INK,\n    arrowprops={\"arrowstyle\": \"->\", \"color\": INK_MUTED, \"lw\": 1.2, \"connectionstyle\": \"arc3,rad=0.15\"},\n    bbox={\"boxstyle\": \"round,pad=0.4\", \"facecolor\": ELEVATED_BG, \"edgecolor\": INK_SOFT, \"alpha\": 0.92, \"lw\": 0.8},\n)\n\n# Labels and title\nax.set_xlabel(\"Probability (p)\", fontsize=10, labelpad=8)\nax.set_ylabel(\"Impurity (normalized)\", fontsize=10, labelpad=8)\nax.set_title(\"line-impurity-comparison · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", pad=14)\nax.tick_params(axis=\"both\", labelsize=8)\nax.set_xlim(0, 1)\nax.set_ylim(-0.02, 1.1)\n\nsns.despine(ax=ax, top=True, right=True)\n\n# Legend — seaborn auto-generates merged hue+style handles\nlegend = ax.legend(\n    title=\"Splitting Criterion\",\n    title_fontsize=8,\n    fontsize=8,\n    loc=\"upper right\",\n    framealpha=0.95,\n    fancybox=False,\n    borderpad=0.8,\n)\nlegend.get_frame().set_linewidth(0.8)\n\nfig.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}