16  Gender wage gaps: Oaxaca-Blinder and causal estimation

Author

Xiang Ao

Published

November 17, 2025

16.1 Oaxaca-Blinder as regression adjustment

The Oaxaca-Blinder (OB) decomposition separates the difference in mean outcomes into a part associated with observed covariates and a part associated with different regression coefficients:

\[E[Y \mid A = 1] - E[Y \mid A = 0] = \underbrace{(E[X \mid A = 1] - E[X \mid A = 0]) \beta_0}_{\text{Explained (Covariate Differences)}} + \underbrace{E[X \mid A = 1] (\beta_1 - \beta_0)}_{\text{Unexplained (Coefficient Differences)}}\]

Here \(A=1\) denotes female and \(A=0\) denotes male. The decomposition depends on which group supplies the reference coefficients, \(\beta_0\) or \(\beta_1\).

Słoczyński (2013, 2015) connects this decomposition to potential outcomes. If we demean the covariates at the overall sample mean, the coefficient gives the population average gender effect (\(\tau_{\text{PAGE}}\)), which corresponds to the ATE. If we demean at the female-sample means, it gives the average effect for women (\(\tau_{\text{PATT}}\)), which corresponds to the ATT.

Under this interpretation, the unexplained gap is the treatment effect averaged over the chosen covariate distribution. The explained part is \(E[Y(0)\mid A=1]-E[Y(0)\mid A=0]\), the selection term in the potential-outcomes decomposition. This causal reading requires the linear model and a meaningful intervention attached to \(A\). Without them, the explained part is only a descriptive composition difference. We can estimate the same quantities with regression adjustment or with a doubly robust estimator.

16.2 2015 CPS data

I use a subsample from the March 2015 U.S. Current Population Survey. It contains 5,150 white, non-Hispanic, full-time workers aged 25–64. The outcome is log hourly wage (lwage), and female is the group indicator.

There are two qualifications. First, gender is not manipulable in Holland’s (1986) sense. I interpret female as a proxy for being perceived and treated as female by an employer, which is closer to an intervention. Second, the extract contains only full-year, full-time, non-self-employed workers. Restricting the sample conditions on employment, which can be affected by sex and by unobserved wage determinants. This opens a collider path that covariate adjustment cannot close. We would need data on non-workers instead (Lee, 2009; Olivetti and Petrongolo, 2008). If employed women are positively selected on wage-raising unobservables, the gaps below understate the population gap.

Code
data <- read_csv("wage2015_subsample_inference.csv") |>
  rename(socl = scl, sohs = shs, sout = so) |>
  mutate(
    exp_dm   = exp1 - mean(exp1, na.rm = TRUE),
    female   = sex,
    occ2     = factor(occ2),
    ind2     = factor(ind2),
    sohs_dm  = sohs - mean(sohs, na.rm = TRUE),
    hsg_dm   = hsg - mean(hsg, na.rm = TRUE),
    socl_dm  = socl - mean(socl, na.rm = TRUE),
    clg_dm   = clg - mean(clg, na.rm = TRUE),
    mw_dm    = mw - mean(mw, na.rm = TRUE),
    sout_dm  = sout - mean(sout, na.rm = TRUE),
    we_dm    = we - mean(we, na.rm = TRUE)
  )

# 1. Unadjusted (raw) difference
reg1 <- lm(lwage ~ female, data = data)
tidy(reg1) |> filter(term == "female")
# A tibble: 1 × 5
  term   estimate std.error statistic p.value
  <chr>     <dbl>     <dbl>     <dbl>   <dbl>
1 female  -0.0383    0.0160     -2.40  0.0165
Code
# 2. Regression adjustment with demeaned human capital & region controls
reg2 <- lm(lwage ~ female * (exp_dm + sohs_dm + hsg_dm + socl_dm + clg_dm + mw_dm + sout_dm + we_dm), 
           data = data)
tidy(reg2) |> filter(term == "female")
# A tibble: 1 × 5
  term   estimate std.error statistic  p.value
  <chr>     <dbl>     <dbl>     <dbl>    <dbl>
1 female   -0.115    0.0147     -7.80 7.39e-15

16.2.1 Why adjustment widens the gap

