We will use data from the National Evaluation of Welfare-to-Work Strategies (NEWWS) study. The study randomly assigned 694 participants, who were mostly low-income single mothers with young children. Labor Force Attachment (LFA) program (208) aimed at moving low-income parents from welfare to work by providing employment-focused incentives and services. Control group (486) received aid from the Aid to Families with Dependent Children (AFDC) program without requirement for working.
Import the data:
library(foreign)
newws = read.dta("E:\\R\\newws.dta")
# Factorize the categorical variable that is coded with numerical values. Otherwise, R would take it as a continuous variable.
Research Question: How is the LFA impact on maternal depression transmitted through employment?
Treatment:
treat 1 if a participant was assigned to LFA and 0
otherwise.
Outcome:
depression maternal depression at the end of the second
year after randomization, which is a summary score of 12 items measuring
depressive symptoms during the past week on a 0-3 scale.
Mediator: emp 1 if one was ever
employed during the two-year period after randomization.
Pretreatment Covariates:
nevmar 1 if never married and 0 otherwise.
emp_prior 1 if employed at baseline and 0 otherwise.
hispanic 1 if Hispanic and 0 otherwise.
ADCPC welfare amount in the year before
randomization
attitude a composite score of two attitude items - so
many family problems that I cannot work at a full time or part time job;
so much to do during the day that I cannot go to a school or job
training program - measured on the scale of 1-4.
depress_prior a composite score of three depressive
symptom items - sad, depressed, blues, and lonely - in the week before
randomization measured on the scale of 1-4.
workpref one’s level of preference for taking care of
family full time than working on the scale of 1-4.
nohsdip 1 if had never obtained a high school diploma or
a General Educational Development certificate and 0 otherwise.
NIE (natural indirect effect of LFA on depression mediated by employment): \[NIE = E[Y(1,M(1))] - E[Y(1,M(0))]\]
The average impact of LFA on maternal depression that is solely attributable to the change in their employment status induced by LFA, when the treatment is fixed at the LFA condition.
NDE (natural direct effect of LFA on depression transmitted through all the other possible pathways): \[NDE = E[Y(1,M(0))] - E[Y(0,M(0))]\]
The average impact of LFA on maternal depression while employment status was kept at the level that would have been observed in the absence of LFA.
PIE (pure indirect effect of LFA on depression transmitted through job search self-efficacy): \[PIE = E[Y(0,M(1))] - E[Y(0,M(0))]\]
The average impact of LFA on maternal depression that is solely attributable to the change in their employment status induced by LFA, when the treatment is fixed at the control condition.
INT (natural treatment-by-mediator interaction effect): \[INT = TDE - NDE = NIE - PIE = {E[Y(1,M(1))] - E[Y(1,M(0))]} - {E[Y(0,M(1))] - E[Y(0,M(0))]}\]
The average difference in how the LFA-induced change in the employment affects depression under the LFA condition and that under the control condition.
Fit the mediator and outcome models:
l.m = glm(emp ~ treat + emp_prior + nevmar + hispanic + ADCPC + attitude + depress_prior + workpref + nohsdip, data = newws, family = binomial("probit"))
summary(l.m)
##
## Call:
## glm(formula = emp ~ treat + emp_prior + nevmar + hispanic + ADCPC +
## attitude + depress_prior + workpref + nohsdip, family = binomial("probit"),
## data = newws)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.1487 -0.9178 -0.5949 0.9980 2.0360
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -9.671e-01 2.788e-01 -3.469 0.000523 ***
## treat 7.471e-01 1.133e-01 6.593 4.3e-11 ***
## emp_prior 7.840e-01 1.172e-01 6.692 2.2e-11 ***
## nevmar -2.786e-02 1.037e-01 -0.269 0.788228
## hispanic 8.800e-02 1.148e-01 0.766 0.443549
## ADCPC -3.029e-05 1.403e-05 -2.159 0.030826 *
## attitude 7.423e-02 8.800e-02 0.844 0.398909
## depress_prior 2.545e-02 6.163e-02 0.413 0.679624
## workpref 1.551e-01 7.436e-02 2.085 0.037057 *
## nohsdip -2.457e-01 1.079e-01 -2.278 0.022752 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 960.01 on 693 degrees of freedom
## Residual deviance: 824.26 on 684 degrees of freedom
## AIC: 844.26
##
## Number of Fisher Scoring iterations: 4
l.y = lm(depression ~ treat * emp + emp_prior + nevmar + hispanic + ADCPC + attitude + depress_prior + workpref + nohsdip, data = newws)
summary(l.y)
##
## Call:
## lm(formula = depression ~ treat * emp + emp_prior + nevmar +
## hispanic + ADCPC + attitude + depress_prior + workpref +
## nohsdip, data = newws)
##
## Residuals:
## Min 1Q Median 3Q Max
## -12.569 -5.282 -1.970 3.826 24.706
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.803e+00 1.568e+00 3.064 0.00227 **
## treat 2.538e+00 9.940e-01 2.553 0.01088 *
## emp 4.412e-01 7.368e-01 0.599 0.54948
## emp_prior 5.929e-01 6.826e-01 0.869 0.38536
## nevmar 9.720e-01 5.865e-01 1.657 0.09793 .
## hispanic 1.508e+00 6.462e-01 2.334 0.01987 *
## ADCPC -5.951e-05 7.998e-05 -0.744 0.45707
## attitude -4.740e-01 4.941e-01 -0.959 0.33771
## depress_prior 1.761e+00 3.476e-01 5.065 5.25e-07 ***
## workpref -2.775e-01 4.180e-01 -0.664 0.50709
## nohsdip 4.134e-01 6.146e-01 0.673 0.50145
## treat:emp -3.332e+00 1.306e+00 -2.551 0.01094 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 7.523 on 682 degrees of freedom
## Multiple R-squared: 0.07147, Adjusted R-squared: 0.05649
## F-statistic: 4.772 on 11 and 682 DF, p-value: 4.303e-07
Run the regression-based mediation analysis:
library(mediation)
set.seed(1) # So that the results will stay the same when you run mediate() again.
med = mediate(l.m, l.y, treat = "treat", mediator = "emp")
Print and visualize the mediation analysis results:
summary(med)
##
## Causal Mediation Analysis
##
## Quasi-Bayesian Confidence Intervals
##
## Estimate 95% CI Lower 95% CI Upper p-value
## ACME (control) 0.1073 -0.2371 0.51 0.588
## ACME (treated) -0.7544 -1.3927 -0.19 0.010 **
## ADE (control) 1.2200 -0.0601 2.59 0.064 .
## ADE (treated) 0.3583 -0.8741 1.67 0.576
## Total Effect 0.4656 -0.7349 1.69 0.456
## Prop. Mediated (control) 0.0853 -3.6313 2.40 0.800
## Prop. Mediated (treated) -0.8247 -11.2715 16.73 0.462
## ACME (average) -0.3236 -0.6810 0.02 0.068 .
## ADE (average) 0.7892 -0.4146 2.07 0.200
## Prop. Mediated (average) -0.3697 -4.3403 7.97 0.512
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Sample Size Used: 694
##
##
## Simulations: 1000
plot(med)
Estimate and test the natural treatment-by-mediator interaction effect:
INT = med$d1.sims - med$d0.sims
mean(INT)
## [1] -0.8616139
quantile(INT, probs = c(0.025, 0.975))
## 2.5% 97.5%
## -1.6261933 -0.2029703
Summarize the findings:
The mediator model output indicates that LFA significantly increased employment rates (p < 0.01). The odds of being employed among those assigned to the LFA group is 2.11 (exp(0.7471)) times that among those from the control group. The mediation analysis output indicates that the total LFA effect is estimated to be 0.47 (95% CI = [-0.73, 1.69]), which can be decomposed into a natural direct effect, estimated to be 1.22 (95% CI = [-0.06, 2.59]), about 15.92% of a standard deviation of the outcome in the control group, and a natural indirect effect, estimated to be -0.75 (95% CI = [-1.39, -0.19]), about -9.78% of a standard deviation of the outcome in the control group. The natural direct effect indicates that, LFA would have increased maternal depression if one’s employment experience is held at the level under the control condition, but not by a statistically significant amount at the significance level of 0.05. In contrast, the natural indirect effect reflects that the LFA-induced increase in employment rate significantly reduced one’s maternal depression under the LFA condition. The counteracting indirect and direct effects explained the insignificant total effect.
The natural indirect effect can be further decomposed into a pure indirect effect, which is estimated to be 0.11 (95% CI = [-0.24, 0.51]), and a natural treatment-by-mediator interaction effect, which is estimated to be -0.86 (95% CI = [-1.63, -0.20]). It indicates that the LFA-induced increase in employment rate reduced maternal depression more under the LFA condition (i.e., natural indirect effect) than under the control condition (i.e., pure indirect effect). Equivalently, the natural treatment-by-mediator interaction effect can also be viewed as the difference between the total direct effect and the natural direct effect, which indicates that LFA would have increased maternal depression to a smaller extent if holding one’s employment experience under the LFA condition (i.e., total direct effect) than if holding that under the control condition (i.e., natural direct effect).
The above analysis is based on the sequential ignorability assumption:
(Strong ignorability of the treatment) No unmeasured confounder of the LFA-employment or LFA-depression relationship, i.e., the treatment is as if randomized within levels of pretreatment covariates.
(Strong ignorability of the mediator) No unmeasured pretreatment confounder and no posttreatment confounder of the employment-depression relationship, i.e., the mediator is as if randomized within the same treatment group or across treatment groups among individuals with the same levels of pretreatment covariates.
(Positivity assumption) Within levels of pretreatment covariates, every individual has a nonzero probability of being assigned to either the LFA group or the control group and a nonzero probability of taking each mediator value within the response space of the mediator under each treatment condition.
A sensitivity analysis is necessary for determining whether the above analysis results are sensitive to a potential violation of the sequential ignorability assumption. In the following sensitivity analysis strategy, the sensitivity parameter is rho, the correlation between the error term of the mediator model and that of the outcome model. Such a correlation can arise if there exist omitted confounders that affect both mediator and outcome because these omitted confounders will be part of the two error terms. Thus, under sequential ignorability, rho=0, and nonzero values of rho imply departures from the ignorability assumption.
With this fact, the proposed sensitivity analysis asks the question of how large rho has to be for the causal mediation effect to go away. If small departures from zero in rho produce an average causal mediation effect that is substantively different from the estimate obtained under sequential ignorability or reverse the significance of the causal effect under sequential ignorability, this suggests that the study is sensitive to the potential violation of the sequential ignorability assumption. The sensitivity analysis plot graphically illustrates this by plotting the estimated effects and their 95% confidence intervals as a function of rho.
Sensitivity analysis:
sens.indirect = medsens(med, effect.type = "indirect")
plot(sens.indirect)
sens.direct = medsens(med, effect.type = "direct")
plot(sens.direct)
The sensitivity analysis results indicate that NIE (ACME_1) turns to be zero or positive when rho is equal to or smaller than -0.2, PIE (ACME_0) turns to be zero or negative when rho is equal to or bigger than 0.05, NDE (ADE_0) turns to be 0 or negative when rho is equal to or smaller than -0.4, and TDE (ADE_1) turns to be 0 or negative when rho is equal to or smaller than -0.1.
We also find that NIE turns to be insignificant when rho is between -0.4 and -0.05, PIE turns to be significant when rho is smaller than -0.05 or bigger than 0.1, NDE turns to be significant when rho is bigger than 0.1, and TDE turns to be significant when rho is bigger than 0.5. Here, the significance level is 0.05.
Because it is hard to determine whether the value of rho for removing the effects, changing the sign of the effects, or changing their significance is likely to exist or not, it is challenging to conclude whether the analytic results are sensitive based on rho without comparisons to other studies. If we choose 0.1 as the cutoff value, then the sign of PIE and the significance of NIE and PIE are sensitive to potential unmeasured pretreatment confounding.
Fit propensity score models:
# Fit a propensity score model for the mediator in the treatment group
data1 = newws[newws$treat == 1, ] # extract treatment group from the original data
l1 = glm(emp ~ treat + emp_prior + nevmar + hispanic + ADCPC + attitude + depress_prior + workpref + nohsdip, data = data1, family = binomial("logit"))
data1$logit.p = predict(l1, type = "link") # save the logit propensity score
# Fit a propensity score model for the mediator in the control group
data0 = newws[newws$treat == 0, ] # extract control group from the original data
l0 = glm(emp ~ treat + emp_prior + nevmar + hispanic + ADCPC + attitude + depress_prior + workpref + nohsdip, data = data0, family = binomial("logit"))
data0$logit.p = predict(l0, type = "link") # save the logit propensity score
# Combine the two separate data with the logit propensity score
newws = rbind(data1, data0)
Note: When applying the above code, you only need to replace newws with your data name and respecify l1 and l0.
Check overlapping in logit of propensity score:
library(ggplot2)
# Overlapping in distributions of logit propensity score between M = 1 and M = 0 in the treatment group
overlap1 = ggplot(data1, aes(x = logit.p, fill = as.factor(emp))) +
geom_density(alpha = .5) +
labs(x = "Logit Propensity Scores", y = "Density", fill = "") +
theme_bw() +
ggtitle("Overlapping in logit propensity scores between M = 1 and M = 0 in the treatment group")
# Overlapping in distributions of logit propensity score between M = 1 and M = 0 in the control group
overlap0 = ggplot(data0, aes(x = logit.p, fill = as.factor(emp))) +
geom_density(alpha = .5) +
labs(x = "Logit Propensity Scores", y = "Density", fill = "") +
theme_bw() +
ggtitle("Overlapping in logit propensity scores between M = 1 and M = 0 in the control group")
# Display the two plots side by side
library(cowplot)
plot_grid(overlap1, overlap0)
Note: When applying the above code, you only need to replace “emp” with your mediator name.
The results indicate that the distributions of the logit of propensity score well overlap between those who were employed and those who were unemployed within each treatment group.
Check balance:
If you have categorical variables with more than two categories, please generate dummies for them first.
mediator = "emp" # Let the program know the name of the mediator (string).
# Treatment group
data1$p = predict(l1, type = "response") # save the propensity score
# Calculate IPTW for those whose M = 1 in the treatment group
data1$iptw[data1[, mediator] == 1] = mean(data1[, mediator] == 1)/data1$p[data1[, mediator] == 1]
# Calculate IPTW for those whose M = 0 in the treatment group
data1$iptw[data1[, mediator] == 0] = mean(data1[, mediator] == 0)/ (1 - data1$p[data1[, mediator] == 0])
# Control group
data0$p = predict(l0, type = "response") # save the propensity score
# Calculate IPTW for those whose M = 1 in the control group
data0$iptw[data0[, mediator] == 1] = mean(data0[, mediator] == 1)/data0$p[data0[, mediator] == 1]
# Calculate IPTW for those whose M = 0 in the control group
data0$iptw[data0[, mediator] == 0] = mean(data0[, mediator] == 0)/ (1 - data0$p[data0[, mediator] == 0])
Note: When applying the above code, you only need to change the mediator name in the first line of code.
balance = function(x, data, treatment, mediator){
sd.mean.diff.before = NULL
sd.mean.diff = NULL
for(i in 1:length(x)){
# balance between M = 1 and M = 0 before weighting
mean.1.before = mean(data[data[, mediator] == 1, x[i]])
mean.0.before = mean(data[data[, mediator] == 0, x[i]])
mean.diff.before = mean.1.before - mean.0.before
sd.mean.diff.before = c(sd.mean.diff.before, mean.diff.before/sd(data[,x[i]]))
# balance between M = 1 and M = 0 after weighting
mean.1 = sum(data[,x[i]] * data$iptw * data[, mediator])/sum(data$iptw * data[, mediator])
mean.0 = sum(data[,x[i]] * data$iptw * (1 - data[, mediator]))/sum(data$iptw * (1 - data[, mediator]))
mean.diff = mean.1 - mean.0
sd.mean.diff = c(sd.mean.diff, mean.diff/sd(data[,x[i]]))
}
results = cbind.data.frame(c(sd.mean.diff.before, sd.mean.diff),c(rep("before weighting", length(x)), rep("after weighting", length(x))), c(x, x))
colnames(results) = c("sd.mean.diff", "weighting", "covariates")
return(results)
}
# Apply the balance checking function
balance.results.1 = balance(x = c("logit.p", "emp_prior", "nevmar", "hispanic", "ADCPC", "attitude", "depress_prior", "workpref", "nohsdip"), data = data1, treatment = "treat", mediator = "emp")
balance.results.0 = balance(x = c("logit.p", "emp_prior", "nevmar", "hispanic", "ADCPC", "attitude", "depress_prior", "workpref", "nohsdip"), data = data0, treatment = "treat", mediator = "emp")
balance.results.1
## sd.mean.diff weighting covariates
## 1 0.80110520 before weighting logit.p
## 2 0.55049118 before weighting emp_prior
## 3 -0.09819591 before weighting nevmar
## 4 0.03852494 before weighting hispanic
## 5 -0.22618511 before weighting ADCPC
## 6 0.30728197 before weighting attitude
## 7 0.06467071 before weighting depress_prior
## 8 0.51057152 before weighting workpref
## 9 -0.41283143 before weighting nohsdip
## 10 0.08459082 after weighting logit.p
## 11 0.03697812 after weighting emp_prior
## 12 -0.02153553 after weighting nevmar
## 13 0.04359517 after weighting hispanic
## 14 -0.01906914 after weighting ADCPC
## 15 0.05606602 after weighting attitude
## 16 -0.01623545 after weighting depress_prior
## 17 0.08095841 after weighting workpref
## 18 -0.01681181 after weighting nohsdip
balance.results.0
## sd.mean.diff weighting covariates
## 1 0.776608319 before weighting logit.p
## 2 0.695477481 before weighting emp_prior
## 3 -0.019902311 before weighting nevmar
## 4 -0.030022466 before weighting hispanic
## 5 -0.438625182 before weighting ADCPC
## 6 0.260893924 before weighting attitude
## 7 -0.015375879 before weighting depress_prior
## 8 0.314749856 before weighting workpref
## 9 -0.192418453 before weighting nohsdip
## 10 0.012963059 after weighting logit.p
## 11 0.006277949 after weighting emp_prior
## 12 0.007300551 after weighting nevmar
## 13 -0.022010343 after weighting hispanic
## 14 -0.019370907 after weighting ADCPC
## 15 0.012635576 after weighting attitude
## 16 -0.002996157 after weighting depress_prior
## 17 0.010356045 after weighting workpref
## 18 0.007203650 after weighting nohsdip
Note: For the above code, you only need to change x, treatment, and mediator when applying the balance checking function.
Generate balance checking plot:
ggplot(balance.results.1, aes(sd.mean.diff, covariates)) +
geom_point(aes(color = weighting)) +
labs(x = "Standardized Mean Differences", y = "") +
theme(legend.title=element_blank()) +
geom_vline(xintercept=c(-0.25,-0.1,0.1,0.25), linetype="longdash", col = c("red", "blue", "blue", "red")) +
xlim(-2.5, 2.5)
ggplot(balance.results.0, aes(sd.mean.diff, covariates)) +
geom_point(aes(color = weighting)) +
labs(x = "Standardized Mean Differences", y = "") +
theme(legend.title=element_blank()) +
geom_vline(xintercept=c(-0.25,-0.1,0.1,0.25), linetype="longdash", col = c("red", "blue", "blue", "red")) +
xlim(-2.5, 2.5)
Note: When applying the above code, you do not need to make any change.
The balance checking results indicate that logit propensity score, workpref, nohsdip, emp_prior, and attitude are originally unbalanced between those who were employed and those who were unemployed within the treatment group, and logit propensity score, workpref, emp_prior, and ADCPC are originally unbalanced between those who were employed and those who were unemployed within the control group. After propensity score adjustment, the distributions of logit propensity score and all the covariates are well balanced between those who were employed and those who were unemployed within each treatment group.
Estimate causal effects:
If you have categorical variables with more than two categories, please generate dummies for them first. Please avoid signs such as “/” in your variables names using the code: gsub(“[[:punct:]]”, ““, x), where x is a vector of covariate names. Please only include g-1 dummies for a categorical variable with g categories.
library(rmpw)
rmpw(data = newws, treatment = "treat", mediator = "emp", outcome = "depression", propensity_x = c("emp_prior", "nevmar", "hispanic", "ADCPC", "attitude", "depress_prior", "workpref", "nohsdip"), outcome_x = c("emp_prior", "nevmar", "hispanic", "ADCPC", "attitude", "depress_prior", "workpref", "nohsdip"),decomposition = 1)
## Estimate Std.Error t value Pr(>|t|)
## Gamma.0 5.0089 1.7036 2.9401 0.0033 *
## Natural Direct Effect 1.2192 0.8494 1.4355 0.1511
## Natural Indirect Effect -0.7607 0.4429 -1.7176 0.0859
## Pure Indirect Effect -0.0217 0.213 -0.1017 0.919
## T-by-M Interaction Effect -0.739 0.5049 -1.4637 0.1433
## emp_prior 0.1219 0.6916 0.1762 0.8601
## nevmar 0.9775 0.6346 1.5404 0.1235
## hispanic 1.436 0.7072 2.0305 0.0423 .
## ADCPC -1e-04 1e-04 -1.0248 0.3055
## attitude -0.4179 0.526 -0.7945 0.4269
## depress_prior 1.7242 0.3647 4.7276 <0.001 **
## workpref -0.2202 0.4452 -0.4947 0.6208
## nohsdip 0.4304 0.6382 0.6744 0.5001
The effect estimates are mostly similar to those obtained from the regression-based mediation analysis. However, the effects here are mostly insignificant, while the regression-based method reported significant NIE, NDE (significant at the significance level of 0.1, but insignificant at the significance level of 0.05), and INT. This is mainly due to the lower estimation efficiency of the weighting-based method. Nevertheless, the weighting-based method is more robust to possible model misspecifications.