{"spec_id":"gain-curve","library":"makie","language":"julia","code":"# anyplot.ai\n# gain-curve: Cumulative Gains Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 93/100 | Created: 2026-09-05\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# --- Theme tokens -------------------------------------------------------\nTHEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nPAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nINK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nINK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nINK_MUTED = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\nELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nBRAND    = colorant\"#009E73\"  # Imprint palette position 1 — model curve\n\n# --- Data: credit scoring model for loan default detection ---------------\nn_applicants = 5000\ndefault_rate = 0.12\ny_true = rand(n_applicants) .< default_rate\n\n# Risk score correlates with the true default label but is imperfect,\n# mirroring a realistic model (AUC well above random, short of perfect).\nlatent = ifelse.(y_true, randn(n_applicants) .+ 1.8, randn(n_applicants))\ny_score = 1 ./ (1 .+ exp.(-latent))\n\norder = sortperm(y_score; rev = true)\ny_true_sorted = y_true[order]\n\ntotal_positives = sum(y_true_sorted)\npct_population = vcat(0.0, (1:n_applicants) ./ n_applicants .* 100)\npct_captured = vcat(0.0, cumsum(y_true_sorted) ./ total_positives .* 100)\n\npositive_rate_pct = total_positives / n_applicants * 100\nperfect_x = [0.0, positive_rate_pct, 100.0]\nperfect_y = [0.0, 100.0, 100.0]\n\n# --- Plot -----------------------------------------------------------------\nfig = Figure(\n    resolution = (1600, 900),\n    fontsize = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title = \"gain-curve · julia · makie · anyplot.ai\",\n    titlesize = 20,\n    titlecolor = INK,\n    xlabel = \"Population Targeted (%)\",\n    ylabel = \"Positives Captured (%)\",\n    xlabelsize = 14,\n    ylabelsize = 14,\n    xlabelcolor = INK,\n    ylabelcolor = INK,\n    xticklabelsize = 12,\n    yticklabelsize = 12,\n    xticklabelcolor = INK_SOFT,\n    yticklabelcolor = INK_SOFT,\n    xtickcolor = INK_SOFT,\n    ytickcolor = INK_SOFT,\n    backgroundcolor = PAGE_BG,\n    topspinevisible = false,\n    rightspinevisible = false,\n    leftspinecolor = INK_SOFT,\n    bottomspinecolor = INK_SOFT,\n    xgridvisible = false,\n    ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    yminorgridvisible = false,\n    limits = (0, 100, 0, 100),\n)\n\nlines!(ax, perfect_x, perfect_y;\n    color = INK_MUTED, linewidth = 2, linestyle = :dot, label = \"Perfect model\")\nlines!(ax, [0, 100], [0, 100];\n    color = INK, linewidth = 2, linestyle = :dash, label = \"Random targeting\")\nlines!(ax, pct_population, pct_captured;\n    color = BRAND, linewidth = 3.5, label = \"Model\")\n\naxislegend(ax;\n    position = :rb,\n    labelcolor = INK_SOFT,\n    backgroundcolor = ELEVATED_BG,\n    framecolor = INK_SOFT,\n    framevisible = true,\n)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}