The raw wage gap is \(-3.8\%\) (\(p = 0.012\)). After controlling for potential experience, education, and region, the estimated gap widens to \(-11.3\%\) (\(p < 0.001\)).

Here adjustment nearly triples the gap. We can see why by comparing the covariate means:

Code
data |>
  group_by(female) |>
  summarise(
    n           = n(),
    mean_lwage  = mean(lwage),
    exp1        = mean(exp1),
    `<HS`       = mean(sohs),
    HS          = mean(hsg),
    `Some Coll` = mean(socl),
    College     = mean(clg),
    Advanced    = mean(ad)
  ) |>
  mutate(across(where(is.numeric), ~round(.x, 3))) |>
  knitr::kable(caption = "Covariate means by gender.")
Covariate means by gender.
female n mean_lwage exp1 <HS HS Some Coll College Advanced
0 2861 2.988 13.784 0.032 0.294 0.273 0.294 0.107
1 2289 2.949 13.731 0.013 0.181 0.284 0.347 0.175

Women in this sample have more education than men: 17.5% have advanced degrees, compared with 10.7% of men, while 1.3% have less than high school, compared with 3.2% of men. Average potential experience is about 13.8 years in both groups. Because education raises wages, the raw comparison partly hides the gap. Holding education fixed gives the larger \(-11.3\%\) gap within education-experience cells.

16.2.2 Regression adjustment with marginaleffects

Instead of manual demeaning, the same estimands can be obtained directly using marginaleffects::avg_comparisons():

Code
reg3 <- lm(lwage ~ female * (exp1 + sohs + hsg + socl + clg + mw + sout + we), data = data)

# Population Average Gender Effect (ATE / PAGE)
avg_comparisons(reg3, variables = "female")

 Estimate Std. Error    z Pr(>|z|)    S  2.5 %  97.5 %
   -0.115     0.0147 -7.8   <0.001 47.2 -0.143 -0.0858

Term: female
Type: response
Comparison: 1 - 0
Code
# Female Subpopulation Gender Effect (ATT / PATT)
avg_comparisons(reg3, variables = "female", newdata = subset(data, female == 1))

 Estimate Std. Error     z Pr(>|z|)    S  2.5 %  97.5 %
   -0.113     0.0148 -7.65   <0.001 45.5 -0.142 -0.0844

Term: female
Type: response
Comparison: 1 - 0

Both the ATE and ATT are \(-11.3\%\). In this sample, averaging the fully interacted OLS model over either covariate distribution gives the same result as the OB decomposition.

16.3 Should we control for occupation and industry?

Pay-gap regressions often adjust for occupation and industry. Let’s see what happens here:

Code
reg4 <- lm(lwage ~ female * (exp1 + sohs + hsg + socl + clg + mw + sout + we + occ2 + ind2), 
           data = data)

avg_comparisons(reg4, variables = "female", newdata = subset(data, female == 1))

 Estimate Std. Error     z Pr(>|z|)    S  2.5 %  97.5 %
   -0.075     0.0162 -4.62   <0.001 18.0 -0.107 -0.0432

Term: female
Type: response
Comparison: 1 - 0

Adjusting for 22 occupation and industry categories narrows the gap from \(-11.3\%\) to \(-7.5\%\).

16.3.1 Different controls answer different questions

If we take sex as the treatment, there is no earlier common cause to adjust for, so \(Y(a)\perp A\) holds without conditioning. Adding controls changes the estimand rather than improving identification. The models below answer different questions; they are not progressively better estimates of one quantity.

Model Held fixed Question answered Estimate
reg1 nothing Total effect of the gender signal \(-3.8\%\)
reg3 education, experience, region Gap within education-experience cells \(-11.3\%\)
reg4 + occupation, industry Gap within occupation cells too \(-7.5\%\)

Occupation and industry are post-treatment (\(A \to \text{Occupation} \to Y\)), and they are also a common effect of gender and of unobserved wage determinants \(U\) such as career preferences or flexibility constraints. Conditioning on them blocks the mediating path and at the same time opens \(A \to \text{Occupation} \leftarrow U \to Y\), which was closed before (Elwert and Winship, 2014; Cinelli, Forney and Pearl, 2022).

