Null model Lavaan: longitudinal CFA

715 views
Skip to first unread message

Amonet

unread,
Mar 12, 2018, 4:16:22 PM3/12/18
to lavaan
Dear all, 

In http://quantpsy.org/pubs/little_preacher_selig_card_2007.pdf the authors say that the null model that is commonly used in cross-sectional CFA, i.e. the independence model, is only "one piece of the 'null' expectation for longitudinal models". They refer to other research when stating: "Instead, the appropriate null model is one in which neither the variances nor the means of corresponding indicators change over time". 

This made me wonder: 
1) Does Lavaan select the appropriate null model automatically when specificying a multiple group or longitudinal CFA when using lavaan() function?
2) If 1) is not the case, how can I do this manually? A toy example would be highly appreciated. 

Kind regards,

Amonet 

Yves Rosseel

unread,
Mar 13, 2018, 4:23:32 AM3/13/18
to lav...@googlegroups.com
On 03/12/2018 09:16 PM, Amonet wrote:
> 1) Does Lavaan select the appropriate null model automatically when
> specificying a multiple group or longitudinal CFA when using lavaan()
> function?

No, because lavaan() doesn't 'know' if your data is longitudinal or not.

> 2) If 1) is not the case, how can I do this manually? A toy example
> would be highly appreciated.

You can fit your own baseline model. And then use the baseline.model=
argument of the fitMeasures() function. See

?fitMeasures

Yves.

Amonet

unread,
Mar 13, 2018, 11:11:19 AM3/13/18
to lavaan
Dear Yves, 

Thank you for your reply. Would the following syntax give the correct longitudinal model? I am following the earlier mentioned paper and Todd D. Little's book on longitudinal SEM (2013). There the author says the longitudinal null model should have indicators with a constant mean and variance over time (i.e. what I do via m1, m2, m3 and r1 to r4, following your advice from lavaan.org) and no covariances among the indicators (latter is achieved by simply not specifying them).
 
I am in doubt of the specification, because the author of the book and paper doesn't say to restrict or not restrict the covariance between factors. In my syntax they are restricted as I do not specify them. If I do specify them however, then I get a non-positive definite covariance matrix of the latent variables (4 out of 6 corrrelations > 1). The extra model specification I use for that is posted below the first. Your advice would be appreciated. 

Model1 <- ' 
        # latent variables
            Motivation0 =~ 1*V_0_1 + m1*V_0_3 + m2*V_0_4 + m3*V_0_6
            Motivation1 =~ 1*V_1_1 + m1*V_1_3 + m2*V_1_4 + m3*V_1_6
            Motivation3 =~ 1*V_3_1 + m1*V_3_3 + m2*V_3_4 + m3*V_3_6
            Motivation4 =~ 1*V_4_1 + m1*V_4_3 + m2*V_4_4 + m3*V_4_6 

        # residual variances
            
            V_0_1 ~~ r1*V_0_1
            V_1_1 ~~ r1*V_1_1
            V_3_1 ~~ r1*V_3_1
            V_4_1 ~~ r1*V_4_1

            V_0_3 ~~ r2*V_0_3
            V_1_3 ~~ r2*V_1_3
            V_3_3 ~~ r2*V_3_3
            V_4_3 ~~ r2*V_4_3
  
            V_0_4 ~~ r3*V_0_4 
            V_1_4 ~~ r3*V_1_4 
            V_3_4 ~~ r3*V_3_4 
            V_4_4 ~~ r3*V_4_4 
          
            V_0_6 ~~ r4*V_0_6
            V_1_6 ~~ r4*V_1_6
            V_3_6 ~~ r4*V_3_6
            V_4_6 ~~ r4*V_4_6

        # Factor / LV  variances
        
          Motivation0~~Motivation0
          Motivation1~~Motivation1
          Motivation3~~Motivation3
          Motivation4~~Motivation4

        # item covariances constrained to 0
            
'

If I specify the factor covariances, I add this to the above model:

       # factor covariances
            Motivation0 ~~ Motivation1 + Motivation3 + Motivation4 
            Motivation1 ~~ Motivation3 + Motivation4
            Motivation3 ~~ Motivation4    


Thanks,

Amonet

Amonet

unread,
Mar 13, 2018, 11:27:49 AM3/13/18
to lavaan
I forgot to add the lavaan () function syntax part. I use: fit0 <- lavaan(model = Model1, data = data). 

Kind regards

Christopher David Desjardins

unread,
Mar 13, 2018, 11:53:57 AM3/13/18
to lav...@googlegroups.com
Hi Amonet,

