Using the new ggplot2 and Positron to recreate a bar plot from the Washington Post (CC371)
Pat recreates a figure from the Washington Post that has a series of titled bar plots that are part of a single figure. He shows a variety of approaches to create this appearance before settling in on using facet_wrap. Along the way, he also tries out Positron and some new features from the recently released version 4 of ggplot2. He recreated the Washington Post figure using R, ggplot2, showtext, ggtext, and other tools from the tidyverse. The functions I used from these packages include library, font_add_google, showtext_opts, showtext_auto, read_csv, rename, mutate, factor, pivot_longer, unique, if_else, is.na, paste0, case_when, ggplot, aes, geom_col, geom_text, from_theme, geom_image, tibble, facet_wrap, coord_cartesian, scale_color_identity, labs, theme, element_geom, element_text, element_blank, element_markdown, element_textbox_simple, unit, margin, margin = margin, and ggsave. The newsletter describing this visualization at a 30,000 ft view can be found here. You can find the original article presenting the figure here. If you have a figure that you would like to see me discuss in a future newsletter and episode of Code Club, email me at pat@riffomonas.org!
library(tidyverse)
library(ggtext)
library(showtext)
library(ggimage)
logo_svg <- "https://static.dwcdn.net/custom/themes/kfforg/KFF-WashingtonPost-New.svg"
font_add_google("Source Sans 3", family = "source_sans", regular.wt = 400, bold.wt = 600)
font_add_google("Source Sans 3", family = "title")
showtext_opts(dpi = 300)
showtext_auto()
p <- read_csv("https://datawrapper.dwcdn.net/CiJiw/2/data.csv") %>%
rename(group = X.1) %>%
mutate(group = factor(group, levels = group)) %>%
pivot_longer(-group, names_to = "vaccine", values_to = "percentage") %>%
mutate(
vaccine = factor(vaccine, levels = unique(vaccine)),
label_text = if_else(is.na(percentage), "", paste0(percentage, "%")),
label_x = if_else(percentage < 40, percentage + 4, 4),
label_color = case_when(
percentage < 40 ~ "black",
vaccine == "Polio" | vaccine == "COVID-19" ~ "black",
TRUE ~ "white"
)
) %>%
ggplot(aes(x = percentage, y = group, fill = vaccine)) +
geom_col(show.legend = FALSE) +
geom_text(
aes(label = label_text, x = label_x, color = label_color,
family = from_theme(family), size = from_theme(fontsize)),
hjust = 0, fontface = "bold", #family = "source_sans"
) +
geom_image(
data = tibble(x = 0, y = 12.25, vaccine = factor("COVID-19")),
mapping = aes(x = x, y = y, image = logo_svg), size = 1, nudge_x = -10) +
facet_wrap(~vaccine, nrow = 1) +
coord_cartesian(
reverse = "y", expand = FALSE, clip = "off",
ylim = c(8.5, 0.5)
) +
scale_color_identity() +
labs(
tag = "Figure 2",
title = "Majorities of Parents Across Partisans Say It Is Important for
Children To Be Vaccinated for MMR, Polio; Partisans Are Divided
on Flu, COVID-19 Vaccines",
subtitle = "Percent who say it is **very** or **somewhat important** for
children in their community to be vaccinated for each of the
following:",
caption = "Note: Among parents of children under age 18. Independents
include those who identify with 'Other' party. See topline for full
question wording.<br><br>Source: KFF/The Washington Post Survey of
Parents (July 18-August 4, 2025)"
) +
theme(
geom = element_geom(family = "source_sans", fontsize = 13),
text = element_text(family = "source_sans"),
palette.color.discrete = c("#004B87", "#1B81D4", "#1A7762", "#00B688"),
axis.ticks = element_blank(),
axis.title = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_markdown(size = 13, hjust = 0, color = "black"),
strip.text = element_textbox_simple(
height = unit(4.5, "lines"), size = 13, valign = 0.5
),
strip.background = element_blank(),
plot.title.position = "plot",
plot.caption.position = "plot",
plot.tag.location = "plot",
plot.tag = element_text(
margin = margin(t = 0, b = 10), size = 9.5, color = "gray30"
),
plot.title = element_textbox_simple(
margin = margin(t = 20), face = "bold", size = 15.5, family = "title"
),
plot.subtitle = element_textbox_simple(
size = 13, color = "gray30", margin = margin(t = 12, b = 8)
),
plot.caption = element_textbox_simple(
width = unit(17, "lines"), hjust = 0, size = 9.5, color = "gray30",
margin = margin(t = 23)
),
panel.grid = element_blank(),
panel.background = element_blank()
)
ggsave("vaccines.png", width = 6, height = 6.295)