{"spec_id":"histogram-stacked","library":"makie","language":"julia","code":"# anyplot.ai\n# histogram-stacked: Stacked Histogram\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 85/100 | Created: 2026-09-05\n\nusing CairoMakie\nusing Colors\nusing Random\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\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# --- Data -----------------------------------------------------------------------\nlocations = [\"Downtown\", \"Uptown\", \"Airport\"]\nwait_downtown = max.(randn(600) .* 2.2 .+ 6.5, 0.2)\nwait_uptown   = max.(randn(450) .* 2.6 .+ 8.5, 0.2)\nwait_airport  = max.(randn(350) .* 3.4 .+ 11.0, 0.2)\nwait_times = vcat(wait_downtown, wait_uptown, wait_airport)\ngroup_labels = vcat(\n    fill(locations[1], length(wait_downtown)),\n    fill(locations[2], length(wait_uptown)),\n    fill(locations[3], length(wait_airport)),\n)\n\nn_bins = 22\nbin_edges = range(minimum(wait_times), maximum(wait_times); length = n_bins + 1)\nbin_width = step(bin_edges)\nbin_centers = (bin_edges[1:end-1] .+ bin_edges[2:end]) ./ 2\n\ncounts = zeros(Int, n_bins, length(locations))\nfor (val, grp) in zip(wait_times, group_labels)\n    bin_idx = clamp(searchsortedlast(bin_edges, val), 1, n_bins)\n    grp_idx = findfirst(==(grp), locations)\n    counts[bin_idx, grp_idx] += 1\nend\n\nx_flat = repeat(bin_centers; outer = length(locations))\nheight_flat = vec(counts)\nstack_flat = repeat(1:length(locations); inner = n_bins)\ncolor_flat = IMPRINT_PALETTE[stack_flat]\n\n# Focal-point insight: which bin sees the most total traffic, and which\n# location dominates it — turns the raw stack into a story instead of\n# just displaying the composition.\ntotals = vec(sum(counts, dims = 2))\npeak_idx = argmax(totals)\npeak_x = bin_centers[peak_idx]\npeak_total = totals[peak_idx]\npeak_group = locations[argmax(counts[peak_idx, :])]\n\n# --- Plot -----------------------------------------------------------------------\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = \"histogram-stacked · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    subtitle          = \"Peak volume at $(round(peak_x, digits = 1)) min ($(peak_total) customers) — $(peak_group) leads this bin\",\n    subtitlesize      = 14,\n    subtitlecolor     = INK_SOFT,\n    xlabel            = \"Wait Time (minutes)\",\n    ylabel            = \"Number of Customers\",\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    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    xgridvisible      = false,\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.15),\n)\n\nbarplot!(ax, x_flat, height_flat;\n         stack = stack_flat, color = color_flat,\n         width = bin_width * 0.95, strokewidth = 1.5, strokecolor = PAGE_BG)\n\n# Callout on the busiest bin — a layered `text!` annotation gives the eye a\n# focal point instead of leaving the composition shift to be inferred.\ntext!(ax, peak_x, peak_total;\n      text = \"▲ $(peak_group) leads\",\n      color = INK, fontsize = 13, font = :bold,\n      align = (:center, :bottom), offset = (0, 6))\nylims!(ax, 0, maximum(totals) * 1.18)\n\nlegend_elements = [PolyElement(color = c) for c in IMPRINT_PALETTE[1:length(locations)]]\nLegend(fig[1, 2], legend_elements, locations, \"Location\";\n       labelcolor = INK_SOFT, titlecolor = INK,\n       backgroundcolor = PAGE_BG, framevisible = false,\n       patchsize = (18, 18), rowgap = 6)\n\n# --- Save -----------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}