interaction effects in random intercept cross-lagged panel models (RI-CLPMs)

library(modsem)

Here we show an example of a random intercept cross-lagged panel model with a within \(\times\) within interaction effect. The example below is based on Testing for Within \(\times\) Within and Between \(\times\) Within Moderation Using Random Intercept Cross-Lagged Panel Models. In particular, it is based on Section 8.4. Within \(\times\) Within Interaction Using the Within-Person Component of a Time-Varying Moderator, and the corresponding Mplus output files on OSF. In the original article, the Bayes estimator was used, but here we use LMS. The LMS approach in Mplus integrates along four dimensions, making estimation quite impractical for this model. The LMS estimator in modsem, however, only has to integrate along two dimensions, making estimation viable.

Simulate Data

library(mvtnorm)
set.seed(235910)

N <- 10000

# Simulate between-person random intercepts
phi <- matrix(c(
  0.806, 0.481, 0.438,
  0.481, 0.758, 0.469,
  0.438, 0.469, 1.290
), nrow = 3, byrow = TRUE)

Xi <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = phi
)

RIemo <- Xi[, 1]
RIper <- Xi[, 2]
RIcon <- Xi[, 3]


# Time 3
psi3 <- matrix(c(
  1.234, 0.328, 0.478,
  0.328, 1.614, 0.427,
  0.478, 0.427, 2.743
), nrow = 3, byrow = TRUE)

zeta3 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi3
)

wemo_3 <- zeta3[, 1]
wper_3 <- zeta3[, 2]
wcon_3 <- zeta3[, 3]


# Time 5
psi5 <- matrix(c(
  1.489, 0.397, 0.301,
  0.397, 1.143, 0.202,
  0.301, 0.202, 0.764
), nrow = 3, byrow = TRUE)

zeta5 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi5
)

wemo_5 <- 0.142 * wemo_3 + 0.071 * wper_3 + 0.086 * wcon_3 - 0.010 * (wcon_3 * wper_3) + zeta5[,1]
wper_5 <- 0.018 * wemo_3 + 0.100 * wper_3 + 0.050 * wcon_3 + zeta5[,2]
wcon_5 <- -0.008 * wemo_3 + 0.006 * wper_3 + 0.105 * wcon_3 + zeta5[,3]

# Time 7
psi7 <- matrix(c(
  1.808, 0.521, 0.475,
  0.521, 1.287, 0.311,
  0.475, 0.311, 0.881
), nrow = 3, byrow = TRUE)

zeta7 <- rmvnorm(
  N,
  mean = c(0, 0, 0),
  sigma = psi7
)

wemo_7 <- 0.361 * wemo_5 + 0.097 * wper_5 + 0.147 * wcon_5 + 0.105 * (wcon_5 * wper_5) + zeta7[,1]
wper_7 <- 0.030 * wemo_5 + 0.348 * wper_5 + 0.123 * wcon_5 + zeta7[,2]
wcon_7 <- 0.074 * wemo_5 + 0.056 * wper_5 + 0.086 * wcon_5 + zeta7[,3]

# Indicators/Observed Variables
data <- data.frame(
  emo_3 = 1.385 + RIemo + wemo_3 + rnorm(N, 0, sqrt(.200)),
  emo_5 = 1.407 + RIemo + wemo_5 + rnorm(N, 0, sqrt(.200)),
  emo_7 = 1.528 + RIemo + wemo_7 + rnorm(N, 0, sqrt(.200)),

  per_3 = 1.563 + RIper + wper_3 + rnorm(N, 0, sqrt(.200)),
  per_5 = 1.176 + RIper + wper_5 + rnorm(N, 0, sqrt(.200)),
  per_7 = 1.242 + RIper + wper_7 + rnorm(N, 0, sqrt(.200)),

  con_3 = 2.827 + RIcon + wcon_3 + rnorm(N, 0, sqrt(.200)),
  con_5 = 1.517 + RIcon + wcon_5 + rnorm(N, 0, sqrt(.200)),
  con_7 = 1.402 + RIcon + wcon_7 + rnorm(N, 0, sqrt(.200))
)

Fit The Model

model.inp <- '
  # Create between components (random intercepts)
  RIemo =~ 1 * emo_3 + 1 * emo_5 + 1 * emo_7;
  RIcon =~ 1 * con_3 + 1 * con_5 + 1 * con_7;

  # Create within-person centered variables
  wemo_3 =~ 1 * emo_3;
  wemo_5 =~ 1 * emo_5;
  wemo_7 =~ 1 * emo_7;

  wcon_3 =~ 1 * con_3;
  wcon_5 =~ 1 * con_5;
  wcon_7 =~ 1 * con_7;

  # Moderator also needs to be decomposed into within and between parts
  RIper =~ 1 * per_3 + 1 * per_5 + 1 * per_7;

  wper_3 =~ 1 * per_3;
  wper_5 =~ 1 * per_5;
  wper_7 =~ 1 * per_7;

  # Constrain the measurement error variances close to zero
  # to allow for reasonable imputation times
  con_3 ~~ 0.2 * con_3
  con_5 ~~ 0.2 * con_5
  con_7 ~~ 0.2 * con_7
  emo_3 ~~ 0.2 * emo_3
  emo_5 ~~ 0.2 * emo_5
  emo_7 ~~ 0.2 * emo_7
  per_3 ~~ 0.2 * per_3
  per_5 ~~ 0.2 * per_5
  per_7 ~~ 0.2 * per_7

  # Estimate the covariance between the random intercepts
  RIemo ~~ RIper
  RIemo ~~ RIcon
  RIper ~~ RIcon

  # Estimate the lagged effects between
  # the within-person centered variables
  wemo_7 ~ wemo_5 + wper_5 + wcon_5
  wper_7 ~ wemo_5 + wper_5 + wcon_5
  wcon_7 ~ wemo_5 + wper_5 + wcon_5

  wemo_5 ~ wemo_3 + wper_3 + wcon_3
  wper_5 ~ wemo_3 + wper_3 + wcon_3
  wcon_5 ~ wemo_3 + wper_3 + wcon_3

  # Specify interaction terms between within-person centred
  # conduct problems and the within-person centered peer problems
  # predict within-person centred emotional problems with interaction
  wemo_7 ~ wcon_5:wper_5
  wemo_5 ~ wcon_3:wper_3

  # Estimate the covariance between the within-person
  # components at the first wave
  wemo_3 ~~ wper_3
  wemo_3 ~~ wcon_3
  wper_3 ~~ wcon_3

  # Estimate the covariances between the residuals of
  # the within-person components (the innovations)
  wemo_5 ~~ wper_5
  wemo_5 ~~ wcon_5
  wper_5 ~~ wcon_5

  wemo_7 ~~ wper_7
  wemo_7 ~~ wcon_7
  wper_7 ~~ wcon_7
'

fit.lms <- modsem(
  model.syntax = model.inp,
  data              = data,
  method            = "lms",
  nodes             = 32,
  optimize          = FALSE, # we're currently unable to optimize starting parameters here
  orthogonal.x      = TRUE,  # make sure the model is identifiable
  orthogonal.y      = TRUE,  # not strictly necessary for this model in particular
  auto.split.syntax = TRUE   # allow eta x eta interactions
)

summary(fit.lms)