{"spec_id":"line-impurity-comparison","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nline-impurity-comparison: Gini Impurity vs Entropy Comparison\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-29\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nimport numpy as np\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Label, Legend, NumeralTickFormatter, Span\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens\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\n# Imprint categorical palette — first series always #009E73\nGINI_COLOR = \"#009E73\"  # Imprint position 1 — brand green\nENTROPY_COLOR = \"#C475FD\"  # Imprint position 2 — lavender\n\n# Data\np_vals = np.linspace(0, 1, 200)\n\n# Gini impurity: 2 * p * (1 - p), already in [0, 1]\ngini = 2 * p_vals * (1 - p_vals)\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_vals * np.log2(p_vals) - (1 - p_vals) * np.log2(1 - p_vals)\nentropy = np.nan_to_num(entropy, nan=0.0)\n\nsource_gini = ColumnDataSource(data={\"p\": p_vals, \"impurity\": gini})\nsource_entropy = ColumnDataSource(data={\"p\": p_vals, \"impurity\": entropy})\n\n# Area between curves to visually emphasize the difference\nsource_diff = ColumnDataSource(\n    data={\"p\": np.concatenate([p_vals, p_vals[::-1]]), \"fill\": np.concatenate([entropy, gini[::-1]])}\n)\n\ntitle = \"line-impurity-comparison · python · bokeh · anyplot.ai\"\n\n# Plot\nfig = figure(\n    width=3200,\n    height=1800,\n    title=title,\n    x_axis_label=\"Probability (p)\",\n    y_axis_label=\"Impurity Measure (normalized to [0, 1])\",\n    x_range=(-0.02, 1.02),\n    y_range=(-0.05, 1.12),\n    toolbar_location=None,\n    min_border_bottom=160,\n    min_border_left=180,\n    min_border_top=110,\n    min_border_right=50,\n)\n\n# Shaded area between curves — muted fill emphasises the gap without competing\nfig.patch(x=\"p\", y=\"fill\", source=source_diff, fill_color=INK_MUTED, fill_alpha=0.08, line_alpha=0)\n\n# Vertical reference line at p=0.5\nref_line = Span(location=0.5, dimension=\"height\", line_color=INK_SOFT, line_width=2, line_dash=\"dotted\", line_alpha=0.5)\nfig.add_layout(ref_line)\n\n# Lines\nline_gini = fig.line(x=\"p\", y=\"impurity\", source=source_gini, line_width=7, line_color=GINI_COLOR)\nline_entropy = fig.line(\n    x=\"p\", y=\"impurity\", source=source_entropy, line_width=7, line_color=ENTROPY_COLOR, line_dash=[14, 7]\n)\n\n# Markers at maxima (p=0.5): Gini peaks at 0.5, Entropy peaks at 1.0\nfig.scatter(\n    x=[0.5, 0.5], y=[0.5, 1.0], size=28, fill_color=[GINI_COLOR, ENTROPY_COLOR], line_color=PAGE_BG, line_width=4\n)\n\n# Connector segment between the two maxima dots\nfig.segment(x0=[0.5], y0=[0.5], x1=[0.5], y1=[1.0], line_color=INK_SOFT, line_width=3, line_dash=\"dotted\")\n\n# Annotation at p=0.5 maximum impurity\nmax_label = Label(\n    x=0.53,\n    y=1.00,\n    text=\"p = 0.5  (maximum impurity)\",\n    text_font_size=\"28pt\",\n    text_font_style=\"bold\",\n    text_color=INK,\n    background_fill_color=ELEVATED_BG,\n    background_fill_alpha=0.92,\n    border_line_color=INK_SOFT,\n    border_line_alpha=0.4,\n    border_line_width=2,\n)\nfig.add_layout(max_label)\n\n# Value labels at each maximum dot\ngini_val_label = Label(\n    x=0.18, y=0.44, text=\"Gini max = 0.5\", text_font_size=\"26pt\", text_color=GINI_COLOR, text_font_style=\"italic\"\n)\nfig.add_layout(gini_val_label)\n\nentropy_val_label = Label(\n    x=0.62, y=0.88, text=\"Entropy max = 1.0\", text_font_size=\"26pt\", text_color=ENTROPY_COLOR, text_font_style=\"italic\"\n)\nfig.add_layout(entropy_val_label)\n\n# HoverTool for interactive HTML export\nhover = HoverTool(\n    renderers=[line_gini, line_entropy], tooltips=[(\"p\", \"@p{0.00}\"), (\"Impurity\", \"@impurity{0.000}\")], mode=\"vline\"\n)\nfig.add_tools(hover)\n\n# Legend with formulas — click_policy lets viewers isolate each curve in HTML\nlegend = Legend(\n    items=[(\"Gini: 2p(1−p)\", [line_gini]), (\"Entropy: −p log₂p − (1−p) log₂(1−p)\", [line_entropy])],\n    location=\"top_left\",\n    label_text_font_size=\"30pt\",\n    label_text_color=INK_SOFT,\n    glyph_width=70,\n    glyph_height=35,\n    spacing=18,\n    padding=30,\n    border_line_alpha=0,\n    background_fill_color=ELEVATED_BG,\n    background_fill_alpha=0.9,\n    click_policy=\"hide\",\n)\nfig.add_layout(legend)\n\n# Text sizing for 3200×1800 canvas\nfig.title.text_font_size = \"50pt\"\nfig.title.text_color = INK\nfig.xaxis.axis_label_text_font_size = \"42pt\"\nfig.yaxis.axis_label_text_font_size = \"42pt\"\nfig.xaxis.major_label_text_font_size = \"34pt\"\nfig.yaxis.major_label_text_font_size = \"34pt\"\nfig.xaxis.axis_label_text_color = INK\nfig.yaxis.axis_label_text_color = INK\nfig.xaxis.major_label_text_color = INK_SOFT\nfig.yaxis.major_label_text_color = INK_SOFT\n\n# Tick formatters\nfig.xaxis.formatter = NumeralTickFormatter(format=\"0.0\")\nfig.yaxis.formatter = NumeralTickFormatter(format=\"0.0\")\n\n# Grid — y-axis only, subtle\nfig.xgrid.grid_line_alpha = 0\nfig.ygrid.grid_line_color = INK\nfig.ygrid.grid_line_alpha = 0.15\nfig.ygrid.grid_line_dash = [4, 4]\n\n# Theme-adaptive chrome\nfig.background_fill_color = PAGE_BG\nfig.border_fill_color = PAGE_BG\nfig.outline_line_color = INK_SOFT\n\nfig.xaxis.axis_line_color = INK_SOFT\nfig.yaxis.axis_line_color = INK_SOFT\nfig.xaxis.major_tick_line_color = INK_SOFT\nfig.yaxis.major_tick_line_color = INK_SOFT\nfig.axis.minor_tick_line_color = None\nfig.axis.major_tick_out = 8\n\n# Save HTML (interactive catalog artifact)\noutput_file(f\"plot-{THEME}.html\")\nsave(fig)\n\n# Screenshot with headless Chrome — Selenium 4 / Selenium Manager.\n# Use CDP Emulation.setDeviceMetricsOverride to pin the viewport to exactly\n# W×H before navigating; --window-size alone under-delivers by ~139 px.\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n"}