# If necessary, install the 'lavaan' package.
# install.packages('lavaan', dependencies = T)
# Load the required packages.
require(lavaan)
# Load in the data
## Traditional RI-CLPM
<- read.table("/Users/jeroenmulder/Git/RICLPM/data/RICLPM.dat",
dat col.names = c(
"x1", "x2", "x3", "x4", "x5",
"y1", "y2", "y3", "y4", "y5")
) ## Extension 1
<- read.table("C:\\Users\\5879167\\Documents\\GitHub\\RI-CLPM\\data\\RICLPM-Z.dat",
datZ col.names = c(
"x1", "x2", "x3", "x4", "x5",
"y1", "y2", "y3", "y4", "y5", "z2", "z1")
)## Extension 2
<- read.table("/Users/jeroenmulder/Git/RICLPM/data/RICLPM-MG.dat",
datMG col.names = c(
"x1", "x2", "x3", "x4", "x5",
"y1", "y2", "y3", "y4", "y5", "G")
) ## Extension 3
<- read.table("/Users/jeroenmulder/GitHub/RI-CLPM/data/RICLPM-MI.dat",
datMI col.names = c(
"x11", "x12", "x13",
"x21", "x22", "x23",
"x31", "x32", "x33",
"x41", "x42", "x43",
"x51", "x52", "x53",
"y11", "y12", "y13",
"y21", "y22", "y23",
"y31", "y32", "y33",
"y41", "y42", "y43",
"y51", "y52", "y53")
)
Using lavaan
Below you can find the code for installing and loading the required package lavaan (Rosseel 2012), as well as for reading in the example data. You can specify the path to the data yourself, or through a menu by using the file.choose()
-function. You can download the simulated example datasets here.
The RI-CLPM
To specify the RI-CLPM we need four parts.
- A between part, consisting of the random intercepts. It is specified using the
=~
command,RIx =~ 1*x1 + 1*x2 ...
, where1*
fixes the factor loading to one. - A within part, consisting of within-unit fluctuations. It is also specified using the
=~
command,wx1 =~ 1*x1; wx2 =~ 1*x2; ...
. - The lagged regressions between the within-unit components, using
wx2 + wy2 ~ wx1 + wy1; wx3 + wy3 ~ wx2 + wy2; ...
. - Relevant covariances in both the between and within part. In the within part the components at wave 1, and their residuals at waves 2 and further are correlated within each wave, using
wx1 ~~ wy1; wx2 ~~ wy2;...
. We also need to specify their (residual) variances here usingwx1 ~~ wx1; wx2 ~~ wx2; ...
. For the between part we have to specify the variances and covariance of the random intercepts usingRIx ~~ RIy;
.
The models below are fitted using the lavaan()
function. Other functions for fitting SEM models using lavaan include sem()
and cfa()
. These functions rely on different defaults than lavaan()
and fitting the below models with these functions can result in model misspecification and subsequently convergence issues.
The code for specifying the basic RI-CLPM is given below.
<- '
RICLPM # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
Imposing constraints to the model can be achieved through pre-multiplication. It means that we have to prepend the number that we want to fix the parameter to, and an asterisk, to the parameter in the model specification. For example, F =~ 0*x1
fixes the factor loading of item x1
to factor F
to 0. Using pre-multiplication we can also constrain parameters to be the same by giving them the same label. Below we specify an RI-CLPM with the following constraints:
- fixed auto-regressive and cross-lagged relations over time,
wx2 ~ a*wx1 + b*wy1; ...
, - time-invariant (residual) (co-)variances in the within-person part
wx2 ~~ cov*wy2; ...
,wx2 ~~ vx*wx2; ...
, andwy2 ~~ vy*wy2; ...
, and - constrained grand means over time,
x1 + ... ~ mx*1
andy1 + ... ~ my*1
.
<- '
RICLPM5 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
# (constrained)
wx2 ~ a*wx1 + b*wy1
wy2 ~ c*wx1 + d*wy1
wx3 ~ a*wx2 + b*wy2
wy3 ~ c*wx2 + d*wy2
wx4 ~ a*wx3 + b*wy3
wy4 ~ c*wx3 + d*wy3
wx5 ~ a*wx4 + b*wy4
wy5 ~ c*wx4 + d*wy4
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations, constrained)
wx2 ~~ cov*wy2
wx3 ~~ cov*wy3
wx4 ~~ cov*wy4
wx5 ~~ cov*wy5
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
# (constrained)
wx1 ~~ wx1 # Variance
wy1 ~~ wy1
wx2 ~~ vx*wx2 # Residual variance
wy2 ~~ vy*wy2
wx3 ~~ vx*wx3
wy3 ~~ vy*wy3
wx4 ~~ vx*wx4
wy4 ~~ vy*wy4
wx5 ~~ vx*wx5
wy5 ~~ vy*wy5
# Constrain grand means over time
x1 + x2 + x3 + x4 + x5 ~ mx*1
y1 + y2 + y3 + y4 + y5 ~ my*1
'
<- lavaan(RICLPM5,
RICLPM5.fit data = dat,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM5.fit, standardized = T)
Rather than imposing constraints on the unstandardized auto-regressive and cross-lagged relations over time, we can impose constraints on the standardized lagged effects, as explained in the FAQ. Compared to the previous model (with constraints over time on the unstandardized lagged effects), there are multiple changes to the syntax. First, we freely estimate the factor loadings that link the observed variables to the within-components (by premultiplying with NA
), rather than fixing them to 1:
'
# Create within-components with freely estimated factor loadings
wx1 =~ NA*x1
wx2 =~ NA*x2
wx3 =~ NA*x3
wx4 =~ NA*x4
wx5 =~ NA*x5
wy1 =~ NA*y1
wy2 =~ NA*y2
wy3 =~ NA*y3
wy4 =~ NA*y4
wy5 =~ NA*y5
'
Second, we set the variances of within-components at first wave to 1, and label the covariance (now also the correlation) between them:
'
# Set variances of within-components at first wave to 1
wx1 ~~ 1*wx1
wy1 ~~ 1*wy1
# Estimate correlation between within-components at first wave
wx1 ~~ cor1*wy1
'
Third, we give the residual variance and covariances between the within-component each a unique label:
'
# Label residual covariances
wx2 ~~ rcov2*wy2
wx3 ~~ rcov3*wy3
wx4 ~~ rcov4*wy4
wx5 ~~ rcov5*wy5
# Label residual variances
wx2 ~~ rvx2*wx2
wy2 ~~ rvy2*wy2
wx3 ~~ rvx3*wx3
wy3 ~~ rvy3*wy3
wx4 ~~ rvx4*wx4
wy4 ~~ rvy4*wy4
wx5 ~~ rvx5*wx5
wy5 ~~ rvy5*wy5
'
Finally, we compute the correlations between the within-components themselves at each wave, and then constrain the residual variances to ensure that the total variance of each within-component equals 1. This is done with the :=
command:
'
# Compute correlations of within-components at each wave
cor2 := a*c + b*d + a*d*cor1 + b*c*cor1 + rcov2
cor3 := a*c + b*d + a*d*cor2 + b*c*cor2 + rcov3
cor4 := a*c + b*d + a*d*cor3 + b*c*cor3 + rcov4
# Contrain residual variances of within-components such that variance of each
# within-component equals 1
rvx2 == 1 - (a*a + b*b + 2*a*b*cor1)
rvy2 == 1 - (c*c + d*d + 2*c*d*cor1)
rvx3 == 1 - (a*a + b*b + 2*a*b*cor2)
rvy3 == 1 - (c*c + d*d + 2*c*d*cor2)
rvx4 == 1 - (a*a + b*b + 2*a*b*cor3)
rvy4 == 1 - (c*c + d*d + 2*c*d*cor3)
rvx5 == 1 - (a*a + b*b + 2*a*b*cor4)
rvy5 == 1 - (c*c + d*d + 2*c*d*cor4)
'
These steps ultimately result in the below syntax. Comparing the constrainted unstandardized and constrained standardized results shows that these are now equivalent.
<- '
RICLPM6 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-components with freely estimated factor loadings
wx1 =~ NA*x1
wx2 =~ NA*x2
wx3 =~ NA*x3
wx4 =~ NA*x4
wx5 =~ NA*x5
wy1 =~ NA*y1
wy2 =~ NA*y2
wy3 =~ NA*y3
wy4 =~ NA*y4
wy5 =~ NA*y5
# Estimate lagged effects between within-person centered variables
# (constrained)
wx2 ~ a*wx1 + b*wy1
wy2 ~ c*wx1 + d*wy1
wx3 ~ a*wx2 + b*wy2
wy3 ~ c*wx2 + d*wy2
wx4 ~ a*wx3 + b*wy3
wy4 ~ c*wx3 + d*wy3
wx5 ~ a*wx4 + b*wy4
wy5 ~ c*wx4 + d*wy4
# Label residual covariances
wx2 ~~ rcov2*wy2
wx3 ~~ rcov3*wy3
wx4 ~~ rcov4*wy4
wx5 ~~ rcov5*wy5
# Estimate correlation between within-components at first wave
wx1 ~~ cor1*wy1
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Set variances of within-components at first wave to 1
wx1 ~~ 1*wx1
wy1 ~~ 1*wy1
# Label residual variances
wx2 ~~ rvx2*wx2
wy2 ~~ rvy2*wy2
wx3 ~~ rvx3*wx3
wy3 ~~ rvy3*wy3
wx4 ~~ rvx4*wx4
wy4 ~~ rvy4*wy4
wx5 ~~ rvx5*wx5
wy5 ~~ rvy5*wy5
# Constrain grand means over time
x1 + x2 + x3 + x4 + x5 ~ mx*1
y1 + y2 + y3 + y4 + y5 ~ my*1
# Compute correlations of within-components at each wave
cor2 := a*c + b*d + a*d*cor1 + b*c*cor1 + rcov2
cor3 := a*c + b*d + a*d*cor2 + b*c*cor2 + rcov3
cor4 := a*c + b*d + a*d*cor3 + b*c*cor3 + rcov4
# Contrain residual variances of within-components such that variance of each
# within-component equals 1
rvx2 == 1 - (a*a + b*b + 2*a*b*cor1)
rvy2 == 1 - (c*c + d*d + 2*c*d*cor1)
rvx3 == 1 - (a*a + b*b + 2*a*b*cor2)
rvy3 == 1 - (c*c + d*d + 2*c*d*cor2)
rvx4 == 1 - (a*a + b*b + 2*a*b*cor3)
rvy4 == 1 - (c*c + d*d + 2*c*d*cor3)
rvx5 == 1 - (a*a + b*b + 2*a*b*cor4)
rvy5 == 1 - (c*c + d*d + 2*c*d*cor4)
'
<- lavaan(RICLPM6,
RICLPM6.fit data = dat,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM6.fit, standardized = F)
summary(RICLPM6.fit, standardized = T)
Extension 1: Including time-invariant predictors and outcomes
Use the tabs below to navigate to the model specification of the RI-CLPM with
- a time-invariant predictor \(z_{1}\) of the observed variables (\(z_{1}\) \(\rightarrow\) observed),
- a time-invariant predictor \(z_{1}\) of the random intercepts (\(z_{1}\) \(\rightarrow\) RIs),
- random intercepts predicting a time-invariant outcome \(z_{2}\) (RIs \(\rightarrow\) \(z_{2}\)), or
- within components predicting a time-invariant outcome \(z_{2}\) (within \(\rightarrow\) \(z_{2}\)).
Below you can find the code for an RI-CLPM with 5 waves and a time-invariant predictor \(z_{1}\) for the observed variables. The effect of \(z_{1}\) on the observed variables is constrained to be the same across waves.
<- '
RICLPM.ext1 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Regression of observed variables on z1 (constrained)
x1 + x2 + x3 + x4 + x5 ~ s1*z1 # Constrained over time.
y1 + y2 + y3 + y4 + y5 ~ s2*z1 # Constrained over time.
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM.ext1,
RICLPM.ext1.fit data = datZ,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM.ext1.fit, standardized = T)
Below you can find the code for an RI-CLPM with 5 waves and a time-invariant predictor \(z_{1}\) for the random intercepts.
<- '
RICLPM1.ext1 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Regression of random intercepts on z1
RIx + RIy ~ z1 # Constrained over time.
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM1.ext1,
RICLPM1.ext1.fit data = datZ,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM1.ext1.fit, standardized = T)
Below you can find code for an RI-CLPM with the between components (the random intercepts) predicting a time-invariant outcome. For sake of completeness, the time-invariant \(z_{1}\) is also included as a predictor the for observed variables (constrained).
<- '
RICLPM3.ext1 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Regression of time-invariant outcome z2 on random intercepts
z2 ~ RIx + RIy
z2 ~~ z2 # Residual variance z2
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Regression of observed variables on z1 (constrained)
x1 + x2 + x3 + x4 + x5 ~ s1*z1
y1 + y2 + y3 + y4 + y5 ~ s2*z1
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM3.ext1,
RICLPM3.ext1.fit data = datZ,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM3.ext1.fit, standardized = T)
Below you can find code for an RI-CLPM with the within components predicting a time-invariant outcome. For sake of completeness, the time-invariant \(z_{1}\) is also included as a predictor the for observed variables (constrained).
<- '
RICLPM4.ext1 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Regression of time-invariant outcome z2 on within components
z2 ~ wx1 + wx2 + wx3 + wx4 + wx5 + wy1 + wy2 + wy3 + wy4 + wy5
z2 ~~ z2 # Residual variance z2
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Regression of observed variables on z1 (constrained)
x1 + x2 + x3 + x4 + x5 ~ s1*z1
y1 + y2 + y3 + y4 + y5 ~ s2*z1
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM4.ext1,
RICLPM4.ext1.fit data = datZ,
missing = 'ML',
meanstructure = T,
int.ov.free = T
) summary(RICLPM4.ext1.fit, standardized = T)
Extension 2: Multiple group
Use the tabs below to navigate to the model specification of the basic multiple-group model, or the model with constrained lagged parameters (and intercepts across groups).
Below you can find the code for a multiple group RI-CLPM with 5 waves.
<- '
RICLPM.ext2 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
wx2 + wy2 ~ wx1 + wy1
wx3 + wy3 ~ wx2 + wy2
wx4 + wy4 ~ wx3 + wy3
wx5 + wy5 ~ wx4 + wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM.ext2,
RICLPM.ext2.fit data = datMG,
missing = 'ML',
group = "G",
meanstructure = T,
int.ov.free = T
)summary(RICLPM.ext2.fit)
Below you can find the code for a multiple group RI-CLPM with 4 waves. The lagged-parameters are constrained to be equal over time.
<- '
RICLPM1.ext2 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables (constrain
# autoregressive effects across groups)
wx2 ~ c(a1, a1)*wx1 + c(b1, b1)*wy1
wy2 ~ c(c1, c1)*wx1 + c(d1, d1)*wy1
wx3 ~ c(a2, a2)*wx2 + c(b2, b2)*wy2
wy3 ~ c(c2, c2)*wx2 + c(d2, d2)*wy2
wx4 ~ c(a3, a3)*wx3 + c(b3, b3)*wy3
wy4 ~ c(c3, c3)*wx3 + c(d3, d3)*wy3
wx5 ~ c(a4, a4)*wx4 + c(b4, b4)*wy4
wy5 ~ c(c4, c4)*wx4 + c(d4, d4)*wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of the within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(RICLPM1.ext2,
RICLPM1.ext2.fit data = datMG,
missing = 'ML',
group = "G",
meanstructure = T,
int.ov.free = T
)summary(RICLPM1.ext2.fit)
Extension 3: Multiple indicators
Use the tabs below to navigate to the model specification of a multiple indicator RI-CLPM, 5 waves and 3 indicators for each variable at each wave. The five steps correspond to:
- the configural model (Step 1),
- weak factorial invariance (Step 2),
- strong factorial invariance (Step 3),
- strong factorial invariance with factor loadings equal to the within-person factor loadings (Extra), and
- the latent RI-CLPM (Step 4).
When we have three indicators \(X\), measured at five waves, we specify three random intercepts to capture the trait-like part of each indicator, that is, RIX1 =~ 1*x11 1*x21 ...
, RIX2 =~ 1*x121 1*x22@1 ...
, and RIX3 =~ 1*x13 1*x23 ...
. In addition, we specify five within-unit components that capture the state-like part at each wave, using WFX1 =~ x11 x12 x13; WFX2 =~ x21 x22 x23; ...
.
At the latent within-unit level, we specify the dynamic model in Mplus using WFX2 ~ WFY1 + WFX1; WFX3 ~ WFY2 + WFX2; ...
. In addition, we allow the within-person factors at the first wave, and their residuals at subsequent waves to be correlated within each wave, WFX1 ~~ WFY1; WFX2 ~~ WFY2; ...
. The six random intercepts are allowed to be freely correlated with each other, either through inclusion of RIX1 + RIX2 + ... ~~ RIX1 + RIX2 + ...
in the model syntax, or by default when using the cfa()
function as we have done below.
<- '
RICLPM1.ext3
################
# BETWEEN PART #
################
# Create between factors (random intercepts) for each indicator separately
RIX1 =~ 1*x11 + 1*x21 + 1*x31 + 1*x41 + 1*x51
RIX2 =~ 1*x12 + 1*x22 + 1*x32 + 1*x42 + 1*x52
RIX3 =~ 1*x13 + 1*x23 + 1*x33 + 1*x43 + 1*x53
RIY1 =~ 1*y11 + 1*y21 + 1*y31 + 1*y41 + 1*y51
RIY2 =~ 1*y12 + 1*y22 + 1*y32 + 1*y42 + 1*y52
RIY3 =~ 1*y13 + 1*y23 + 1*y33 + 1*y43 + 1*y53
##################################
# WITHIN PART: MEASUREMENT MODEL #
##################################
# Factor models for X at 5 waves
WFX1 =~ x11 + x12 + x13
WFX2 =~ x21 + x22 + x23
WFX3 =~ x31 + x32 + x33
WFX4 =~ x41 + x42 + x43
WFX5 =~ x51 + x52 + x53
# Factor models for Y at 5 waves
WFY1 =~ y11 + y12 + y13
WFY2 =~ y21 + y22 + y23
WFY3 =~ y31 + y32 + y33
WFY4 =~ y41 + y42 + y43
WFY5 =~ y51 + y52 + y53
#########################
# WITHIN PART: DYNAMICS #
#########################
# Specify lagged effects between within-person centered latent variables
WFX2 + WFY2 ~ WFX1 + WFY1
WFX3 + WFY3 ~ WFX2 + WFY2
WFX4 + WFY4 ~ WFX3 + WFY3
WFX5 + WFY5 ~ WFX4 + WFY4
# Estimate correlations within same wave
WFX1 ~~ WFY1
WFX2 ~~ WFY2
WFX3 ~~ WFY3
WFX4 ~~ WFY4
WFX5 ~~ WFY5
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Constrain covariance of between factors and exogenous within factors to 0
RIX1 + RIX2 + RIX3 + RIY1 + RIY2 + RIY3 ~~ 0*WFY1 + 0*WFX1
'
<- cfa(RICLPM1.ext3,
RICLPM1.ext3.fit data = datMI,
missing = 'ML',
meanstructure = TRUE
)summary(RICLPM1.ext3.fit, standardized = T)
In second step, we constrain the factor loadings to be invariant over time using the labels a*
, b*
, etc.
<- '
RICLPM2.ext3
################
# BETWEEN PART #
################
# Create between factors (random intercepts) for each indicator separately
RIX1 =~ 1*x11 + 1*x21 + 1*x31 + 1*x41 + 1*x51
RIX2 =~ 1*x12 + 1*x22 + 1*x32 + 1*x42 + 1*x52
RIX3 =~ 1*x13 + 1*x23 + 1*x33 + 1*x43 + 1*x53
RIY1 =~ 1*y11 + 1*y21 + 1*y31 + 1*y41 + 1*y51
RIY2 =~ 1*y12 + 1*y22 + 1*y32 + 1*y42 + 1*y52
RIY3 =~ 1*y13 + 1*y23 + 1*y33 + 1*y43 + 1*y53
##################################
# WITHIN PART: MEASUREMENT MODEL #
##################################
# Factor models for X at 5 waves (constrained)
WFX1 =~ a*x11 + b*x12 + c*x13
WFX2 =~ a*x21 + b*x22 + c*x23
WFX3 =~ a*x31 + b*x32 + c*x33
WFX4 =~ a*x41 + b*x42 + c*x43
WFX5 =~ a*x51 + b*x52 + c*x53
# Factor models for Y at 5 waves (constrained)
WFY1 =~ d*y11 + e*y12 + f*y13
WFY2 =~ d*y21 + e*y22 + f*y23
WFY3 =~ d*y31 + e*y32 + f*y33
WFY4 =~ d*y41 + e*y42 + f*y43
WFY5 =~ d*y51 + e*y52 + f*y53
#########################
# WITHIN PART: DYNAMICS #
#########################
# Specify lagged effects between within-person centered latent variables
WFX2 + WFY2 ~ WFX1 + WFY1
WFX3 + WFY3 ~ WFX2 + WFY2
WFX4 + WFY4 ~ WFX3 + WFY3
WFX5 + WFY5 ~ WFX4 + WFY4
# Estimate correlations within same wave
WFX1 ~~ WFY1
WFX2 ~~ WFY2
WFX3 ~~ WFY3
WFX4 ~~ WFY4
WFX5 ~~ WFY5
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Constrain covariance of between factors and exogenous within factors to 0
RIX1 + RIX2 + RIX3 + RIY1 + RIY2 + RIY3 ~~ 0*WFY1 + 0*WFX1
'
<- cfa(RICLPM2.ext3,
RICLPM2.ext3.fit data = datMI,
missing = 'ML'
)summary(RICLPM2.ext3.fit, standardized = T)
Multiple indicator RI-CLPM 4 waves with 3 indicators for each variable at each wave (24 observed variables). Fitting a model with constraints to ensure strong factorial invariance, with a random intercept for each indicator separately.
<- '
RICLPM3.ext3
################
# BETWEEN PART #
################
# Create between factors (random intercepts) for each indicator separately
RIX1 =~ 1*x11 + 1*x21 + 1*x31 + 1*x41 + 1*x51
RIX2 =~ 1*x12 + 1*x22 + 1*x32 + 1*x42 + 1*x52
RIX3 =~ 1*x13 + 1*x23 + 1*x33 + 1*x43 + 1*x53
RIY1 =~ 1*y11 + 1*y21 + 1*y31 + 1*y41 + 1*y51
RIY2 =~ 1*y12 + 1*y22 + 1*y32 + 1*y42 + 1*y52
RIY3 =~ 1*y13 + 1*y23 + 1*y33 + 1*y43 + 1*y53
##################################
# WITHIN PART: MEASUREMENT MODEL #
##################################
# Factor models for X at 5 waves (constrained)
WFX1 =~ a*x11 + b*x12 + c*x13
WFX2 =~ a*x21 + b*x22 + c*x23
WFX3 =~ a*x31 + b*x32 + c*x33
WFX4 =~ a*x41 + b*x42 + c*x43
WFX5 =~ a*x51 + b*x52 + c*x53
# Factor models for Y at 5 waves (constrained)
WFY1 =~ d*y11 + e*y12 + f*y13
WFY2 =~ d*y21 + e*y22 + f*y23
WFY3 =~ d*y31 + e*y32 + f*y33
WFY4 =~ d*y41 + e*y42 + f*y43
WFY5 =~ d*y51 + e*y52 + f*y53
# Constrained intercepts over time (necessary for strong factorial
# invariance; without these contraints we have week factorial invariance)
x11 + x21 + x31 + x41 + x51 ~ g*1
x12 + x22 + x32 + x42 + x52 ~ h*1
x13 + x23 + x33 + x43 + x53 ~ i*1
y11 + y21 + y31 + y41 + y51 ~ j*1
y12 + y22 + y32 + y42 + y52 ~ k*1
y13 + y23 + y33 + y43 + y53 ~ l*1
# Free latent means from t = 2 onward (only do this in combination with
# constraints on intercepts; without these, this would not be specified)
WFX2 + WFX3 + WFX4 + WFX5 + WFY2 + WFY3 + WFY4 + WFY5 ~ 1
#########################
# WITHIN PART: DYNAMICS #
#########################
# Specify lagged effects between within-person centered latent variables
WFX2 + WFY2 ~ WFX1 + WFY1
WFX3 + WFY3 ~ WFX2 + WFY2
WFX4 + WFY4 ~ WFX3 + WFY3
WFX5 + WFY5 ~ WFX4 + WFY4
# Estimate correlations within same wave
WFX1 ~~ WFY1
WFX2 ~~ WFY2
WFX3 ~~ WFY3
WFX4 ~~ WFY4
WFX5 ~~ WFY5
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Constrain covariance of between factors and exogenous within factors to 0
RIX1 + RIX2 + RIX3 + RIY1 + RIY2 + RIY3 ~~ 0*WFY1 + 0*WFX1
'
<- cfa(RICLPM3.ext3,
RICLPM3.ext3.fit data = datMI,
missing = 'ML'
)summary(RICLPM3.ext3.fit, standardized = T)
Multiple indicator RI-CLPM, 5 waves with 3 indicators for each variable at each wave (30 observed variables). Fitting a model With constraints to ensure strong factorial invariance, with a random intercept for each indicator separately, for which a factor model is specified, with factor loadings equal to the within- person factor loadings.
<- '
RICLPM4.ext3
################
# BETWEEN PART #
################
# Create between factors (random intercepts) for each indicator separately
RIX1 =~ 1*x11 + 1*x21 + 1*x31 + 1*x41 + 1*x51
RIX2 =~ 1*x12 + 1*x22 + 1*x32 + 1*x42 + 1*x52
RIX3 =~ 1*x13 + 1*x23 + 1*x33 + 1*x43 + 1*x53
RIY1 =~ 1*y11 + 1*y21 + 1*y31 + 1*y41 + 1*y51
RIY2 =~ 1*y12 + 1*y22 + 1*y32 + 1*y42 + 1*y52
RIY3 =~ 1*y13 + 1*y23 + 1*y33 + 1*y43 + 1*y53
# Create a single random intercept for all X variables, and another for all
# Y variables and constrain the factor loadings to be identical to
# within-person factor loadings
RIX =~ a*RIX1 + b*RIX2 + c*RIX3
RIY =~ d*RIY1 + e*RIY2 + f*RIY3
# Add (co)variance between two higher-order random intercepts
RIX ~~ RIY
RIX ~~ RIX
RIY ~~ RIY
# Constrain measurement error variances of the second order factor model to 0
RIX1 ~~ 0*RIX1
RIX2 ~~ 0*RIX2
RIX3 ~~ 0*RIX3
RIY1 ~~ 0*RIY1
RIY2 ~~ 0*RIY2
RIY3 ~~ 0*RIY3
##################################
# WITHIN PART: MEASUREMENT MODEL #
##################################
# Factor models for X at 5 waves (constrained)
WFX1 =~ a*x11 + b*x12 + c*x13
WFX2 =~ a*x21 + b*x22 + c*x23
WFX3 =~ a*x31 + b*x32 + c*x33
WFX4 =~ a*x41 + b*x42 + c*x43
WFX5 =~ a*x51 + b*x52 + c*x53
# Factor models for Y at 5 waves (constrained)
WFY1 =~ d*y11 + e*y12 + f*y13
WFY2 =~ d*y21 + e*y22 + f*y23
WFY3 =~ d*y31 + e*y32 + f*y33
WFY4 =~ d*y41 + e*y42 + f*y43
WFY5 =~ d*y51 + e*y52 + f*y53
# Constrained intercepts over time (necessary for strong factorial invariance;
# without these contraints we have week factorial invariance)
x11 + x21 + x31 + x41 + x51 ~ g*1
x12 + x22 + x32 + x42 + x52 ~ h*1
x13 + x23 + x33 + x43 + x53 ~ i*1
y11 + y21 + y31 + y41 + y51 ~ j*1
y12 + y22 + y32 + y42 + y52 ~ k*1
y13 + y23 + y33 + y43 + y53 ~ l*1
# Free latent means from t = 2 onward (only do this in combination with
# constraints on intercepts; without these, this would not be identified)
WFX2 + WFX3 + WFX4 + WFX5 + WFY2 + WFY3 + WFY4 + WFY5 ~ 1
#########################
# WITHIN PART: DYNAMICS #
#########################
# Specify lagged effects between within-person centered latent variables
WFX2 + WFY2 ~ WFX1 + WFY1
WFX3 + WFY3 ~ WFX2 + WFY2
WFX4 + WFY4 ~ WFX3 + WFY3
WFX5 + WFY5 ~ WFX4 + WFY4
# Estimate correlations within same wave
WFX1 ~~ WFY1
WFX2 ~~ WFY2
WFX3 ~~ WFY3
WFX4 ~~ WFY4
WFX5 ~~ WFY5
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Constrain covariance of between factors and exogenous within factors to 0
RIX + RIY + RIX1 + RIX2 + RIX3 + RIY1 + RIY2 + RIY3 ~~ 0*WFY1 + 0*WFX1
'
<- cfa(RICLPM4.ext3,
RICLPM4.ext3.fit data = datMI,
missing = 'ML'
)summary(RICLPM4.ext3.fit, standardized = T)
Multiple indicator RI-CLPM, 5 waves with 3 indicators for each variable at each wave (30 observed variables). Fitting a model with constraints to ensure strong factorial invariance, with the RI-CLPM at the latent level.
<- '
RICLPM5.ext3
#####################
# MEASUREMENT MODEL #
#####################
# Factor models for X at 5 waves (constrained)
FX1 =~ a*x11 + b*x12 + c*x13
FX2 =~ a*x21 + b*x22 + c*x23
FX3 =~ a*x31 + b*x32 + c*x33
FX4 =~ a*x41 + b*x42 + c*x43
FX5 =~ a*x51 + b*x52 + c*x53
# Factor models for Y at 5 waves (constrained)
FY1 =~ d*y11 + e*y12 + f*y13
FY2 =~ d*y21 + e*y22 + f*y23
FY3 =~ d*y31 + e*y32 + f*y33
FY4 =~ d*y41 + e*y42 + f*y43
FY5 =~ d*y51 + e*y52 + f*y53
# Constrained intercepts over time (this is necessary for strong factorial
# invariance; without these contraints we have week factorial invariance)
x11 + x21 + x31 + x41 + x51 ~ g*1
x12 + x22 + x32 + x42 + x52 ~ h*1
x13 + x23 + x33 + x43 + x53 ~ i*1
y11 + y21 + y31 + y41 + y51 ~ j*1
y12 + y22 + y32 + y42 + y52 ~ k*1
y13 + y23 + y33 + y43 + y53 ~ l*1
# Free latent means from t = 2 onward (only do this in combination with
# constraints on intercepts; without these, this would not be identified)
FX2 + FX3 + FX4 + FX5 + FY2 + FY3 + FY4 + FY5 ~ 1
################
# BETWEEN PART #
################
# Create between factors (random intercepts)
RIX =~ 1*FX1 + 1*FX2 + 1*FX3 + 1*FX4 + 1*FX5
RIY =~ 1*FY1 + 1*FY2 + 1*FY3 + 1*FY4 + 1*FY5
# Set residual variances of all FX and FY variables to 0
FX1 ~~ 0*FX1
FX2 ~~ 0*FX2
FX3 ~~ 0*FX3
FX4 ~~ 0*FX4
FX5 ~~ 0*FX5
FY1 ~~ 0*FY1
FY2 ~~ 0*FY2
FY3 ~~ 0*FY3
FY4 ~~ 0*FY4
FY5 ~~ 0*FY5
###############
# WITHIN PART #
###############
# Create within-components
WFX1 =~ 1*FX1
WFX2 =~ 1*FX2
WFX3 =~ 1*FX3
WFX4 =~ 1*FX4
WFX5 =~ 1*FX5
WFY1 =~ 1*FY1
WFY2 =~ 1*FY2
WFY3 =~ 1*FY3
WFY4 =~ 1*FY4
WFY5 =~ 1*FY5
# Specify lagged effects between within-person centered latent variables
WFX2 + WFY2 ~ WFX1 + WFY1
WFX3 + WFY3 ~ WFX2 + WFY2
WFX4 + WFY4 ~ WFX3 + WFY3
WFX5 + WFY5 ~ WFX4 + WFY4
# Estimate correlations within same wave
WFX2 ~~ WFY2
WFX3 ~~ WFY3
WFX4 ~~ WFY4
WFX5 ~~ WFY5
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Set correlations between between-factors (random intercepts) and within-
# factors at wave 1 at 0
RIX + RIY ~~ 0*WFX1 + 0*WFY1
'
<- cfa(RICLPM5.ext3,
RICLPM5.ext3.fit data = datMI,
missing = 'ML'
)summary(RICLPM5.ext3.fit, standardized = T)
\(\bar{\chi}^{2}\)-test
The use of the chi-square difference test is wide-spread in the SEM community to test constraints on parameters. However, when constraints are placed on the bound of the parameter space, we should use the chi-bar-square test (\(\bar{\chi}^{2}\)-test) (Stoel et al. 2006). For example, if we constrain the variances of all random intercepts (and their covariance) in the RI-CLPM to zero, we obtain a model that is nested under the RI-CLPM, and that is statistically equivalent to the traditional cross-lagged panel model (CLPM). Below you can find R code for performing the chi-bar-square test (code by Rebecca M. Kuiper) for comparing these two models. It involves
- fitting both the RI-CLPM (
RICLPM.fit
) and CLPM (CLPM.fit
); - extracting the covariance matrix of the random intercepts;
- extracting the \(\chi^{2}\) and degrees of freedom of both models; and
- performing the \(\bar{\chi}^{2}\)-test using the
ChiBarSq.DiffTest
package (Kuiper 2020).
# Install and load required packages
## library(devtools)
## install_github("rebeccakuiper/ChiBarSq.DiffTest")
library(ChiBarSq.DiffTest)
# Step 1: Fit RI-CLPM (RICLPM.fit) and CLPM (CLPM.fit)
# Step 2: Find indices needed for covariance matrix of random intercepts
vcov(RICLPM.fit) # Full covariance matrix
<- c(22, 23) # The 22nd and 23rd indices regard random intercepts
indices <- length(indices) # Number of random intercepts
q <- vcov(RICLPM.fit)[indices, indices] # Extract covariance matrix of random intercepts
S
# Step 3: Extract Chi-square and degrees of freedom
<- summary(CLPM.fit, fit.measures = TRUE)[1]$FIT[c("chisq")]
Chi2_clpm <- summary(RICLPM.fit, fit.measures = TRUE)[1]$FIT[c("chisq")]
Chi2_riclpm
<- summary(CLPM.fit, fit.measures = TRUE)[1]$FIT[c("df")]
df_clpm <- summary(RICLPM.fit, fit.measures = TRUE)[1]$FIT[c("df")]
df_riclpm
# Step 4: Perform Chi-bar-square test (and obtain Chi-bar-square weights)
<- ChiBarSq.DiffTest(q, S, Chi2_clpm, Chi2_riclpm, df_clpm, df_riclpm)
ChiBar2DiffTest
ChiBar2DiffTest$p_value ChiBar2DiffTest
GORICA
Rather than evaluating a null-hypothesis of no effect, researchers may be interested in evaluating a theory-based, informative hypothesis (Altinisik et al. 2021). Informative hypotheses can reflect a priori theories, and often contain orderings of (standardized) parameters. For example, one may hypothesize that one cross-lagged relationship is higher than another one; often referred to as “causal dominance”. Such a causal dominance hypothesis can be evaluated with an AIC-type criterion called the GORICA (Altinisik et al. 2021; Sukpan and Kuiper 2023). Below you find code for evaluating a causal dominance hypothesis using the GORICA, for both the RI-CLPM with time-independent and time-specific parameters. We make use of the example data provided with these online supplementary materials. For more information, we refer to https://github.com/rebeccakuiper/Tutorials.
In the below R code, we use the GORICA to evaluate the informative hypothesis that the cross-lagged effects from \(X\) to \(Y\) are stronger than those from \(Y\) to \(X\) (i.e., \(X\) is causally dominant). To specify the informative hypothesis, we use the labels of parameters as used in the model syntax: b2
, c2
, b3
, c3
, etc. Analysis shows that the informative hypothesis (i.e., \(X\) is causally dominant) has 13.137 times more support than its complement (i.e., \(Y\) is causally dominant).
# R package for testing of linear (in)equality restrictions on parameters in linear models
library(restriktor)
# Informative hypothesis about cross-lagged effects (using parameter labels in model)
<- "abs(b2) < abs(c2); abs(b3) < abs(c3);
H1 abs(b4) < abs(c4); abs(b5) < abs(c5)"
# Fit RI-CLPM
<- '
RICLPM_H1 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
wx2 ~ a2*wx1 + b2*wy1
wy2 ~ c2*wx1 + d2*wy1
wx3 ~ a3*wx2 + b3*wy2
wy3 ~ c3*wx2 + d3*wy2
wx4 ~ a4*wx3 + b4*wy3
wy4 ~ c4*wx3 + d4*wy3
wx5 ~ a5*wx4 + b5*wy4
wy5 ~ c5*wx4 + d5*wy4
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations)
wx2 ~~ wy2
wx3 ~~ wy3
wx4 ~~ wy4
wx5 ~~ wy5
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
wx1 ~~ wx1 # Variances
wy1 ~~ wy1
wx2 ~~ wx2 # Residual variances
wy2 ~~ wy2
wx3 ~~ wx3
wy3 ~~ wy3
wx4 ~~ wx4
wy4 ~~ wy4
wx5 ~~ wx5
wy5 ~~ wy5
'
<- lavaan(
fit_H1 model = RICLPM_H1,
data = dat,
missing = "ML",
meanstructure = TRUE,
int.ov.free = TRUE
)
summary(fit_H1, standardized = TRUE)
# Compute GORICA values and weights (use `standardized = TRUE`)
<- goric(
GORICA_H1
fit_H1, standardized = TRUE,
hypotheses = list(H1ws.l = H1),
comparison = "complement", # Test informative hypothesis versus its complement
type = "gorica"
)
GORICA_H1
The GORICA can also be used to evaluate informative hypotheses when parameters are constrained over time. In the below R code, the lagged effects, residuals variances, and intercepts are restricted to be time-invariant. We again use the GORICA to evaluate the informative hypothesis that the cross-lagged effects from \(X\) to \(Y\) are stronger than those from \(Y\) to \(X\) (i.e., \(X\) is causally dominant). Analysis shows that the informative hypothesis (i.e., \(X\) is causally dominant) has 785.564 times more support than its complement (i.e., \(Y\) is causally dominant).
# Informative hypothesis about time-invariant cross-lagged effects
<- "abs(b) < abs(c)"
H2
# Fit RI-CLPM
<- '
RICLPM_H2 # Create between components (random intercepts)
RIx =~ 1*x1 + 1*x2 + 1*x3 + 1*x4 + 1*x5
RIy =~ 1*y1 + 1*y2 + 1*y3 + 1*y4 + 1*y5
# Create within-person centered variables
wx1 =~ 1*x1
wx2 =~ 1*x2
wx3 =~ 1*x3
wx4 =~ 1*x4
wx5 =~ 1*x5
wy1 =~ 1*y1
wy2 =~ 1*y2
wy3 =~ 1*y3
wy4 =~ 1*y4
wy5 =~ 1*y5
# Estimate lagged effects between within-person centered variables
# (constrained)
wx2 ~ a*wx1 + b*wy1
wy2 ~ c*wx1 + d*wy1
wx3 ~ a*wx2 + b*wy2
wy3 ~ c*wx2 + d*wy2
wx4 ~ a*wx3 + b*wy3
wy4 ~ c*wx3 + d*wy3
wx5 ~ a*wx4 + b*wy4
wy5 ~ c*wx4 + d*wy4
# Estimate covariances between residuals of within-person centered variables
# (i.e., innovations, constrained)
wx2 ~~ cov*wy2
wx3 ~~ cov*wy3
wx4 ~~ cov*wy4
wx5 ~~ cov*wy5
# Estimate covariance between within-person centered variables at first wave
wx1 ~~ wy1 # Covariance
# Estimate variance and covariance of random intercepts
RIx ~~ RIx
RIy ~~ RIy
RIx ~~ RIy
# Estimate (residual) variance of within-person centered variables
# (constrained)
wx1 ~~ wx1 # Variance
wy1 ~~ wy1
wx2 ~~ vx*wx2 # Residual variance
wy2 ~~ vy*wy2
wx3 ~~ vx*wx3
wy3 ~~ vy*wy3
wx4 ~~ vx*wx4
wy4 ~~ vy*wy4
wx5 ~~ vx*wx5
wy5 ~~ vy*wy5
# Constrain grand means over time
x1 + x2 + x3 + x4 + x5 ~ mx*1
y1 + y2 + y3 + y4 + y5 ~ my*1
'
<- lavaan(
fit_H2 model = RICLPM_H2,
data = dat,
missing = "ML",
meanstructure = TRUE,
int.ov.free = TRUE
)
summary(fit_H2, standardized = TRUE)
# Compute GORICA values and weights
<- goric(
GORICA_H2
fit_H2, hypotheses = list(H2),
comparison = "complement", # Test informative hypothesis versus its complement
type = "gorica"
)
GORICA_H2