Preregistration

Hypotheses

  • H1: Class-based zero-sum beliefs positively predict cross-race working-class solidarity.
  • H2: Class-based zero-sum beliefs positively predict support for redistributive economic policy.
  • H3: The relationship between class-based zero-sum beliefs and support for redistributive economic policy is at least partially explained by cross-race working-class solidarity.
  • H4: The effect of class-based zero-sum beliefs on cross-race solidarity will be moderated by race-based zero-sum beliefs such that the positive relationship will hold only for those who are low on race-based zero-sum beliefs.

Analysis plan

  • Mediation model: Class-based zero-sum beliefs as the predictor; support for redistributive policy as the outcome; cross-race class-solidarity as the mediator.
  • A-path linear model: Class-based zero-sum beliefs as the predictor; cross-race class-solidarity as the outcome variable; race-based zero-sum beliefs as the moderator.

Eligibility

total_n = nrow(df_cbzs)

Total N = 501

Preregistered exclusions:

  • check_1: select somewhat disagree
  • check_2: bot-check
elg_n = nrow(df_cbzs_elg)

df_cbzs %>% 
  group_by(att_1,att_2) %>% 
  summarise(N = n()) %>% 
  ungroup() %>% 
  mutate(Perc = round(100*(N/sum(N)),2)) %>% 
  ungroup() %>% 
  arrange(desc(Perc)) %>% 
  kbl() %>% 
  kable_styling(bootstrap_options = "hover",
                full_width = F,
                position = "left")
## `summarise()` has grouped output by 'att_1'. You can override using the
## `.groups` argument.
att_1 att_2 N Perc
1 1 488 97.41
1 0 11 2.20
0 1 2 0.40

Eligible N = 488

Demographics

Gender

df_cbzs_elg %>% 
  mutate(gender = ifelse(is.na(gender) | gender == "","other",gender)) %>% 
  group_by(gender) %>% 
  summarise(N = n()) %>% 
  ungroup() %>% 
  mutate(Perc = round(100*(N/sum(N)),2)) %>% 
  ungroup() %>% 
  arrange(desc(Perc)) %>% 
  kbl() %>% 
  kable_styling(bootstrap_options = "hover",
                full_width = F,
                position = "left")
gender N Perc
woman 276 56.56
man 209 42.83
other 3 0.61

Race

race N Perc
White 465 95.29
multiracial 23 4.71

Age

Mean age: 43.6.

Income

median_income_num <- df_cbzs_elg %>% 
  mutate(income_num = as.numeric(income)) %>% 
  summarise(median = median(income_num, na.rm = TRUE)) %>% 
  pull(median)

df_cbzs_elg %>% 
  ggplot(aes(x = income)) +
  geom_bar() +
  geom_vline(xintercept = median_income_num, 
             color = "lightblue", linetype = "dashed") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black", face = "bold"),
        axis.title.x = element_blank(),
        axis.title.y = element_blank()) +
  coord_flip()

Education

edu N Perc
noHS 2 0.41
GED 138 28.28
2yearColl 58 11.89
4yearColl 187 38.32
MA 74 15.16
PHD 27 5.53
NA 2 0.41

SES

ses N Perc
Lower Class 51 10.45
Lower Middle Class 156 31.97
Middle Class 214 43.85
Upper Middle Class 63 12.91
Upper Class 4 0.82

Working-class identification

To what extent do you identify as working-class? (0 = Not at All Working-Class; 50 = Moderately Working-Class; 100 = Very Strongly Working-Class)

df_cbzs_elg %>% 
  ggplot(aes(x = class_id)) +
  geom_histogram(fill = "lightblue",
                 binwidth = 5,
                 color = NA) +
  scale_x_continuous(breaks = seq(0,100,10),
                     limits = c(-10,110)) +
  ylab("count") +
  geom_vline(xintercept = mean(df_cbzs_elg$class_id,na.rm = T),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold"),
        axis.title.x = element_text(color = "black",
                                   face = "bold"))

Politics

Political ideology

Participants were asked about the extent to which they subscribe to the following ideologies on a scale of 1-7 (select NA if unfamiliar): Conservatism, Liberalism, Democratic Socialism, Libertarianism, Progressivism.

