
12 Waffle charts
theme_pogs()
Graphs in this chapter use fmggp2::theme_pogs(), modeled on Pogs: a cardboard milk cap background, a navy frame and title, red, sky blue, yellow, teal, purple, and rust prints, and a Bradley Hand title.
12.1 Description
Waffle charts use small squares or icons to show how a whole thing is divided into parts, grouped into categories that fill a grid. They’re useful for visualizing percentages or proportions from a total and can include a legend or labels to explain each category. Waffle chart legends should be positioned on top or bottom and justified horizontally to preserve shape and improve readability.
We’ll build a waffle chart using the ggwaffle package.
12.2 Set up
PACKAGES:
Install packages.
show/hide
# ggwaffle is GitHub-only
install.packages("pak")
pak::pak("liamgilbey/ggwaffle")
library(ggwaffle)
library(ggplot2)
library(dplyr) # for tibble() and data manipulationDATA:
In the mid 90s, kids traded and battled with Pogs: cardboard milk caps, printed and holographic pogs, and heavy plastic “slammers” used to flip them. We’ll simulate a collection of 100 pieces with sample(), giving each type its own chance of turning up.
Waffle charts require a special data transformation with ggwaffle::waffle_iron(). Set the group argument in ggwaffle::aes_d() to the categorical variable you want to see the relative counts for, and set rows = 10 so 100 pieces fill a 10 by 10 grid. Setting the seed with set.seed() means you’ll get the same collection every time you run the code below.
show/hide
set.seed(1993) # same 100 pogs every time the code runs
pog_collection <- tibble(
pog_id = 1:100, # 100 different pogs, one row each
type = sample(
c("Milk caps", "Printed pogs", "Holographic", "Slammers"), # four kinds of pogs
size = 100, replace = TRUE, # one kind for each pog
prob = c(0.45, 0.30, 0.15, 0.10) # milk caps are the most common
)
)
waffle_pogs <- ggwaffle::waffle_iron(pog_collection,
aes_d(group = type), # one square per pog, grouped by kind
rows = 10) # a 10 by 10 grid
glimpse(waffle_pogs)
#> Rows: 100
#> Columns: 3
#> $ y <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, …
#> $ x <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2…
#> $ group <chr> "Holographic", "Holographic", "Hol…12.3 Distribution
A waffle chart is built for one special total: 100. When there are 100 squares, every square is one percent, so you can read a percentage just by counting squares. Below we’ll move from the collection, to a picture, to the formula.
COUNT IT:
Start with the first ten pieces in the collection.
show/hide
head(pog_collection$type, 10)
#> [1] "Milk caps" "Milk caps" "Milk caps"
#> [4] "Printed pogs" "Printed pogs" "Printed pogs"
#> [7] "Milk caps" "Milk caps" "Holographic"
#> [10] "Milk caps"Six of these ten are milk caps, three are printed pogs, and one is holographic. Counting all 100 pieces gives us counts that are also percentages. The expected column is what we’d get if the collection matched the probabilities in sample() exactly.
show/hide
pog_collection |>
count(type) |>
mutate(expected = c(15, 45, 30, 10)) DRAW IT:
A bar model shows the collection as one bar of 100 pieces split into four parts.
show/hide
pog_collection |>
count(type) |>
ggplot(aes(x = n, y = "100 pieces", fill = type)) +
geom_col() +
geom_text(aes(label = n), color = c("white", "white", "white", "black"),
position = position_stack(vjust = 0.5)) +
labs(x = "Number of pieces", y = NULL, fill = "Type")
A waffle chart is the same bar folded into columns of 10. Each column of the waffle below is ten pieces, so the milk caps fill a little more than four columns and the slammers fill a little more than one.
show/hide
ggplot(data = waffle_pogs, aes(x = x, y = y, fill = group)) +
ggwaffle::geom_waffle() +
coord_equal() +
labs(x = NULL, y = NULL, fill = NULL) +
theme(axis.text = element_blank(),
panel.grid = element_blank(),
legend.position = "top") +
guides(fill = guide_legend(nrow = 2))
NAME IT:
Every piece in the collection is an independent draw from four types, so the four counts follow a multinomial distribution:
\[ (N_{\text{milk}}, N_{\text{printed}}, N_{\text{holo}}, N_{\text{slam}}) \sim \text{Multinomial}(n = 100, \; p = (0.45, 0.30, 0.15, 0.10)) \]
With \(n = 100\), the expected count for each type is its probability times 100, which is the same number as its percentage. The counts wobble around those expectations by about \(\sqrt{100\,p\,(1 - p)}\) pieces: about 5 for milk caps and 3 for slammers. Our collection has 14 slammers instead of the expected 10, a difference of a little more than one standard deviation. Change the number in set.seed() and the squares trade places, but they always add to 100.
12.4 Grammar
CODE:
Create labels with
labs()Initialize the graph with
ggplot()and providedataMap the
xandyto thexandyaxesMap
grouptofillAdd
ggwaffle::geom_waffle()Keep the squares square with
coord_equal()Remove the axis text and grid lines, and move the legend to the top with
theme()Split the legend into two rows with
guides()so it fits above the square waffle
show/hide
labs_waffle <- labs(
title = "A Pogs collection",
caption = "Simulated data",
subtitle = "100 pieces, one square each",
x = "", y = "", fill = NULL)
ggp2_waffle <- ggplot(data = waffle_pogs,
aes(x = x,
y = y,
fill = group)) +
ggwaffle::geom_waffle() +
coord_equal()
ggp2_waffle +
labs_waffle +
theme(axis.text = element_blank(),
panel.grid = element_blank(),
legend.position = "top") +
guides(fill = guide_legend(nrow = 2))GRAPH:
