The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

Getting Started with mlmoderator

Overview

mlmoderator provides a unified workflow for probing, plotting, and interpreting multilevel interaction effects from mixed-effects models fitted with lme4::lmer().

The core workflow is:

mod <- lmer(math ~ ses * climate + (1 + ses | school), data = dat)

mlm_probe(mod, pred = "ses", modx = "climate")   # simple slopes
mlm_jn(mod,    pred = "ses", modx = "climate")   # Johnson-Neyman region
mlm_plot(mod,  pred = "ses", modx = "climate")   # interaction plot
mlm_summary(mod, pred = "ses", modx = "climate") # full report

The built-in dataset

library(mlmoderator)
library(lme4)

data(school_data)
head(school_data)
#>   school student  math    ses climate gender
#> 1      1       1 61.06 -0.005   1.371 female
#> 2      1       2 60.54  0.760   1.371   male
#> 3      1       3 60.47  0.039   1.371   male
#> 4      1       4 59.82  0.735   1.371 female
#> 5      1       5 59.16 -0.146   1.371   male
#> 6      1       6 49.71 -0.058   1.371 female
str(school_data)
#> 'data.frame':    3000 obs. of  6 variables:
#>  $ school : Factor w/ 100 levels "1","2","3","4",..: 1 1 1 1 1 1 1 1 1 1 ...
#>  $ student: int  1 2 3 4 5 6 7 8 9 10 ...
#>  $ math   : num  61.1 60.5 60.5 59.8 59.2 ...
#>  $ ses    : num  -0.005 0.76 0.039 0.735 -0.146 ...
#>  $ climate: num  1.37 1.37 1.37 1.37 1.37 ...
#>  $ gender : Factor w/ 2 levels "female","male": 1 2 2 1 2 1 2 2 1 1 ...

school_data contains 3,000 students nested in 100 schools. The outcome (math) was generated with a true cross-level interaction between student SES (ses) and school climate (climate).

Step 1: Center variables

Before fitting, it is good practice to center predictors. mlm_center() supports grand-mean centering, group-mean (within-cluster) centering, and a full within–between decomposition.

# Group-mean center SES within school
dat <- mlm_center(school_data,
                  vars    = "ses",
                  cluster = "school",
                  type    = "group")

# Grand-mean center school climate (level-2 variable)
dat <- mlm_center(dat,
                  vars = "climate",
                  type = "grand")

head(dat[, c("school", "ses", "ses_c", "climate", "climate_c")])
#>   school    ses       ses_c climate climate_c
#> 1      1 -0.005  0.01513333   1.371   1.33844
#> 2      1  0.760  0.78013333   1.371   1.33844
#> 3      1  0.039  0.05913333   1.371   1.33844
#> 4      1  0.735  0.75513333   1.371   1.33844
#> 5      1 -0.146 -0.12586667   1.371   1.33844
#> 6      1 -0.058 -0.03786667   1.371   1.33844

Step 2: Fit the model

mod <- lmer(math ~ ses_c * climate_c + gender + (1 + ses_c | school),
            data = dat, REML = TRUE)
summary(mod)
#> Linear mixed model fit by REML ['lmerMod']
#> Formula: math ~ ses_c * climate_c + gender + (1 + ses_c | school)
#>    Data: dat
#> 
#> REML criterion at convergence: 18414.9
#> 
#> Scaled residuals: 
#>     Min      1Q  Median      3Q     Max 
#> -3.7607 -0.6591 -0.0070  0.6580  3.1325 
#> 
#> Random effects:
#>  Groups   Name        Variance Std.Dev. Corr  
#>  school   (Intercept)  7.841   2.8002         
#>           ses_c        0.168   0.4099   -0.28 
#>  Residual             24.916   4.9916         
#> Number of obs: 3000, groups:  school, 100
#> 
#> Fixed effects:
#>                 Estimate Std. Error t value
#> (Intercept)     49.84009    0.30871 161.446
#> ses_c            1.42623    0.10133  14.075
#> climate_c        1.01234    0.28421   3.562
#> gendermale       0.07486    0.18608   0.402
#> ses_c:climate_c  0.46554    0.09932   4.687
#> 
#> Correlation of Fixed Effects:
#>             (Intr) ses_c  clmt_c gndrml
#> ses_c       -0.105                     
#> climate_c   -0.002  0.000              
#> gendermale  -0.300  0.012  0.007       
#> ss_c:clmt_c  0.001 -0.009 -0.105 -0.005