means <- df_cbzs_elg %>%
  dplyr::select(PID,ideo_con:ideo_prog) %>% 
  pivot_longer(-PID,
               names_to = "ideo",
               values_to = "score") %>% 
  filter(!is.na(score)) %>% 
  group_by(ideo) %>% 
  summarise(score = mean(score)) %>% 
  ungroup()

df_cbzs_elg %>%
  dplyr::select(PID,ideo_con:ideo_prog) %>% 
  pivot_longer(-PID,
               names_to = "ideo",
               values_to = "score") %>% 
  filter(!is.na(score)) %>%  
  ggplot() +
  geom_density(aes(x = score), fill = "lightblue",color = NA) +
  scale_x_continuous(limits = c(1,7),
                     breaks = seq(1,7,1)) +
  geom_vline(data = means,mapping = aes(xintercept = score),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold")) +
  facet_wrap(~ideo,nrow = 2)

Party affiliation

party_id N Perc
Independent 169 34.63
Democrat 164 33.61
Republican 155 31.76

Measures

Class-based Zero-Sum Beliefs

  1. If the upper class becomes richer, this comes at the expense of the working class
  2. If the upper class makes more money, then the working class makes less money
  3. If the upper class does better economically, this does NOT come at the expense of the working class [R]

alpha = 0.92

df_cbzs_elg %>% 
  ggplot(aes(x = zs_class)) +
  geom_density(fill = "lightblue",
                 color = NA) +
  scale_x_continuous(breaks = seq(1,7,1),
                     limits = c(1,7)) +
  ylab("density") +
  geom_vline(xintercept = mean(df_cbzs_elg$zs_class,na.rm = T),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold"),
        axis.title.x = element_text(color = "black",
                                   face = "bold"))

Race-based Zero-Sum Beliefs

  1. If non-white become richer, this comes at the expense of white people
  2. If non-white people make more money, then white people make less money
  3. If non-white people do better economically, this does NOT come at the expense of white people [R]

alpha = 0.82

df_cbzs_elg %>% 
  ggplot(aes(x = zs_race)) +
  geom_density(fill = "lightblue",
                 color = NA) +
  scale_x_continuous(breaks = seq(1,7,1),
                     limits = c(1,7)) +
  ylab("density") +
  geom_vline(xintercept = mean(df_cbzs_elg$zs_race,na.rm = T),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold"),
        axis.title.x = element_text(color = "black",
                                   face = "bold"))

Class Solidarity

  1. I feel a sense of solidarity with non-White (e.g., Black, Asian, Hispanic) working class people.
  2. The problems facing White and non-White working class people are similar enough for us to be allies.
  3. What happens to non-White working class communities has something to do with what happens to White working class people.

alpha = 0.61

df_cbzs_elg %>% 
  ggplot(aes(x = crs)) +
  geom_density(fill = "lightblue",
                 color = NA) +
  scale_x_continuous(breaks = seq(1,7,1),
                     limits = c(1,7)) +
  ylab("density") +
  geom_vline(xintercept = mean(df_cbzs_elg$crs,na.rm = T),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold"),
        axis.title.x = element_text(color = "black",
                                   face = "bold"))

Support for redistributive policy

Participants saw one of the following policies and indicated their support for it (1 = Strongly Oppose to 7 = Strongly Support)

Minimum wage increase

Congress has not increased the federal minimum wage, currently set at $7.25, since 2009. Some Congresspeople are proposing a policy that would gradually raise the federal minimum wage to $20 an hour by 2028. After 2028, the minimum wage would be adjusted each year to keep pace with growth in the median wage, a measure of wages for typical workers.

Student debt relief

Some Congresspeople are proposing a policy that would help to address the student loan debt crisis by forgiving up to $50,000 in loans per borrower. Approximately 42 million Americans, or about 1 in 6 American adults, owe a cumulative $1.6 trillion in student loans. Student loans are now the second-largest slice of household debt after mortgages, bigger than credit card debt.

Housing

Some Congresspeople are proposing a housing affordability policy that would help ensure that every American has a place to live. The policy would allow for smaller, lower cost homes like duplexes, townhouses, and garden apartments to be built and developed, allowing new nonprofit homes and reducing overall housing prices.

Climate change

Some Congresspeople are proposing a Green New Deal bill which would phase out the use of fossil fuels, with the government providing clean energy jobs for people who can’t find employment in the private sector. All jobs would pay at least $20 an hour, and include healthcare benefits and collective bargaining rights.

df_cbzs_elg %>% 
  ggplot(aes(x = support)) +
  geom_histogram(fill = "lightblue",
                 binwidth = 1,
                 color = NA) +
  scale_x_continuous(breaks = seq(1,7,1),
                     limits = c(0,8)) +
  ylab("count") +
  geom_vline(xintercept = mean(df_cbzs_elg$support,na.rm = T),
             color = "black",
             linetype = "dashed",
             size = 1.1) +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.ticks = element_blank(),
        axis.line = element_line(color = "grey66"),
        axis.text.y = element_text(color = "black"),
        axis.text.x = element_text(color = "black",
                                   face = "bold"),
        axis.title.x = element_text(color = "black",
                                   face = "bold"))

