{"spec_id":"box-horizontal","library":"makie","language":"julia","code":"# anyplot.ai\n# box-horizontal: Horizontal Box Plot\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 91/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 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]\nconst BRAND = IMPRINT_PALETTE[1]\n\n# --- Data -----------------------------------------------------------------\njob_titles = [\n    \"Senior Data Platform Engineer\",\n    \"Machine Learning Researcher\",\n    \"Product Marketing Manager\",\n    \"Backend Software Engineer\",\n    \"Customer Success Associate\",\n    \"UX Research Coordinator\",\n]\nn_per_group = 15\nmeans = [128.0, 142.0, 96.0, 118.0, 68.0, 82.0]  # thousand USD\nstds  = [14.0, 18.0, 11.0, 13.0, 9.0, 10.0]\n\nsalaries = Float64[]\ngroup_idx = Int[]\nfor (i, (m, s)) in enumerate(zip(means, stds))\n    append!(salaries, m .+ s .* randn(n_per_group))\n    append!(group_idx, fill(i, n_per_group))\nend\n\n# Sort categories by median salary for easier comparison\nmedians = [median(salaries[group_idx .== i]) for i in eachindex(job_titles)]\nsort_order = sortperm(medians)\nsorted_titles = job_titles[sort_order]\nrank_of = Dict(sort_order[rank] => rank for rank in eachindex(sort_order))\nplot_positions = [rank_of[g] for g in group_idx]\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              = \"box-horizontal · julia · makie · anyplot.ai\",\n    titlesize          = 20,\n    titlecolor         = INK,\n    xlabel             = \"Annual Salary (thousand USD)\",\n    ylabel             = \"Job Title\",\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    xgridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.15),\n    ygridvisible       = false,\n    yticks             = (1:length(sorted_titles), sorted_titles),\n)\n\nboxplot!(\n    ax, plot_positions, salaries;\n    orientation         = :horizontal,\n    color               = (BRAND, 0.75),\n    strokecolor         = INK,\n    strokewidth         = 1.5,\n    mediancolor         = INK,\n    medianlinewidth     = 2.5,\n    whiskercolor        = INK_SOFT,\n    whiskerlinewidth    = 1.5,\n    outliercolor        = BRAND,\n    outlierstrokecolor  = INK,\n    outlierstrokewidth  = 1,\n    markersize          = 10,\n    width               = 0.78,\n)\n\n# Annotate each box with its median salary, offset past the whisker tip so\n# the label never collides with the box or outlier markers.\nsorted_medians = medians[sort_order]\ndata_range = maximum(salaries) - minimum(salaries)\nlabel_x = maximum(salaries) + 0.04 * data_range\ntext!(\n    ax, fill(label_x, length(sorted_medians)), 1:length(sorted_medians);\n    text      = [string(round(Int, m)) for m in sorted_medians],\n    align     = (:left, :center),\n    fontsize  = 12,\n    color     = INK_SOFT,\n)\nxlims!(ax, minimum(salaries) - 0.05 * data_range, label_x + 0.12 * data_range)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}