A wellbeing researcher wants to know whether working adults who practice mindfulness more often report greater life satisfaction, and whether that relationship runs through how much they ruminate on negative thoughts, once job demands are taken into account.
You have anonymized survey data from 220 working adults: weekly
mindfulness-practice frequency, a rumination score, job demands, and a
life-satisfaction score. The data is
mindfulness_satisfaction_homework.csv. Set up and interpret
this path model independently, following the same logic as the lab
practical – as a model for how to reason through each stage, not a
template to copy mechanically, since the variables and the model here
are simpler (a single mediator, not two).
| Variable | Type | Description |
|---|---|---|
| adult_id | Integer | Anonymous participant identifier, 1-220 |
| mindfulness_frequency | Continuous | Average number of mindfulness-practice sessions per week |
| rumination | Continuous | Rumination score, 0-100 (mediator; higher = more rumination) |
| job_demands | Continuous | Job demands score, 0-100 (covariate, controlled on life satisfaction) |
| life_satisfaction | Continuous | Life satisfaction score, 0-100 (outcome; higher = more satisfied) |
These questions ask for more independent judgment than the lab
practical. In several places you are asked to carry out a step yourself,
using the lab practical’s stages as your guide, rather than being told
exactly which line of code to write. Use
mindfulness_satisfaction_homework.csv.
Part A – Getting to know the data
mindfulness_frequency,
rumination, job_demands, and
life_satisfaction. Report the correlation between
mindfulness_frequency and job_demands, and
explain what it implies for treating job_demands as a
covariate rather than a mediator.rumination against
mindfulness_frequency. Describe, in a sentence or two, what
the plot suggests.Part B – Research question 1: does mindfulness-practice frequency predict life satisfaction at all?
life_satisfaction from mindfulness_frequency
alone. Report and interpret the coefficient, with correct units, and
state what it represents in a mediation framework.Part C – Research question 2: is the mindfulness-satisfaction relationship carried through rumination?
lavaan path model with
rumination as a mediator between
mindfulness_frequency and life_satisfaction,
entering job_demands as a covariate on
life_satisfaction. Label the \(a\), \(b\), and \(c'\) paths, and define the indirect and
total effects using :=. Fit the model with
se = "bootstrap" and bootstrap = 5000.Part D – Reporting
job_demands correlates
with life_satisfaction, it must also be acting as a second
mediator of the mindfulness-satisfaction relationship, in the same way
rumination does. Explain, referring to how
job_demands is entered into the model, why it is a
covariate rather than a mediator here, and what would need to change in
the model for it to be tested as a mediator instead.d <- read.csv("data/mindfulness_satisfaction_homework.csv")
str(d)
## 'data.frame': 220 obs. of 5 variables:
## $ adult_id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ mindfulness_frequency: num 6.6 2.9 0.9 2.1 0.9 0 0.7 2 2 5.3 ...
## $ rumination : num 0 54.9 40.1 49.6 72.4 52 46.9 52.5 42.6 21.1 ...
## $ job_demands : num 37.8 35.7 45.3 73.8 56.7 34.6 24.7 61.4 50.3 38.8 ...
## $ life_satisfaction : num 96.6 52.4 63.1 38 31 59.5 61.4 58.1 66.7 79.2 ...
d %>%
summarise(n = n(),
mindfulness_frequency_M = mean(mindfulness_frequency), mindfulness_frequency_SD = sd(mindfulness_frequency),
rumination_M = mean(rumination), rumination_SD = sd(rumination),
job_demands_M = mean(job_demands), job_demands_SD = sd(job_demands),
life_satisfaction_M = mean(life_satisfaction), life_satisfaction_SD = sd(life_satisfaction)) %>%
kable(digits = 2)
| n | mindfulness_frequency_M | mindfulness_frequency_SD | rumination_M | rumination_SD | job_demands_M | job_demands_SD | life_satisfaction_M | life_satisfaction_SD |
|---|---|---|---|---|---|---|---|---|
| 220 | 2.54 | 1.43 | 38.78 | 12.65 | 50.61 | 12.62 | 63.4 | 10.8 |
cor(d$mindfulness_frequency, d$job_demands)
## [1] 0.006812
mindfulness_frequency and job_demands are
close to uncorrelated in this sample, consistent with
job_demands being an independent covariate rather than
something mindfulness practice itself changes.
ggplot(d, aes(mindfulness_frequency, rumination)) +
geom_point(alpha = 0.6, color = ntnublue) +
geom_smooth(method = "lm", se = FALSE, color = red) +
labs(x = "Mindfulness sessions per week", y = "Rumination",
title = "a-path: mindfulness practice and rumination")
fit_total <- lm(life_satisfaction ~ mindfulness_frequency, data = d)
summary(fit_total)
##
## Call:
## lm(formula = life_satisfaction ~ mindfulness_frequency, data = d)
##
## Residuals:
## Min 1Q Median 3Q Max
## -25.268 -5.155 -0.053 6.070 27.350
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 52.35 1.22 42.8 <2e-16 ***
## mindfulness_frequency 4.35 0.42 10.4 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 8.86 on 218 degrees of freedom
## Multiple R-squared: 0.33, Adjusted R-squared: 0.327
## F-statistic: 107 on 1 and 218 DF, p-value: <2e-16
confint(fit_total)
## 2.5 % 97.5 %
## (Intercept) 49.943 54.76
## mindfulness_frequency 3.524 5.18
Each additional weekly mindfulness session is associated with a 4.352-point increase in predicted life satisfaction, and this total effect is statistically significant. As in the lab practical, this coefficient (conventionally \(c\)) is the starting point the mediation model below will decompose.
model_med <- '
life_satisfaction ~ b*rumination + cprime*mindfulness_frequency + job_demands
rumination ~ a*mindfulness_frequency
indirect := a*b
total := indirect + cprime
'
fit_med <- sem(model_med, data = d, se = "bootstrap", bootstrap = 5000)
summary(fit_med, standardized = TRUE, fit.measures = TRUE)
## lavaan 0.6-21 ended normally after 1 iteration
##
## Estimator ML
## Optimization method NLMINB
## Number of model parameters 6
##
## Number of observations 220
##
## Model Test User Model:
##
## Test statistic 0.335
## Degrees of freedom 1
## P-value (Chi-square) 0.563
##
## Model Test Baseline Model:
##
## Test statistic 270.847
## Degrees of freedom 5
## P-value 0.000
##
## User Model versus Baseline Model:
##
## Comparative Fit Index (CFI) 1.000
## Tucker-Lewis Index (TLI) 1.013
##
## Loglikelihood and Information Criteria:
##
## Loglikelihood user model (H0) -1569.828
## Loglikelihood unrestricted model (H1) -1569.660
##
## Akaike (AIC) 3151.655
## Bayesian (BIC) 3172.017
## Sample-size adjusted Bayesian (SABIC) 3153.003
##
## Root Mean Square Error of Approximation:
##
## RMSEA 0.000
## 90 Percent confidence interval - lower 0.000
## 90 Percent confidence interval - upper 0.148
## P-value H_0: RMSEA <= 0.050 0.658
## P-value H_0: RMSEA >= 0.080 0.233
##
## Standardized Root Mean Square Residual:
##
## SRMR 0.011
##
## Parameter Estimates:
##
## Standard errors Bootstrap
## Number of requested bootstrap draws 5000
## Number of successful bootstrap draws 5000
##
## Regressions:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## life_satisfaction ~
## rumintn (b) -0.336 0.048 -6.938 0.000 -0.336 -0.395
## mndfln_ (cprm) 2.560 0.399 6.420 0.000 2.560 0.339
## jb_dmnd -0.273 0.040 -6.750 0.000 -0.273 -0.320
## rumination ~
## mndfln_ (a) -5.378 0.528 -10.186 0.000 -5.378 -0.606
##
## Variances:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## .life_satisfctn 53.635 5.023 10.679 0.000 53.635 0.466
## .rumination 100.807 10.975 9.185 0.000 100.807 0.633
##
## Defined Parameters:
## Estimate Std.Err z-value P(>|z|) Std.lv Std.all
## indirect 1.808 0.327 5.532 0.000 1.808 0.240
## total 4.368 0.378 11.545 0.000 4.368 0.579
pe <- parameterEstimates(fit_med, ci = TRUE, boot.ci.type = "perc")
pe %>% kable(digits = 3)
| lhs | op | rhs | label | est | se | z | pvalue | ci.lower | ci.upper |
|---|---|---|---|---|---|---|---|---|---|
| life_satisfaction | ~ | rumination | b | -0.336 | 0.048 | -6.938 | 0 | -0.429 | -0.239 |
| life_satisfaction | ~ | mindfulness_frequency | cprime | 2.560 | 0.399 | 6.420 | 0 | 1.806 | 3.373 |
| life_satisfaction | ~ | job_demands | -0.273 | 0.040 | -6.750 | 0 | -0.353 | -0.195 | |
| rumination | ~ | mindfulness_frequency | a | -5.378 | 0.528 | -10.186 | 0 | -6.384 | -4.335 |
| life_satisfaction | life_satisfaction | 53.635 | 5.023 | 10.679 | 0 | 43.230 | 62.786 | ||
| rumination | rumination | 100.807 | 10.975 | 9.185 | 0 | 79.480 | 122.410 | ||
| mindfulness_frequency | mindfulness_frequency | 2.023 | 0.000 | NA | NA | 2.023 | 2.023 | ||
| mindfulness_frequency | job_demands | 0.122 | 0.000 | NA | NA | 0.122 | 0.122 | ||
| job_demands | job_demands | 158.569 | 0.000 | NA | NA | 158.569 | 158.569 | ||
| indirect | := | a*b | indirect | 1.808 | 0.327 | 5.532 | 0 | 1.194 | 2.465 |
| total | := | indirect+cprime | total | 4.368 | 0.378 | 11.545 | 0 | 3.640 | 5.106 |
The \(a\)-path shows that each additional weekly mindfulness session is associated with a -5.378-point change in rumination, and the \(b\)-path shows that, holding mindfulness frequency and job demands constant, each additional point of rumination is associated with a -0.336-point change in life satisfaction. The bootstrapped indirect effect is 1.808, 95% CI [1.194, 2.465].
As in the lab practical, whether mediation is full or partial should be settled with a formal nested-model test on the direct path, not by inspecting \(c'\)’s own \(p\)-value alone.
model_med_constrained <- '
life_satisfaction ~ b*rumination + 0*mindfulness_frequency + job_demands
rumination ~ a*mindfulness_frequency
indirect := a*b
'
fit_med_constrained <- sem(model_med_constrained, data = d, se = "bootstrap", bootstrap = 5000)
lrt <- anova(fit_med, fit_med_constrained)
lrt
##
## Chi-Squared Difference Test
##
## Df AIC BIC Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
## fit_med 1 3152 3172 0.34
## fit_med_constrained 2 3182 3199 32.29 31.9 0.375 1 1.6e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The table above shows both models’ chi-square and degrees of freedom, the difference in chi-square and in degrees of freedom on the difference row, and the resulting \(p\)-value from referring that difference to a chi-square distribution on the corresponding degrees of freedom. Here the test is significant (\(\Delta\chi^2(1)\) = 31.95, \(p\) < .001): fixing \(c'\) to zero clearly worsens fit, so the direct path from mindfulness frequency to life satisfaction is still needed, and rumination only partially mediates the relationship.
lavaanPlot(model = fit_med, coefs = TRUE, stand = TRUE, stars = "regress",
node_options = list(shape = "box", fontname = "Helvetica"),
edge_options = list(color = "grey40"))
pe %>%
filter(label %in% c("a", "b", "cprime", "indirect", "total")) %>%
transmute(Effect = recode(label,
a = "a-path (mindfulness -> rumination)",
b = "b-path (rumination -> life satisfaction)",
cprime = "Direct effect (c')",
indirect = "Indirect (via rumination)",
total = "Total effect"),
Estimate = est, `CI lower` = ci.lower, `CI upper` = ci.upper) %>%
kable(digits = 3)
| Effect | Estimate | CI lower | CI upper |
|---|---|---|---|
| b-path (rumination -> life satisfaction) | -0.336 | -0.429 | -0.239 |
| Direct effect (c’) | 2.560 | 1.806 | 3.373 |
| a-path (mindfulness -> rumination) | -5.378 | -6.384 | -4.335 |
| Indirect (via rumination) | 1.808 | 1.194 | 2.465 |
| Total effect | 4.368 | 3.640 | 5.106 |
Weekly mindfulness-practice frequency was associated with higher life satisfaction among the 220 working adults in this sample (total effect \(c\)). Part of this relationship was carried through rumination: more frequent mindfulness practice was associated with less rumination, and less rumination was associated with higher life satisfaction, producing a bootstrapped indirect effect distinguishable from zero. A formal chi-square difference test showed that the direct path from mindfulness frequency to life satisfaction was still required once rumination was accounted for, so rumination partially, not fully, mediated this relationship. Because mindfulness-practice frequency, rumination, job demands, and life satisfaction were all measured rather than manipulated, these results describe an association consistent with a mediating role for rumination, but do not on their own establish that increasing how often an adult practices mindfulness would causally reduce rumination or increase life satisfaction.
Expected answers to the Questions above, reusing the models fitted in the Guided Walkthrough section.
Part A
job_demands
as an independent covariate rather than a variable mindfulness practice
itself changes, which is why it is entered as a control on the outcome
rather than modeled as a mediator.Part B
summary(fit_total) above for \(SE\), \(t\), \(df\), and \(p\)). This is the total effect, \(c\): the overall relationship between
mindfulness frequency and life satisfaction before rumination is
introduced as a mediator.Part C
model_med object and fit_med above
for the labeled lavaan syntax and the fitted model.anova(fit_med, fit_med_constrained) output above for both
models’ chi-square and \(df\) and the
difference row). The direct path is therefore still needed: rumination
carries part, but not all, of the relationship between mindfulness
frequency and life satisfaction.total was defined in the model as exactly that
sum.Part D
lavaanPlot diagram of fit_med
above.job_demands is entered only as
+ job_demands in the life_satisfaction
equation: it has no equation of its own predicting it from
mindfulness_frequency, so the model never estimates a path
from mindfulness frequency to job demands, and no indirect route through
job demands is defined. To test job_demands as a mediator
instead of a covariate, the model would need an additional equation,
job_demands ~ mindfulness_frequency, and a corresponding
indirect effect defined as the product of that new path and the existing
job_demands slope on life_satisfaction,
exactly as rumination is modeled.Common errors and points to watch for
job_demands as though it were a second
mediator, when it was never modeled as a consequence of
mindfulness_frequency.sessionInfo()
## R version 4.4.2 (2024-10-31)
## Platform: aarch64-apple-darwin20
## Running under: macOS 27.0.1
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Europe/Oslo
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.4.0 knitr_1.51 lavaanPlot_0.8.1 lavaan_0.6-21 lubridate_1.9.5 forcats_1.0.1
## [7] stringr_1.6.0 dplyr_1.2.0 purrr_1.2.1 readr_2.2.0 tidyr_1.3.2 tibble_3.3.1
## [13] ggplot2_4.0.2 tidyverse_2.0.0 pacman_0.5.1
##
## loaded via a namespace (and not attached):
## [1] DiagrammeR_1.0.11 sass_0.4.10 generics_0.1.4 xml2_1.5.2 lattice_0.22-9 stringi_1.8.7
## [7] hms_1.1.4 digest_0.6.39 magrittr_2.0.4 evaluate_1.0.5 grid_4.4.2 timechange_0.4.0
## [13] RColorBrewer_1.1-3 fastmap_1.2.0 Matrix_1.7-5 jsonlite_2.0.0 mgcv_1.9-4 viridisLite_0.4.3
## [19] scales_1.4.0 pbivnorm_0.6.0 textshaping_1.0.5 jquerylib_0.1.4 mnormt_2.1.2 cli_3.6.5
## [25] rlang_1.1.7 visNetwork_2.1.4 splines_4.4.2 withr_3.0.2 cachem_1.1.0 yaml_2.3.12
## [31] otel_0.2.0 parallel_4.4.2 tools_4.4.2 tzdb_0.5.0 vctrs_0.7.2 R6_2.6.1
## [37] stats4_4.4.2 lifecycle_1.0.5 htmlwidgets_1.6.4 pkgconfig_2.0.3 pillar_1.11.1 bslib_0.10.0
## [43] gtable_0.3.6 glue_1.8.0 systemfonts_1.3.2 xfun_0.57 tidyselect_1.2.1 rstudioapi_0.18.0
## [49] farver_2.1.2 nlme_3.1-168 htmltools_0.5.9 labeling_0.4.3 svglite_2.2.2 rmarkdown_2.30
## [55] compiler_4.4.2 S7_0.2.1 quadprog_1.5-8