Mosaic Plot for Categorical Association Analysis — Makie.jl

A mosaic plot visualizes contingency tables by dividing a rectangular area into smaller rectangles whose areas are proportional to cell frequencies. This statistical visualization technique effectively shows relationships and associations between two or more categorical variables, making it easy to identify patterns, dependencies, and deviations from expected frequencies in cross-tabulated data.

Mosaic Plot for Categorical Association Analysis rendered with Makie.jl

Renders

Julia source (Makie.jl)

# anyplot.ai
# mosaic-categorical: Mosaic Plot for Categorical Association Analysis
# Library: makie 0.21.9 | Julia 1.11.9
# Quality: 87/100 | Created: 2026-09-02

using CairoMakie
using Colors
using Random

Random.seed!(42)

# --- Theme tokens -----------------------------------------------------------
const THEME    = get(ENV, "ANYPLOT_THEME", "light")
const PAGE_BG  = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
const INK      = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
const IMPRINT_PALETTE = [
    colorant"#009E73", colorant"#C475FD", colorant"#4467A3", colorant"#BD8233",
    colorant"#AE3030", colorant"#2ABCCD", colorant"#954477", colorant"#99B314",
]
# Per-segment label ink chosen for contrast against each fixed data color
# (green/lavender read best with dark text, blue reads best with light text).
const CELL_LABEL_INK = [colorant"#1A1A17", colorant"#1A1A17", colorant"#FFFFFF"]

# --- Data ---------------------------------------------------------------------
# Employee satisfaction survey cross-tabulated by department and satisfaction level.
departments = ["Engineering", "Sales", "Support", "Marketing"]
satisfaction_levels = ["Low", "Medium", "High"]
department_sizes = [220, 180, 150, 130]
satisfaction_shares = [
    0.10 0.35 0.55
    0.20 0.45 0.35
    0.35 0.40 0.25
    0.15 0.40 0.45
]

n_rows = length(departments)
n_cols = length(satisfaction_levels)
counts = zeros(Int, n_rows, n_cols)
for i in 1:n_rows, j in 1:n_cols
    counts[i, j] = round(Int, department_sizes[i] * satisfaction_shares[i, j] * (0.9 + 0.2 * rand()))
end

row_totals = vec(sum(counts; dims = 2))
total = sum(counts)

# Most notable cell: the department with the highest conditional share of
# "Low" satisfaction, called out below with a bolder tile outline.
low_col = findfirst(==("Low"), satisfaction_levels)
notable_row = argmax(satisfaction_shares[:, low_col])

# --- Mosaic geometry ------------------------------------------------------------
# Column widths encode marginal proportions of department; segment heights within
# each column encode the conditional proportion of satisfaction level.
gap_x = 0.02
gap_y = 0.02
usable_width = 1.0 - gap_x * (n_rows - 1)
usable_height = 1.0 - gap_y * (n_cols - 1)
column_widths = row_totals ./ total .* usable_width

x_starts = zeros(n_rows)
for i in 2:n_rows
    x_starts[i] = x_starts[i - 1] + column_widths[i - 1] + gap_x
end

# --- Plot -----------------------------------------------------------------------
fig = Figure(
    size            = (1200, 1200),
    fontsize        = 14,
    backgroundcolor = PAGE_BG,
)

ax = Axis(
    fig[1, 1];
    title              = "mosaic-categorical · julia · makie · anyplot.ai",
    titlesize          = 20,
    titlecolor         = INK,
    xlabel             = "Department",
    ylabel             = "Satisfaction share",
    xlabelcolor        = INK,
    ylabelcolor        = INK,
    xlabelsize         = 14,
    ylabelsize         = 14,
    xticklabelcolor    = INK_SOFT,
    yticklabelcolor    = INK_SOFT,
    xticklabelsize     = 12,
    yticklabelsize     = 12,
    backgroundcolor    = PAGE_BG,
    topspinevisible    = false,
    rightspinevisible  = false,
    leftspinecolor     = INK_SOFT,
    bottomspinecolor   = INK_SOFT,
    xgridvisible       = false,
    ygridvisible       = false,
)

for i in 1:n_rows
    y_start = 0.0
    for j in 1:n_cols
        height = counts[i, j] / row_totals[i] * usable_height
        rect = Rect2f(x_starts[i], y_start, column_widths[i], height)
        is_notable = i == notable_row && j == low_col
        stroke_color = is_notable ? INK : PAGE_BG
        stroke_width = is_notable ? 3.5 : 2
        poly!(ax, rect; color = IMPRINT_PALETTE[j], strokecolor = stroke_color, strokewidth = stroke_width)

        if height > 0.05
            pct = round(Int, counts[i, j] / total * 100)
            text!(
                ax, x_starts[i] + column_widths[i] / 2, y_start + height / 2;
                text = "$(pct)%", color = CELL_LABEL_INK[j], fontsize = 11,
                align = (:center, :center),
            )
        end

        y_start += height + gap_y
    end
end

ax.xticks = (x_starts .+ column_widths ./ 2, departments)
ax.yticks = (0:0.25:1, ["0%", "25%", "50%", "75%", "100%"])
xlims!(ax, -0.02, 1.02)
ylims!(ax, -0.02, 1.02)

legend_elements = [PolyElement(color = IMPRINT_PALETTE[j]) for j in 1:n_cols]
Legend(
    fig[1, 2], legend_elements, satisfaction_levels, "Satisfaction level";
    framevisible = false, labelcolor = INK, titlecolor = INK, tellheight = false,
)
colsize!(fig.layout, 1, Relative(0.82))

# --- Save -------------------------------------------------------------------
save("plot-$(THEME).png", fig; px_per_unit = 2)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/mosaic-categorical/makie/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "mosaic-categorical",
  "language": "julia",
  "library": "makie",
  "page": "https://anyplot.ai/mosaic-categorical/julia/makie",
  "hub": "https://anyplot.ai/mosaic-categorical",
  "code_json": "https://api.anyplot.ai/specs/mosaic-categorical/makie/code",
  "spec_json": "https://api.anyplot.ai/specs/mosaic-categorical",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/mosaic-categorical/julia/makie/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/mosaic-categorical/julia/makie/plot-dark.png",
  "quality_score": 87.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Mosaic Plot for Categorical Association Analysis on anyplot.ai.

Other implementations