{"spec_id":"pdp-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# pdp-basic: Partial Dependence Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 92/100 | Created: 2026-09-05\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 INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\n\n# Imprint categorical palette — 8 hues, theme-independent, hybrid-v3 sort\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",  # 1 — brand green, ALWAYS first series\n    colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\", colorant\"#AE3030\",\n    colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# --- Data ---------------------------------------------------------------\n# Partial dependence of a gradient-boosting home-price model on square\n# footage: predictions rise steeply for small homes, then saturate past\n# ~2,500 sqft — a nonlinear pattern a linear model could never surface.\n# Centered at zero, per PDP convention, so the curve reads as a relative\n# effect rather than an absolute price.\nn_grid = 80\nsqft = collect(range(500.0, 4000.0, length = n_grid))\n\nraw_effect = 95.0 .* log.(sqft ./ 500.0)\npartial_dependence = raw_effect .- mean(raw_effect)\n\n# Prediction variability (from the underlying trees' bootstrap spread) is\n# tightest where training homes cluster around 1,800 sqft and widens toward\n# the sparsely sampled extremes.\nband_width = 8.0 .+ 42.0 .* exp.(-((sqft .- 1800.0) .^ 2) ./ (2 * 900.0^2))\nci_lower = partial_dependence .- band_width\nci_upper = partial_dependence .+ band_width\n\n# Rug: the training homes' actual square footage, showing where evidence for\n# the curve is dense vs. sparse.\nn_train = 160\ntrain_sqft = clamp.(1800.0 .+ 520.0 .* randn(n_train), 500.0, 4000.0)\n\n# --- Plot -----------------------------------------------------------------\ntitle_str = \"pdp-basic · julia · makie · anyplot.ai\"\n\nfig = Figure(\n    resolution      = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\ny_lo   = minimum(ci_lower)\ny_hi   = maximum(ci_upper)\ny_span = y_hi - y_lo\n\nrug_bottom = y_lo - 0.16 * y_span\nrug_top    = y_lo - 0.05 * y_span\n\nax = Axis(\n    fig[1, 1];\n    title              = title_str,\n    titlesize          = 23,\n    titlecolor         = INK,\n    xlabel             = \"Square Footage\",\n    ylabel             = \"Partial Dependence on Price (\\$K)\",\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    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridvisible       = false,\n    ygridvisible       = true,\n    ygridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    limits             = (nothing, nothing, rug_bottom - 0.02 * y_span, y_hi + 0.08 * y_span),\n)\n\nhlines!(ax, [0.0]; color = INK_SOFT, linewidth = 1.5, linestyle = :dash)\n\nband!(ax, sqft, ci_lower, ci_upper; color = (IMPRINT_PALETTE[1], 0.18))\nlines!(ax, sqft, partial_dependence; color = IMPRINT_PALETTE[1], linewidth = 3.5)\n\nrug_segments = Vector{Point2f}(undef, 2 * length(train_sqft))\nfor (i, v) in enumerate(train_sqft)\n    rug_segments[2i - 1] = Point2f(v, rug_bottom)\n    rug_segments[2i]     = Point2f(v, rug_top)\nend\nlinesegments!(ax, rug_segments; color = (INK_SOFT, 0.45), linewidth = 1.2)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}