Step 3: Probe simple slopes

mlm_probe() computes the simple slope of the focal predictor (ses_c) at selected values of the moderator (climate_c).

probe_out <- mlm_probe(mod, pred = "ses_c", modx = "climate_c")
probe_out
#> 
#> --- Simple Slopes: mlm_probe ---
#> Focal predictor : ses_c 
#> Moderator       : climate_c 
#> Confidence level: 0.95 
#> 
#>  climate_c slope    se      t   df      p ci_lower ci_upper
#>     -1.036 0.944 0.145  6.504 2995 < .001    0.659    1.228
#>      0.000 1.426 0.101 14.075 2995 < .001    1.228    1.625
#>      1.036 1.909 0.144 13.277 2995 < .001    1.627    2.191

By default, moderator values are set to −1 SD, Mean, and +1 SD. Other options include "quartiles", "tertiles", or "custom" values via at.

mlm_probe(mod, pred = "ses_c", modx = "climate_c",
          modx.values = "quartiles")
#> 
#> --- Simple Slopes: mlm_probe ---
#> Focal predictor : ses_c 
#> Moderator       : climate_c 
#> Confidence level: 0.95 
#> 
#>  climate_c slope    se      t   df      p ci_lower ci_upper
#>     -0.649 1.124 0.121  9.318 2995 < .001    0.887    1.360
#>      0.057 1.453 0.101 14.324 2995 < .001    1.254    1.652
#>      0.629 1.719 0.119 14.502 2995 < .001    1.487    1.952

Step 4: Johnson–Neyman interval

The Johnson–Neyman (JN) interval identifies the exact moderator value(s) at which the simple slope of ses_c crosses the significance threshold.

jn_out <- mlm_jn(mod, pred = "ses_c", modx = "climate_c")
jn_out
#> 
#> --- Johnson-Neyman Interval: mlm_jn ---
#> Focal predictor : ses_c 
#> Moderator       : climate_c 
#> Alpha           : 0.05 
#> Moderator range : -3.026 to 2.254 
#> 
#> Johnson-Neyman boundary/boundaries ( climate_c ):
#>    -2.088 
#> 
#> Interpretation: The simple slope of 'ses_c' is statistically
#> significant (p < 0.05) for values of 'climate_c'
#>   between -2.07 and 2.254.

Step 5: Plot the interaction

mlm_plot() returns a ggplot object, so it is fully customisable.

mlm_plot(mod, pred = "ses_c", modx = "climate_c")

Add confidence bands, raw data points, or custom labels:

mlm_plot(mod,
         pred         = "ses_c",
         modx         = "climate_c",
         points       = TRUE,
         point_alpha  = 0.2,
         x_label      = "Student SES (group-mean centred)",
         y_label      = "Mathematics Achievement",
         legend_title = "School Climate")

Step 6: Full summary report

mlm_summary() combines the interaction coefficient, simple slopes, and JN region into a single printable object.

mlm_summary(mod, pred = "ses_c", modx = "climate_c")
#> 
#> ========================================
#>   Multilevel Moderation Summary
#> ========================================
#> Focal predictor : ses_c 
#> Moderator       : climate_c 
#> Confidence level: 0.95 
#> 
#> --- Interaction Term ---
#>   ses_c:climate_c               b =   0.466  SE =  0.099  t(2995) =  4.687  p = < .001  [0.271, 0.66]
#> 
#> --- Simple Slopes ---
#>   climate_c      slope        se         t        df         p  95% CI lo  95% CI hi 
#> ------------------------------------------------------------------------------------ 
#>   -1.036         0.944     0.145     6.504    2995.0    < .001     0.659     1.228
#>   0.000          1.426     0.101    14.075    2995.0    < .001     1.228     1.625
#>   1.036          1.909     0.144    13.277    2995.0    < .001     1.627     2.191
#> 
#> --- Johnson-Neyman Region ---
#>   JN boundary/boundaries for 'climate_c': -2.088
#> 
#> ========================================

Tips

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.