1. Download Legewie_ESS_02.dta from Absalon, then copy in this starter code. It defines the attack window and prepares the data — keeping only Portuguese mainstream respondents:
pacman::p_load(tidyverse, haven, estimatr, modelsummary)
ESS_raw <- read_dta("Legewie_ESS_02.dta") # from your project folder
# Bali terror attack, and the ESS fieldwork window around it
event_date_begin <- as.Date("2002-09-14")
event_date       <- as.Date("2002-10-13")
event_date_end   <- as.Date("2002-10-20")

ESS <- ESS_raw %>%
  mutate(
    # Day of the survey interview
    int_date = sprintf("%s-%s-%s", inwyr, inwmm, inwdd) %>% as.Date(),
    # Treatment indicator
    treat = case_when(
      int_date > event_date & int_date <= event_date_end ~ "After the attack",
      int_date < event_date & int_date > event_date_begin ~ "Before the attack",
      TRUE ~ as.character(NA)) %>% fct_relevel("Before the attack", "After the attack"),
    # Attitudes towards immigrants: the average of 7 items, z-standardised
    anti_immi = rowMeans(select(., imtcjob, imbleco, imbgeco, imueclt,
                                imwbcnt, imwbcrm, imbghct), na.rm = TRUE) %>%
      scale() %>% as.numeric(),
    # Subtract from max to make larger values more xenophobic
    anti_immi = max(anti_immi, na.rm = TRUE) - anti_immi,
    # Immigrant origin
    brncntr = as_factor(brncntr),
    mocntr = as_factor(mocntr),
    facntr = as_factor(facntr),
    # Weights
    pspwght = pweight * dweight) %>%
  # Sample restriction: mainstream members (no immigrant origin) in Portugal
  filter(str_detect(cntry, "PT") &
           brncntr == "yes" & mocntr == "yes" & facntr == "yes") %>%
  # Keep only the variables we need
  select(treat, anti_immi, cntry, pspwght) %>%
  drop_na() # Delete cases with missing values

Stuck?

  1. Estimate the treatment effect with a weighted OLS of anti_immi on treat. Among the Portuguese, the average causal effect of the Bali attack is standard deviations (two digits; point or comma is fine).
ols_pt <- lm_robust(anti_immi ~ treat, data = ESS, weights = pspwght)
modelsummary(list("Xenophobia (PT)" = ols_pt), stars = TRUE,
             coef_rename = c("treatAfter the attack" = "After the attack"),
             gof_map = c("nobs", "r.squared"))
Xenophobia (PT)
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) 3.621***
(0.082)
After the attack 0.330**
(0.116)
Num.Obs. 291
R2 0.032
  1. Its standard error is — less than half the coefficient, so the effect is statistically significant (the t-value clears the usual threshold of ).

  2. Make a coefficient plot of the Portuguese effect (drop the intercept).

plotdata_pt <- ols_pt %>% tidy() %>%
  filter(term != "(Intercept)") %>%
  mutate(term = "After the attack", cntry = "Portugal")

ggplot(plotdata_pt, aes(y = estimate, x = cntry,
                        ymin = conf.low, ymax = conf.high)) +
  geom_hline(yintercept = 0, color = "#901A1E", lty = "dashed") +
  geom_pointrange() + coord_flip() +
  labs(x = "", y = "Change in xenophobia after the attack (SD)") +
  theme_minimal()

  1. Now add Sweden: change the sample restriction to "PT|SE", then estimate the effect for Sweden only (data = ESS2 %>% filter(cntry == "SE")). The Swedish effect is , and it is .
ESS2 <- ESS_raw %>%
  mutate(
    # Day of the survey interview
    int_date = sprintf("%s-%s-%s", inwyr, inwmm, inwdd) %>% as.Date(),
    # Treatment indicator
    treat = case_when(
      int_date > event_date & int_date <= event_date_end ~ "After the attack",
      int_date < event_date & int_date > event_date_begin ~ "Before the attack",
      TRUE ~ as.character(NA)) %>% fct_relevel("Before the attack", "After the attack"),
    # Attitudes towards immigrants: the average of 7 items, z-standardised
    anti_immi = rowMeans(select(., imtcjob, imbleco, imbgeco, imueclt,
                                imwbcnt, imwbcrm, imbghct), na.rm = TRUE) %>%
      scale() %>% as.numeric(),
    # Subtract from max to make larger values more xenophobic
    anti_immi = max(anti_immi, na.rm = TRUE) - anti_immi,
    # Immigrant origin
    brncntr = as_factor(brncntr),
    mocntr = as_factor(mocntr),
    facntr = as_factor(facntr),
    # Weights
    pspwght = pweight * dweight) %>%
  # Sample restriction: mainstream members (no immigrant origin) in Portugal and Sweden
  filter(str_detect(cntry, "PT|SE") &
           brncntr == "yes" & mocntr == "yes" & facntr == "yes") %>%
  # Keep only the variables we need
  select(treat, anti_immi, cntry, pspwght) %>%
  drop_na() # Delete cases with missing values
ols_se <- lm_robust(anti_immi ~ treat, data = ESS2 %>% filter(cntry == "SE"),
                    weights = pspwght)
modelsummary(list("Xenophobia (SE)" = ols_se), stars = TRUE,
             coef_rename = c("treatAfter the attack" = "After the attack"),
             gof_map = c("nobs", "r.squared"))
Xenophobia (SE)
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) 3.096***
(0.080)
After the attack 0.011
(0.103)
Num.Obs. 328
R2 0.000
  1. Write one sentence (as a # comment) comparing the two countries, then check:

The Bali attack raised xenophobia by about 0.33 SD in Portugal (significant) but only 0.01 SD in Sweden (not significant) — the same shock produced different reactions in different societies. Look at you: this is exactly the kind of natural-experiment analysis published in the American Journal of Sociology.


Bonus (for the fast): put Portugal and Sweden side by side in one modelsummary() table, as in earlier lectures. Where exactly do the two countries differ?

modelsummary(list("Portugal" = ols_pt, "Sweden" = ols_se), stars = TRUE,
             coef_rename = c("treatAfter the attack" = "After the attack"),
             gof_map = c("nobs", "r.squared"))
Portugal Sweden
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) 3.621*** 3.096***
(0.082) (0.080)
After the attack 0.330** 0.011
(0.116) (0.103)
Num.Obs. 291 328
R2 0.032 0.000

The attack coefficient is about 0.33 SD and significant in Portugal, but only 0.01 SD and not significant in Sweden. Next week you will learn to show two models in one coefficient plot.