The four-point move from \(-11.3\%\) to \(-7.5\%\) therefore cannot be read as occupational sorting accounting for a third of the gap. Three things are mixed into it and nothing in the output separates them: genuine mediation, collider bias from the gender-\(U\) association that conditioning induces, and ordinary confounding of the occupation-wage relationship. The direction of the second can be signed. If women who enter male-dominated, high-paying occupations are positively selected on \(U\), then within those occupations they are better on unobservables than the men they are compared with, which pushes the within-occupation gap toward zero. Part of the attenuation may be bias rather than sorting.

Calling \(-7.5\%\) a controlled direct effect is licensed only under no unmeasured mediator-outcome confounding (VanderWeele and Robinson, 2014). \(U\) is exactly such a confounder, and in a wage setting it is the reason this literature exists. Report the number as the within-occupation adjusted gap and state the condition that would upgrade it to a CDE.

16.4 A nonparametric AIPW estimate

The regression adjustment still depends on its functional form. To make that part more flexible, I use augmented inverse probability weighting (AIPW) with SuperLearner through npcausal (Kennedy, 2022). The learner library includes earth, glmnet, ranger, and xgboost.

Code
library(npcausal)

# To avoid prolonged cross-fitting during interactive compilation, we load the
# pre-computed SuperLearner results (SL.earth, SL.glmnet, SL.mean, SL.ranger, SL.xgboost)
# from the book cache if present; otherwise, compute from scratch.
cache_base <- "gwg_cache/html/npcausal1_5538101f2b9ff4f8b21593068dc74ef6"
if (file.exists(paste0(cache_base, ".rdx"))) {
  cache_env <- new.env()
  lazyLoad(cache_base, envir = cache_env)
  aipw_ate <- cache_env$aipw_ate
  aipw_att <- cache_env$aipw_att
} else {
  SL.library <- c("SL.earth", "SL.glmnet", "SL.mean", "SL.ranger", "SL.xgboost")
  reg3 <- lm(lwage ~ exp_dm + sohs + hsg + socl + clg + mw + sout + we + occ2 + ind2, data = data)
  W <- as.data.frame(model.matrix(reg3)[, -1])
  Y <- data$lwage
  A <- data$female
  set.seed(123)
  aipw_ate <- ate(y = Y, a = A, x = W, nsplits = 5, sl.lib = SL.library)
  aipw_att <- att(y = Y, a = A, x = W, nsplits = 5, sl.lib = SL.library)
}
Code
aipw_ate$res
     parameter         est         se       ci.ll       ci.ul pval
1      E{Y(0)}  2.99695506 0.01093465  2.97552315  3.01838697    0
2      E{Y(1)}  2.93906408 0.01327342  2.91304817  2.96507999    0
3 E{Y(1)-Y(0)} -0.05789098 0.01597522 -0.08920241 -0.02657955    0
Code
aipw_att$res
      parameter         est         se       ci.ll       ci.ul pval
1      E(Y|A=1)  2.94948490 0.01160804  2.92673314  2.97223666    0
2   E{Y(0)|A=1}  3.01689509 0.01221863  2.99294658  3.04084360    0
3 E{Y-Y(0)|A=1} -0.06741019 0.01409116 -0.09502886 -0.03979152    0

16.4.1 Comparing the estimates

The AIPW estimate of the ATT is \(-6.7\%\) (\(95\%\text{ CI}: [-9.5\%,-3.9\%]\)), close to the interacted OLS estimate of \(-7.5\%\). This suggests that the linear model is a reasonable approximation to the same conditional mean. AIPW only relaxes the functional-form assumption. It does not solve post-treatment adjustment, selection into the sample, or the interpretation of gender as a treatment.

16.5 What the estimates mean

The raw gap is \(-3.8\%\). Holding education, experience, and region fixed gives \(-11.3\%\), partly because women in this sample have more education. Adding occupation and industry gives a within-occupation gap of \(-7.5\%\); it is a controlled direct effect only if there is no unmeasured occupation-wage confounding. AIPW gives \(-6.7\%\), so the within-occupation result is not driven mainly by the linear functional form. None of these methods solves selection into full-time employment, and no single number should be called the gender wage gap without saying which question it answers.