Using ggplot2 to desmontrate the regressive nature of Trump's spending bill (CC364)

July 23, 2025 • PD Schloss • 4 min read

Pat recreates a barplot from the New York Times showing the regressive impacts of Trump’s spending bill on various income groups in the United States using the ggplot2 R package. The plot was generated using functions from the tidyverse including the dplyr, ggplot2, readxl, showtext, ggtext, and glue packages. The functions he uses from these packages include aes, as.numeric, case_when, download.file, element_rect, element_text, element_textbox_simple, factor, font_add_google, function, geom_col, geom_hline, geom_text, ggplot, glue, if_else, labs, library, margin, mutate, read_excel, scale_color_manual, scale_fill_manual, scale_x_discrete, select, showtext_auto, showtext_opts, str_remove, theme, and theme_void. The newsletter describing this visualization at a 30,000 ft view can be found here. You can find the original article describing the data here for free. The data are [availble from the CBO]https://archive.is/ksh2e. 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!

Want more practice on the concepts covered in Code Club? You can sign up for my weekly newsletter at https://shop.riffomonas.org/youtube to get practice problems, tips, and insights. If you’re interested in purchasing a video workshop be sure to check out https://riffomonas.org/workshops/

Support Riffomonas by becoming a Patreon member! https://www.patreon.com/riffomonas

You can also find complete tutorials for learning R with the tidyverse using… Microbial ecology data: https://www.riffomonas.org/minimalR/ General data: https://www.riffomonas.org/generalR/

If you want to cite this video, please consider citing https://journals.asm.org/doi/10.1128/mra.01310-22

```R library(tidyverse) library(showtext) library(ggtext) library(readxl) library(glue)

font_add_google(“Libre Franklin”, “franklin”, regular.wt = 500) font_add_google(“Libre Franklin”, “franklin_light”) showtext_opts(dpi = 300) showtext_auto()

1. Get the data! Link in bottom right corner of plot to the CBO

* In MS Excel format

* Read in with {readxl}

2. Do some data clean up to get in tidy format to make bar plot

* create a variable to indicate positive or negative net effect

* create bar plot with geom_col()

* add text with geom_text()

* stylize the percent to have the percent sign and positive/negative sign

* color label by direction of effect

* match the colors and remove the legend

#

glue(“+{net_effect}%”)

paste0(“+”, net_effect, “%”)

#

3. Axis work

* remove x and y axis lines, text, ticks

* remove gridlines and background

* add zero line with geom_hline

* add decile information to top of negative bars, bottom of positive bars

4. Add titles

* Need to stylize various elements

nd_to_range <- function(x){

decile_number <- as.numeric(str_remove(x, “\w\w$”)) upper <- 10 * decile_number lower <- 10 * (decile_number - 1)

glue(“{lower}th-\n{upper}th”)

}

decile_to_text <- function(x){

case_when(x == “Lowest” ~ “Bottom\n10%”, x == “Highest” ~ “Top\n10%”, TRUE ~ nd_to_range(x))

}

data_url <- “https://www.cbo.gov/system/files/2025-06/61387-Data.xlsx” download.file(data_url, “cbo-data.xlsx”)

nudge <- 0.2

read_excel(“cbo-data.xlsx”, sheet = “Figure 2”, range = “A8:F18”) %>% select(decile = “Household \r\nincome decile”, net_effect = “Net effect”) %>% mutate(direction = if_else(net_effect < 0, “negative”, “positive”), decile = decile_to_text(decile), decile = factor(decile, levels = decile), decile_y = if_else(net_effect < 0, 0.45, -0.45), label_y = if_else(net_effect < 0, net_effect - nudge, net_effect + nudge), label = if_else(net_effect < 0, glue(“{net_effect}%”), glue(“+{net_effect}%”))) %>% ggplot(aes(x = decile, y = net_effect, fill = direction, label = label)) + geom_col(width = 0.8) + geom_text(aes(y = label_y, color = direction), family = “franklin”, size = 9, size.unit = “pt”) + geom_text(aes(y = decile_y, label = decile), color = “gray50”, lineheight = 0.9, size = 8, size.unit = “pt”, family = “franklin_light”) + geom_hline(yintercept = 0, linewidth = 0.3) + scale_fill_manual(breaks = c(“negative”, “positive”), values = c(“#D35400”, “#59A497”)) + scale_color_manual(breaks = c(“negative”, “positive”), values = c(“#D35400”, “#59A497”)) + scale_x_discrete(expand = expansion(0, 0.48)) + labs( title = “How the Bill Would Affect Households at Different Income Ranks”, subtitle = “Estimated annual average change in resources between 2026-34”, caption = “Note: Estimated annual average effect of the House version of the One Big Beautiful Bill Act on after-tax income. Groups are based on income adjusted for household size.    •     Source: Congressional Budget Office” ) + theme_void() + theme( text = element_text(family = “franklin”), legend.position = “none”, plot.background = element_rect(fill = “white”, color = NA), plot.title = element_text(face = “bold”, size = 11, margin = margin(l = -10)), plot.subtitle = element_text(size = 9.5, color = “gray40”, margin = margin(l = -10, t = 8, b = 12)), plot.caption = element_textbox_simple(color = “gray40”, size = 8, margin = margin(l = -10, t = 15), lineheight = 1.4), plot.margin = margin(t = 8, l = 18, r = 10, b = 6) )```