{"spec_id":"gain-curve","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ngain-curve: Cumulative Gains Chart\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-11\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\nfrom sklearn.datasets import make_classification\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito palette: first series is brand green\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# Generate sample data for classification model evaluation\nnp.random.seed(42)\nX, y_true = make_classification(\n    n_samples=1000, n_features=10, n_informative=5, n_redundant=2, n_classes=2, weights=[0.7, 0.3], random_state=42\n)\n\n# Simulate model predictions (logistic-like scores)\nnp.random.seed(42)\ny_score = 1 / (1 + np.exp(-(X[:, 0] * 0.8 + X[:, 1] * 0.5 + np.random.randn(1000) * 0.3)))\n\n# Calculate cumulative gains curve\nsorted_indices = np.argsort(y_score)[::-1]\ny_true_sorted = y_true[sorted_indices]\n\n# Calculate cumulative gains\ntotal_positives = y_true.sum()\ncumulative_positives = np.cumsum(y_true_sorted)\ngains = cumulative_positives / total_positives * 100\n\n# Calculate percentage of population\npopulation_pct = np.arange(1, len(y_true) + 1) / len(y_true) * 100\n\n# Sample points for smoother pygal rendering (every 2%)\nsample_indices = [0] + list(range(19, len(population_pct), 20)) + [len(population_pct) - 1]\npop_sampled = [population_pct[i] for i in sample_indices]\ngains_sampled = [gains[i] for i in sample_indices]\n\n# Perfect model line: vertical at positive rate, then horizontal at 100%\npositive_rate = (y_true.sum() / len(y_true)) * 100\nperfect_model = [(0, 0), (positive_rate, 100), (100, 100)]\n\n# Create custom style for theme-adaptive rendering\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n)\n\n# Create XY chart\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    title=\"gain-curve · pygal · anyplot.ai\",\n    x_title=\"Population Targeted (%)\",\n    y_title=\"Cumulative Gains (%)\",\n    style=custom_style,\n    show_dots=False,\n    stroke_style={\"width\": 5},\n    range=(0, 100),\n    xrange=(0, 100),\n    show_x_guides=True,\n    show_y_guides=True,\n    legend_at_bottom=True,\n    truncate_legend=-1,\n)\n\n# Add perfect model reference line\nchart.add(\"Perfect Model\", perfect_model)\n\n# Add model gain curve\nmodel_data = [(pop_sampled[i], gains_sampled[i]) for i in range(len(pop_sampled))]\nchart.add(\"Model Gains\", model_data)\n\n# Add random baseline (diagonal line)\nbaseline_data = [(0, 0), (100, 100)]\nchart.add(\"Random Baseline\", baseline_data)\n\n# Save as PNG and HTML with theme suffix\nchart.render_to_png(f\"plot-{THEME}.png\")\nchart.render_to_file(f\"plot-{THEME}.html\")\n"}