Residual balancing is a method of constructing weights for marginal structural models, which can be used to estimate marginal effects of time-varying treatments and controlled direct/mediator effects in causal mediation analysis. Compared with inverse probability-of-treatment weights (IPW), residual balancing weights tend to be more robust and more efficient, and are easier to use with continuous exposures. This package provides three main functions, rbwPoint()
, rbwPanel()
and rbwMed()
, that produce residual balancing weights for analyzing point treatments, time-varying treatments, and causal mediation, respectively.
Reference
You can install the released version of rbw from CRAN with:
And the development version from GitHub with:
The rbwPoint()
function constructs residual balancing weights for estimating the average effect of a point treatment. The following example illustrates its use by estimating the average effect of televised political advertisements (treat
) on campaign contributions (Cont
) among 16,265 zipcodes in the 2004 and 2008 US presidential elections.
library(rbw)
# install.packages("survey")
library(survey)
# residual balancing weights
rbwPoint_fit <- rbwPoint(treat, baseline_x = c(log_TotalPop, PercentOver65, log_Inc, PercentHispanic, PercentBlack, density, per_collegegrads, CanCommute), data = advertisement)
#> Entropy minimization converged within tolerance level
# attach residual balancing weights to data
advertisement$rbw_point <- rbwPoint_fit$weights
# fit marginal structural model
rbw_design <- svydesign(ids = ~ 1, weights = ~ rbw_point, data = advertisement)
# the outcome model includes the treatment, the square of the treatment,
# and state-level fixed effects (Fong, Hazlett, and Imai 2018)
msm_rbwPoint <- svyglm(Cont ~ treat + I(treat^2) + factor(StFIPS), design = rbw_design)
summary(msm_rbwPoint)
#>
#> Call:
#> svyglm(formula = Cont ~ treat + I(treat^2) + factor(StFIPS),
#> design = rbw_design)
#>
#> Survey design:
#> svydesign(ids = ~1, weights = ~rbw_point, data = advertisement)
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 3.57549 2.79045 1.281 0.200095
#> treat 0.43986 1.70909 0.257 0.796901
#> I(treat^2) 0.01552 0.16555 0.094 0.925332
#> factor(StFIPS)5 -1.25821 1.07914 -1.166 0.243656
#> factor(StFIPS)6 64.54555 5.42773 11.892 < 2e-16 ***
#> factor(StFIPS)10 12.25245 6.06566 2.020 0.043403 *
#> factor(StFIPS)13 11.14059 2.98022 3.738 0.000186 ***
#> factor(StFIPS)17 20.98707 4.37924 4.792 1.66e-06 ***
#> factor(StFIPS)20 -1.89078 1.17473 -1.610 0.107516
#> factor(StFIPS)21 -1.75188 1.21534 -1.441 0.149469
#> factor(StFIPS)23 -2.07515 1.60297 -1.295 0.195489
#> factor(StFIPS)24 36.79553 8.26927 4.450 8.66e-06 ***
#> factor(StFIPS)25 48.39716 7.34165 6.592 4.47e-11 ***
#> factor(StFIPS)27 2.31899 2.11116 1.098 0.272027
#> factor(StFIPS)28 -0.11943 1.25105 -0.095 0.923948
#> factor(StFIPS)30 -4.49525 1.58284 -2.840 0.004517 **
#> factor(StFIPS)31 -3.16796 1.00206 -3.161 0.001573 **
#> factor(StFIPS)34 23.32090 4.04985 5.758 8.64e-09 ***
#> factor(StFIPS)36 29.47346 4.29735 6.859 7.21e-12 ***
#> factor(StFIPS)40 0.58593 1.16360 0.504 0.614588
#> factor(StFIPS)45 1.14183 1.46973 0.777 0.437230
#> factor(StFIPS)46 -4.75496 1.99265 -2.386 0.017033 *
#> factor(StFIPS)47 5.66276 1.84947 3.062 0.002203 **
#> factor(StFIPS)48 18.32801 2.37261 7.725 1.19e-14 ***
#> factor(StFIPS)50 -0.50451 1.93071 -0.261 0.793860
#> factor(StFIPS)56 2.17016 3.10951 0.698 0.485244
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for gaussian family taken to be 13738.91)
#>
#> Number of Fisher Scoring iterations: 2
The rbwPanel()
function constructs residual balancing weights for estimating marginal effects of time-varying treatments. The following example illustrates its use by estimating the effect of negative campaign advertising (d.gone.neg
) on election outcomes (demprcnt
) for 113 Democratic candidates in US Senate and Gubernatorial elections.
# models for time-varying confounders
m1 <- lm(dem.polls ~ (d.gone.neg.l1 + dem.polls.l1 + undother.l1) * factor(week), data = campaign_long)
m2 <- lm(undother ~ (d.gone.neg.l1 + dem.polls.l1 + undother.l1) * factor(week), data = campaign_long)
xmodels <- list(m1, m2)
# residual balancing weights
rbwPanel_fit <- rbwPanel(treatment = d.gone.neg, xmodels = xmodels, id = id, time = week, data = campaign_long)
#> Entropy minimization converged within tolerance level
# merge weights into wide-format data
campaign_wide2 <- merge(campaign_wide, rbwPanel_fit$weights, by = "id")
# fit a marginal structural model (adjusting for baseline confounders)
rbw_design <- svydesign(ids = ~ 1, weights = ~ rbw, data = campaign_wide2)
msm_rbw <- svyglm(demprcnt ~ cum_neg * deminc + camp.length + factor(year) + office, design = rbw_design)
summary(msm_rbw)
#>
#> Call:
#> svyglm(formula = demprcnt ~ cum_neg * deminc + camp.length +
#> factor(year) + office, design = rbw_design)
#>
#> Survey design:
#> svydesign(ids = ~1, weights = ~rbw, data = campaign_wide2)
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 50.39769 2.52269 19.978 < 2e-16 ***
#> cum_neg 0.96579 0.45496 2.123 0.036143 *
#> deminc 17.04229 2.66426 6.397 4.62e-09 ***
#> camp.length -0.09085 0.06175 -1.471 0.144222
#> factor(year)2002 -5.57359 1.53081 -3.641 0.000425 ***
#> factor(year)2004 -6.22630 1.67340 -3.721 0.000322 ***
#> factor(year)2006 -1.51220 1.93697 -0.781 0.436751
#> office 0.02811 1.11988 0.025 0.980026
#> cum_neg:deminc -2.99678 0.65932 -4.545 1.49e-05 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for gaussian family taken to be 25.21453)
#>
#> Number of Fisher Scoring iterations: 2
In causal mediation analysis, the rbwMed()
function can be used to construct residual balancing weights for estimating the controlled direct effect or the controlled mediator effect with a marginal structural model. The following example illustrates its use by estimating the controlled direct effect of shared democracy (democ
) on public support for war (strike
) at different levels of perceived morality of war (immoral
) for a sample of respondents in a survey experiment.
# models for post-treatment confounders
m1 <- lm(threatc ~ ally + trade + h1 + i1 + p1 + e1 + r1 +
male + white + age + ed4 + democ, data = peace)
m2 <- lm(cost ~ ally + trade + h1 + i1 + p1 + e1 + r1 +
male + white + age + ed4 + democ, data = peace)
m3 <- lm(successc ~ ally + trade + h1 + i1 + p1 + e1 + r1 +
male + white + age + ed4 + democ, data = peace)
# residual balancing weights
rbwMed_fit <- rbwMed(treatment = democ, mediator = immoral,
zmodels = list(m1, m2, m3), interact = TRUE,
baseline_x = c(ally, trade, h1, i1, p1, e1, r1, male, white, age, ed4),
data = peace)
#> Entropy minimization converged within tolerance level
# attach residual balancing weights to data
peace$rbw_cde <- rbwMed_fit$weights
# fit marginal structural model
rbw_design <- svydesign(ids = ~ 1, weights = ~ rbw_cde, data = peace)
msm_rbwMed <- svyglm(strike ~ democ * immoral, design = rbw_design)
summary(msm_rbwMed)
#>
#> Call:
#> svyglm(formula = strike ~ democ * immoral, design = rbw_design)
#>
#> Survey design:
#> svydesign(ids = ~1, weights = ~rbw_cde, data = peace)
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 2.74428 0.06255 43.875 < 2e-16 ***
#> democ -0.37399 0.09893 -3.780 0.000164 ***
#> immoral -1.36569 0.15082 -9.055 < 2e-16 ***
#> democ:immoral 0.09091 0.19782 0.460 0.645899
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for gaussian family taken to be 1.384994)
#>
#> Number of Fisher Scoring iterations: 2