14  Matching and Weighting - Part 2: weighting

Published

May 31, 2025

This chapter follows R’s WeightIt package (Greifer et al.). The assumptions — SUTVA, ignorability, and overlap — are the same as in the matching chapter.

14.1 Weighting methods

Weighting creates a pseudo-population in which \(X\) is balanced across treatment and control. The most common approach is inverse probability of treatment weighting (IPTW), which uses the propensity score. Several alternatives are gaining traction.

14.1.1 Inverse probability of treatment weighting (IPTW)

IPTW is the most common weighting method. It uses the inverse of the propensity score as weights. For the ATE, weight each treated unit by \(1/p\) and each control unit by \(1/(1-p)\), where \(p\) is the propensity score. For the ATT — which is what the examples below request via estimand = "ATT" — the treated units all get weight \(1\) and the controls get \(p/(1-p)\); you can see this in the weight summaries, where the treated range is exactly \(1\) to \(1\). Either way the point is the same: create a pseudo population in which the distribution of \(X\) is balanced between the groups.

14.1.2 Covariate balancing propensity score (CBPS)

The same idea, but with a different way to estimate the propensity score. CBPS is a form of logistic regression where balance constraints are incorporated to a generalized method of moments estimation of the model coefficients.

14.1.3 Entropy balancing

Entropy balancing involves the specification of an optimization problem, the solution to which is then used to compute the weights. The constraints of the primal optimization problem correspond to covariate balance on the means (for binary and multi-category treatments) or treatment-covariate covariances (for continuous treatments), positivity of the weights, and that the weights sum to a certain value. Entropy balancing is doubly robust (for the ATT) in the sense that it is consistent either when the true propensity score model is a logistic regression of the treatment on the covariates or when the true outcome model for the control units is a linear regression of the outcome on the covariates, and it attains a semi-parametric efficiency bound when both are true. Entropy balancing will always yield exact mean balance on the included terms (not necessarily on the distribution of the covariates).

14.1.4 Energy balancing

Energy balancing is a method of estimating weights using optimization without a propensity score.

The primary benefit of energy balancing is that all features of the covariate distribution are balanced, not just means, as with other optimization-based methods like entropy balancing.

14.1.5 SuperLearner

SuperLearner works by fitting several machine learning models to the treatment and covariates and then taking a weighted combination of the generated predicted values to use as the propensity scores, which are then used to construct weights.

14.2 Example

Here is an example with Lalonde data, we are interested in the effect of treatment on “re78” (real earnings 1978).

14.2.1 IPW

Code
library("WeightIt")
library(cobalt)
data(lalonde)
w.out1 <- weightit(treat ~ age + educ + race + married + nodegree + re74 + re75,
                  data = lalonde, estimand = "ATT", method = "glm")
w.out1 #print the output
A weightit object
 - method: "glm" (propensity score weighting with GLM)
 - number of obs.: 614
 - sampling weights: none
 - treatment: 2-category
 - estimand: ATT (focal: 1)
 - covariates: age, educ, race, married, nodegree, re74, re75
Code
summary(w.out1)
                  Summary of weights

- Weight ranges:

          Min                                 Max
treated 1.            ||                    1.   
control 0.009 |---------------------------| 3.743

- Units with the 5 most extreme weights by group:
                                    
            5     4    3     2     1
 treated    1     1    1     1     1
          597   573  381   411   303
 control 3.03 3.059 3.24 3.523 3.743

- Weight statistics:

        Coef of Var   MAD Entropy # Zeros
treated       0.    0.      0.          0
control       1.818 1.289   1.098       0

- Effective Sample Sizes:

           Control Treated
Unweighted  429.       185
Weighted     99.82     185

The treated weights are all exactly 1, as the ATT requires, and the control weights run from 0.009 to 3.743. The cost is in the effective sample size: 429 controls carry the information of 99.8 equally weighted ones, a loss of more than three quarters.

