# Cross-Level Interaction Workflow

## What is a cross-level interaction?

A cross-level interaction occurs when a level-2 variable (e.g., school climate) moderates the relationship between a level-1 predictor (e.g., student SES) and the outcome (e.g., math achievement).

Formally, the two-level model is:

Level 1: \[math_{ij} = \beta_{0j} + \beta_{1j} \cdot SES_{ij} + e_{ij}\]

Level 2: \[\beta_{0j} = \gamma_{00} + \gamma_{01} \cdot Climate_j + u_{0j}\] \[\beta_{1j} = \gamma_{10} + \gamma_{11} \cdot Climate_j + u_{1j}\]

Substituting gives the composite model: \[math_{ij} = \gamma_{00} + \gamma_{10} SES_{ij} + \gamma_{01} Climate_j + \gamma_{11} SES_{ij} \times Climate_j + u_{0j} + u_{1j} SES_{ij} + e_{ij}\]

The term \(\gamma_{11}\) is the cross-level interaction: it captures how much the SES slope varies as a function of school climate.

## Setup

    library(mlmoderator)
    library(lme4)
    #> Warning: package 'lme4' was built under R version 4.5.2
    #> Loading required package: Matrix
    #> Warning: package 'Matrix' was built under R version 4.5.2

    data(school_data)

## Centering strategy for cross-level interactions

For cross-level interactions, the recommended centering strategy is:

- Level-1 predictor (SES): group-mean center (within-school centering)
- Level-2 moderator (climate): grand-mean center

This separates within-school from between-school variance in SES, and makes the intercept interpretable as the school-average outcome at average climate.

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

    dat <- mlm_center(dat,
                      vars = "climate",
                      type = "grand")      # grand-mean-centred climate

    # Check
    cat("Within-school SES mean (should be ~0):", round(mean(dat$ses_c), 4), "\n")
    #> Within-school SES mean (should be ~0): 0
    cat("Grand-mean climate (should be ~0):",     round(mean(dat$climate_c), 4), "\n")
    #> Grand-mean climate (should be ~0): 0

## Fit the random-slopes model

Because we hypothesise that the SES slope varies across schools (hence the cross-level interaction), we include a random slope for SES.

    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

The `ses_c:climate_c` interaction is the cross-level interaction of interest.

## Probe simple slopes

    probe <- mlm_probe(mod,
                       pred        = "ses_c",
                       modx        = "climate_c",
                       modx.values = "mean-sd",
                       conf.level  = 0.95)
    probe
    #>
    #> --- 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

Interpretation: At schools with above-average climate (+1 SD), the positive effect of SES on math is amplified. At schools with below-average climate (−1 SD), the SES effect is weaker (and may not be significant).

## Johnson–Neyman region

The JN interval pinpoints the exact climate values where the SES effect transitions from non-significant to significant.

    jn <- mlm_jn(mod, pred = "ses_c", modx = "climate_c")
    jn
    #>
    #> --- 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.

## Visualise

    mlm_plot(
      mod,
      pred         = "ses_c",
      modx         = "climate_c",
      modx.values  = "mean-sd",
      interval     = TRUE,
      x_label      = "Student SES (group-mean centred)",
      y_label      = "Mathematics Achievement",
      legend_title = "School Climate"
    )

## Full summary for reporting

    mlm_summary(mod,
                pred        = "ses_c",
                modx        = "climate_c",
                modx.values = "mean-sd",
                jn          = TRUE)
    #>
    #> ========================================
    #>   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
    #>
    #> ========================================

## Reporting guidance

When reporting a cross-level interaction, include:

1. The interaction coefficient — \(\hat{\gamma}_{11}\), its SE, t-ratio, and p-value.
2. Simple slopes — the slope of the level-1 predictor at each chosen level of the moderator, with 95% CIs.
3. The JN region — the moderator value(s) at which the slope crosses significance.
4. An interaction plot — a figure showing predicted outcome lines across the level-1 predictor for each selected moderator value.

Example write-up:

> There was a significant cross-level interaction between student SES and school climate, \(\hat{\gamma}_{11}\) = [value], SE = [value], t([df]) = [value], p = [value]. Simple slope analysis indicated that the positive effect of SES on math achievement was significantly stronger at schools with high climate (+1 SD: \(b\) = [value], 95% CI [lo, hi]) than at schools with low climate (−1 SD: \(b\) = [value], 95% CI [lo, hi]). Johnson–Neyman analysis revealed that the SES slope was statistically significant for climate values above [JN boundary].

## Assumptions to check

- Random slope variance — a non-trivial \(\tau_{11}\) supports the rationale for the cross-level interaction.
- Sufficient level-2 sample size — cross-level interactions require at least 30–50 level-2 units for stable estimation.
- Centering — ensure level-1 predictors are group-mean centred if the level-2 variable moderates the within-cluster slope.