Using the new ggplot2 and Positron to recreate a bar plot from the Washington Post (CC371)

October 3, 2025 • PD Schloss • 8 min read

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)