{"spec_id":"calibration-curve","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\ncalibration-curve: Calibration Curve\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-10\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    coord_fixed,\n    element_line,\n    element_rect,\n    element_text,\n    geom_abline,\n    geom_line,\n    geom_point,\n    ggplot,\n    labs,\n    scale_size_identity,\n    theme,\n    theme_minimal,\n)\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\"\nBRAND = \"#009E73\"\n\n# Data - Simulate a classifier with realistic calibration characteristics\nnp.random.seed(42)\nn_samples = 2000\n\n# Generate predicted probabilities from a slightly overconfident model\ny_prob = np.random.beta(2, 3, n_samples)\n\n# Generate true labels - model is slightly overconfident\ncalibration_shift = 0.08\ntrue_prob = np.clip(y_prob - calibration_shift + np.random.normal(0, 0.05, n_samples), 0, 1)\ny_true = (np.random.random(n_samples) < true_prob).astype(int)\n\n# Calculate calibration curve using 10 bins\nn_bins = 10\nbin_edges = np.linspace(0, 1, n_bins + 1)\nbin_indices = np.digitize(y_prob, bin_edges) - 1\nbin_indices = np.clip(bin_indices, 0, n_bins - 1)\n\n# Calculate mean predicted probability and fraction of positives per bin\nmean_predicted = []\nfraction_positives = []\nbin_counts = []\n\nfor i in range(n_bins):\n    mask = bin_indices == i\n    if mask.sum() > 0:\n        mean_predicted.append(y_prob[mask].mean())\n        fraction_positives.append(y_true[mask].mean())\n        bin_counts.append(mask.sum())\n    else:\n        mean_predicted.append(np.nan)\n        fraction_positives.append(np.nan)\n        bin_counts.append(0)\n\n# Calculate Expected Calibration Error (ECE)\nece = 0\nfor i in range(n_bins):\n    if bin_counts[i] > 0:\n        ece += bin_counts[i] * abs(fraction_positives[i] - mean_predicted[i])\nece /= n_samples\n\n# Create DataFrame for calibration curve\ndf_calibration = pd.DataFrame(\n    {\"mean_predicted\": mean_predicted, \"fraction_positives\": fraction_positives, \"bin_counts\": bin_counts}\n).dropna()\n\n# Add size column for point sizing based on bin counts\nmax_count = df_calibration[\"bin_counts\"].max()\ndf_calibration[\"point_size\"] = 3 + 5 * (df_calibration[\"bin_counts\"] / max_count)\n\n# Create calibration curve plot\nanyplot_theme = 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=INK, size=0.3, alpha=0.10),\n    panel_grid_minor=element_line(color=INK, size=0.2, alpha=0.05),\n    panel_border=element_rect(color=INK_SOFT, fill=None, size=0.8),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT, size=0.8),\n    plot_title=element_text(size=24, color=INK),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT),\n    legend_title=element_text(color=INK),\n)\n\nplot = (\n    ggplot(df_calibration, aes(x=\"mean_predicted\", y=\"fraction_positives\"))\n    + geom_abline(intercept=0, slope=1, linetype=\"dashed\", color=INK_SOFT, size=1.2)\n    + geom_line(color=BRAND, size=2)\n    + geom_point(aes(size=\"point_size\"), color=BRAND, fill=BRAND, stroke=0, alpha=0.8)\n    + scale_size_identity()\n    + labs(\n        x=\"Mean Predicted Probability\",\n        y=\"Fraction of Positives (Observed)\",\n        title=f\"calibration-curve · plotnine · anyplot.ai (ECE = {ece:.3f})\",\n    )\n    + coord_fixed(ratio=1, xlim=(0, 1), ylim=(0, 1))\n    + theme_minimal()\n    + anyplot_theme\n    + theme(figure_size=(12, 12))\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}