[Stata] Decomposing Group Gaps: oaxaca, fairlie, mvdcmp, and nopo
Decomposition methods split a difference in an outcome between two groups into two parts: the part that comes from the groups having different characteristics (the “explained” or “endowments” part), and the part that remains when the groups are compared at the same characteristics (the “unexplained” or “coefficients” part). The method began with Blinder (1973) and Oaxaca (1973) for wage gaps by gender and race, and it is now common in health disparities research.
This post runs four Stata commands on the same comparison, adults with limited English proficiency and other adults, because each answers a slightly different version of the question:
- oaxaca by Ben Jann, the standard Blinder-Oaxaca decomposition for a continuous outcome, with detailed contributions by variable.
- fairlie by Ben Jann, Fairlie’s nonlinear decomposition for a binary outcome.
- mvdcmp by Daniel Powers, Hirotoshi Yoshioka, and Myeong-Su Yun, a decomposition for logit, probit, Poisson, and other nonlinear models that splits both parts by variable.
- nopo by Maximilian Sprengholz and Maik Hamjediers, Ñopo’s matching-based decomposition, which also shows how much of a gap comes from people with no comparable counterpart in the other group.

Installation
ssc install oaxaca
ssc install fairlie
ssc install mvdcmp
ssc install devcon
ssc install nopo
ssc install kmatchoaxaca (4.1.1, April 2023) and fairlie run on Stata 9.2 or newer, mvdcmp (3.0, July 2023) needs Stata 16, and nopo (1.0.0, February 2024) needs Stata 15. mvdcmp calls devcon and nopo calls kmatch, so both have to be installed as well.
Data
The examples use the 2024 California Health Interview Survey (CHIS) adult public use file, which has 24,810 adult respondents. The CHIS public use files are free and available through a brief application on the UCLA Center for Health Policy Research site. Every post in this series builds its variables with the same short do-file, 00_chis_prep.do:
use "chis_adult_2024.dta", clear
* outcomes
gen k6 = distress if distress >= 0 // K6 score, past month (0-24)
gen nousual = usual == 2 // no usual source of care
gen delay = ah22 == 1 // delayed or did not get medical care, past 12 months
gen erv = er == 1 // any ER visit, past 12 months
gen uninsured = ins == 2
gen needhelp = af81 == 1 if af81 > 0 // needed help for emotional/mental or substance problem
gen fairpoor = ab1 >= 4 // fair or poor self-rated health
gen spd = dstrs12 == 1 // likely serious psychological distress, past year
* groups
gen lep = spk_eng == 3 // speaks English not well or not at all
gen foreign = ah33new == 2
gen female = srsex == 2
* covariates
gen age = srage_p1
recode ahedc_p1 (1 2 = 1 "Less than high school") (3 = 2 "High school") ///
(4 5 6 = 3 "Some college") (7 8 9 = 4 "Bachelor's or more"), gen(educ)
gen pov = povll
label define povllf 4 "300%+ FPL", modify
label values pov povllf
recode ombsrr_p1 (1 = 1 "Latino") (2 = 2 "White") (3 = 3 "Black") (5 = 4 "Asian") ///
(4 6 = 5 "Other or multiracial"), gen(race)
* CHIS design: final weight plus 80 jackknife replicate weights
svyset [pw=rakedw0], jkrweight(rakedw1-rakedw80, multiplier(1)) vce(jackknife) mseLimited English proficiency (lep) means the respondent reported speaking English not well or not at all; 1,520 respondents (6.1 percent) meet that definition. Commands that accept the svy prefix use the jackknife replicate weights; commands that do not accept it use the final weight rakedw0 as a probability weight, which gives the right point estimates and robust standard errors that ignore the replicate design.
For these commands, the education and income categories enter as indicator variables, and age is in decades so its coefficients are easier to read:
tab educ, gen(ed)
tab pov, gen(pv)
gen age10 = age/10The gaps
svy: mean nousual k6, over(lep)---------------------------------------------------------------
| Jknife *
| Mean std. err. [95% conf. interval]
--------------+------------------------------------------------
c.nousual@lep |
No | .1443258 .0031556 .1380447 .1506068
Yes | .229081 .01448 .2002594 .2579027
|
c.k6@lep |
No | 4.520119 .0491033 4.422382 4.617857
Yes | 4.030831 .1388643 3.754429 4.307234
---------------------------------------------------------------Adults with limited English proficiency are more likely to have no usual source of care (22.9 percent compared with 14.4 percent) and report slightly lower K6 distress scores (4.03 compared with 4.52).
oaxaca for a continuous outcome
oaxaca k6 age10 female uninsured normalize(ed1-ed4) normalize(pv1-pv4) ///
[pw=rakedw0], by(lep) pooledBlinder-Oaxaca decomposition Number of obs = 24,794
Group 1: lep = 0 N of obs 1 = 23,275
Group 2: lep = 1 N of obs 2 = 1,519
explained: (X1 - X2) * b
unexplained: X1 * (b1 - b) + X2 * (b - b2)
with b from pooled model (including group dummy)
------------------------------------------------------------------------------
k6 | Coefficient Std. err. z P>|z| [95% conf. interval]
-------------+----------------------------------------------------------------
overall |
group_1 | 4.520119 .0568359 79.53 0.000 4.408723 4.631516
group_2 | 4.030831 .1733719 23.25 0.000 3.691029 4.370634
difference | .4892879 .1824504 2.68 0.007 .1316917 .8468842
explained | .000868 .1573911 0.01 0.996 -.3076129 .309349
unexplained | .4884199 .2357918 2.07 0.038 .0262764 .9505634
-------------+----------------------------------------------------------------
explained |
age10 | .4858763 .0573815 8.47 0.000 .3734106 .5983421
female | -.0290274 .0110474 -2.63 0.009 -.0506799 -.007375
uninsured | .0345112 .034016 1.01 0.310 -.0321591 .1011814
ed1 | -.0918062 .109787 -0.84 0.403 -.3069846 .1233723
...
pv4 | -.2474612 .0383409 -6.45 0.000 -.322608 -.1723145
------------------------------------------------------------------------------pooled uses the coefficients from a model fit to both groups (with a group indicator) as the reference, which is a common choice when neither group is a natural standard. normalize() matters for categorical predictors: without it, the detailed unexplained contributions of a set of indicators change with the choice of reference category, and normalizing reports them as deviations from the overall mean so they do not depend on that choice.
The K6 gap is 0.49 points. The explained part is essentially zero (0.001), but not because characteristics do not matter: the contributions offset each other. Adults with limited English proficiency are older on average, and older adults report less distress, so age alone accounts for +0.49 points. Their lower incomes push the other way, and the top income category alone accounts for −0.25 points. Looking only at the total explained part would hide both.
fairlie for a binary outcome
The Blinder-Oaxaca decomposition is linear. For a binary outcome, Fairlie’s method (Fairlie, 2005) uses a logit or probit model and computes each variable’s contribution by replacing its distribution in one group with that of the other, one variable at a time. Because the order of replacement can affect the results, ro randomizes the order in each replication, and reps() sets the number of replications.
set seed 20260915
fairlie nousual age10 female uninsured ed2 ed3 ed4 pv2 pv3 pv4 [pw=rakedw0], ///
by(lep) pooled(lep) reps(100) roNon-linear decomposition by lep (G) Number of obs = 24,810
N of obs G=0 = 23290
N of obs G=1 = 1520
Pr(Y!=0|G=0) = .14410169
Pr(Y!=0|G=1) = .23155036
Difference = -.08744867
Total explained = -.03883133
------------------------------------------------------------------------------
nousual | Coefficient Std. err. z P>|z| [95% conf. interval]
-------------+----------------------------------------------------------------
age10 | .0311272 .0024141 12.89 0.000 .0263956 .0358588
female | .0035586 .000837 4.25 0.000 .0019182 .0051991
uninsured | -.0337109 .0030137 -11.19 0.000 -.0396175 -.0278042
ed2 | -.0012268 .0013439 -0.91 0.361 -.0038608 .0014072
ed3 | -.0015906 .0029146 -0.55 0.585 -.0073032 .0041219
ed4 | -.0122313 .0058304 -2.10 0.036 -.0236586 -.000804
pv2 | .0032396 .0013855 2.34 0.019 .000524 .0059551
pv3 | .0002019 .0004828 0.42 0.676 -.0007443 .0011482
pv4 | -.0282868 .0049914 -5.67 0.000 -.0380697 -.0185039
------------------------------------------------------------------------------fairlie reports the difference as group 0 minus group 1, so the gap is −0.087 and contributions with the same sign as the gap help account for it. The characteristics in the model account for 0.039 of the 0.087 gap, or 44 percent. Being uninsured (−0.034) and the top income category (−0.028) account for most of that, while age works against it (+0.031), because adults with limited English proficiency are older and older adults are more likely to have a usual source of care. pooled(lep) uses a pooled model that includes the group indicator, as the help file recommends. Note that fairlie does not take a seed() option; set the seed before the command.
mvdcmp
mvdcmp puts the estimation command after a colon, and splits both the characteristics part (E) and the coefficients part (C) by variable. The group named first is the reference group A.
mvdcmp lep: logit nousual age10 female uninsured ed2 ed3 ed4 pv2 pv3 pv4 [pw=rakedw0] Reference group (A):lep==1 Mean = 0.2316
Comparison group (B):lep==0 Mean = 0.1441
-----------------------------------------------------------------------------
nousual | Coef. Std. Err. z P>|z| Pct.
-------------------------------+---------------------------------------------
E | 0.02601 0.02053 1.27 0.205 29.74
C | 0.06144 0.02187 2.81 0.005 70.26
-------------------------------+---------------------------------------------
R | 0.08745 0.01717 5.09 0.000
Due to Difference in Characteristics (E)
age10 | -0.02005 0.00817 -2.45 0.014 -22.93
female | -0.00338 0.00276 -1.22 0.221 -3.87
uninsured | 0.02346 0.00776 3.02 0.002 26.82
...
pv4 | 0.01773 0.02191 0.81 0.418 20.27(The output is shortened here.) The raw gap R is 0.087, the same as in fairlie. Here 30 percent is attributed to characteristics, compared with 44 percent in fairlie, because mvdcmp evaluates the characteristics part with the coefficients of group A, adults with limited English proficiency, while the fairlie call above used pooled coefficients. The choice of reference coefficients is part of the method, and a paper should report which one it used. The pattern by variable is the same in both: insurance and income account for part of the gap and age offsets it.
mvdcmp has a norm() option for sets of indicators. When I gave it the full sets (ed1-ed4, pv1-pv4), the logit model dropped one indicator in each set and the total gap R no longer matched the observed difference, so the example uses ordinary indicators with an omitted reference category.
nopo
The regression-based methods compare the groups as if every combination of characteristics occurred in both. Ñopo’s decomposition (Ñopo, 2008) matches people exactly on their characteristics and separates the part of the gap that comes from people who have no match in the other group. Hamjediers and Sprengholz (2023) argue that this lack of common support is often ignored in regression decompositions. Exact matching needs categorical variables, so age enters in four groups.
gen agegrp = irecode(age, 29, 44, 64)
nopo decomp k6 agegrp female uninsured educ pov [pw=rakedw0], by(lep)Nopo decomposition N = 24794
Exact matching: N strata = 242
N matched strata = 179
------------------------------------------------------------------------------
| N / % Mean
| --------------------------------- ------------
Group | Matched Unmatched Total k6
-----------------------------+------------------------------------------------
A: lep == 0 (bref) | 22409 866 23275 4.52
No | 96.3 3.7
B: lep == 1 (xref) | 1500 19 1519 4.03
Yes | 98.7 1.3
------------------------------------------------------------------------------
k6 | Coefficient
-------------+----------------------------------------------------------------
D | -.4892879
D0 | -.4030192
DX | .0785433
DA | -.1550645
DB | -.0097475
------------------------------------------------------------------------------D is the total gap (B minus A, −0.49). D0 is the part that remains among matched people with the same characteristics (−0.40), DX is the part due to different distributions of characteristics among matched people (+0.08), and DA and DB are the parts due to unmatched people in each group. Here 866 adults without limited English proficiency, 3.7 percent of that group, have combinations of characteristics that no adult with limited English proficiency in the sample shares, and they account for −0.16 points of the gap. The four parts sum to the total.
nopo gapoverdist plots each component across the distribution of the outcome:
nopo gapoverdist, twopts(scale(1.4))
The gap is close to zero at most quantiles and largest in the top fifth of the distribution, where adults with limited English proficiency report less severe distress. The lines are jagged because K6 takes only whole-number values, so many quantiles share the same score.
Notes
Decomposition is descriptive. The explained part says how much of a gap goes with differences in measured characteristics under the chosen reference coefficients; it does not say that changing those characteristics would close the gap. These commands take probability weights, and oaxaca and mvdcmp also accept the svy prefix, but the examples above use [pw=rakedw0] with robust standard errors rather than the CHIS replicate weights.
References
Blinder, A. S. (1973). Wage discrimination: Reduced form and structural estimates. The Journal of Human Resources, 8(4), 436–455. https://doi.org/10.2307/144855
Fairlie, R. W. (2005). An extension of the Blinder-Oaxaca decomposition technique to logit and probit models. Journal of Economic and Social Measurement, 30(4), 305–316. https://doi.org/10.3233/JEM-2005-0259
Hamjediers, M., & Sprengholz, M. (2023). Comparing the incomparable? Issues of lacking common support, functional-form misspecification, and insufficient sample size in decompositions. Sociological Methodology, 53(2), 344–365. https://doi.org/10.1177/00811750231169729
Jann, B. (2008). The Blinder–Oaxaca decomposition for linear regression models. The Stata Journal, 8(4), 453–479. https://doi.org/10.1177/1536867X0800800401
Ñopo, H. (2008). Matching as a tool to decompose wage gaps. Review of Economics and Statistics, 90(2), 290–299. https://doi.org/10.1162/rest.90.2.290
Oaxaca, R. (1973). Male-female wage differentials in urban labor markets. International Economic Review, 14(3), 693–709. https://doi.org/10.2307/2525981
Powers, D. A., Yoshioka, H., & Yun, M.-S. (2011). mvdcmp: Multivariate decomposition for nonlinear response models. The Stata Journal, 11(4), 556–576. https://doi.org/10.1177/1536867X1101100404
