{"spec_id":"calibration-curve","library":"makie","language":"julia","code":"# anyplot.ai\n# calibration-curve: Calibration Curve\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 94/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\n\nRandom.seed!(42)\n\n# --- Theme tokens -------------------------------------------------------\nconst THEME      = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG    = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK        = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT   = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED  = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\nconst BRAND = IMPRINT_PALETTE[1]\n\n# --- Data -----------------------------------------------------------------\n# Diagnostic screening classifier: a hidden risk score drives the true outcome,\n# but the reported probabilities are overconfident (pushed toward 0 and 1).\nn = 4000\nrisk_score = randn(n)\ntrue_prob = 1.0 ./ (1.0 .+ exp.(-1.1 .* risk_score))\ny_true = Float64.(rand(n) .< true_prob)\n\nlogit_true = log.(true_prob ./ (1.0 .- true_prob))\ny_prob = clamp.(1.0 ./ (1.0 .+ exp.(-1.9 .* logit_true .+ 0.15 .* randn(n))), 0.001, 0.999)\n\nn_bins = 10\nedges = range(0.0, 1.0; length = n_bins + 1)\nmean_pred = fill(NaN, n_bins)\nfrac_pos = fill(NaN, n_bins)\nbin_count = zeros(Int, n_bins)\n\nfor i in 1:n_bins\n    lo, hi = edges[i], edges[i + 1]\n    mask = i < n_bins ? (y_prob .>= lo) .& (y_prob .< hi) : (y_prob .>= lo) .& (y_prob .<= hi)\n    bin_count[i] = count(mask)\n    if bin_count[i] > 0\n        mean_pred[i] = mean(y_prob[mask])\n        frac_pos[i] = mean(y_true[mask])\n    end\nend\n\nvalid = bin_count .> 0\nmp = mean_pred[valid]\nfp = frac_pos[valid]\nbrier_score = mean((y_prob .- y_true) .^ 2)\nece = sum(bin_count[valid] ./ n .* abs.(fp .- mp))\n\n# --- Plot -------------------------------------------------------------------\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 16,\n    backgroundcolor = PAGE_BG,\n)\n\nax_cal = Axis(\n    fig[1, 1];\n    title             = \"calibration-curve · julia · makie · anyplot.ai\",\n    titlesize         = 22,\n    titlecolor        = INK,\n    ylabel            = \"Fraction of positives\",\n    ylabelcolor       = INK,\n    ylabelsize        = 16,\n    xticklabelsize    = 13,\n    yticklabelsize    = 13,\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    xminorgridvisible = false,\n    yminorgridvisible = false,\n    limits            = (0, 1, 0, 1),\n)\n\nband!(ax_cal, mp, min.(fp, mp), max.(fp, mp); color = (BRAND, 0.12))\nlines!(ax_cal, [0.0, 1.0], [0.0, 1.0];\n    color = INK_SOFT, linestyle = :dash, linewidth = 2.5, label = \"Perfect calibration\")\nlines!(ax_cal, mp, fp; color = BRAND, linewidth = 3)\nscatter!(ax_cal, mp, fp; color = BRAND, markersize = 18, strokewidth = 1.5, strokecolor = PAGE_BG, label = \"Diagnostic model\")\n\naxislegend(ax_cal, position = :rb, framevisible = true, backgroundcolor = ELEVATED_BG, labelcolor = INK)\n\ntext!(ax_cal, 0.03, 0.94;\n    text = \"Brier score: $(round(brier_score, digits = 3))\\nECE: $(round(ece, digits = 3))\",\n    color = INK_SOFT, fontsize = 15, align = (:left, :top))\n\nhidexdecorations!(ax_cal; label = true, ticklabels = true, ticks = false, grid = false)\n\nax_hist = Axis(\n    fig[2, 1];\n    xlabel            = \"Predicted probability\",\n    ylabel            = \"Count\",\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xlabelsize        = 16,\n    ylabelsize        = 16,\n    xticklabelsize    = 13,\n    yticklabelsize    = 13,\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    limits            = (0, 1, nothing, nothing),\n)\n\nhist!(ax_hist, y_prob; bins = edges, color = (INK_MUTED, 0.55), strokewidth = 1, strokecolor = PAGE_BG)\n\nlinkxaxes!(ax_cal, ax_hist)\nrowsize!(fig.layout, 1, Relative(0.68))\nrowsize!(fig.layout, 2, Relative(0.32))\nrowgap!(fig.layout, 8)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}