The paper you cite states "Instead, the appropriate null model is one in which neither the variances nor the means of corresponding indicators change over time." Based on this using the HolzingerSwineford1939 data set, cause I don't have yours, I think it should look like this:


littles.null <- '

# for illustration,
# x1, x4, and x7 are the same indicator over time
# x2, x5, and x8 are the same indicator over time
# and x3, x6, and x9 are the same indicator over time

# constrain variances to be equal
x1 ~~ v1*x1
x4 ~~ v1*x4
x7 ~~ v1*x7

x2 ~~ v2*x2
x5 ~~ v2*x5
x8 ~~ v2*x8

x3 ~~ v3*x3
x6 ~~ v3*x6
x9 ~~ v3*x9

# constrain means to be equal
x1 ~ m1*1
x4 ~ m1*1
x7 ~ m1*1

x2 ~ m2*1
x5 ~ m2*1
x8 ~ m2*1

x3 ~ m3*1
x6 ~ m3*1
x9 ~ m3*1
'
ft.ln <- cfa(littles.null, data = HolzingerSwineford1939)
summary(fit.ln, fit.measures = T)
fitmeasures(ft.ln, fit.measures = c("TLI", "CFI", "chisq", "RMSEA"))
inspect(ft.ln, "cov.ov")
inspect(ft.ln, "mean.ov")

traditional.null <- '
x1 ~~ x1
x2 ~~ x2
x3 ~~ x3
x4 ~~ x4
x5 ~~ x5
x6 ~~ x6
x7 ~~ x7
x8 ~~ x8
x9 ~~ x9
'
fit.tn <- cfa(HS.model, data = HolzingerSwineford1939)
summary(fit.tn, fit.measures = T)
fitmeasures(fit.tn, fit.measures = c("TLI", "CFI", "chisq", "RMSEA"))
inspect(fit.tn, "cov.ov")


--
You received this message because you are subscribed to the Google Groups "lavaan" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lavaan+unsubscribe@googlegroups.com.
To post to this group, send email to lav...@googlegroups.com.
Visit this group at https://groups.google.com/group/lavaan.
For more options, visit https://groups.google.com/d/optout.



--

Amonet

unread,
Mar 13, 2018, 2:13:33 PM3/13/18
to lavaan
Hi Christopher, 

I believe you are right. Thanks a lot for your help. 
I didn't think of the 'independence model' as being that, not my brightest moment :) 

For the variables I provided in my question, it the null model would then be (in longitudinal context): 

Model0 <- ' 

# residual variances

V_0_1 ~~ r1*V_0_1
V_1_1 ~~ r1*V_1_1
V_3_1 ~~ r1*V_3_1
V_4_1 ~~ r1*V_4_1

V_0_3 ~~ r2*V_0_3
V_1_3 ~~ r2*V_1_3
V_3_3 ~~ r2*V_3_3
V_4_3 ~~ r2*V_4_3

V_0_4 ~~ r3*V_0_4 
V_1_4 ~~ r3*V_1_4 
V_3_4 ~~ r3*V_3_4 
V_4_4 ~~ r3*V_4_4 

V_0_6 ~~ r4*V_0_6
V_1_6 ~~ r4*V_1_6
V_3_6 ~~ r4*V_3_6
V_4_6 ~~ r4*V_4_6


# equal means 

V_0_1 ~ m1*1
V_1_1 ~ m1*1
V_3_1 ~ m1*1
V_4_1 ~ m1*1

V_0_3 ~ m2*1
V_1_3 ~ m2*1
V_3_3 ~ m2*1
V_4_3 ~ m2*1

V_0_4 ~ m3*1
V_1_4 ~ m3*1
V_3_4 ~ m3*1 
V_4_4 ~ m3*1 

V_0_6 ~ m4*1
V_1_6 ~ m4*1
V_3_6 ~ m4*1
V_4_6 ~ m4*1

'



Thanks again! 

Best wishes

Christopher David Desjardins

unread,
Mar 13, 2018, 2:29:15 PM3/13/18
to lav...@googlegroups.com
That looks right to me.

--
You received this message because you are subscribed to the Google Groups "lavaan" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lavaan+unsubscribe@googlegroups.com.
To post to this group, send email to lav...@googlegroups.com.
Visit this group at https://groups.google.com/group/lavaan.
For more options, visit https://groups.google.com/d/optout.

Amonet

unread,
Mar 13, 2018, 4:51:31 PM3/13/18
to lavaan
Hi,