Code
library(cobalt)
bal.tab(w.out1, stats = c("m", "v"), thresholds = c(m = .05))
Balance Measures
                Type Diff.Adj         M.Threshold V.Ratio.Adj
prop.score  Distance  -0.0205     Balanced, <0.05      1.0324
age          Contin.   0.1188 Not Balanced, >0.05      0.4578
educ         Contin.  -0.0284     Balanced, <0.05      0.6636
race_black    Binary  -0.0022     Balanced, <0.05           .
race_hispan   Binary   0.0002     Balanced, <0.05           .
race_white    Binary   0.0021     Balanced, <0.05           .
married       Binary   0.0186     Balanced, <0.05           .
nodegree      Binary   0.0184     Balanced, <0.05           .
re74         Contin.  -0.0021     Balanced, <0.05      1.3206
re75         Contin.   0.0110     Balanced, <0.05      1.3938

Balance tally for mean differences
                    count
Balanced, <0.05         9
Not Balanced, >0.05     1

Variable with the greatest mean difference
 Variable Diff.Adj         M.Threshold
      age   0.1188 Not Balanced, >0.05

Effective sample sizes
           Control Treated
Unadjusted  429.       185
Adjusted     99.82     185

Nine of ten contrasts now pass the strict 0.05 threshold, against none before weighting. age is the exception at 0.1188. Note also the variance ratios, which IPTW does not target: age sits at 0.458 and re74 at 1.321, so the means are balanced while the spreads are not.

14.2.2 entropy balancing

Code
w.out2 <- weightit(treat ~ age + educ + race + married + nodegree + re74 + re75,
                  data = lalonde, estimand = "ATT", method = "ebal")
summary(w.out2)
                  Summary of weights

- Weight ranges:

          Min                                 Max
treated 1.           ||                     1.   
control 0.008 |---------------------------| 4.062

- Units with the 5 most extreme weights by group:
                                     
             5     4    3     2     1
 treated     1     1    1     1     1
           608   381  597   303   411
 control 3.073 3.235 3.45 3.897 4.062

- Weight statistics:

        Coef of Var   MAD Entropy # Zeros
treated       0.    0.      0.          0
control       1.834 1.287   1.101       0

- Effective Sample Sizes:

           Control Treated
Unweighted  429.       185
Weighted     98.46     185
Code
bal.tab(w.out2, stats = c("m", "v"), thresholds = c(m = .05))
Balance Measures
               Type Diff.Adj     M.Threshold V.Ratio.Adj
age         Contin.       -0 Balanced, <0.05      0.4096
educ        Contin.       -0 Balanced, <0.05      0.6635
race_black   Binary        0 Balanced, <0.05           .
race_hispan  Binary        0 Balanced, <0.05           .
race_white   Binary       -0 Balanced, <0.05           .
married      Binary       -0 Balanced, <0.05           .
nodegree     Binary        0 Balanced, <0.05           .
re74        Contin.       -0 Balanced, <0.05      1.3265
re75        Contin.       -0 Balanced, <0.05      1.3351

Balance tally for mean differences
                    count
Balanced, <0.05         9
Not Balanced, >0.05     0

Variable with the greatest mean difference
 Variable Diff.Adj     M.Threshold
     re74       -0 Balanced, <0.05

Effective sample sizes
           Control Treated
Unadjusted  429.       185
Adjusted     98.46     185

Every mean difference is now zero to three decimals — that is the constraint entropy balancing imposes directly, rather than hoping a propensity model delivers it — and all nine pass. The effective sample size is 98.46, essentially the same as IPTW’s 99.82, so the exact balance came at no extra cost in precision here. The variance ratios are unchanged in character (age 0.410, re74 1.327): entropy balancing constrains means, not distributions.

Suppose we are satisfied with the balance, we can use the weights to estimate the treatment effect. We can use the “marginaleffects” package to do that.

Code
# Fit outcome model
fit <- lm_weightit(re78 ~ treat * (age + educ + race + married +
                                     nodegree + re74 + re75),
                   data = lalonde, weightit = w.out2)
# G-computation for the treatment effect
library("marginaleffects")
avg_comparisons(fit, variables = "treat",
                newdata = subset(lalonde, treat == 1))

 Estimate Std. Error    z Pr(>|z|)   S 2.5 % 97.5 %
     1273        770 1.65   0.0983 3.3  -236   2783

Term: treat
Type: probs
Comparison: 1 - 0

The entropy-balanced ATT is $1,273 with a standard error of $770, \(p = 0.098\), and a 95% interval of \([-236, 2783]\) that includes zero.

14.2.3 energy balancing

Code
w.out3 <- tryCatch(
  weightit(treat ~ age + educ + race + married +
                  nodegree + re74 + re75, data = lalonde,
                method = "energy", estimand = "ATE"),
  error = function(e) { message("Energy balancing failed: ", e$message); NULL }
)
if (!is.null(w.out3)) summary(w.out3)
                  Summary of weights

- Weight ranges:

        Min                                  Max
treated   0 |---------------------------| 14.041
control   0 |---------------------|       11.065

- Units with the 5 most extreme weights by group:
                                        
            10   179   125    137    124
 treated 5.984  6.23 6.758 13.202 14.041
           537   553   573    420    608
 control 5.499 6.404 6.599  8.274 11.065

- Weight statistics:

        Coef of Var   MAD Entropy # Zeros
treated       1.821 1.054   0.92        0
control       1.264 0.866   0.579       0

- Effective Sample Sizes:

           Control Treated
Unweighted  429.    185.  
Weighted    165.42   43.04
Code
if (!is.null(w.out3)) {
  fit <- lm_weightit(re78 ~ treat * (age + educ + race + married +
                                       nodegree + re74 + re75),
                     data = lalonde, weightit = w.out3)
  # G-computation for the treatment effect
  avg_comparisons(fit, variables = "treat")
}

 Estimate Std. Error       z Pr(>|z|)   S 2.5 % 97.5 %
      -73       1085 -0.0672    0.946 0.1 -2200   2054

Term: treat
Type: probs
Comparison: 1 - 0

Two things are worth noticing in that output rather than passing over it. First, the estimand changed: w.out3 requests the ATE, while the entropy-balancing fit above targeted the ATT, so the two numbers (\(-73\) against \(1273\)) are not competing estimates of the same quantity. Second, look at the weight summary. Entropy balancing gives every treated unit a weight of exactly \(1\), as the ATT requires. Energy balancing spreads the treated weights from \(0\) to \(14.0\) — one treated unit is effectively discarded and another counts as fourteen — and the resulting standard error (\(1085\)) is much larger than entropy balancing’s (\(770\)). Balancing the whole covariate distribution rather than just its means is a genuine advantage, but it is bought with weight variability, and that shows up directly in the precision of the estimate.

14.3 comparing matching and weighting

Matching and weighting are popular in different fields. We used to say matching involves discarding data therefore changing the estimand, but now with full matching and other methods, matching can be done without losing data. We used to believe weighting can be unstable, as weights can be highly variable, but with CBPS, entropy balancing and energy balancing, weighting can perform much better.

So, in practice, we can try different methods, either matching or weighting, and see which one gives us better balance. Then use “marginaleffects” to get the treatment effect.

It is worth putting numbers on that, because on this data the choice changes the conclusion. Full matching in the previous chapter gave an ATT of $1,977 with a standard error of $704 and \(p = 0.005\). Entropy balancing above gives $1,273 with $770 and \(p = 0.098\). The two point estimates are about one standard error apart, and one interval excludes zero while the other does not.

Note which way that cuts. Entropy balancing achieves exact mean balance, while full matching leaves educ at \(-0.096\); the better-balanced method gives the smaller and less significant estimate. Better balance on means is not the same thing as a more credible answer, and reporting whichever method happened to cross the significance line would be the wrong response to this. The honest summary reports both and says the data do not settle it.


Systematic treatment: R · Julia.