Entropy balancing for covariate overlap (ebal)
Follow the analysis.
Select a node to explore the method
Assemble treatment indicator and covariates
Data preparation — shapes the raw inputs into what the estimator expects.
Build a binary treatment vector and the covariate set whose means you want the reweighted controls to match the treated group on.
# Install: install.packages("ebal")
library(ebal)
set.seed(42)
n_t <- 75; n_c <- 250
df <- data.frame(
treat = c(rep(1,n_t), rep(0,n_c)),
age = c(rnorm(n_t,45,8), rnorm(n_c,38,10)),
educ = c(rnorm(n_t,16,2.5),rnorm(n_c,13,3)),
income = c(rnorm(n_t,65,12), rnorm(n_c,50,15)))
df$y <- 0.1*df$age + 0.3*df$educ + 0.05*df$income + 5*df$treat + rnorm(nrow(df),0,3)
Before using this step, make its assumptions and its output explicit. A useful peer note explains what you would check, what could go wrong, or what would change your interpretation.
Questions to explore- Which assumption matters most here, and what evidence could challenge it?
- What would you check before passing this output to the next step?
Choose a question to start your comment.
Peer discussion
- Bring your experience, a question, or a different perspective.
Log in to comment on this step.
Run entropy balancing
The core estimate — where the causal quantity itself is computed.
ebalance() solves for control weights that exactly reproduce the treated-group covariate moments under maximum entropy.
fit <- ebalance(treat ~ age + educ + income, data = df)
Before using this step, make its assumptions and its output explicit. A useful peer note explains what you would check, what could go wrong, or what would change your interpretation.
Questions to explore- Which assumption matters most here, and what evidence could challenge it?
- What would you check before passing this output to the next step?
Choose a question to start your comment.
Peer discussion
- Bring your experience, a question, or a different perspective.
Log in to comment on this step.
Check exact moment balance
A pre-flight check — run this before trusting any estimate downstream.
summary(fit) prints treated vs control covariate means before and after weighting; post-weighting means coincide with the treated means.
summary(fit)
Before using this step, make its assumptions and its output explicit. A useful peer note explains what you would check, what could go wrong, or what would change your interpretation.
Questions to explore- Which assumption matters most here, and what evidence could challenge it?
- What would you check before passing this output to the next step?
Choose a question to start your comment.
Peer discussion
- Bring your experience, a question, or a different perspective.
Log in to comment on this step.
Weighted ATT estimate
The core estimate — where the causal quantity itself is computed.
Pass the balancing weights to a weighted regression of the outcome on treatment to recover the ATT (true value 5).
df$w <- weights(fit)
coef(lm(y ~ treat, data = df, weights = w))["treat"]
Before using this step, make its assumptions and its output explicit. A useful peer note explains what you would check, what could go wrong, or what would change your interpretation.
Questions to explore- Which assumption matters most here, and what evidence could challenge it?
- What would you check before passing this output to the next step?
Choose a question to start your comment.
Peer discussion
- Bring your experience, a question, or a different perspective.
Log in to comment on this step.
Love plot of standardized differences
Reporting — turn the numbers into a figure or table a reader can act on.
plot(fit) draws a base-R Love plot of standardized differences before vs after weighting, one row per covariate.
plot(fit)
Before using this step, make its assumptions and its output explicit. A useful peer note explains what you would check, what could go wrong, or what would change your interpretation.
Questions to explore- Which assumption matters most here, and what evidence could challenge it?
- What would you check before passing this output to the next step?
Choose a question to start your comment.
Peer discussion
- Bring your experience, a question, or a different perspective.
Log in to comment on this step.
Inputs & data
A binary treatment indicator and a matrix of covariates to balance.
Show data format & exampleHide example
| treat | age | income | educ |
|---|---|---|---|
| 1 | 40 | 52000 | 16 |
| 0 | 38 | 47000 | 12 |
| 1 | 45 | 61000 | 18 |
| 0 | 33 | 41000 | 11 |
Result figure rendered by StatsOtter from the package's documented example — unofficial community showcase; all credit to the original authors.
The estimate
\min_{w}\ \sum_i w_i\log\frac{w_i}{q_i}\quad\text{s.t.}\quad \sum_{i:\,Z_i=0} w_i\,c_r(X_i)=\bar c_r^{\,\text{treated}}
⚠️ Unofficial community showcase of ebal (docs). Not affiliated with the authors — all credit to Guido Imbens & coauthors; this summarizes public documentation.
What it does. ebal computes weights that make the reweighted control group's covariate means (and higher moments) exactly equal the treated group's, so you can estimate effects on balanced samples. How it works. It solves a maximum-entropy optimization that finds weights staying as close as possible to uniform while satisfying the user's balance constraints—replacing the usual 'estimate propensity score, check balance, respecify' loop with a single calibration step. Assumptions. Unconfoundedness and overlap; balance constraints must be feasible given the data. Imbens's contribution. Entropy balancing (Hainmueller 2012) sits squarely in the propensity-score / overlap-weighting tradition that Imbens systematized in his influential review, Nonparametric Estimation of Average Treatment Effects Under Exogeneity (Imbens 2004, Review of Economics and Statistics), which framed balancing and overlap as central to observational causal estimation.
What you get — A vector of balancing weights for control units that equalize covariate moments with the treated group.
Example output
Entropy balancing
Means on covariates: treatment group vs. weighted control group
Treated Control(pre) Control(post) StdDiff(pre) StdDiff(post)
age 45.213 37.842 45.213 0.793 0.000
educ 15.987 12.948 15.987 1.064 0.000
income 64.512 49.871 64.512 0.987 0.000
Constraints converged: TRUE
Max. moment deviation: 1.21e-07
N treated = 75 N control = 250 (effective = 213.4)
Discuss the workflow (0)
Log in to join the discussion.