-
-
Save ryanburge/7dac81aa49011a2b7023cde4ce0104e8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| aaa1 <- cces23 %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, | |
| vv == 2 ~ 0)) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(type = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition") %>% | |
| mutate(year = 2023) | |
| aaa2 <- cces25 %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, | |
| vv == 2 ~ 0)) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(type = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition") %>% | |
| mutate(year = 2025) | |
| one <- bind_rows(aaa1, aaa2) %>% mutate(pid3 = "Full Sample") | |
| aaa1 <- cces23 %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, | |
| vv == 2 ~ 0)) %>% | |
| cces_pid3(pid7) %>% | |
| group_by(pid3) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(type = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition") %>% | |
| mutate(year = 2023) | |
| aaa2 <- cces25 %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, | |
| vv == 2 ~ 0)) %>% | |
| cces_pid3(pid7) %>% | |
| group_by(pid3) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(type = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition") %>% | |
| mutate(year = 2025) | |
| two <- bind_rows(aaa1, aaa2) | |
| all <- bind_rows(one, two) %>% filter(pid3 != "NA") | |
| all %>% | |
| mutate(pid3 = factor(pid3, levels = c("Full Sample", "Democrat", "Independent", "Republican"))) %>% | |
| filter(!is.na(pid3)) %>% | |
| ggplot(., aes(x = factor(year), y = mean, fill = pid3)) + | |
| geom_col(color = "black") + | |
| facet_wrap(~ pid3, nrow = 1) + | |
| scale_fill_manual(values = c( | |
| "Full Sample" = "azure4", | |
| "Democrat" = "dodgerblue", | |
| "Independent" = "darkorchid", | |
| "Republican" = "firebrick" | |
| )) + | |
| theme_rb() + | |
| lab_bar_white(above = FALSE, type = mean, pos = .05, sz = 10) + | |
| error_bar() + | |
| theme(axis.text.x = element_text(size = 20)) + | |
| y_pct() + | |
| labs(x = "", y = "", title = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition", | |
| caption= "@ryanburge | Data: Cooperative Election Study, 2023-2025") | |
| save("ces_trans_chagne.png") | |
| aaa1 <- cces23 %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2023) | |
| aaa2 <- cces25 %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2025) | |
| one <- bind_rows(aaa1, aaa2) %>% mutate(gen = "Full Sample") | |
| aaa1 <- cces23 %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| mutate(gen = frcode(birthyr >= 1997 ~ "Gen Z", | |
| birthyr >= 1981 ~ "Millennial", | |
| birthyr >= 1965 ~ "Gen X", | |
| birthyr >= 1946 ~ "Boomer", | |
| birthyr < 1946 ~ "Silent/Greatest")) %>% | |
| group_by(gen) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2023) | |
| aaa2 <- cces25 %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| mutate(gen = frcode(birthyr >= 1997 ~ "Gen Z", | |
| birthyr >= 1981 ~ "Millennial", | |
| birthyr >= 1965 ~ "Gen X", | |
| birthyr >= 1946 ~ "Boomer", | |
| birthyr < 1946 ~ "Silent/Greatest")) %>% | |
| group_by(gen) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2025) | |
| two <- bind_rows(aaa1, aaa2) | |
| all <- bind_rows(one, two) %>% filter(!is.na(gen)) | |
| two %>% | |
| mutate(gen = factor(gen, levels = c("Silent/Greatest", "Boomer", "Gen X", "Millennial", "Gen Z"))) %>% | |
| ggplot(., aes(x = factor(year), y = mean, fill = gen)) + | |
| geom_col(color = "black") + | |
| facet_wrap(~ gen, nrow = 1) + | |
| scale_fill_manual(values = c( | |
| "Gen Z" = "#4e79a7", | |
| "Millennial" = "#f28e2b", | |
| "Gen X" = "#59a14f", | |
| "Boomer" = "#e15759", | |
| "Silent/Greatest" = "#b07aa1" | |
| )) + | |
| theme_rb() + | |
| lab_bar_white(above = FALSE, type = mean, pos = .05, sz = 10) + | |
| error_bar() + | |
| theme(axis.text.x = element_text(size = 20)) + | |
| y_pct() + | |
| labs(x = "", y = "", | |
| title = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition", | |
| caption = "@ryanburge | Data: Cooperative Election Study, 2023-2025") | |
| save("ces_trans_gen.png", wd = 12, ht = 6) | |
| gg1 <- reltrad(cces23, "23") %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| group_by(trad2) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2023) | |
| gg2 <- reltrad(cces25, "25") %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| group_by(trad2) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2025) | |
| all <- bind_rows(gg1, gg2) | |
| all %>% | |
| filter(!is.na(trad2)) %>% | |
| mutate(lab = round(mean * 100)) %>% | |
| ggplot(., aes(x = fct_rev(trad2), y = mean, color = factor(year))) + | |
| geom_pointrange(data = . %>% filter(year == 2025), | |
| aes(ymin = lower, ymax = upper), | |
| position = position_nudge(x = 0.15), size = 0.8) + | |
| geom_pointrange(data = . %>% filter(year == 2023), | |
| aes(ymin = lower, ymax = upper), | |
| position = position_nudge(x = -0.15), size = 0.8) + | |
| geom_text(data = . %>% filter(year == 2025), | |
| aes(label = paste0(lab, "%"), y = mean), | |
| position = position_nudge(x = 0.55), | |
| hjust = 0.5, size = 3.5, family = "bold", show.legend = FALSE) + | |
| geom_text(data = . %>% filter(year == 2023), | |
| aes(label = paste0(lab, "%"), y = mean), | |
| position = position_nudge(x = -0.55), | |
| hjust = 0.5, size = 3.5, family = "bold", show.legend = FALSE) + | |
| coord_flip() + | |
| theme_rb(legend = TRUE) + | |
| y_pct() + | |
| scale_color_d3(breaks = c("2025", "2023")) + | |
| # guides(color = guide_legend(reverse = TRUE)) + | |
| theme(legend.text = element_text(size = 20)) + | |
| labs(x = "", y = "", color = "", | |
| title = "Support for Banning Gender-Affirming Care for Minors by Religious Tradition", | |
| caption = "@ryanburge | Data: Cooperative Election Study, 2023-2025") | |
| save("ces_trans_trad2.png", wd = 10, ht = 8) | |
| gg1 <- cces23 %>% | |
| cces_trad(religpew) %>% | |
| mutate(vv = CC23_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| cces_pid3(pid7) %>% | |
| group_by(trad2, pid3) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2023) | |
| gg2 <- cces25 %>% | |
| cces_trad(religpew) %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, vv == 2 ~ 0)) %>% | |
| cces_pid3(pid7) %>% | |
| group_by(trad2, pid3) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) %>% | |
| mutate(year = 2025) | |
| bind_rows(gg1, gg2) %>% | |
| filter(pid3 != "NA", !is.na(trad2)) %>% | |
| mutate(lab = round(mean, 2)) %>% | |
| ggplot(., aes(x = factor(year), y = mean, fill = pid3)) + | |
| geom_col(color = "black") + | |
| facet_grid(trad2 ~ pid3) + | |
| pid3_fill() + | |
| theme_rb() + | |
| y_pct() + | |
| error_bar() + | |
| lab_bar(above = FALSE, type = lab, pos = .08, sz = 5) + | |
| labs(x = "", y = "Share Agreeing", | |
| title = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("pid3_trans_minor_trad2_change.png", wd = 10, ht = 14) | |
| plot_data <- bind_rows(gg1, gg2) %>% | |
| filter(pid3 != "NA", pid3 != "Independent", !is.na(trad2)) %>% | |
| filter(!trad2 %in% c("Orthodox", "Muslim", "Buddhist", "Hindu", "Unclassified")) %>% | |
| mutate(trad2 = droplevels(fct_rev(trad2))) %>% | |
| select(trad2, pid3, mean, year) %>% | |
| pivot_wider(names_from = year, values_from = mean, names_prefix = "yr_") %>% | |
| mutate(y_base = as.numeric(trad2), | |
| y_pos = case_when(pid3 == "Democrat" ~ y_base + 0.2, | |
| pid3 == "Republican" ~ y_base - 0.2)) | |
| trad_labels <- levels(plot_data$trad2) | |
| plot_data %>% | |
| ggplot(., aes(color = pid3)) + | |
| geom_segment(aes(x = yr_2023, | |
| xend = yr_2025 - (yr_2025 - yr_2023) * 0.1, | |
| y = y_pos, yend = y_pos), | |
| linewidth = 1, | |
| arrow = arrow(length = unit(0.3, "cm"), type = "open", ends = "last")) + | |
| geom_point(aes(x = yr_2023, y = y_pos), shape = 21, fill = "white", size = 3) + | |
| geom_point(aes(x = yr_2025, y = y_pos), shape = 19, size = 3) + | |
| scale_y_continuous(breaks = seq_along(trad_labels), labels = trad_labels) + | |
| scale_color_manual(values = c("dodgerblue4", 'firebrick3')) + | |
| theme_rb(legend = TRUE) + | |
| theme(legend.text = element_text(size = 20)) + | |
| x_pct() + | |
| labs(x = "", y = "", color = "", | |
| title = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition", | |
| subtitle = "Open circle = 2023, Filled circle = 2025", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("pid3_trans_minor_trad2_dumbbell.png", wd = 10, ht = 6) | |
| library(fst) | |
| library(tidyverse) | |
| source("https://gist.githubusercontent.com/ryanburge/4bb20dbacfb068949dee4fe42fbd490b/raw/rb_functions2026.R") | |
| # Load data | |
| ces23 <- read_fst("E://data/cces23.fst") | |
| ces25 <- read_fst("E://data/cces25.fst") | |
| # Recode and stack | |
| ces23_r <- ces23 %>% | |
| mutate( | |
| year = 0, | |
| outcome = case_when(CC23_343a == 1 ~ 1, CC23_343a == 2 ~ 0, TRUE ~ NA_real_), | |
| female = case_when(gender4 == 2 ~ 1, gender4 == 1 ~ 0, TRUE ~ NA_real_), | |
| age = 2023 - birthyr, | |
| educ = ifelse(educ %in% c(98, 99), NA, educ), | |
| faminc = ifelse(faminc_new %in% c(97, 998, 999), NA, faminc_new), | |
| pid7 = ifelse(pid7 %in% c(8, 9, 98, 99), NA, pid7), | |
| ideo5 = ifelse(ideo5 %in% c(6, 8, 9), NA, ideo5), | |
| attend = ifelse(pew_churatd %in% c(7, 98, 99), NA, pew_churatd), | |
| religimp = ifelse(pew_religimp %in% c(8, 9), NA, pew_religimp), | |
| bornagain = case_when(pew_bornagain == 1 ~ 1, pew_bornagain == 2 ~ 0, TRUE ~ NA_real_), | |
| white = case_when(race == 1 ~ 1, TRUE ~ 0), | |
| black = case_when(race == 2 ~ 1, TRUE ~ 0), | |
| hispanic = case_when(race == 3 ~ 1, TRUE ~ 0), | |
| weight = commonweight | |
| ) %>% | |
| select(year, outcome, female, age, educ, faminc, pid7, ideo5, | |
| attend, religimp, bornagain, white, black, hispanic, weight) | |
| ces25_r <- ces25 %>% | |
| mutate( | |
| year = 1, | |
| outcome = case_when(CC25_343a == 1 ~ 1, CC25_343a == 2 ~ 0, TRUE ~ NA_real_), | |
| female = case_when(gender4 == 2 ~ 1, gender4 == 1 ~ 0, TRUE ~ NA_real_), | |
| age = 2025 - birthyr, | |
| educ = ifelse(educ %in% c(98, 99), NA, educ), | |
| faminc = ifelse(faminc_new %in% c(97, 998, 999), NA, faminc_new), | |
| pid7 = ifelse(pid7 %in% c(8, 9, 98, 99), NA, pid7), | |
| ideo5 = ifelse(ideo5 %in% c(6, 8, 9), NA, ideo5), | |
| attend = ifelse(pew_churatd %in% c(7, 98, 99), NA, pew_churatd), | |
| religimp = ifelse(pew_religimp %in% c(8, 9), NA, pew_religimp), | |
| bornagain = case_when(pew_bornagain == 1 ~ 1, pew_bornagain == 2 ~ 0, TRUE ~ NA_real_), | |
| white = case_when(race == 1 ~ 1, TRUE ~ 0), | |
| black = case_when(race == 2 ~ 1, TRUE ~ 0), | |
| hispanic = case_when(race == 3 ~ 1, TRUE ~ 0), | |
| weight = commonweight | |
| ) %>% | |
| select(year, outcome, female, age, educ, faminc, pid7, ideo5, | |
| attend, religimp, bornagain, white, black, hispanic, weight) | |
| stacked <- bind_rows(ces23_r, ces25_r) | |
| # LPM with year interactions — interaction coefs = what drove the shift | |
| m1 <- lm(outcome ~ year * (female + age + educ + faminc + pid7 + ideo5 + | |
| attend + religimp + bornagain + white + black + hispanic), | |
| data = stacked, weights = weight) | |
| summary(m1) | |
| library(broom) | |
| tidy(m1) %>% | |
| filter(str_detect(term, "year:")) %>% | |
| mutate( | |
| term = str_remove(term, "year:"), | |
| term = frcode( | |
| term == "female" ~ "Female", | |
| term == "age" ~ "Age", | |
| term == "educ" ~ "Education", | |
| term == "faminc" ~ "Family Income", | |
| term == "pid7" ~ "Party ID (Rep. direction)", | |
| term == "ideo5" ~ "Ideology (Con. direction)", | |
| term == "attend" ~ "Church Attendance", | |
| term == "religimp" ~ "Religion Less Important", | |
| term == "bornagain" ~ "Born Again", | |
| term == "white" ~ "White", | |
| term == "black" ~ "Black", | |
| term == "hispanic" ~ "Hispanic" | |
| ), | |
| sig = p.value < 0.05, | |
| conf.low = estimate - 1.96 * std.error, | |
| conf.high = estimate + 1.96 * std.error | |
| ) %>% | |
| ggplot(aes(x = estimate, y = fct_reorder(term, estimate), color = sig)) + | |
| geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") + | |
| geom_pointrange(aes(xmin = conf.low, xmax = conf.high), size = 0.6) + | |
| scale_color_manual(values = c("FALSE" = "gray60", "TRUE" = "firebrick")) + | |
| theme_rb(legend = FALSE) + | |
| labs(x = "Change in Predicted Support (2023 to 2025)", | |
| y = "", | |
| title = "What drove the shift in support for gender-affirming care bans?", | |
| subtitle = "Interaction coefficients from DiD regression (significant terms in red)", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("trans_did_coefplot.png", wd = 9, ht = 6) | |
| # Standardize continuous predictors | |
| stacked_z <- stacked %>% | |
| mutate(across(c(age, educ, faminc, pid7, ideo5, attend, religimp), scale)) | |
| stacked_z <- stacked %>% | |
| mutate( | |
| religimp = 5 - religimp # now 4 = very important, 1 = not at all | |
| ) %>% | |
| mutate(across(c(age, educ, faminc, pid7, ideo5, attend, religimp), scale)) | |
| # Rerun model with standardized predictors | |
| m2 <- lm(outcome ~ year * (female + age + educ + faminc + pid7 + ideo5 + | |
| attend + religimp + bornagain + white + black + hispanic), | |
| data = stacked_z, weights = weight) | |
| # Plot interactions only | |
| tidy(m2) %>% | |
| filter(str_detect(term, "year:")) %>% | |
| mutate( | |
| term = str_remove(term, "year:"), | |
| term = frcode( | |
| term == "female" ~ "Female", | |
| term == "age" ~ "Age", | |
| term == "educ" ~ "Education", | |
| term == "faminc" ~ "Family Income", | |
| term == "pid7" ~ "Party ID (Rep. direction)", | |
| term == "ideo5" ~ "Ideology (Con. direction)", | |
| term == "attend" ~ "Church Attendance", | |
| term == "religimp" ~ "Religion Less Important", | |
| term == "bornagain" ~ "Born Again", | |
| term == "white" ~ "White", | |
| term == "black" ~ "Black", | |
| term == "hispanic" ~ "Hispanic" | |
| ), | |
| sig = p.value < 0.05, | |
| conf.low = estimate - 1.96 * std.error, | |
| conf.high = estimate + 1.96 * std.error | |
| ) %>% | |
| ggplot(aes(x = estimate, y = fct_reorder(term, estimate), color = sig)) + | |
| geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") + | |
| geom_pointrange(aes(xmin = conf.low, xmax = conf.high), size = 0.6) + | |
| # geom_text(aes(label = round(estimate, 3)), hjust = -0.3, size = 3.5, color = "black") + | |
| scale_color_manual(values = c("FALSE" = "gray60", "TRUE" = "firebrick")) + | |
| theme_rb(legend = FALSE) + | |
| labs(x = "Standardized Change in Predicted Support (2023 to 2025)", | |
| y = "", | |
| title = "What drove the shift in support for gender-affirming care bans?", | |
| subtitle = "Standardized interaction coefficients — significant terms in red", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("trans_did_coefplot_std.png", wd = 9, ht = 5) | |
| gg1 <- cces25 %>% | |
| cces_trad(religpew) %>% | |
| mutate(vv = CC25_343a) %>% | |
| mutate(vv = case_when(vv == 1 ~ 1, | |
| vv == 2 ~ 0)) %>% | |
| # group_by(trad2) %>% | |
| mean_ci(vv, wt = commonweight, ci = .84) | |
| trad2_colors <- c( | |
| "White Evangelical" = "#E8A87C", | |
| "Non-White Evangelical" = "#F5C9A0", | |
| "Mainline" = "#85B8E8", | |
| "Black Protestant" = "#6A9FD4", | |
| "White Catholic" = "#B39DDB", | |
| "Non-White Catholic" = "#CDB8E8", | |
| "LDS" = "#80CBC4", | |
| "Orthodox" = "#FFE082", | |
| "Jewish" = "#A5D6A7", | |
| "Muslim" = "#80C9A0", | |
| "Buddhist" = "#B2DFDB", | |
| "Hindu" = "#FFCC80", | |
| "Atheist" = "#9E9E9E", | |
| "Agnostic" = "#BDBDBD", | |
| "Nothing in Particular" = "#D4D4D4", | |
| "Unclassified" = "#E8E8E4" | |
| ) | |
| gg1 %>% | |
| ggplot(., aes(x = reorder(trad2, mean), y = mean, fill = trad2)) + | |
| geom_col(color = "black") + | |
| coord_flip() + | |
| theme_rb() + | |
| y_pct() + | |
| error_bar() + | |
| scale_fill_manual(values = trad2_colors) + | |
| lab_bar(above = FALSE, type = mean, pos = .05, sz = 7) + | |
| labs(x = "", y = "", title = "Make it illegal for health care professionals to provide someone younger\nthan 18 with medical care for a gender transition", | |
| caption = "@ryanburge | Data: Cooperative Election Study, 2025") | |
| save("gender_transitition_ces25_trad2.png") | |
| ces23_gen <- ces23 %>% | |
| mutate( | |
| year = "2023", | |
| outcome = case_when(CC23_343a == 1 ~ 1, CC23_343a == 2 ~ 0, TRUE ~ NA_real_), | |
| gender = case_when(gender4 == 1 ~ "Man", gender4 == 2 ~ "Woman", TRUE ~ NA_character_), | |
| generation = case_when( | |
| birthyr <= 1945 ~ "Silent/Greatest", | |
| birthyr <= 1964 ~ "Boomer", | |
| birthyr <= 1980 ~ "Gen X", | |
| birthyr <= 1996 ~ "Millennial", | |
| birthyr <= 2012 ~ "Gen Z", | |
| TRUE ~ NA_character_ | |
| ), | |
| weight = commonweight | |
| ) %>% | |
| filter(!is.na(outcome), !is.na(gender), !is.na(generation)) %>% | |
| group_by(year, generation, gender) %>% | |
| mean_ci(outcome, wt = weight) | |
| ces25_gen <- ces25 %>% | |
| mutate( | |
| year = "2025", | |
| outcome = case_when(CC25_343a == 1 ~ 1, CC25_343a == 2 ~ 0, TRUE ~ NA_real_), | |
| gender = case_when(gender4 == 1 ~ "Man", gender4 == 2 ~ "Woman", TRUE ~ NA_character_), | |
| generation = case_when( | |
| birthyr <= 1945 ~ "Silent/Greatest", | |
| birthyr <= 1964 ~ "Boomer", | |
| birthyr <= 1980 ~ "Gen X", | |
| birthyr <= 1996 ~ "Millennial", | |
| birthyr <= 2012 ~ "Gen Z", | |
| TRUE ~ NA_character_ | |
| ), | |
| weight = commonweight | |
| ) %>% | |
| filter(!is.na(outcome), !is.na(gender), !is.na(generation)) %>% | |
| group_by(year, generation, gender) %>% | |
| mean_ci(outcome, wt = weight) | |
| gg <- bind_rows(ces23_gen, ces25_gen) %>% | |
| mutate( | |
| generation = frcode( | |
| generation == "Silent/Greatest" ~ "Silent/Greatest", | |
| generation == "Boomer" ~ "Boomer", | |
| generation == "Gen X" ~ "Gen X", | |
| generation == "Millennial" ~ "Millennial", | |
| generation == "Gen Z" ~ "Gen Z" | |
| ), | |
| gender = factor(gender, levels = c("Woman", "Man")) | |
| ) | |
| plot_data <- gg %>% | |
| pivot_wider(names_from = year, values_from = mean, names_prefix = "yr_") %>% | |
| mutate(y_pos = as.numeric(gender)) | |
| gender_labels <- levels(plot_data$gender) | |
| plot_data %>% | |
| ggplot(aes(color = gender)) + | |
| geom_segment(aes(x = yr_2023, | |
| xend = yr_2025 - (yr_2025 - yr_2023) * 0.1, | |
| y = y_pos, yend = y_pos), | |
| linewidth = 1, | |
| arrow = arrow(length = unit(0.3, "cm"), type = "open", ends = "last")) + | |
| geom_point(aes(x = yr_2023, y = y_pos), shape = 21, fill = "white", size = 3) + | |
| geom_point(aes(x = yr_2025, y = y_pos), shape = 19, size = 3) + | |
| scale_y_continuous(breaks = seq_along(gender_labels), labels = gender_labels) + | |
| facet_wrap(~ generation, ncol = 5) + | |
| scale_color_manual(values = c("Man" = "steelblue", "Woman" = "firebrick")) + | |
| theme_rb(legend = TRUE) + | |
| x_pct() + | |
| labs(x = "", y = "", color = "", | |
| title = "Support for gender-affirming care ban by generation and gender", | |
| subtitle = "Open circle = 2023, Filled circle = 2025", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("trans_gen_gender.png", wd = 14, ht = 5) | |
| ces25 %>% | |
| mutate( | |
| outcome = case_when(CC25_343a == 1 ~ 1, CC25_343a == 2 ~ 0, TRUE ~ NA_real_), | |
| gender = case_when(gender4 == 1 ~ "Man", gender4 == 2 ~ "Woman", TRUE ~ NA_character_), | |
| generation = case_when( | |
| birthyr <= 1945 ~ "Silent/Greatest", | |
| birthyr <= 1964 ~ "Boomer", | |
| birthyr <= 1980 ~ "Gen X", | |
| birthyr <= 1996 ~ "Millennial", | |
| birthyr <= 2012 ~ "Gen Z", | |
| TRUE ~ NA_character_ | |
| ), | |
| generation = frcode( | |
| generation == "Silent/Greatest" ~ "Silent/Greatest", | |
| generation == "Boomer" ~ "Boomer", | |
| generation == "Gen X" ~ "Gen X", | |
| generation == "Millennial" ~ "Millennial", | |
| generation == "Gen Z" ~ "Gen Z" | |
| ) | |
| ) %>% | |
| filter(!is.na(outcome), !is.na(gender), !is.na(generation)) %>% | |
| group_by(generation, gender) %>% | |
| mean_ci(outcome, wt = commonweight, ci = .84) %>% | |
| ggplot(aes(x = mean, y = fct_rev(generation), color = gender)) + | |
| geom_pointrange(aes(xmin = lower, xmax = upper), | |
| position = position_dodge(width = 0.4), size = 0.6, stroke = 1, shape = 21, fill = 'white') + | |
| geom_text(data = . %>% filter(gender == "Woman"), | |
| aes(label = paste0(round(mean * 100, 0), "%")), | |
| position = position_dodge(width = 0.4), | |
| vjust = -1.6, size = 3.5, fontface = "bold", show.legend = FALSE) + | |
| geom_text(data = . %>% filter(gender == "Man"), | |
| aes(label = paste0(round(mean * 100, 0), "%")), | |
| position = position_dodge(width = 0.4), | |
| vjust = 2.6, size = 3.5, fontface = "bold", show.legend = FALSE) + | |
| scale_color_calc() + | |
| guides(color = guide_legend(reverse = TRUE)) + | |
| theme_rb(legend = TRUE) + | |
| theme(legend.text = element_text(size = 20)) + | |
| x_pct() + | |
| labs(x = "", y = "", color = "", | |
| title = "Support for gender-affirming care ban by generation and gender", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2025") | |
| save("trans_gen_gender_25.png", wd = 9, ht = 6) | |
| bind_rows( | |
| ces23 %>% mutate(year = "2023", outcome = case_when(CC23_343a == 1 ~ 1, CC23_343a == 2 ~ 0, TRUE ~ NA_real_)), | |
| ces25 %>% mutate(year = "2025", outcome = case_when(CC25_343a == 1 ~ 1, CC25_343a == 2 ~ 0, TRUE ~ NA_real_)) | |
| ) %>% | |
| mutate( | |
| gender = case_when(gender4 == 1 ~ "Man", gender4 == 2 ~ "Woman", TRUE ~ NA_character_), | |
| generation = case_when( | |
| birthyr <= 1945 ~ "Silent/Greatest", | |
| birthyr <= 1964 ~ "Boomer", | |
| birthyr <= 1980 ~ "Gen X", | |
| birthyr <= 1996 ~ "Millennial", | |
| birthyr <= 2012 ~ "Gen Z", | |
| TRUE ~ NA_character_ | |
| ), | |
| generation = frcode( | |
| generation == "Silent/Greatest" ~ "Silent/Greatest", | |
| generation == "Boomer" ~ "Boomer", | |
| generation == "Gen X" ~ "Gen X", | |
| generation == "Millennial" ~ "Millennial", | |
| generation == "Gen Z" ~ "Gen Z" | |
| ), | |
| weight = commonweight | |
| ) %>% | |
| filter(!is.na(outcome), !is.na(gender), !is.na(generation)) %>% | |
| group_by(year, generation, gender) %>% | |
| mean_ci(outcome, wt = weight) %>% | |
| select(year, generation, gender, mean) %>% | |
| pivot_wider(names_from = gender, values_from = mean) %>% | |
| mutate(gap = Man - Woman) %>% | |
| ggplot(aes(x = gap, y = fct_rev(generation), color = year, group = year)) + | |
| geom_vline(xintercept = 0, linetype = "dashed", color = "gray50") + | |
| geom_point(size = 4) + | |
| geom_text(aes(label = paste0("+", round(gap * 100, 1), "%")), | |
| vjust = -1.2, size = 3.5, fontface = "bold", show.legend = FALSE) + | |
| scale_color_manual(values = c("2023" = "gray50", "2025" = "firebrick")) + | |
| theme_rb(legend = TRUE) + | |
| x_pct() + | |
| labs(x = "Gap in support (Men minus Women)", y = "", color = "", | |
| title = "The gender gap in support for gender-affirming care bans has collapsed", | |
| subtitle = "Percentage point difference between men and women, by generation", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("trans_gender_gap.png", wd = 9, ht = 6) | |
| gg1 <- bind_rows( | |
| ces23 %>% select(birthyr, gender4, commonweight, CC23_343a) %>% | |
| mutate(year = "2023", outcome = case_when(CC23_343a == 1 ~ 1, CC23_343a == 2 ~ 0, TRUE ~ NA_real_)), | |
| ces25 %>% select(birthyr, gender4, commonweight, CC25_343a) %>% | |
| mutate(year = "2025", outcome = case_when(CC25_343a == 1 ~ 1, CC25_343a == 2 ~ 0, TRUE ~ NA_real_)) | |
| ) %>% | |
| mutate( | |
| gender = case_when(gender4 == 1 ~ "Man", gender4 == 2 ~ "Woman", TRUE ~ NA_character_), | |
| generation = frcode( | |
| birthyr <= 1964 ~ "Boomer", | |
| birthyr <= 1980 ~ "Gen X", | |
| birthyr <= 1996 ~ "Millennial", | |
| birthyr <= 2012 ~ "Gen Z" | |
| ), | |
| weight = commonweight | |
| ) %>% | |
| filter(!is.na(outcome), !is.na(gender), !is.na(generation)) %>% | |
| group_by(year, generation, gender) %>% | |
| mean_ci(outcome, wt = weight, ci = .84) | |
| gg1 %>% | |
| ggplot(aes(x = factor(year), y = mean, fill = gender)) + | |
| geom_col(color = "black", position = "dodge") + | |
| facet_wrap(~ generation, ncol = 2) + | |
| scale_fill_calc() + | |
| error_bar() + | |
| guides(fill = guide_legend(reverse = FALSE)) + | |
| theme_rb(legend = TRUE) + | |
| y_pct() + | |
| lab_bar_white(pos = .075, sz = 6, type = mean, above = FALSE) + | |
| theme(plot.title = element_text(size = 12)) + | |
| labs(x = "", y = "", fill = "", | |
| title = "Support for gender-affirming care ban by generation and gender", | |
| caption = "@ryanburge\nData: Cooperative Election Study, 2023 & 2025") | |
| save("trans_gen_gender_bars.png", wd = 6, ht = 6) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment