help mata mm_ebal()

Title

mm_ebal() -- Entropy balancing

Syntax

Initialize the balancing problem:

S = mm_ebal_init(X1, w1, X0, w0 [, target, cov, nc, dfc, nostd])

where

X1 is the data matrix of the treatment group; rows are observations, columns
are variables; X1 and X0 must have the same number of columns

w1 is a column vector containing base weights of the treatment group; use
w1 = 1 for unweighted data; if not scalar, w1 must have
the same number of rows as X1

X0 is the data matrix of the control group; rows are observations, columns
are variables; X1 and X0 must have the same number of columns

w0 is a column vector containing base weights of the control group; use
w0 = 1 for unweighted data; if not scalar, w0 must have
the same number of rows as X0

target specifies the target moments to be balanced; target is a vector containing
elements equal to 1 (balance mean), 2 (balance mean and variance), or
3 (balance mean, variance, and skewness); the elements of target will
be applied to the variables one after the other; if target is shorter
then the number of variables, the elements will be recycled; for example,
target = 2 will balance all means and all variances; the default is
target = 1 (balance all means)

cov!=0 balances the covariances
(in addition to the moments requested by target)

nc!=0 includes the normalization constraint (target sum of weights)
in the optimization problem, rather than rescaling the weights ex
ante; in this case, if perfect balance is not possible, the sum of the
balancing weights is no longer guaranteed to be equal to the size of the
treatment group

dfc!=0 applies degrees-of-freedom correction to balancing constraints
for variances and covariances; by default, mm_ebal() uses constraints
that are consistent with computing variances and covariances in the
reweighted control group based on weights that are normalized to sum to the
size of the treatment group (or with variance formulas that ignore
degrees-of-freedom adjustment in the denominator); if dfc!=0 is
specified, the results are consistent with
weights normalized to the size of the control group; dfc only
affects variances and covariances that are not collinear with lower
moments

nostd!=0 suppresses standardization of the constraint matrix; by default,
mm_ebal() divides the columns of the constraint matrix by the
standard deviations of the corresponding terms in the treatment group (if the
standard deviations exist); this should make the optimization more stable as all
constraints have a similar scaling and the balancing loss is expressed in terms of
standardized differences; specify nostd!=0 to omit such standardization

Run the balancing algorithm:

balanced = mm_ebal(S)

where
balanced will be 1 if balance is achieved (balancing loss < btol) and
0 else

Retrieve the balancing weights:

W = mm_ebal_W(S)

where
W will be a column vector containing the weights that can be used to adjust
the control group to the treatment group; the sum of W will equal the
size (sum of weights) of the treatment group

Define optimization settings before running mm_ebal():

mm_ebal_btol(S, btol)
set the balancing
tolerance; default is btol = 1e-5; balancing is achieved if the
balancing loss is smaller than btol

mm_ebal_trace(S, trace)
set
what is displayed in the iteration log; default is trace =
"value"; trace = "none" suppresses the iteration log;
see optimize() for details

mm_ebal_maxiter(S, maxiter)
set the maximum
number of iterations; the default is c(maxiter) as set by
set maxiter (16,000 by default)

mm_ebal_ptol(S, ptol)
set the convergence
tolerance for the parameter vector (lambda coefficients); default is
ptol = 1e-6; convergence is reached if ptol or vtol is
satisfied; also see optimize()

mm_ebal_vtol(S, vtol)
set the convergence
tolerance for the balancing loss; default is vtol = 1e-7;
convergence is reached if ptol or vtol is satisfied; also see
optimize()

mm_ebal_difficult(S, flag)
set the stepping
algorithm to be used in nonconcave regions; flag=0 (the default) uses
the standard algorithm; flag!=0 uses an alternative algorithm; see the
singular H methods in optimize() and
the description of the difficult option in maximize

mm_ebal_nowarn(S, flag)
set whether
the message "convergence not achieved" is to be displayed if convergence is
not reached within the maximum number of iterations; flag=0 (the default)
displays the message; flag!=0 suppresses the message

mm_ebal_Z(S, Z)
set the starting value
for the parameter vector (lambda coefficients); the default is
Z = J(1, c, 0) where c is the number of balancing constraints; if
nc!=0 the default is Z = (ln(N1/N0), J(1, c, 0)) where N1
and N0 are the group sizes (sum of weights)

If applied without 2nd argument, the above functions return the current value of the
setting. For example, type btol = mm_ebal_btol(S)
to obtain the current balancing tolerance setting.

Retrieve auxiliary results after running mm_ebal():

balanced = mm_ebal_balanced(S)
1 if balance is achieved (balancing loss < btol), 0 else

conv = mm_ebal_conv(S)
1 if
the optimization algorithm converged, 0 else

rc = mm_ebal_rc(S)

scalar containing the return code issued by optimize() in case of error

Z = mm_ebal_Z(S)

row vector containing the fitted parameter vector (lambda coefficients)

g = mm_ebal_g(S)
row vector containing
the gradient at Z

v = mm_ebal_v(S)
scalar containing the
balancing loss at Z

i = mm_ebal_i(S)
scalar containing the number
of iterations

N = mm_ebal_N(S)
scalar containing the
target sum of weights (size of the treatment group)

C = mm_ebal_C(S)
constraint matrix (excluding
collinear columns); rows are observations

CC = mm_ebal_CC(S)
(non-redundant) collinear
columns from constraint matrix; rows are observations

Q = mm_ebal_Q(S)
column vector containing the
(rescaled) base weights of the control group

Description

mm_ebal() performs entropy balancing proposed by Hainmueller (2012).
Given data from a treatment group and a control group, the goal of
mm_ebal() is to find a vector of weights such that selected moments
(e.g. means and variances) in the reweighted control group match the
corresponding moments in the treatment group (i.e., to find weights that
balance the treatment and control groups with respect to the moments).

The code of mm_ebal() is loosely based on the Stata package
ebalance (version 1.5.4, 2015-01-29) by Hainmueller and Xu (2011,
2013) and on R package ebal (version 0.1-6, 2014-01-27) by Hainmueller
(2014). Instead of using a custom algorithm, however, mm_ebal() is
implemented in terms of Mata's optimize(). Results will
be highly accurate as long as a balancing solution exists.

A newer function for entropy balancing with somewhat different
features is available as mm_ebalance(). A wrapper
that obtains balancing weights in a single
line of code is available as mm_wbal(). A Stata command
based on mm_ebalance() is available as
ebalfit (Jann 2021).

Remark on how mm_ebal() handles collinearity: (1) Terms that are
collinear across both groups will be ignored because the
corresponding balancing constraints are redundant (example: variance of a
binary variable). (2) Terms that are collinear only in the control group
will be ignored during optimization, however, since the corresponding
balancing constraints are not redundant, these terms will be taken into
account when computing the final balancing loss after optimization has
completed (example: empty factor-variable level).

Examples

Basic procedure

The basic procedure is to first define the balancing problem using
mm_ebal_init(), then apply mm_ebal() to find the balancing
weights, and then to retrieve the fitted weights using mm_ebal_W(). Here
is an example:

. sysuse nlsw88, clear
. drop if missing(wage, union, age, grade, hours, ttl_exp, tenure)
. generate byte nonunion = 1 - union
. mata:
: X1 = st_data(., "age grade hours ttl_exp tenure", "union")
: X0 = st_data(., "age grade hours ttl_exp tenure", "nonunion")
: S = mm_ebal_init(X1, 1, X0, 1)
: (void) mm_ebal(S)
: w = mm_ebal_W(S)
: end

We can now confirm that the weights do balance the data. In the original sample,
the group means and the mean differences are:

. mata: mean(X1)', mean(X0)', mean(X1)' - mean(X0)'

Applying the balancing weights to the control group removes the mean differences
(apart from roundoff error):

. mata: mean(X1)', mean(X0, w)', mean(X1)' - mean(X0, w)'

Balancing higher order moments

By default, mm_ebal() computes weights that balance the means of the
provided variables. Specify target and cov to
balance additional moments. In the following example, tar = 2
and cov = 1 is used to balance all means, variances, and
covariances:

. mata:
: S = mm_ebal_init(X1, 1, X0, 1, 2, 1)
: (void) mm_ebal(S)
: w = mm_ebal_W(S)
: mean(X1) - mean(X0, w)
: variance(X1) - variance(X0, w)
: end

Estimating an ATT based on the balanced sample

We can now use the balancing weights to estimate an average treatment
effect on the treated (ATT). Using unbalanced data, the estimate of the effect
of union status on wages is 1.46 USD:

. regress wage union

Adjusting covariate imbalance using the weights computed by mm_ebal()
we get an ATT of .82 USD:

. generate double w = 1
. `"mata: st_store(., "w", "nonunion", w)"'
. regress wage union [pweight = w]

Adjusting a sample to known population moments

Assume that the mean age in the relevant population is known to be 35 with a standard deviation of 2.5
and that the mean grade is 12 with a standard deviation of 1.9. The population
size is 1.2 million. You could adjust the sample to these values as follows:

. sysuse nlsw88, clear
. drop if missing(age, grade)
. mata:
: age = st_data(., "age")
: grade = st_data(., "grade")
: N = 1.2e6; dfc = N / (N - 1)
: pop = (35, 2.5^2, 12, 1.9^2)
: sample = (age, (age:-35):^2 * dfc, grade, (grade:-12):^2 * dfc)
: S = mm_ebal_init(pop, N, sample, 1)
: (void) mm_ebal(S)
: w = mm_ebal_W(S)
: sum(w)
: mean(age, w), sqrt(variance(age, w))
: mean(grade, w), sqrt(variance(grade, w))
: end

Orthogonalization of variables

Entropy balancing can also be used, for example, to find weights that
orthogonalize the data (such that correlations between variables are
zero). Here is an example:

. sysuse nlsw88, clear
. drop if missing(age, hours, tenure)
. mata:
: X = st_data(., "age hours tenure")
: C = X :- mean(X)
: C = C, C[,1]:*C[,2], C[,1]:*C[,3], C[,2]:*C[,3]
: S = mm_ebal_init(J(1, cols(C), 0), rows(C), C, 1)
: (void) mm_ebal(S)
: w = mm_ebal_W(S)
: mean(X) // results from original data
: variance(X)
: mean(X, w) // reweighted results
: variance(X, w)
: end

The trick is to first center the data and then fit weights such that
the means of the pairwise products between the centered variables become zero.

In the above example, the means of the variables are preserved, but the
variances change. To preserve the variances in addition to the means,
standardize the data (excluding degrees of freedom correction) and add the
squares of the standardized variables to the optimization problem:

. mata:
: N = rows(X)
: C = (X:-mean(X)) :/ sqrt(diagonal(variance(X))*(N-1)/N)'
: C = C, C:^2:-1, C[,1]:*C[,2], C[,1]:*C[,3], C[,2]:*C[,3]
: S = mm_ebal_init(J(1, cols(C), 0), rows(C), C, 1)
: (void) mm_ebal(S)
: w = mm_ebal_W(S)
: mean(X) // results from original data
: variance(X)
: mean(X, w) // reweighted results
: variance(X, w)
: end

Source code

mm_ebal.mata

References

Hainmueller, J. (2012). Entropy Balancing for Causal Effects: A
Multivariate Reweighting Method to Produce Balanced Samples in
Observational Studies. Political Analysis
20(1): 25-46. DOI: 10.1093/pan/mpr025

Hainmueller, J. (2014). ebal: Entropy reweighting to create balanced
samples. http://CRAN.R-project.org/package=ebal.

Hainmueller, J., Y. Xu (2011). EBALANCE: Stata module to perform Entropy
reweighting to create balanced samples. Statistical Software Components
S457326, Boston College Department of Economics. http://ideas.repec.org/c/boc/bocode/s457326.html.

Hainmueller, J., Y. Xu (2013). ebalance: A Stata Package for Entropy Balancing.
Journal of Statistical Software
54(7):1-18. DOI: 10.18637/jss.v054.i07

Jann, B. (2021). ebalfit: Stata module to perform entropy balancing. Available
from http://github.com/benjann/ebalfit/.

Author

Ben Jann, University of Bern, ben.jann@unibe.ch

Also see

Online: help for
[M-5] optimize(),
moremata, mm_ebalance(), mm_wbal(),
ebalance (if installed), ebalfit (if installed),