Multiple OLS in Practice

Confounders, mediators, and controls you can’t measure

Merlin Schaeffer · Department of Sociology

2026-07-23

By the end of today you can …

  1. tell a confounder from a mediator on a causal diagram — and know that you control the first but usually not the second;

  2. read a multiple regression coefficient as “holding the other variables constant” — and see how it does that (Frisch–Waugh);

  3. use a smart summary control to stand in for confounders you cannot measure.

One part per goal. Two running questions: the gender pay gap in Denmark, and — back to Lecture 2 — does state ownership of the economy reduce poverty?

Confounders vs. mediators

Part 1 of 2

Adding a variable to a regression can sharpen a comparison — or quietly ruin it. The difference is the arrow’s direction.

Two jobs for a control variable

Confounder \(C\) (red) — sits on a backdoor path: an arrow runs into the treatment \(D\) and into the outcome \(Y\).

\(\rightarrow\) Control it. Multiple OLS blocks the backdoor and makes the comparison fairer.

Mediator \(M\) (blue) — sits on the causal path \(D \rightarrow M \rightarrow Y\): an arrow runs out of the treatment.

\(\rightarrow\) Usually leave it in. Controlling a mediator removes part of the very effect you want.

Why the direction matters

To get closer to the overall causal effect \(D \rightarrow Y\), control observed confounders — but never control a mediator: you would throw away the part of the effect that runs through it.

The exception is deliberate: sometimes we want the partial effect of \(D\) that does not run through \(M\) — then controlling \(M\) is exactly the point. Know which question you are asking.

A clear case of mediation

Danish women earn less per month than Danish men. How much of that gap is because women more often work fewer contracted hours?

Work hours is a mediator: gender shapes hours, and hours shape pay.

pacman::p_load(tidyverse, haven, estimatr, modelsummary)

# ESS round 9, Denmark. Monthly gross wage, gender, contracted work hours.
ESS <- read_spss("../assets/ESS9e03_1.sav") %>%
  filter(cntry == "DK") %>%
  select(pspwght, gndr, wkhct, grspnum, infqbst) %>%
  mutate(
    across(c(pspwght, wkhct, grspnum), zap_labels),
    gndr   = as_factor(gndr),
    grwage = case_when(          # Put everyone on a monthly basis
      infqbst == 1 ~ 4 * grspnum,   # weekly  -> monthly
      infqbst == 3 ~ grspnum / 12,  # yearly  -> monthly
      TRUE         ~ grspnum        # already monthly
    )
  ) %>%
  filter(wkhct > 0) %>%
  drop_na()
mod1 <- lm_robust(grwage ~ gndr,         data = ESS, weights = pspwght)
mod2 <- lm_robust(grwage ~ gndr + wkhct, data = ESS, weights = pspwght)

modelsummary(
  list("Gross wage" = mod1, "Gross wage" = mod2),
  coef_rename = c("gndrFemale" = "Female", "wkhct" = "Weekly hours"),
  stars = TRUE, gof_map = c("nobs", "r.squared"),
  output = "kableExtra"
)
Gross wage Gross wage
(Intercept) 41997.795*** 6778.675
(4727.672) (5160.501)
Female −9492.532+ −6096.550
(5174.552) (5106.762)
Weekly hours 977.304***
(99.479)
Num.Obs. 778 778
R2 0.003 0.011
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

The raw gap of 9,493 kr./month shrinks to 6,097 kr. once we hold work hours constant: about 36% of the gender pay gap runs through fewer contracted hours. The rest does not — and controlling hours would hide it.

An unclear case — back to Lecture 2

In Lecture 2 we asked: do state-owned (socialist) economies reduce poverty? Descriptively — no. But they also offer fewer civil liberties, and civil liberties themselves predict less poverty.

Discuss: are civil liberties a confounder or a mediator of the state ownership → poverty relationship? Should we control for them?

The double-headed dashed arrow is the honest part: with cross-country data we cannot be sure whether fewer civil liberties cause state ownership or the reverse.

The revised research question

Would state-owned economies reduce poverty moreif they did not also curb their citizens’ civil liberties?

That is the partial effect of state ownership that does not run through civil liberties. Multiple OLS can estimate it.

Preparation

pacman::p_load(
  tidyverse,    # Data manipulation and visualization
  wbstats,      # World Bank data via API
  vdemdata,     # Varieties of Democracy (V-Dem) data
  estimatr,     # OLS with robust standard errors
  modelsummary  # Regression tables
)
# V-Dem: civil liberties (equality before the law and individual liberty) and state ownership
Dat_vdem <- vdem %>%
  as_tibble() %>%
  select(country_text_id, year,
         civ_liberties = v2xcl_rol,
         state_own_raw   = v2clstown) %>%
  mutate(state_ownership = -state_own_raw) # Reverse: higher = MORE state ownership

# World Bank: extreme poverty (< $3.00 a day). Live, with cached fallback.
Dat_poverty <- tryCatch(
  wb_data("SI.POV.DDAY", start_date = 1972, end_date = 2025),
  error = function(e) readRDS("data/wb_poverty_raw.rds")
) %>%
  rename(poverty = SI.POV.DDAY, year = date, country_text_id = iso3c) %>%
  select(country_text_id, year, country, poverty) %>%
  drop_na(poverty) %>%
  group_by(country) %>% filter(year == max(year)) %>% ungroup()

(Dat <- inner_join(Dat_poverty, Dat_vdem,
                   by = c("country_text_id", "year")) %>%
   drop_na(poverty, state_ownership, civ_liberties))
# # A tibble: 161 × 7
#    country_text_id  year country    poverty civ_liberties state_own_raw state_ownership
#    <chr>           <dbl> <chr>        <dbl>         <dbl>         <dbl>           <dbl>
#  1 ALB              2020 Albania        0.3         0.914         1.57           -1.57 
#  2 DZA              2011 Algeria        0           0.575        -1.91            1.91 
#  3 AGO              2018 Angola        39.3         0.559        -0.746           0.746
#  4 ARG              2024 Argentina      1           0.844         1.39           -1.39 
#  5 ARM              2024 Armenia        0.8         0.839         1.64           -1.64 
#  6 AUS              2020 Australia      0.9         0.949         1.44           -1.44 
#  7 AUT              2023 Austria        0.5         0.937         0.479          -0.479
#  8 AZE              2005 Azerbaijan     0           0.38         -0.617           0.617
#  9 BGD              2022 Bangladesh     5.9         0.338         0.371          -0.371
# 10 BRB              2016 Barbados       1.7         0.919         0.753          -0.753
# # ℹ 151 more rows

Multiple OLS: one control changes everything

# Bivariate: state ownership only
ols <- lm_robust(poverty ~ state_ownership, data = Dat)

# Multiple: add civil liberties
ols_mult <- lm_robust(
  poverty ~ state_ownership + civ_liberties,
  data = Dat
)

modelsummary(
  list("Poverty" = ols, "Poverty" = ols_mult),
  coef_rename = c("state_ownership" = "State ownership",
                  "civ_liberties" = "Civil liberties"),
  stars = TRUE, gof_map = c("nobs", "r.squared"),
  output = "kableExtra"
)
Poverty Poverty
(Intercept) 15.914*** 37.041***
(2.166) (6.020)
State ownership 2.742 −3.781+
(1.705) (2.058)
Civil liberties −37.012***
(8.501)
Num.Obs. 161 161
R2 0.017 0.124
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

On its own, state ownership looks harmless. Holding civil liberties constant, its sign flips — among countries with the same civil liberties, more state ownership goes with less poverty. Civil liberties were masking it.

What does “holding constant” actually do?

Frisch and Waugh (1933) showed that “controlling for” a variable is exactly a three-step residualisation — regression by subtraction.

Frisch–Waugh, step 1

Regress the outcome on the control, keep the residuals — the part of poverty that civil liberties do not explain.

Dat <- Dat %>%
  add_residuals(
    model = lm(poverty ~ civ_liberties, data = .),
    var   = "e_poverty"
  )

Frisch–Waugh, step 2

Regress the treatment on the control, keep the residuals — the part of state ownership that civil liberties do not explain.

Dat <- Dat %>%
  add_residuals(
    model = lm(state_ownership ~ civ_liberties, data = .),
    var   = "e_stateown"
  )

Frisch–Waugh, step 3

Regress the two sets of residuals on each other. Its slope is identical to the multiple-regression coefficient.

ols_resid <- lm_robust(e_poverty ~ e_stateown, data = Dat)

modelsummary(
  list("Multiple OLS" = ols_mult,
       "Residualised" = ols_resid),
  coef_rename = c("state_ownership" = "State ownership",
                  "civ_liberties" = "Civil liberties",
                  "e_stateown" = "State ownership (resid.)"),
  coef_omit = "(Intercept)",
  stars = TRUE, gof_map = c("nobs"),
  output = "kableExtra"
)
Multiple OLS Residualised
State ownership −3.781+
(2.058)
Civil liberties −37.012***
(8.501)
State ownership (resid.) −3.781+
(2.049)
Num.Obs. 161 161
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

Same number, two routes: the residualised slope on e_stateown equals the multiple model’s state_ownership coefficient. That is all controlling for civil liberties does.

Show the result

To predict, set the control to an informative constant (here: average civil liberties), then vary the treatment.

Break

Your turn: exercise 1

You practice multiple OLS on the carbon divide from Lecture 4: do regions differ in emissions beyond their wealth?

Open exercise 1 in a new tab ↗

Smart summary controls

Part 2 of 2

“To control for a wide range of factors seems daunting: the possibilities are virtually infinite, and many characteristics are hard to quantify.”

— Angrist and Pischke (2014, p. 51)

Football and domestic violence

Discuss: what confounds the effect of a team losing on domestic violence? (Team quality, the kind of fan, the kind of city …) How could a single variable stand in for all of them?

The point spread already prices in everything the market knows about both teams — one number that summarises a world of confounders (Card and Dahl, 2011).

Football and domestic violence

By comparing games with the same pre-game point spread, Card and Dahl (2011) isolate the emotional shock of an upset loss — a loss when your team was predicted to win.

An upset loss raises at-home male-on-female violence by about 10% — and it barely moves as more controls are added (columns 1→5). The summary control did the work.

Source: Card and Dahl (2011)

Do selective universities pay off?

Graduates of selective universities earn more. But the ambitious, well-connected, and able both choose selective universities and earn more anyway — a swarm of confounders (\(C\)).

Discuss: what single thing could summarise a student’s ambition and ability as the admissions system saw it?

The smart summary control: where you applied

Dale and Krueger (2002) compare students who applied to and were admitted to the same set of schools — then some went to the more selective one, some did not.

Sharing an application portfolio is a summary of ambition and ability as the system measured it: a stand-in for the confounders no survey captures.

Source: Dale and Krueger (2002)

The premium disappears

Without selection controls, each +100 points of school-average SAT buys about +7.6% earnings.

Among matched applicants, the effect collapses to −0.016 ≈ 0. The “selectivity premium” was mostly who chose selective schools, not the schools themselves.

Source: Dale and Krueger (2002)

No Harvard premium → no KU premium

If a selective US university barely raises earnings once you compare like with like, a selective Danish one probably doesn’t either.

\(\rightarrow\) Your KU degree pays off because you are able and put in the work — the diploma mostly reflects that, it does not manufacture it.

Break

Your turn: exercise 2

You visualise the adjustment: a before/after coefficient plot and model predictions — again on the carbon divide.

Open exercise 2 in a new tab ↗

Today’s general lessons

  1. A confounder opens a backdoor path (arrow into the treatment) — control it. A mediator lies on the causal path (arrow out of the treatment) — usually don’t, or you delete part of the effect.

  2. Adding a control can move a coefficient a lot — even flip its sign (state ownership & poverty). Always ask why a variable belongs in the model.

  3. Frisch–Waugh: “controlling for \(C\)” = residualise \(Y\) on \(C\), residualise \(D\) on \(C\), regress the residuals. Same number, clearer intuition.

  4. When confounders are unmeasurable, a smart summary control — a point spread, a shared application portfolio — can stand in for many of them at once.

  5. Multiple OLS improves comparisons; only a real experiment guarantees them.

Check yourself: today’s goals

  • Point to the confounder and the mediator on a DAG, and say which you would put in the regression — and why.
  • Explain, in the three Frisch–Waugh steps, what “holding civil liberties constant” does to the state ownership coefficient.
  • Give one example of a smart summary control and the confounders it stands in for.

Shaky on any of these? That is what this week’s Absalon quiz and the Friday exercise class are for.

Today’s important functions

  • lm_robust(y ~ d + c, ...): multiple OLS — the coefficient on d holds c constant.
  • modelr::add_residuals(model = ..., var = ...): keep a model’s residuals (the Frisch–Waugh workflow).
  • tidy() + geom_pointrange() + coord_flip(): the coefficient plot.
  • predict(model, newdata = ..., interval = "confidence"): predictions — set controls to an informative constant.
  • modelsummary(list(...)): put bivariate and multiple models side by side.

References

Angrist, J. D. and J. Pischke (2014). Mastering ’Metrics: The Path from Cause to Effect. Princeton University Press.

Card, D. and G. B. Dahl (2011). “Family Violence and Football: The Effect of Unexpected Emotional Cues on Violent Behavior*“. In: The Quarterly Journal of Economics, pp. 103-143.

Dale, S. B. and A. B. Krueger (2002). “Estimating the Payoff to Attending a More Selective College: An Application of Selection on Observables and Unobservables*“. In: The Quarterly Journal of Economics, pp. 1491-1527.

Frisch, R. and F. V. Waugh (1933). “Partial Time Regressions as Compared with Individual Trends”. In: Econometrica, pp. 387-401.