Skip to content

Instantly share code, notes, and snippets.

@elliottmorris
Last active December 2, 2025 13:12
Show Gist options
  • Select an option

  • Save elliottmorris/3399e606d9be1fd28574a09be63fdac3 to your computer and use it in GitHub Desktop.

Select an option

Save elliottmorris/3399e606d9be1fd28574a09be63fdac3 to your computer and use it in GitHub Desktop.
library(tidyverse)
library(janitor)
library(zoo)
library(scales)
clean_and_avg = function(series){
dat = read_csv(sprintf('%s.csv',series))
dat = janitor::clean_names(dat)
dat$date = as.Date(as.POSIXct(dat$unix_timestamp/1000, origin = "1970-01-01", tz = "UTC"))
dat = dat %>%
group_by(party,date) %>%
summarise(n = n()) %>%
group_by(party) %>%
arrange(party, date) %>%
mutate(ntot = cumsum(n)) %>%
group_by(party) %>%
mutate(daily_avg = rollapply(data = n, width = 30, partial = T, fill = NA, FUN = mean, na.rm = T, align = 'right')) %>%
ungroup() %>%
mutate(index = rescale(daily_avg, to = c(0, 100))) %>%
rename({{series}} := daily_avg) %>%
select(all_of(c('party','date',series)))
return(dat)
}
dat =
expand_grid(party = c("Democrat","Republican"),
date = seq.Date(ymd('2010-01-01'),ymd('2025-11-25'))) %>%
left_join(
clean_and_avg('inflation'),
by=c('party','date')
) %>%
left_join(
clean_and_avg('affordability'),
by=c('party','date')
) %>%
left_join(
clean_and_avg('prices'),
by=c('party','date')
)
dat = dat %>%
mutate_at(c('inflation','affordability','prices'),
na.approx, na.rm = F)
dat %>%
filter(date >= ymd('2024-01-01')) %>%
gather(word, avg, 3:5) %>%
filter(word == 'affordability') %>%
ungroup() %>%
mutate(index = rescale(avg, to = c(0, 100))) %>%
ggplot(., aes(x = date, y = index, col = party)) +
# annotation one
annotate(geom='text',x = ymd('2025-06-20'),y=80,
label = 'Zohran Mamdani wins 2025\nDemocratic mayoral primary\nin New York City',
hjust = 1) +
geom_vline(xintercept = ymd('2025-06-24'),col='black') +
# annotation 2
annotate(geom='text',x = ymd('2025-11-01'),y=90,
label = '2025\nstatewide\nand NYC\ngeneral\nelections',
hjust = 1) +
geom_vline(xintercept = ymd('2025-11-04'),col='black') +
# annotation 3
annotate(geom='text',x = ymd('2024-11-01'),y=90,
label = 'Trump wins\n2024 election',
hjust = 1) +
geom_vline(xintercept = ymd('2024-11-05'),col='black') +
# annotation 4
annotate(geom='text',x = ymd('2024-07-15'),y=60,
label = 'Joe Biden drops out\nof 2024 presidential election',
hjust = 1) +
geom_vline(xintercept = ymd('2024-07-21'),col='black') +
# rest of chart
geom_line() +
scale_color_manual(values = c('Democrat' = 'blue','Republican'='red')) +
coord_cartesian(ylim=c(0,100)) +
scale_x_date(date_labels = '%b %Y') +
labs(x = 'Date',
y = 'Mentions index\n(max monthly avg = 100)',
subtitle = 'Index of monthly average mentions of the word "affordability" in congressional emails,\nby political party. 100 = max monthly value.',
title='Republicans are playing catch-up on "affordability"',
col = 'Party',
caption = 'Data: DCInbox.com\nSource: Strength In Numbers/gelliottmorris.com') +
theme_minimal() +
theme(legend.position = 'top',legend.justification = 'left',
plot.caption = element_text(hjust=0),
panel.grid.minor.x = element_blank())
dat %>%
filter(date >= ymd('2025-06-01')) %>%
gather(word, avg, 3:5) %>%
filter(word == 'affordability') %>%
ungroup() %>%
mutate(index = rescale(avg, to = c(0, 100))) %>%
ggplot(., aes(x = date, y = index, col = party)) +
# annotation one
annotate(geom='text',x = ymd('2025-06-25'),y=60,
label = 'Zohran Mamdani wins 2025\nDemocratic mayoral primary\nin New York City',
hjust = 0) +
geom_vline(xintercept = ymd('2025-06-24'),col='black') +
# annotation 2
annotate(geom='text',x = ymd('2025-11-01'),y=90,
label = '2025\nstatewide\nand NYC\ngeneral\nelections',
hjust = 1) +
geom_vline(xintercept = ymd('2025-11-04'),col='black') +
# rest of chart
geom_line() +
scale_color_manual(values = c('Democrat' = 'blue','Republican'='red')) +
coord_cartesian(ylim=c(0,100)) +
scale_x_date(date_breaks = 'month', date_labels = '%b') +
labs(x = '2025',
y = 'Mentions index\n(max monthly avg = 100)',
subtitle = 'Index of monthly average mentions of the word "affordability" in congressional emails,\nby political party. 100 = max monthly value.',
title='Republicans are playing catch-up on "affordability"',
col = 'Party',
caption = 'Data: DCInbox.com\nSource: Strength In Numbers/gelliottmorris.com') +
theme_minimal() +
theme(legend.position = 'top',legend.justification = 'left',
plot.caption = element_text(hjust=0),
panel.grid.minor.x = element_blank())
dat %>%
filter(date >= ymd('2024-01-01')) %>%
gather(word, avg, 3:5) %>%
#filter(word != 'prices') %>%
ungroup() %>%
mutate(index = rescale(avg, to = c(0, 100))) %>%
ggplot(., aes(x = date, y = index, col = word)) +
geom_line() +
facet_wrap(~party,ncol=1) +
theme_minimal() +
scale_color_manual(values = c('affordability' = 'green','inflation'='gray40','prices'='pink')) +
coord_cartesian(ylim=c(0,100)) +
scale_x_date(date_breaks = '6 month', date_labels = '%b %Y') +
labs(x = 'Date',
y = 'Mentions index\n(max monthly avg = 100)',
subtitle = 'Index of monthly average mentions of words related to affordability and prices\nin congressional emails, by word political party. 100 = max monthly value.',
title='"Affordability" is the new "inflation"',
col = 'Word',
caption = 'Data: DCInbox.com\nSource: Strength In Numbers/gelliottmorris.com') +
theme_minimal() +
theme(legend.position = 'top',legend.justification = 'left',
plot.caption = element_text(hjust=0),
panel.grid.minor.x = element_blank())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment