I am currently estimating a random-intercept cross-lagged panel model (RI-CLPM) in lavaan and would like to incorporate auxiliary variables (dummy-coded disciplinary background variables) to strengthen the plausibility of the Missing at Random (MAR) assumption.
Using semTools::cfa.auxiliary(), the model converges without any problems. However, I receive the following warning:
lavaan WARNING: the covariance matrix of the residuals of the observed variables (theta) is not positive definite.
Despite this warning, the model appears to be functioning. The overall model fit is good, there are no Heywood cases or negative residual variances, and the parameter estimates are virtually identical to those obtained from the model without auxiliary variables.
While searching for an explanation, I found a previous discussion (https://groups.google.com/g/lavaan/c/kr0JWacZLT8/m/rfGquwzSCgAJ). Based on the suggestions in that discussion, I attempted to implement the auxiliary variables manually using the saturated correlates approach, specifying them directly in the lavaan syntax ( specifying covariances among each of the observed covariates, covariances between the observed covariates and all within person factors, and variances for the observed covariates)
However, this approach raises a new set of issues:
At this point, I am unsure whether the problem lies in my manual specification or whether the warning produced by cfa.auxiliary() might be expected in this type of model.
I would therefore greatly appreciate any advice on the following questions:
I have attached my model syntax (with cfa.auxiliary and manual) and would be very grateful for any suggestions or comments.
Thank you very much for your time and help!
Approach using cfa.auxiliary:
RICLPM_lifesat <- '
#####################
# MEASUREMENT MODEL #
#####################
# Factor models for perceived success at 4 waves (constrained)
psuc_t1 =~ 1*psuc1_t1 + a*psuc2_t1 + b*psuc3_t1
psuc_t2 =~ 1*psuc1_t2 + a*psuc2_t2 + b*psuc3_t2
psuc_t3 =~ 1*psuc1_t3 + a*psuc2_t3 + b*psuc3_t3
psuc_t4 =~ 1*psuc1_t4 + a*psuc2_t4 + b*psuc3_t4
# Constrained intercepts over time (this is necessary for strong factorial
# invariance; without these contraints we have weak factorial invariance)
psuc1_t1 + psuc1_t2 + psuc1_t3 + psuc1_t4 ~ i1*1
psuc2_t1 + psuc2_t2 + psuc2_t3 + psuc2_t4 ~ i2*1
psuc3_t1 + psuc3_t2 + psuc3_t3 + psuc3_t4 ~ i3*1
# Free latent means from t = 2 onward (only do this in combination with
# constraints on intercepts; without these, this would not be identified)
psuc_t2 + psuc_t3 + psuc_t4 ~ 1
# Correlated residuals of identical indicators across waves
psuc1_t1 ~~ psuc1_t2 + psuc1_t3 + psuc1_t4
psuc1_t2 ~~ psuc1_t3 + psuc1_t4
psuc1_t3 ~~ psuc1_t4
psuc2_t1 ~~ psuc2_t2 + psuc2_t3 + psuc2_t4
psuc2_t2 ~~ psuc2_t3 + psuc2_t4
psuc2_t3 ~~ psuc2_t4
psuc3_t1 ~~ psuc3_t2 + psuc3_t3 + psuc3_t4
psuc3_t2 ~~ psuc3_t3 + psuc3_t4
psuc3_t3 ~~ psuc3_t4
################
# BETWEEN PART #
################
# Create between factors (random intercepts)
RI_psuc =~ 1*psuc_t1 + 1*psuc_t2 + 1*psuc_t3 + 1*psuc_t4
RI_lsat =~ 1*lsat_t1 + 1*lsat_t2 + 1*lsat_t3 + 1*lsat_t4
# Set residual variances of repeated measures (RI_psuc, RI_lsat) to zero
psuc_t1 ~~ 0*psuc_t1
psuc_t2 ~~ 0*psuc_t2
psuc_t3 ~~ 0*psuc_t3
psuc_t4 ~~ 0*psuc_t4
lsat_t1 ~~ 0*lsat_t1
lsat_t2 ~~ 0*lsat_t2
lsat_t3 ~~ 0*lsat_t3
lsat_t4 ~~ 0*lsat_t4
###############
# WITHIN PART #
###############
# Create within-components
W_psuc1 =~ 1*psuc_t1
W_psuc2 =~ 1*psuc_t2
W_psuc3 =~ 1*psuc_t3
W_psuc4 =~ 1*psuc_t4
W_lsat1 =~ 1*lsat_t1
W_lsat2 =~ 1*lsat_t2
W_lsat3 =~ 1*lsat_t3
W_lsat4 =~ 1*lsat_t4
# Specify lagged effects between within-person centered latent variables
W_psuc2 + W_lsat2 ~ W_psuc1 + W_lsat1
W_psuc3 + W_lsat3 ~ W_psuc2 + W_lsat2
W_psuc4 + W_lsat4 ~ W_psuc3 + W_lsat3
# Estimate correlations within same wave
W_psuc1 ~~ W_lsat1
W_psuc2 ~~ W_lsat2
W_psuc3 ~~ W_lsat3
W_psuc4 ~~ W_lsat4
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Set correlations between between-factors (random intercepts) and within-
# factors at wave 1 at 0
RI_psuc + RI_lsat ~~ 0*W_psuc1 + 0*W_lsat1
'
fit_RICLPM_lifesat <- cfa.auxiliary(
RICLPM_lifesat,
data = data,
aux = c("fg_Andere", "fg_Ingenieur", "fg_MatNat", "fg_Medizin", "fg_Sozial"), #Reference group Humanities
missing = "FIML",
estimator = "MLR",
fixed.x = FALSE
)
summary(
fit_RICLPM_lifesat,
standardized = TRUE,
fit.measures = TRUE,
rsquare = TRUE
)
Manual approach:
RICLPM_lifesat <- '
#####################
# MEASUREMENT MODEL #
#####################
# Factor models for perceived success at 4 waves (constrained)
psuc_t1 =~ 1*psuc1_t1 + a*psuc2_t1 + b*psuc3_t1
psuc_t2 =~ 1*psuc1_t2 + a*psuc2_t2 + b*psuc3_t2
psuc_t3 =~ 1*psuc1_t3 + a*psuc2_t3 + b*psuc3_t3
psuc_t4 =~ 1*psuc1_t4 + a*psuc2_t4 + b*psuc3_t4
# Constrained intercepts over time (this is necessary for strong factorial
# invariance; without these contraints we have weak factorial invariance)
psuc1_t1 + psuc1_t2 + psuc1_t3 + psuc1_t4 ~ i1*1
psuc2_t1 + psuc2_t2 + psuc2_t3 + psuc2_t4 ~ i2*1
psuc3_t1 + psuc3_t2 + psuc3_t3 + psuc3_t4 ~ i3*1
# Free latent means from t = 2 onward (only do this in combination with
# constraints on intercepts; without these, this would not be identified)
psuc_t2 + psuc_t3 + psuc_t4 ~ 1
# Correlated residuals of identical indicators across waves
psuc1_t1 ~~ psuc1_t2 + psuc1_t3 + psuc1_t4
psuc1_t2 ~~ psuc1_t3 + psuc1_t4
psuc1_t3 ~~ psuc1_t4
psuc2_t1 ~~ psuc2_t2 + psuc2_t3 + psuc2_t4
psuc2_t2 ~~ psuc2_t3 + psuc2_t4
psuc2_t3 ~~ psuc2_t4
psuc3_t1 ~~ psuc3_t2 + psuc3_t3 + psuc3_t4
psuc3_t2 ~~ psuc3_t3 + psuc3_t4
psuc3_t3 ~~ psuc3_t4
################
# BETWEEN PART #
################
# Create between factors (random intercepts)
RI_psuc =~ 1*psuc_t1 + 1*psuc_t2 + 1*psuc_t3 + 1*psuc_t4
RI_lsat =~ 1*lsat_t1 + 1*lsat_t2 + 1*lsat_t3 + 1*lsat_t4
# Set residual variances of repeated measures (RI_psuc, RI_lsat) to zero
psuc_t1 ~~ 0*psuc_t1
psuc_t2 ~~ 0*psuc_t2
psuc_t3 ~~ 0*psuc_t3
psuc_t4 ~~ 0*psuc_t4
lsat_t1 ~~ 0*lsat_t1
lsat_t2 ~~ 0*lsat_t2
lsat_t3 ~~ 0*lsat_t3
lsat_t4 ~~ 0*lsat_t4
###############
# WITHIN PART #
###############
# Create within-components
W_psuc1 =~ 1*psuc_t1
W_psuc2 =~ 1*psuc_t2
W_psuc3 =~ 1*psuc_t3
W_psuc4 =~ 1*psuc_t4
W_lsat1 =~ 1*lsat_t1
W_lsat2 =~ 1*lsat_t2
W_lsat3 =~ 1*lsat_t3
W_lsat4 =~ 1*lsat_t4
# Specify lagged effects between within-person centered latent variables
W_psuc2 + W_lsat2 ~ W_psuc1 + W_lsat1
W_psuc3 + W_lsat3 ~ W_psuc2 + W_lsat2
W_psuc4 + W_lsat4 ~ W_psuc3 + W_lsat3
# Estimate correlations within same wave
W_psuc1 ~~ W_lsat1
W_psuc2 ~~ W_lsat2
W_psuc3 ~~ W_lsat3
W_psuc4 ~~ W_lsat4
##########################
# ADDITIONAL CONSTRAINTS #
##########################
# Set correlations between between-factors (random intercepts) and within-
# factors at wave 1 at 0
RI_psuc + RI_lsat ~~ 0*W_psuc1 + 0*W_lsat1
#######################
# AUXILIARY VARIABLES #
#######################
# variances of auxiliary variables
#humanities is reference category
fg_Andere ~~ fg_Andere
fg_Ingenieur ~~ fg_Ingenieur
fg_MatNat ~~ fg_MatNat
fg_Medizin ~~ fg_Medizin
fg_Sozial ~~ fg_Sozial
# covariances among auxiliary variables
fg_Andere ~~ fg_Ingenieur + fg_MatNat + fg_Medizin + fg_Sozial
fg_Ingenieur ~~ fg_MatNat + fg_Medizin + fg_Sozial
fg_MatNat ~~ fg_Medizin + fg_Sozial
fg_Medizin ~~ fg_Sozial
# saturated correlations with latent within-person factors
W_psuc1 + W_psuc2 + W_psuc3 + W_psuc4 ~~
fg_Andere + fg_Ingenieur + fg_MatNat + fg_Medizin + fg_Sozial
W_lsat1 + W_lsat2 + W_lsat3 + W_lsat4 ~~
fg_Andere + fg_Ingenieur + fg_MatNat + fg_Medizin + fg_Sozial
'
fit_RICLPM_lifesat <- cfa(
RICLPM_lifesat,
data = data,
missing = "FIML",
estimator = "MLR",
fixed.x = FALSE
)
summary(
fit_RICLPM_lifesat,
standardized = TRUE,
fit.measures = TRUE,
rsquare = TRUE
)