Correlations

Analysis Plan

Mediation Model

Predictor: Class ZSB

Mediator: Cross-race solidarity

Outcome: Support for policy

Bootstraps: 10,000

form.m <- reformulate("zs_class", response = "crs")

form.y <- reformulate(c("zs_class", "crs"), response = "support")

# Fit linear models
m.fit <- lm(form.m, data = df_cbzs_elg)        # a-path
y.fit <- lm(form.y, data = df_cbzs_elg)        # b and c'-paths

# Fit outcome model WITHOUT mediator to get c-path (total effect)
y.fit.total <- lm(
  reformulate("zs_class", response = "support"),
  data = df_cbzs_elg
)

# Mediation analysis with bootstrapping (10,000 sims)
med.fit <- mediation::mediate(
  model.m   = m.fit,
  model.y   = y.fit,
  treat     = "zs_class",
  mediator  = "crs",
  boot      = TRUE,
  sims      = 10000
)

med_tbl <- tibble(
  Effect   = c("ACME (indirect)", "ADE (direct)",
               "Total Effect", "Prop. Mediated"),
  Estimate = c(med.fit$d0,        med.fit$z0,
               med.fit$tau.coef,  med.fit$n0),
  CI.lower = c(med.fit$d0.ci[1],  med.fit$z0.ci[1],
               med.fit$tau.ci[1], med.fit$n0.ci[1]),
  CI.upper = c(med.fit$d0.ci[2],  med.fit$z0.ci[2],
               med.fit$tau.ci[2], med.fit$n0.ci[2]),
  p.value  = c(med.fit$d0.p,      med.fit$z0.p,
               med.fit$tau.p,     med.fit$n0.p)
)

kbl(med_tbl) %>% 
  kable_styling(bootstrap_options = "hover",
                full_width = F,
                position = "left")
Effect Estimate CI.lower CI.upper p.value
ACME (indirect) 0.0742914 0.0407380 0.1146186 0
ADE (direct) 0.3027089 0.1931744 0.4167262 0
Total Effect 0.3770003 0.2696739 0.4859205 0
Prop. Mediated 0.1970594 0.1053765 0.3347072 0
# Helper to generate significance stars
p_stars <- function(p) {
  if (p < .001) return("***")
  if (p < .01)  return("**")
  if (p < .05)  return("*")
  return("")
}

# Extract coefficients & p-values
a_coef   <- coef(m.fit)["zs_class"]
a_p      <- summary(m.fit)$coefficients["zs_class", "Pr(>|t|)"]

b_coef   <- coef(y.fit)["crs"]
b_p      <- summary(y.fit)$coefficients["crs", "Pr(>|t|)"]

cprime   <- coef(y.fit)["zs_class"]
cprime_p <- summary(y.fit)$coefficients["zs_class", "Pr(>|t|)"]

c_total  <- coef(y.fit.total)["zs_class"]
c_total_p <- summary(y.fit.total)$coefficients["zs_class", "Pr(>|t|)"]

