1 Scenario and Dataset

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)

2 Questions

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

  1. Load the dataset and report the sample size, and the mean and standard deviation of 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.
  2. Produce a scatterplot of 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?

  1. Fit a simple linear regression predicting 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?

  1. Specify a 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.
  2. Report the \(a\)-path coefficient and interpret its sign in substantive terms: what does it say about how mindfulness practice relates to rumination?
  3. Report the \(b\)-path and \(c'\)-path coefficients, and interpret the sign of each.
  4. Report the bootstrapped indirect effect and its percentile confidence interval. Does the sign of the indirect effect make sense given the signs of the \(a\)-path and \(b\)-path? Explain why or why not, referencing what each path represents.
  5. Compare the direct effect \(c'\) to the total effect \(c\) from Question 3. Is the relationship between mindfulness frequency and life satisfaction fully or partially mediated by rumination? Support your answer with a formal chi-square difference test comparing this model to a nested model in which \(c'\) is fixed to zero, not just by inspecting \(c'\)’s own \(p\)-value.
  6. Report the bootstrapped total effect and its confidence interval, and confirm that it equals the indirect effect plus \(c'\).

Part D – Reporting

  1. Build a summary table listing the direct, indirect, and total effects from the mediation model, each with its bootstrapped 95% confidence interval.
  2. Produce a path diagram of the fitted model showing the standardized path coefficients.
  3. A colleague argues that, because 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.
  4. Write a short results paragraph, addressed to a workplace-wellbeing programme manager, summarizing what this model does and does not establish about the value of mindfulness practice, the role of rumination, and the observational nature of this data (mindfulness-practice frequency, rumination, job demands, and life satisfaction were all measured, not manipulated).

3 Guided Walkthrough

3.1 Inspecting the data

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")

3.2 Model 1 – does mindfulness-practice frequency predict life satisfaction at all? (total effect)

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.

3.3 Model 2 – is the mindfulness-satisfaction relationship carried through rumination?

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"))

3.4 Building the summary table of effects

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

3.5 Reporting the results

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.

4 Solutions

Expected answers to the Questions above, reusing the models fitted in the Guided Walkthrough section.

Part A

  1. \(N\) = 220. See the descriptives chunk above for means and standard deviations. \(r\)(mindfulness_frequency, job_demands) = 0.007: close to zero, consistent with treating 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.
  2. See the scatterplot above: rumination decreases, roughly linearly, as weekly mindfulness-practice frequency increases.

Part B

  1. 4.352 (see 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

  1. See the model_med object and fit_med above for the labeled lavaan syntax and the fitted model.
  2. \(a\) = -5.378: more frequent mindfulness practice is associated with lower rumination, consistent with mindfulness practice reducing the tendency to dwell on negative thoughts.
  3. \(b\) = -0.336: holding mindfulness frequency and job demands constant, more rumination is associated with lower life satisfaction. \(c'\) = 2.56: holding rumination and job demands constant, more frequent mindfulness practice is still associated with higher life satisfaction directly.
  4. Indirect effect = 1.808, 95% CI [1.194, 2.465]. The sign makes sense: the \(a\)-path is negative (more mindfulness, less rumination) and the \(b\)-path is negative (more rumination, lower satisfaction), and the product of two negative paths is positive, matching a positive indirect route from mindfulness frequency to life satisfaction through reduced rumination.
  5. Partial mediation. The direct effect shrinks from \(c\) = 4.352 (Question 3) to \(c'\) = 2.56, but it remains significant (\(p\) < .001), and fixing \(c'\) to zero significantly worsens fit, \(\Delta\chi^2(1)\) = 31.95, \(p\) < .001 (see the 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.
  6. Total effect = 4.368, 95% CI [3.64, 5.106]. It equals the indirect effect plus \(c'\): 1.808 + 2.56 = 4.368, since total was defined in the model as exactly that sum.

Part D

  1. See the Building the summary table of effects section above.
  2. See the lavaanPlot diagram of fit_med above.
  3. 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.
  4. See the Reporting the results paragraph above for a model answer, including the explicit non-causal caveat given that all four variables were measured rather than manipulated.

Common errors and points to watch for

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