In this post we will be creating waffle plots with R, using the waffle() function from the package of the same name. In terms of necessary packages I would recommend dplyr, tidyr and of course waffle.
library(dplyr)
library(tidyr)
library(waffle)
Importing The Data
Waffle plots are best used to visualise count data, often as an alternative to something like a pie chart or bar chart. The data we will be using is a count of medals won at the Paris 2024 Olympic Games by country. This dataset is from Kaggle.
Processing The Data
olympic_data <- read.csv('olympics2024.csv')
print(head(olympic_data))
Output:
Rank Country Country.Code Gold Silver Bronze Total
1 United States US 40 44 42 126
2 China CHN 40 27 24 91
3 Japan JPN 20 12 13 45
4 Australia AUS 18 19 16 53
5 France FRA 16 26 22 64
6 Netherlands NED 15 7 12 34
olympic_data <- olympic_data |>
select(-c(Total, Country.Code))
olympic_data$Country <- as.factor(olympic_data$Country)
To plot a waffle plot we need our data in long format using pivot_longer().
long_data <- olympic_data |>
pivot_longer(
cols = c(Gold, Silver, Bronze),
names_to = "Medal",
values_to = "Count"
)
Output:
Rank Country Medal Count
1 United States Gold 40
1 United States Silver 44
1 United States Bronze 42
2 China Gold 40
2 China Silver 27
2 China Bronze 24
Creating Waffle Plots
GB_data <- long_data |> filter(Country == 'Great Britain')
waffle(data.frame(GB_data$Medal, GB_data$Count))

We could do with tidying this plot up. It is also worth finding a number of rows that avoids leftover squares β we can find this using prime factors.
library(numbers)
total <- sum(GB_data$Count)
print(primeFactors(total))
Output:
[1] 5 13
waffle(data.frame(GB_data$Medal, GB_data$Count), rows = 5,
title = "Team GB Medals: Paris 2024",
legend_pos = "bottom")

waffle(data.frame(GB_data$Medal, GB_data$Count), rows = 5,
title = "Team GB Medals: Paris 2024",
legend_pos = "bottom",
colors = c('#d4af37', '#c0c0c0', '#cd7f32'))

We can also turn this into a pictogram using fontawesome.
library(extrafont)
library(fontawesome)
loadfonts(device = 'all')
waffle(data.frame(GB_data$Medal, GB_data$Count), rows = 5,
title = "Team GB Medals: Paris 2024",
legend_pos = "bottom",
colors = c('#d4af37', '#c0c0c0', '#cd7f32'),
use_glyph = 'medal', glyph_size = 8)

Combining Plots
To simplify our code I am going to create a function to make a waffle plot for any country.
country_waffle <- function(country, data = long_data, legend_pos = 'none', size = 0.25) {
data <- data |> filter(data$Country == country)
plot <- waffle(data.frame(data$Medal, data$Count),
size = size, legend_pos = legend_pos,
keep = FALSE, colors = c('#d4af37', '#c0c0c0', '#cd7f32'),
title = paste(country, ':', data$Rank[1]))
return(plot)
}
country_waffle("Australia", size = 1, legend_pos='bottom')

Now letβs combine the top five countries with cowplot.
library(cowplot)
top_countries <- filter(long_data, Rank %in% c(1, 2, 3, 4, 5))$Country |> unique()
title <- ggdraw() +
draw_label(
"Paris 2024 Olympic Games: Medals for Top 5 Countries",
fontface = 'bold',
hjust = 0
)
plot_grid(title, geom_blank(), geom_blank(),
country_waffle(top_countries[1]),
country_waffle(top_countries[2]),
country_waffle(top_countries[3]),
country_waffle(top_countries[4]),
country_waffle(top_countries[5], legend_pos = 'right'),
ncol=3)

Alternatively we can use the built in iron() function from waffle.
iron(country_waffle(top_countries[1]),
country_waffle(top_countries[2]),
country_waffle(top_countries[3], legend_pos = 'bottom'))

The iron() function is better for a smaller number of plots as it can squish things quite a bit.
This is not the only way of creating waffle plots β in a future post I will be looking at creating waffle plots with geom_waffle instead, which is definitely the way to go when trying to do something a bit more complicated.