
Version 0.5.1, built 2026-09-05
ggmosaic2 is a standalone continuation of the
ggmosaic package, which was removed from CRAN around
November 2025 and appeared unmaintained (a pull request
adding residual-based shading went unanswered). Rather than fork under
the original name, ggmosaic2 is developed independently,
building on ggmosaic’s original authors (Haley Jeppson,
Heike Hofmann, Di Cook; credited as authors in
DESCRIPTION). ggmosaic was designed to create
visualizations of categorical data and is capable of producing bar
charts, stacked bar charts, mosaic plots, and double decker plots.
ggmosaic2 extends this with support for fitted loglinear
models to show patterns of association among variables in frequency
tables via residual-based shading, which was its original intention, as
opposed to the “fill by factor levels” (Marimekko shading) of the
original ggmosaic package. It also adds jittered-point
overlays showing individual observations, reflecting non-independence as
variation in point density, a physical analog for departures from
independence (Friendly, 1995). Additional improvements include improved
spacing, theme appearance, and proper faceting.
See Friendly (1994, 1999) for the theory of mosaic displays and
Jeppson & Hofmann (2023) for the description of the original
ggplot2 implementation. Full citations are in References below.
You can install the latest version of ggmosaic2 from R-universe
with:
install.packages("ggmosaic2", repos = "https://friendly.r-universe.dev")or from GitHub with:
# install.packages("devtools")
devtools::install_github("friendly/ggmosaic2")The datasets::HairEyeColor is a classic example of what
can be learned from a mosaic plot. It is a 3-way table, containing the
frequencies of 592 students who were asked to give their hair color and
eye color, classified by Sex.
ftable(Hair ~ Eye + Sex, data=HairEyeColor)
#> Hair Black Brown Red Blond
#> Eye Sex
#> Brown Male 32 53 10 3
#> Female 36 66 16 4
#> Blue Male 11 50 10 30
#> Female 9 34 7 64
#> Hazel Male 10 25 7 5
#> Female 5 29 7 5
#> Green Male 3 15 7 8
#> Female 2 14 7 8To provide some context, the main questions here are:
Here, we just illustrate how to display this dataset using
ggmosaic2. See the vignette, Introducing
ggmosaic2: an enhanced ggmosaic for how the current implementation
differs from that in the original ggmosaic package,
including use of the fill= aesthetic for Marimekko-style
shading and the use of spacing of the tiles to preserve a visual
hierarchy of the cells belonging to the various factors in the
table.
With default (uniform) shading, a mosaic plot just shows the relative
frequencies of each combination of Sex, Eye,
and Hair, via the area of each tile. The total frequency is
first split by Hair color, then subdivided by
Eye color, and finally by Sex.
library(ggmosaic2)
HairEyeColor |>
as.data.frame() |>
ggplot(aes(x = product(Sex, Eye, Hair), weight = Freq)) +
geom_mosaic() +
theme_mosaic(rot_labels = 45)
Data must be in either frequency form (i.e., containing a
"Freq" column or equivalent) or case form (i.e., each row
contains an individual observation) to be used with
geom_mosaic(). Data in frequency form must have its
frequency column mapped to the weight= argument of
geom_mosaic(). To accommodate the alternate splitting in
horizontal and vertical directions, geom_mosaic() uses a
product() to specify the geometrical aesthetic of the
plot.
See the vignette Three Forms of Frequency Tables for Mosaic Displays for a fuller discussion of case form, frequency form, and table form, and how to convert between them.
These basic mosaic displays are easier to read if the tiles are
colored according to the levels of one of the factors, allowing your eye
to more easily track its levels across the splits by other factors. You
can do this using the fill aesthetic, giving the name of
one of the factors in the table. Doing this for Hair color
and Sex facilitates different comparisons among the
frequencies.
library(patchwork)
p1 <- HairEyeColor |>
as.data.frame() |>
ggplot(aes(x = product(Sex, Eye, Hair), weight = Freq, fill = Eye)) +
geom_mosaic() +
theme_mosaic(rot_labels = 30) +
theme(legend.position = "none")
p2 <- HairEyeColor |>
as.data.frame() |>
ggplot(aes(x = product(Sex, Eye, Hair), weight = Freq, fill = Sex)) +
geom_mosaic() +
theme_mosaic(rot_labels = 30) +
theme(legend.position = "none")
p1 + p2
To see whether hair and eye color are associated, fit a loglinear
model of joint independence
(expected = "independence") and shade each tile by its
residual from that model with scale_fill_residual(). Tiles
shaded blue occur more often than expected under independence; tiles
shaded red occur less often.
HairEyeColor |>
as.data.frame() |>
ggplot(aes(x = product(Sex, Eye, Hair), weight = Freq)) +
geom_mosaic(expected = "independence") +
scale_fill_residual(limits = c(-4, 4)) +
theme_mosaic(rot_labels = 45)
The expected argument also accepts
"saturated" and "conditional" shortcuts, or a
custom model formula, for fitting other loglinear models. See the
vignette ggmosaic
and Loglinear Models for a fuller treatment of model fitting and
residual-based shading.
geom_mosaic_jitter() overlays one jittered point per
individual observation on top of a mosaic plot, so that non-independence
shows up both as residual shading and as variation in point density
within each tile. It needs one row per observation, so first expand
HairEyeColor from its frequency-table form using
tidyr::uncount().
set.seed(1945)
HairEyeColor |>
as.data.frame() |>
tidyr::uncount(Freq) |>
ggplot(aes(x = product(Sex, Eye, Hair))) +
geom_mosaic(expected = "independence") +
scale_fill_residual(limits = c(-4, 4)) +
geom_mosaic_jitter(alpha = 0.3) +
theme_mosaic(rot_labels = 45)
Mosaic displays
Friendly, M. (1994). Mosaic Displays for Multi-Way Contingency Tables. Journal of the American Statistical Association, 89(425), 190–200. doi:10.1080/01621459.1994.10476460
Friendly, M. (1995). Conceptual and Visual Models for Categorical Data. The American Statistician, 49(2), 153–160. doi:10.1080/00031305.1995.10476134
Friendly, M. (1999). Extending Mosaic Displays: Marginal, Conditional, and Partial Views of Categorical Data. Journal of Computational and Graphical Statistics, 8(3), 373–395. doi:10.1080/10618600.1999.10474820
ggmosaic