# Paste coefficient + stars
a_label      <- paste0("a = ", round(a_coef, 3), p_stars(a_p))
b_label      <- paste0("b = ", round(b_coef, 3), p_stars(b_p))
cprime_label <- paste0("c' = ", round(cprime, 3), p_stars(cprime_p))
c_label      <- paste0("c = ", round(c_total, 3), p_stars(c_total_p))

# Plot
ggplot() +
  xlim(0, 3) + ylim(0, 2) +

  # Nodes
  annotate("text", x = 0.5, y = 1,   label = "zs_class", fontface = "bold") +
  annotate("text", x = 1.5, y = 1,   label = "crs",     fontface = "bold") +
  annotate("text", x = 2.5, y = 1,   label = "support",  fontface = "bold") +

  # a-path (X → M)
  annotate("segment",
           x = 0.7, xend = 1.3, y = 1, yend = 1,
           arrow = arrow(length = unit(0.20, "cm"))) +
  annotate("text", x = 1.0, y = 1.15, label = a_label) +

  # b-path (M → Y)
  annotate("segment",
           x = 1.7, xend = 2.3, y = 1, yend = 1,
           arrow = arrow(length = unit(0.20, "cm"))) +
  annotate("text", x = 2.0, y = 1.15, label = b_label) +

  # c'-path (direct effect)
  annotate("segment",
           x = 0.5, xend = 2.5, y = 0.9, yend = 0.9,
           arrow = arrow(length = unit(0.20, "cm"))) +
  annotate("text", x = 1.5, y = 0.75, label = cprime_label) +

  # c-path (total effect, dashed)
  annotate("segment",
           x = 0.5, xend = 2.5, y = 1.1, yend = 1.1,
           linetype = "dashed",
           arrow = arrow(length = unit(0.20, "cm"))) +
  annotate("text", x = 1.5, y = 1.25, label = c_label) +
  theme_void()

Linear Model

Predictor: Class ZSB

Moderator: Race ZSB

Outcome: Cross-Race Solidarity

m1 <- lm(crs ~ zs_class*zs_race,data = df_cbzs_elg)

eta_table <- eta_squared(m1)
etas_for_table <- c(NA,eta_table$Eta2)
apa_lm <- apa_print(m1)
table_for_print <- apa_lm$table %>% 
  mutate(eta2 = round(etas_for_table,3))
 
kbl(table_for_print) %>% 
  kable_styling(bootstrap_options = "hover",
                full_width = F,
                position = "left")
term estimate conf.int statistic df p.value eta2
Intercept 4.77 [4.15, 5.39] 15.03 484 < .001 NA
Zs class 0.18 [0.06, 0.30] 3.03 484 .003 0.076
Zs race -0.24 [-0.50, 0.01] -1.90 484 .058 0.083
Zs class \(\times\) Zs race 0.00 [-0.05, 0.05] -0.04 484 .970 0.000
interact_plot(m1,
              pred = "zs_class",
              modx = "zs_race",
              interval = T)

Exploratory Analysis

Linear Model

Predictor: Class ZSB

Moderator: Race ZSB

Outcome: Support for policy

m1 <- lm(support ~ zs_class*zs_race,data = df_cbzs_elg)

eta_table <- eta_squared(m1)
etas_for_table <- c(NA,eta_table$Eta2)
apa_lm <- apa_print(m1)
table_for_print <- apa_lm$table %>% 
  mutate(eta2 = round(etas_for_table,3))
 
kbl(table_for_print) %>% 
  kable_styling(bootstrap_options = "hover",
                full_width = F,
                position = "left")
term estimate conf.int statistic df p.value eta2
Intercept 5.22 [4.20, 6.24] 10.10 484 < .001 NA
Zs class 0.18 [-0.01, 0.38] 1.85 484 .065 0.116
Zs race -0.77 [-1.18, -0.36] -3.67 484 < .001 0.054
Zs class \(\times\) Zs race 0.09 [0.01, 0.17] 2.23 484 .026 0.010
interact_plot(m1,
              pred = "zs_class",
              modx = "zs_race",
              interval = T)