Sorry to keep bothering you all. 
Applying the help of Christopher as suggested, I fitted the longitudinal null model. 
As Yves suggested, I used fitMeasures(fit, baseline.model = ...). It works when I use the default estimator, i.e. Maximum Likelihood estimation in 'fit'. 

When I use estimator = 'MLM' to get robust SEs and scaled test statistics, it doesn't seem to work: Error in fit.indep@test[[2]] : subscript out of bounds

Is it correct the function doesn't work yet with this estimator? It's not a big problem, as apperantly the adjusted null model is only a very minor (negligible) difference in terms of fit anyways. 

Kind regards

Yves Rosseel

unread,
Mar 14, 2018, 4:08:32 AM3/14/18
to lav...@googlegroups.com
You must use the same estimator for the baseline model!

Yves.

Amonet

unread,
Mar 14, 2018, 4:32:15 AM3/14/18
to lavaan
Thanks for the quick reply, it indeed works!

Best wishes

Op woensdag 14 maart 2018 09:08:32 UTC+1 schreef Yves Rosseel:

Amonet

unread,
Mar 20, 2018, 12:54:59 PM3/20/18
to lavaan
Dear Yves,

I just wanted to check if the following is correct:

I specified a null model using 'estimator = 'MLM' for robust SE and robust GoF statistics. I then used this null model as the baseline model to check the goodness of fit of another model that is also estimated using 'estimator = 'MLM'. 

Does fitMeasures(fit = myfit_with_MLM, baseline.model = null_model_with_MLM) compute the robust goodness of fit indices based on the robust statistics of the specified null model? The null model has a baseline.chi.square.scaling.factor of 1.208 (and chisq.scaling factor of 1.058), while "my_fit_with_MLM" has a baseline.chisq.scaling.factor of 1.058 (and chisq.scaling.factor of 1.135). I am somewhat confused by this. 

So in both the null model and the hypothesized model the MLM estimator is used and my question is whether Lavaan 'knows' that I want it to use the robust GoF statistics of the null model to compute the robust GoF statistics of the hypothesized model. Probably this is done automatically, but I wanted to be sure. Sorry if this is a stupid question.

Kind regards

Terrence Jorgensen

unread,
Mar 27, 2018, 5:51:50 AM3/27/18
to lavaan
Does fitMeasures(fit = myfit_with_MLM, baseline.model = null_model_with_MLM) compute the robust goodness of fit indices based on the robust statistics of the specified null model?

Yes, that is correct.  If lavaan sees that you passed a lavaan object to the baseline.model= argument, it will use that instead of fitting the default independence model.

The null model has a baseline.chi.square.scaling.factor of 1.208 (and chisq.scaling factor of 1.058), while "my_fit_with_MLM" has a baseline.chisq.scaling.factor of 1.058 (and chisq.scaling.factor of 1.135). I am somewhat confused by this

Me too.  Whenever I fit my custom null model to the same data with the same arguments, the baseline.chisq.scaling.factor is the same for both my null model and my hypothesized model, because in either case, lavaan fits the same default baseline model to calculate any requested incremental fit indices.

library(lavaan)
example
(cfa)
fitMeasures
(update(fit, estimator = "MLM"))["baseline.chisq.scaling.factor"] # 1.164138
## custom null model
mod
.null <- paste0("x", 1:9, " ~~ foo*x", 1:9)
cat
(mod.null, sep = "\n") # more restricted than independence model
fitMeasures
(update(fit, model = mod.null, estimator = "MLM"))["baseline.chisq.scaling.factor"] # 1.164138

Terrence D. Jorgensen
Postdoctoral Researcher, Methods and Statistics
Research Institute for Child Development and Education, the University of Amsterdam

Amonet

unread,
Mar 27, 2018, 4:01:55 PM3/27/18
to lavaan
Thanks a lot for checking this. You're (obviously) right, I must have made some mistake somewhere. The results are now in line what you're telling. 

Kind regards

Op dinsdag 27 maart 2018 11:51:50 UTC+2 schreef Terrence Jorgensen:

Yves Rosseel

unread,
Mar 30, 2018, 2:41:33 AM3/30/18
to lav...@googlegroups.com
On 03/20/2018 05:54 PM, Amonet wrote:
> So in both the null model and the hypothesized model the MLM estimator
> is used and my question is whether Lavaan 'knows' that I want it to use
> the robust GoF statistics of the null model to compute the robust GoF
> statistics of the hypothesized model. Probably this is done
> automatically, but I wanted to be sure.

Just to confirm: yes, this is done automatically.

Yves.
Reply all
Reply to author
Forward
0 new messages