{"spec_id":"coefficient-confidence","library":"makie","language":"julia","code":"# anyplot.ai\n# coefficient-confidence: Coefficient Plot with Confidence Intervals\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 90/100 | Created: 2026-09-01\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Printf\n\nRandom.seed!(42)\n\n# Theme tokens (see prompts/default-style-guide.md \"Background\" + \"Theme-adaptive Chrome\")\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\"\nBRAND     = colorant\"#009E73\"  # Imprint palette position 1 — significant coefficients\n\n# Data — coefficients from a multiple linear regression predicting housing sale\n# price (thousands of USD), controlling for all other predictors.\nvariables = [\n    \"Square Footage (per 100 sq ft)\", \"Distance to Downtown (mi)\", \"Bathrooms\", \"School Rating\",\n    \"Garage Spaces\", \"Renovated (Y/N)\", \"Lot Size (acres)\", \"Bedrooms\", \"Age (years)\",\n]\ncoefficients = [45.2, -12.6, 18.7, 15.3, 9.8, 8.1, 6.4, -3.2, -2.1]\nstandard_errors = 0.28 .* abs.(coefficients) .+ 1.2 .+ 2.0 .* rand(length(coefficients))\nci_lower = coefficients .- 1.96 .* standard_errors\nci_upper = coefficients .+ 1.96 .* standard_errors\nsignificant = .!((ci_lower .<= 0) .& (0 .<= ci_upper))\n\n# Order by coefficient magnitude, largest effect first (drawn at the top)\norder = sortperm(abs.(coefficients), rev = true)\nvariables = variables[order]\ncoefficients = coefficients[order]\nci_lower = ci_lower[order]\nci_upper = ci_upper[order]\nsignificant = significant[order]\n\nn = length(variables)\ny_positions = collect(n:-1:1)\npoint_colors = [sig ? BRAND : INK_MUTED for sig in significant]\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             = \"coefficient-confidence · julia · makie · anyplot.ai\",\n    titlesize         = 24,\n    titlecolor        = INK,\n    xlabel            = \"Coefficient Estimate (Δ Sale Price, \\$ thousands)\",\n    xlabelsize        = 16,\n    xlabelcolor       = INK,\n    xticklabelsize    = 14,\n    xticklabelcolor   = INK_SOFT,\n    yticks            = (1:n, reverse(variables)),\n    yticklabelsize    = 14,\n    yticklabelcolor   = INK_SOFT,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinevisible   = false,\n    bottomspinecolor   = INK_SOFT,\n    xgridvisible       = true,\n    ygridvisible       = false,\n    xgridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n)\n\nvlines!(ax, [0.0]; color = INK_SOFT, linestyle = :dash, linewidth = 2)\n\n# rangebars! draws directly from ci_lower to ci_upper (no manual delta math\n# needed, unlike errorbars!'s relative-offset API) — the more idiomatic Makie\n# primitive for plotting precomputed interval bounds.\nrangebars!(\n    ax, y_positions, ci_lower, ci_upper;\n    direction = :x, color = point_colors, whiskerwidth = 14, linewidth = 3,\n)\nscatter!(\n    ax, coefficients, y_positions;\n    color = point_colors, markersize = 22, strokewidth = 1.5, strokecolor = PAGE_BG,\n)\n\n# Compact value labels next to each point estimate, colored to match\n# significance, so exact magnitudes don't require reading off the axis.\n# Anchored on the outward side of the CI (away from the zero line) so labels\n# for negative coefficients never collide with the dashed reference line.\npos_mask = coefficients .>= 0\nneg_mask = .!pos_mask\ntext!(\n    ax, ci_upper[pos_mask] .+ 1.5, y_positions[pos_mask];\n    text = [@sprintf(\"%+.1f\", c) for c in coefficients[pos_mask]],\n    color = point_colors[pos_mask], fontsize = 13, align = (:left, :center),\n)\ntext!(\n    ax, ci_lower[neg_mask] .- 1.5, y_positions[neg_mask];\n    text = [@sprintf(\"%+.1f\", c) for c in coefficients[neg_mask]],\n    color = point_colors[neg_mask], fontsize = 13, align = (:right, :center),\n)\n\n# Legend — semantic mapping must be explicit per style guide\nelem_sig = MarkerElement(color = BRAND, marker = :circle, markersize = 22, strokewidth = 1.5, strokecolor = PAGE_BG)\nelem_ns  = MarkerElement(color = INK_MUTED, marker = :circle, markersize = 22, strokewidth = 1.5, strokecolor = PAGE_BG)\naxislegend(\n    ax, [elem_sig, elem_ns], [\"Significant (95% CI excludes zero)\", \"Not significant\"];\n    position = :rb, labelcolor = INK_SOFT, labelsize = 13,\n    backgroundcolor = (THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"),\n    framevisible = false,\n)\n\nxlims!(ax, minimum(ci_lower) - 8, maximum(ci_upper) + 9)\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}