Back to xenophobia and the ESS. Today you ask whether the effect of age on xenophobia is straight-line — or a curve.
pacman::p_load(tidyverse, haven, estimatr, modelsummary)
ESS <- read_spss("ESS9e03_1.sav") # from your project folder
ESS <- ESS %>%
filter(cntry == "DK" | cntry == "BG") %>% # DK = Denmark, BG = Bulgaria
mutate(
cntry = as_factor(cntry) %>% fct_relevel("Denmark"),
gndr = as_factor(gndr),
eduyrs = case_when(eduyrs < 9 ~ 9, eduyrs > 21 ~ 21, TRUE ~ zap_labels(eduyrs)),
agea = zap_labels(agea),
imwbcnt = 10 - zap_labels(imwbcnt) # reverse: higher = more xenophobic
) %>%
select(idno, cntry, pspwght, gndr, eduyrs, agea, imwbcnt) %>%
drop_na()
pacman::p_load(...) run without red errors? Fix
that first.read_spss() can't find the file?
ESS9e03_1.sav must sit in your project
folder.Predict before you compute. Do you expect the effect of age on xenophobia to be a straight line, or to bend (rise faster at some ages than others)? There is no wrong guess — commit to one, then test it.
Fit a quadratic of age. Use
poly(I(agea / 10), 2, raw = TRUE) so the coefficients are
per decade, and keep country, gender and education as controls,
weighted by pspwght.
lm_robust(imwbcnt ~ cntry + gndr + eduyrs + poly(I(agea / 10), 2, raw = TRUE), data = ESS, weights = pspwght)
ols_poly <- lm_robust(
imwbcnt ~ cntry + gndr + eduyrs + poly(I(agea / 10), 2, raw = TRUE),
data = ESS, weights = pspwght
)
modelsummary(list("Xenophobia" = ols_poly),
coef_rename = c("poly(I(agea/10), 2, raw = TRUE)1" = "Age (per decade)",
"poly(I(agea/10), 2, raw = TRUE)2" = "Age² (per decade)"),
stars = TRUE, gof_map = c("nobs", "r.squared"), output = "kableExtra")
The linear term is and the squared term is . Because the squared term is negative, the age slope .
b1 <- coef(ols_poly)["poly(I(agea/10), 2, raw = TRUE)1"]
b2 <- coef(ols_poly)["poly(I(agea/10), 2, raw = TRUE)2"]
(-b1 / (2 * b2)) * 10 # turning point, in years
That turning point is far outside the ages anyone in the data actually has (15–90). So within the real range, xenophobia .
Write one sentence as a # comment
interpreting what the quadratic told you — then compare:
Xenophobia rises with age across the whole observed range, but the negative squared term means the rise decelerates — each additional decade adds a little less than the one before. The curve would technically turn around near age 180, which is meaningless for humans, so in practice this is a gently flattening increase, not a hump. A straight line would have been a defensible simplification here; the curvature is real but mild.
ggplot(ESS, aes(x = agea, y = imwbcnt)) +
geom_jitter(alpha = 0.06, height = 0.2) +
geom_smooth(method = "lm", formula = y ~ x, se = FALSE, aes(colour = "Straight line")) +
geom_smooth(method = "lm", formula = y ~ poly(x, 2, raw = TRUE), se = FALSE,
aes(colour = "Quadratic")) +
scale_colour_manual(values = c("Straight line" = "#425570", "Quadratic" = "#901A1E"),
name = NULL) +
labs(x = "Age", y = "Xenophobia (0–10)") +
theme_minimal()
The two lines barely diverge — visual confirmation that the curvature is slight.
Discuss with your neighbour.