Skip to content

Instantly share code, notes, and snippets.

@ryanburge
Created May 16, 2025 21:14
Show Gist options
  • Select an option

  • Save ryanburge/365e9afc71d0c2f3ef4009ca22a78ae9 to your computer and use it in GitHub Desktop.

Select an option

Save ryanburge/365e9afc71d0c2f3ef4009ca22a78ae9 to your computer and use it in GitHub Desktop.
gg1 <- cces %>%
filter(year > 2007) %>%
cces_trad(religion) %>%
mutate(rep = case_when(pid7 == 5 | pid7 == 6 | pid7 == 7 ~ 1,
pid7 <= 4 ~ 0)) %>%
mutate(gender = frcode(gender == 1 ~ "Men",
gender == 2 ~ "Women")) %>%
group_by(year, gender, trad2) %>%
mean_ci(rep, wt = weight, ci = .84) %>% filter(gender != "NA")
gg1 %>%
ggplot(., aes(x = year, y = mean, color = gender, group = gender)) +
geom_point(stroke = .25, shape = 21, alpha = .5) +
geom_smooth(se = FALSE) +
facet_wrap(~ trad2) +
theme_rb(legend = TRUE) +
y_pct() +
scale_color_calc() +
theme(legend.text = element_text(size = 22)) +
labs(x = "", y = "", title = "Share Who Identify as Republican by Religious Tradition and Gender", caption = "@ryanburge\nData: Cooperative Election Study, 2008-2024")
save("gender_gap_all24.png", ht = 10)
gg1 <- cces %>%
cces_trad(religion) %>%
filter(year >= 2022) %>%
mutate(age4 = frcode(age <= 35 ~ "18-35",
age >= 36 & age <= 50 ~ "36-50",
age >= 51 & age <= 64 ~ "51-64",
age >= 65 ~ "65+")) %>%
mutate(rep = case_when(pid7 == 5 | pid7 == 6 | pid7 == 7 ~ 1,
pid7 <= 4 ~ 0)) %>%
mutate(gender = frcode(gender == 1 ~ "Men",
gender == 2 ~ "Women")) %>%
filter(gender != "NA") %>%
group_by(trad2, gender, age4) %>%
mean_ci(rep, wt = weight, ci = .84)
gg1 %>%
ggplot(., aes(x = age4, y = mean, fill = gender)) +
geom_col(color = "black", position = "dodge") +
facet_wrap(~ trad2) +
theme_rb(legend = TRUE) +
error_bar() +
scale_y_continuous(labels = percent) +
scale_fill_manual(
values = c(
"Men" = "#9ecae1", # Light Blue
"Women" = "#fdd0a2" # Light Orange
),
name = NULL
) +
labs(x = "", y = "", title = "Share Who Identify as Republican by Age, Gender, and Religious Tradition", caption = "@ryanburge\nData: Cooperative Election Study, 2022-2024")
save("gender_gap_by_age24.png", ht = 12)
####
process_evangelical_gender <- function(data, year, vote_var, weight_var, vote_map,
gender_filter = NULL, extra_filters = NULL, label = NULL) {
df <- data %>%
filter(evangelical == 1)
if (!is.null(gender_filter)) {
df <- df %>% filter(!!rlang::parse_expr(gender_filter))
}
if (!is.null(extra_filters)) {
df <- df %>% filter(!!rlang::parse_expr(extra_filters))
}
df %>%
mutate(vote = .data[[vote_var]]) %>%
mutate(vote = frcode(!!!vote_map)) %>%
ct(vote, wt = .data[[weight_var]], show_na = FALSE) %>%
mutate(year = year, type = label)
}
cces08 <- cces08 %>% mutate(gender = V208)
male_evang <- bind_rows(
process_evangelical_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 1", label = "Male"),
process_evangelical_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 1", label = "Male"),
process_evangelical_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 1", extra_filters = 'CL_matched == "Y"', label = "Male"),
process_evangelical_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 1", extra_filters = 'CL_voter_status <= 5', label = "Male"),
process_evangelical_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 1", label = "Male")
)
female_evang <- bind_rows(
process_evangelical_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 2", label = "Female"),
process_evangelical_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 2", label = "Female"),
process_evangelical_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 2", extra_filters = 'CL_matched == "Y"', label = "Female"),
process_evangelical_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 2", extra_filters = 'CL_voter_status <= 5', label = "Female"),
process_evangelical_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 2", label = "Female")
)
evangelical_gender <- bind_rows(male_evang, female_evang) %>%
mutate(year = as.factor(year),
type = factor(type, levels = c("Male", "Female")))
rep <- evangelical_gender %>% filter(vote == "Republican")
process_catholic_gender <- function(data, year, vote_var, weight_var, vote_map,
gender_filter = NULL, extra_filters = NULL, label = NULL) {
df <- data %>%
filter(catholic == 1)
if (!is.null(gender_filter)) {
df <- df %>% filter(!!rlang::parse_expr(gender_filter))
}
if (!is.null(extra_filters)) {
df <- df %>% filter(!!rlang::parse_expr(extra_filters))
}
df %>%
mutate(vote = .data[[vote_var]]) %>%
mutate(vote = frcode(!!!vote_map)) %>%
ct(vote, wt = .data[[weight_var]], show_na = FALSE) %>%
mutate(year = year, type = label)
}
catholic_male <- bind_rows(
process_catholic_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 1", label = "Male"),
process_catholic_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 1", label = "Male"),
process_catholic_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 1", extra_filters = 'CL_matched == "Y"', label = "Male"),
process_catholic_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 1", extra_filters = 'CL_voter_status <= 5', label = "Male"),
process_catholic_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 1", label = "Male")
)
catholic_female <- bind_rows(
process_catholic_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 2", label = "Female"),
process_catholic_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 2", label = "Female"),
process_catholic_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 2", extra_filters = 'CL_matched == "Y"', label = "Female"),
process_catholic_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 2", extra_filters = 'CL_voter_status <= 5', label = "Female"),
process_catholic_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 2", label = "Female")
)
catholic_gender <- bind_rows(catholic_male, catholic_female) %>%
mutate(year = as.factor(year),
type = factor(type, levels = c("Male", "Female")))
process_aa_gender <- function(data, year, vote_var, weight_var, vote_map,
gender_filter = NULL, extra_filters = NULL, label = NULL) {
df <- data %>%
filter(religpew == 9 | religpew == 10)
if (!is.null(gender_filter)) {
df <- df %>% filter(!!rlang::parse_expr(gender_filter))
}
if (!is.null(extra_filters)) {
df <- df %>% filter(!!rlang::parse_expr(extra_filters))
}
df %>%
mutate(vote = .data[[vote_var]]) %>%
mutate(vote = frcode(!!!vote_map)) %>%
ct(vote, wt = .data[[weight_var]], show_na = FALSE) %>%
mutate(year = year, type = label)
}
aa_male <- bind_rows(
process_aa_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 1", label = "Male"),
process_aa_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 1", label = "Male"),
process_aa_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 1", extra_filters = 'CL_matched == "Y"', label = "Male"),
process_aa_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 1", extra_filters = 'CL_voter_status <= 5', label = "Male"),
process_aa_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 1", label = "Male")
)
aa_female <- bind_rows(
process_aa_gender(cces08, 2008, "CC410", "V201", vote_codes$`2008`,
gender_filter = "gender == 2", label = "Female"),
process_aa_gender(cces12, 2012, "CC410a", "weight_vv_post", vote_codes$`2012`,
gender_filter = "gender == 2", label = "Female"),
process_aa_gender(cces16, 2016, "CC16_410a", "commonweight_vv_post", vote_codes$`2016`,
gender_filter = "gender == 2", extra_filters = 'CL_matched == "Y"', label = "Female"),
process_aa_gender(cces20, 2020, "CC20_410", "vvweight_post", vote_codes$`2020`,
gender_filter = "gender == 2", extra_filters = 'CL_voter_status <= 5', label = "Female"),
process_aa_gender(cces24, 2024, "CC24_410", "commonpostweight", vote_codes$`2024`,
gender_filter = "gender == 2", label = "Female")
)
aa_gender <- bind_rows(aa_male, aa_female) %>%
mutate(year = as.factor(year),
type = factor(type, levels = c("Male", "Female")))
evangelical_rep <- evangelical_gender %>% filter(vote == "Republican") %>%
mutate(group = "Evangelical")
catholic_rep <- catholic_gender %>% filter(vote == "Republican") %>%
mutate(group = "Catholic")
aa_rep <- aa_gender %>% filter(vote == "Republican") %>%
mutate(group = "Atheist/Agnostic")
rep_combined <- bind_rows(evangelical_rep, catholic_rep, aa_rep)
rep_combined %>%
ggplot(aes(x = type, y = pct, fill = type)) +
geom_col(color = "black") +
facet_grid(group ~ year) +
scale_fill_manual(values = c("Male" = "#9ecae1", "Female" = "#fdd0a2")) +
theme_rb() +
lab_bar(above = TRUE, pos = .075, sz = 8, type= pct) +
scale_y_continuous(labels = percent, limits = c(0, .9)) +
labs(
x = NULL, y = "",
fill = NULL,
title = "Republican Vote Share Among Christians, and Atheist/Agnostic by Gender (2008–2024)",
caption = "@ryanburge\nData: Cooperative Election Study, 2008-2024"
)
save("vote_share_cath_prot_aa.png", ht = 9)
library(dplyr)
library(broom)
library(ggplot2)
# 2016 model
cces16_mod <- cces16 %>%
mutate(
age = 2016 - birthyr,
vote_trump = case_when(CC16_410a == 1 ~ 1L, CC16_410a == 2 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)), # ref = Female
educ = as.numeric(educ),
income = as.numeric(faminc),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(!is.na(vote_trump), !is.na(gender), !is.na(educ), !is.na(income), !is.na(age), !is.na(white))
mod16 <- suppressWarnings(
glm(vote_trump ~ gender + age + educ + income + white,
data = cces16_mod,
weights = commonweight_vv_post,
family = binomial())
)
# 2020 model
cces20_mod <- cces20 %>%
mutate(
age = 2020 - birthyr,
vote_trump = case_when(CC20_410 == 2 ~ 1L, CC20_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
educ = as.numeric(educ),
income = as.numeric(faminc_new),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(!is.na(vote_trump), !is.na(gender), !is.na(educ), !is.na(income), !is.na(age), !is.na(white))
mod20 <- suppressWarnings(
glm(vote_trump ~ gender + age + educ + income + white,
data = cces20_mod,
weights = vvweight_post,
family = binomial())
)
# 2024 model
cces24_mod <- cces24 %>%
mutate(
age = 2024 - birthyr,
vote_trump = case_when(CC24_410 == 2 ~ 1L, CC24_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
educ = as.numeric(educ),
income = as.numeric(faminc_new),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(!is.na(vote_trump), !is.na(gender), !is.na(educ), !is.na(income), !is.na(age), !is.na(white))
mod24 <- suppressWarnings(
glm(vote_trump ~ gender + age + educ + income + white,
data = cces24_mod,
weights = commonpostweight,
family = binomial())
)
# Extract odds ratios for gender effect
results <- bind_rows(
tidy(mod16, exponentiate = TRUE, conf.int = TRUE) %>%
filter(term == "gender1") %>%
mutate(year = 2016),
tidy(mod20, exponentiate = TRUE, conf.int = TRUE) %>%
filter(term == "gender1") %>%
mutate(year = 2020),
tidy(mod24, exponentiate = TRUE, conf.int = TRUE) %>%
filter(term == "gender1") %>%
mutate(year = 2024)
)
results <- results %>%
mutate(label = paste0("+", round((estimate - 1) * 100), "%"))
# Plot
ggplot(results, aes(x = factor(year), y = estimate)) +
geom_point(size = 3, color = "#08519c") +
geom_errorbar(aes(ymin = conf.low, ymax = conf.high), width = 0.2) +
geom_hline(yintercept = 1, linetype = "dashed", color = "gray50") +
geom_text(
aes(label = label),
vjust = -3.0,
size = 9, family = "font"
) +
labs(
title = "Odds Ratio of Voting for Trump (Men vs. Women)",
subtitle = "Controlling for Age, Education, Income, and Race",
x = "Year",
y = "Odds Ratio (Men Compared to Women)",
caption = "@ryanburge\nData: Cooperative Election Study, 2016-2024"
) +
scale_y_continuous(limits = c(1, max(results$conf.high) + 0.1)) +
theme_rb()
save("odds_ratio_gender_trump_race24.png")
# Protestant - 2016
mod16_prot <- cces16 %>%
filter(evangelical == 1) %>%
mutate(
age = 2016 - birthyr,
vote_trump = case_when(CC16_410a == 1 ~ 1L, CC16_410a == 2 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonweight_vv_post, family = binomial())
# Catholic - 2016
mod16_cath <- cces16 %>%
filter(religpew == 2) %>%
mutate(
age = 2016 - birthyr,
vote_trump = case_when(CC16_410a == 1 ~ 1L, CC16_410a == 2 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonweight_vv_post, family = binomial())
# Atheist/Agnostic - 2016
mod16_aa <- cces16 %>%
filter(religpew %in% c(9, 10)) %>%
mutate(
age = 2016 - birthyr,
vote_trump = case_when(CC16_410a == 1 ~ 1L, CC16_410a == 2 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonweight_vv_post, family = binomial())
# Protestant - 2020
mod20_prot <- cces20 %>%
filter(evangelical == 1) %>%
mutate(
age = 2020 - birthyr,
vote_trump = case_when(CC20_410 == 2 ~ 1L, CC20_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = vvweight_post, family = binomial())
# Catholic - 2020
mod20_cath <- cces20 %>%
filter(religpew == 2) %>%
mutate(
age = 2020 - birthyr,
vote_trump = case_when(CC20_410 == 2 ~ 1L, CC20_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = vvweight_post, family = binomial())
# Atheist/Agnostic - 2020
mod20_aa <- cces20 %>%
filter(religpew %in% c(9, 10)) %>%
mutate(
age = 2020 - birthyr,
vote_trump = case_when(CC20_410 == 2 ~ 1L, CC20_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = vvweight_post, family = binomial())
# Protestant - 2024
mod24_prot <- cces24 %>%
filter(evangelical == 1) %>%
mutate(
age = 2024 - birthyr,
vote_trump = case_when(CC24_410 == 2 ~ 1L, CC24_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonpostweight, family = binomial())
# Catholic - 2024
mod24_cath <- cces24 %>%
filter(religpew == 2) %>%
mutate(
age = 2024 - birthyr,
vote_trump = case_when(CC24_410 == 2 ~ 1L, CC24_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonpostweight, family = binomial())
# Atheist/Agnostic - 2024
mod24_aa <- cces24 %>%
filter(religpew %in% c(9, 10)) %>%
mutate(
age = 2024 - birthyr,
vote_trump = case_when(CC24_410 == 2 ~ 1L, CC24_410 == 1 ~ 0L, TRUE ~ NA_integer_),
gender = factor(gender, levels = c(2, 1)),
income = as.numeric(faminc_new),
educ = as.numeric(educ),
white = if_else(race == 1, 1L, 0L)
) %>%
filter(complete.cases(vote_trump, gender, age, income, educ, white)) %>%
glm(vote_trump ~ gender + age + educ + income + white,
data = ., weights = commonpostweight, family = binomial())
results <- bind_rows(
tidy(mod16_prot, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2016, group = "Evangelical"),
tidy(mod16_cath, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2016, group = "Catholic"),
tidy(mod16_aa, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2016, group = "Atheist/Agnostic"),
tidy(mod20_prot, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2020, group = "Evangelical"),
tidy(mod20_cath, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2020, group = "Catholic"),
tidy(mod20_aa, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2020, group = "Atheist/Agnostic"),
tidy(mod24_prot, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2024, group = "Evangelical"),
tidy(mod24_cath, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2024, group = "Catholic"),
tidy(mod24_aa, exponentiate = TRUE, conf.int = TRUE) %>% filter(term == "gender1") %>% mutate(year = 2024, group = "Atheist/Agnostic")
) %>%
mutate(label = paste0("+", round((estimate - 1) * 100), "%"))
library(ggplot2)
results <- results %>%
mutate(group = factor(group, levels = c("Evangelical", "Catholic", "Atheist/Agnostic")))
ggplot(results, aes(x = factor(year), y = estimate, group = group, color = group)) +
geom_errorbar(aes(ymin = conf.low, ymax = conf.high), width = 0.2) +
geom_point(size = 3, stroke = 1, shape = 21, fill = "white") +
geom_text(
aes(y = max(results$conf.high) + 0.15, label = label), # fixed Y position
size = 7, family = "font", color = "black"
) +
scale_color_manual(values = c(
"Evangelical" = "#1f78b4", # Blue
"Catholic" = "#33a02c", # Green
"Atheist/Agnostic" = "#e31a1c" # Red
)) +
facet_wrap(~ group) +
geom_hline(yintercept = 1, linetype = "dashed", color = "gray50") +
scale_y_continuous(limits = c(1, max(results$conf.high) + 0.2)) +
labs(
title = "Odds Ratio of Voting for Trump (Men vs. Women)",
subtitle = "Controlling for Age, Education, Income, and Race",
x = "Year",
y = "Odds Ratio (Men Compared to Women)",
caption = "@ryanburge\nData: Cooperative Election Study, 2016–2024"
) +
theme_rb()
save("odds_ratio_gender_gap24_rel_groups.png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment