1. Download the APAD dataset from Absalon into your project folder, then copy in this starter code — the same preparation as on the slides:
pacman::p_load(tidyverse, estimatr, modelsummary)
load("APAD.RData") # from your project folder
APAD <- APAD %>%
  mutate(
    news    = news_hrs * 60 + news_mins,
    news_yn = if_else(news >= 15, 1, 0)
  )

Stuck?

  1. The variable antidiscr_law asks how good it is for a country to have a law against ethnic discrimination at work: (1) Very bad … (5) Very good. Write it yourself: a weighted scatter plot with news on the x-axis and antidiscr_law on the y-axis, dot size = the weight gewFAKT, and a weighted regression line.

Map size = gewFAKT inside geom_point() and weight = gewFAKT inside geom_smooth(method = "lm"). Hide the size legend with theme(legend.position = "none").

ggplot(APAD, aes(y = antidiscr_law, x = news)) +
  geom_point(aes(size = gewFAKT), alpha = 1/3) +
  geom_smooth(aes(weight = gewFAKT), method = "lm") +
  scale_y_continuous(breaks = 1:5,
    labels = c("Very bad", "Bad", "So/so", "Good", "Very good")) +
  labs(y = "How good is a law against ethnic discrimination?",
       x = "Daily minutes of news consumption") +
  theme_minimal() + theme(legend.position = "none")

  1. A day has only 1,440 minutes, yet some respondents reported implausibly high news time. Cap everyone above 4 hours (240 min) at 240, then redraw. What does this cleaning step do?
APAD <- APAD %>% mutate(news = if_else(news > 240, 240, news))

ggplot(APAD, aes(y = antidiscr_law, x = news)) +
  geom_point(aes(size = gewFAKT), alpha = 1/3) +
  geom_smooth(aes(weight = gewFAKT), method = "lm") +
  scale_y_continuous(breaks = 1:5,
    labels = c("Very bad", "Bad", "So/so", "Good", "Very good")) +
  labs(y = "How good is a law against ethnic discrimination?",
       x = "Daily minutes of news consumption") +
  theme_minimal() + theme(legend.position = "none")

  1. Before you compute: using the binary news_yn, do you expect news readers to support the law more or less than non-readers — and strongly?

  2. Now run the weighted OLS antidiscr_law ~ news_yn and read off:

ols_law <- lm_robust(antidiscr_law ~ news_yn, weights = gewFAKT, data = APAD)

modelsummary(list("Anti-discr. law" = ols_law), stars = TRUE,
             coef_rename = c("news_yn" = "Reads news"),
             gof_map = c("nobs", "r.squared"))
Anti-discr. law
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) 4.159***
(0.125)
Reads news -0.101
(0.142)
Num.Obs. 1060
R2 0.001
  1. Write one sentence (as a # comment) interpreting the coefficient, then compare:

Among these 1093 respondents, news readers support an anti-discrimination law about 0.1 points less than non-readers, on average — but the difference is tiny and not statistically significant (SE 0.14 is larger than the coefficient). And, as in the lecture, this is a naïve, confounded comparison, not a causal effect.


Bonus (for the fast): news readers and non-readers differ in age. Add age to the model (antidiscr_law ~ news_yn + age). Does the news_yn coefficient change once age is held constant?

modelsummary(
  list("Naïve" = lm_robust(antidiscr_law ~ news_yn, weights = gewFAKT, data = APAD),
       "+ age" = lm_robust(antidiscr_law ~ news_yn + age, weights = gewFAKT, data = APAD)),
  stars = TRUE, coef_rename = c("news_yn" = "Reads news"),
  gof_map = c("nobs", "r.squared"))
Naïve + age
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) 4.159*** 4.044***
(0.125) (0.188)
Reads news -0.101 -0.122
(0.142) (0.142)
age 0.003
(0.004)
Num.Obs. 1060 1060
R2 0.001 0.003

Adjusting for one confounder (age) already nudges the coefficient — a first taste of the multiple regression to come. But age is only one observed difference; unobserved ones remain.