Categorical Latent Growth Curve Model

1,067 views
Skip to first unread message

Kamal Kishore

unread,
Jul 4, 2013, 2:03:33 AM7/4/13
to lav...@googlegroups.com
Dear Lavaan users,
                           I need to specify categorical latent growth curve models for dichotomous response. the assumptions of continuous repeated outcomes will not be followed for categorical data.  I have few questions:

1) How to specify categorical latent growth curve model equivalent to below mentioned model?
2) I have data with MCAR mechanism, do lavaan uses all the available data or follow listwise deletion or some other procedure? 
3) Can anyone point to a paper for introduction and interpretation of categorical latent growth curve models?
4) Can I use SEM function instead of growth function? 
 
         Model.11<- ' 
         # INTERCEPT AND SLOPE EQUATIONS
          i=~1*Impair1+1*Impair2+1*Impair3+1*Impair4+1*Impair5+1*Impair6
          s=~0*Impair1+1*Impair2+2*Impair3+3*Impair4+4*Impair5+5*Impair6
       
       # REGRESSSION EQUATION OF FACTORS ON INTERCEPT AND SLOPE
       i~ Edu+MMSE
       s~ Edu+MMSE
       '
   fit.11<- growth(Model.11, data=Impair,missing="ml")
   summary(fit.11,standardized=TRUE, fit.measures=TRUE,rsq=TRUE)   

 Any help will be greatly appreciated.Thank you in advance. 

   

Alex Schoemann

unread,
Jul 8, 2013, 12:10:21 PM7/8/13
to lav...@googlegroups.com
I'll try to answer your questions, others may chime in to correct me:

1. I believe all you would need to do is use the ordered argument in the growth command to specify each of your variables as categorical. The lavaan website has a nice description of this (http://lavaan.ugent.be/tutorial/cat.html). You'll also have to take out the missing='ml' argument as ml is unavailable for categorical data at this juncture.
2. I believe that lavaan will use listwise deletion. Your best bet is probably to use multiple imputation to handle your missing data and the runMI function in the semTools package to fit the models.
3. Not off the top of my head...
4. Yes, the only difference between the two will be the starting values that are used. The default starting values differ across the cfa, sem and growth functions.

Alex

Kamal Kishore

unread,
Jul 9, 2013, 12:59:09 AM7/9/13
to lav...@googlegroups.com
First of all thanks 'Alex' for pumping in oxygen in this topic. I carried out the analysis with the description provided In lavaan, but I am facing two issue. I am displaying the code used and partial results from the same.

 load("Impair.c.rda")
 #LGC:
   library(lavaan)
   library("pbivnorm")
   library(qgraph) 

 
  Model.L<- ' 
          # INTERCEPT AND SLOPE EQUATIONS
          i=~1*Impair1+1*Impair2+1*Impair3+1*Impair4+1*Impair5+1*Impair6
 
          s=~0*Impair1+1*Impair2+2*Impair3+3*Impair4+4*Impair5+5*Impair6
          
           '
   fit.L<- sem(Model.L, data=Impair.c,ordered=c("Impair1","Impair2","Impair3"
               ,"Impair4","Impair5","Impair6"))

    summary(fit.L,standardized=TRUE, fit.measures=TRUE)# 

                                              "THE PROBLEMS"

1) Intercept should be fixed at zero but not slope, why is it happening ? for reference see http://www.statmodel.com/download/Topic3-v.pdf
 (slide 209) by Muthen and Muthen for M-plus.

  Intercepts:
    i                  0.000                               0.000    0.000
    s                 0.000                               0.000    0.000
   
2)  Dichotomous variables have single threshold (Bollen and Curran, p. 236) and see results from above link (slide 209). On application of sem or growth function in lavaan the thresholds are different:

  Thresholds:
    Impair1|t1        0.919    0.151    6.080    0.000    0.919    0.919
    Impair2|t1        0.842    0.147    5.711    0.000    0.842    0.842
    Impair3|t1        0.602    0.138    4.356    0.000    0.602    0.602
    Impair4|t1        0.919    0.151    6.080    0.000    0.919    0.919
    Impair5|t1        0.880    0.149    5.897    0.000    0.880    0.880
    Impair6|t1        1.095    0.162    6.767    0.000    1.095    1.095
  

  I am unable to fix these issues, hopefully some brilliant mind out there knows and are willing to help?

Thank you in advance for any help.

    


      


--
You received this message because you are subscribed to a topic in the Google Groups "lavaan" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/lavaan/rMtscrnhv0w/unsubscribe.
To unsubscribe from this group and all its topics, send an email to lavaan+un...@googlegroups.com.
To post to this group, send email to lav...@googlegroups.com.
Visit this group at http://groups.google.com/group/lavaan.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Thanks & Regards,

Kamal Kishore
Research  Scholar
Department of Biostatistics
National Institute of Mental Health and Neuro Sciences (NIMHANS) 
Banglore-560029
Mob-+91-9591349768
Email:kamalkis...@gmail.com

yrosseel

unread,
Jul 9, 2013, 3:15:24 AM7/9/13
to lav...@googlegroups.com
On 07/09/2013 06:59 AM, Kamal Kishore wrote:

> Model.L<- '
> # INTERCEPT AND SLOPE EQUATIONS
> i=~1*Impair1+1*Impair2+1*Impair3+1*Impair4+1*Impair5+1*Impair6
> s=~0*Impair1+1*Impair2+2*Impair3+3*Impair4+4*Impair5+5*Impair6
> '
> fit.L<- sem(Model.L,

> "THE PROBLEMS"
>
> 1) Intercept should be fixed at zero but not slope,

If you are using the 'sem()' function, by default, the means of all
latent variables (in a single group analysis) are fixed to zero.

For simple growth models, you can use the growth() function instead;
here the latent means are estimated by default (and the intercepts of
the observed variables are all fixed to zero).

Or you may wish to use the lavaan() function, and specify all the
(non-zero) parameters yourself, either in the syntax, or using the
auto.* flags.

> 2) Dichotomous variables have single threshold (Bollen and Curran, p.
> 236) and see results from above link (slide 209). On application of sem
> or growth function in lavaan the thresholds are different:

They will be the same, if the model you are estimating is the same.

Yves.

Kamal Kishore

unread,
Jul 9, 2013, 6:21:11 AM7/9/13
to lav...@googlegroups.com
  Mr. Yves and Alex thanks for your patience and help. As per your suggestion I applied growth function, but it results in warning :

  fit.L<- growth(Model.L, data=Impair.c,ordered=c("Impair1","Impair2","Impair3"
                ,"Impair4","Impair5","Impair6"))
Warning messages:
1: In estimateVCOV(lavaanModel, samplestats = lavaanSampleStats, options = lavaanOptions,  :
  lavaan WARNING: could not compute standard errors!

2: In computeTestStatistic(lavaanModel, partable = lavaanParTable,  :
  lavaan WARNING: could not compute scaled test statistic

>    summary(fit.L,standardized=TRUE, fit.measures=TRUE)#  

The output is :

lavaan (0.5-14) converged normally after  11 iterations

  Number of observations                            95

  Estimator                                       DWLS      Robust
  Minimum Function Test Statistic               13.256          NA
  Degrees of freedom                                10          10
  P-value (Chi-square)                           0.210          NA
  Scaling correction factor                                     NA
  Shift parameter                                     
    for simple second-order correction (Mplus variant)

Model test baseline model:

  Minimum Function Test Statistic              200.688     160.290
  Degrees of freedom                                15          15
  P-value                                        0.000       0.000

Full model versus baseline model:

  Comparative Fit Index (CFI)                    0.982          NA
  Tucker-Lewis Index (TLI)                       0.974          NA

Root Mean Square Error of Approximation:

  RMSEA                                          0.059          NA
  90 Percent Confidence Interval          0.000  0.133          NA     NA
  P-value RMSEA <= 0.05                          0.382          NA

 
Moreover threshold I was talking about are not same over a period of time as is displayed below in red. it should be constant or am I assuming something wrong?

    Impair1|t1        0.919                              0.919    0.919
    Impair2|t1        0.842                               0.842    0.842
    Impair3|t1        0.602                               0.602    0.602
    Impair4|t1        0.919                               0.919    0.919
    Impair5|t1        0.880                               0.880    0.880
    Impair6|t1        1.095

Alex Schoemann

unread,
Jul 9, 2013, 11:17:36 AM7/9/13
to lav...@googlegroups.com
Interesting. I'll let Yves confirm this but it looks like the growth function isn't automatically constraining thresholds to equality across time (which accounts for the errors too, the mean structure of the model isn't identified). You can do this manually in your syntax (the label a for all thresholds constrains them to equality):

Impair1 | a*t1
Impair2 | a*t1
Impair3 | a*t1
Impair4 | a*t1
Impair5 | a*t1
Impair6 | a*t1

Kamal Kishore

unread,
Jul 10, 2013, 2:36:39 AM7/10/13
to lav...@googlegroups.com
 I would like to congratulate and thanks the Authors, especially Yves and Alex for wonderful package and help. I am able to sort out the issues using "lavaan" function with          their help, but there are some points which I feel need their attention for the noble cause they initiated.

 The Script:
 Model.15<- ' 
           # INTERCEPT AND SLOPE EQUATIONS
           i=~1*Impair1+1*Impair2+1*Impair3+1*Impair4+1*Impair5+1*Impair6
           s=~0*Impair1+1*Impair2+2*Impair3+3*Impair4+4*Impair5+5*Impair6
    
            Impair1 | a*t1
            Impair2 | a*t1
            Impair3 | a*t1
            Impair4 | a*t1
            Impair5 | a*t1
            Impair6 | a*t1   '
          
    fit.15<- growth(Model.15, data=Impair.c,ordered=c("Impair1","Impair2","Impair3","Impair4","Impair5","Impair6"))
    summary(fit.15,standardized=TRUE, fit.measures=TRUE)

1) It still gives warning message and do not compute s.e. and hence p-values.
Warning messages:
1: In estimateVCOV(lavaanModel, samplestats = lavaanSampleStats, options = lavaanOptions,  :
  lavaan WARNING: could not compute standard errors!

2: In computeTestStatistic(lavaanModel, partable = lavaanParTable,  :
  lavaan WARNING: could not compute scaled test statistic

2)  It is freely estimating both Intercept and slope.

 Intercepts:
    i                 0.145                               0.169    0.169
    s                -0.036                              -0.214   -0.214

3) Application of sem function is keeping intercept and slope to zero by default.

 fit.15<- sem(Model.15, data=Impair.c,ordered=c("Impair1","Impair2","Impair3","Impair4","Impair5","Impair6"))
Intercepts:
     i                 0.000                               0.000    0.000
    s                 0.000                               0.000    0.000

  Once again congratulations for this wonderful package and all the help. 

On Thursday, 4 July 2013 11:33:33 UTC+5:30, Kamal Kishore wrote:

yrosseel

unread,
Jul 10, 2013, 4:48:29 AM7/10/13
to lav...@googlegroups.com
On 07/09/2013 05:17 PM, Alex Schoemann wrote:
> Interesting. I'll let Yves confirm this

confirmed. on my TODO list.

Yves.

yrosseel

unread,
Jul 10, 2013, 5:00:16 AM7/10/13
to lav...@googlegroups.com
On 07/10/2013 08:36 AM, Kamal Kishore wrote:
> *The Script:*
> **Model.15<- '
> # INTERCEPT AND SLOPE EQUATIONS
> i=~1*Impair1+1*Impair2+1*Impair3+1*Impair4+1*Impair5+1*Impair6
> s=~0*Impair1+1*Impair2+2*Impair3+3*Impair4+4*Impair5+5*Impair6
> Impair1 | a*t1
> Impair2 | a*t1
> Impair3 | a*t1
> Impair4 | a*t1
> Impair5 | a*t1
> Impair6 | a*t1 '
> fit.15<- growth(Model.15,
> data=Impair.c,ordered=c("Impair1","Impair2","Impair3","Impair4","Impair5","Impair6"))
> summary(fit.15,standardized=TRUE, fit.measures=TRUE)
>
> 1)It still gives warning message

You need to fix the latent mean of the intercept to zero. It is not
identified:

i ~ 0*1

This should be done automatically too (together with the equal
thresholds). Todo.


Yves.

Yves Rosseel

unread,
Jul 10, 2013, 7:54:33 AM7/10/13
to lav...@googlegroups.com
Moreover, using the 'delta' parameterization, we can allow for free 'scaling' parameters for the different time points (the first one is fixed to 1). For my own reference, this is the full syntax (using growth() in 0.5-13) for a simple growth model using artificial data:

example(growth)

Databin <- Demo.growth
Databin[,c("t1","t2","t3","t4")] <- lapply(Databin[,c("t1","t2","t3","t4")],
                                           cut, 2, labels=FALSE)

myModel <- '
   # intercept and slope with fixed coefficients
   i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
   s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4

   # fix mean of intercept factor
   i ~ 0*1

   # equal thresholds
   t1 | a*t1
   t2 | a*t1
   t3 | a*t1
   t4 | a*t1

   # scales
   t1 ~*~ 1*t1
   t2 ~*~ NA*t2
   t3 ~*~ NA*t3
   t4 ~*~ NA*t4
'
fit <- growth(myModel, data=Databin, ordered=c("t1","t2","t3","t4"))
summary(fit)

This will produce identical results compared to the 'default' behaviour of Mplus.

Yves.

A.Bi.

unread,
Mar 25, 2015, 11:08:57 AM3/25/15
to lav...@googlegroups.com
Dear all,
I am also trying to fit a latent growth model with ordinal data.
I used the syntax recommended above, but I then Igot this  warning message:Error in tmp[cbind(REP$row[idx], REP$col[idx])] <- lavpartable$free[idx] :
  NAs are not allowed in subscripted assignments
 Furthermore, standard errors and the WLSMV test statistic are not computed.
Here is my syntax:
model_dep <- "
#latente definitionen
dass_dep_t1 =~ dass3_t1+ dass5_t1+ dass10_t1+ dass13_t1+ dass16_t1+ dass17_t1+ dass21_t1
dass_dep_t2 =~ dass3_t2+ dass5_t2+ dass10_t2+ dass13_t2+ dass16_t2+ dass17_t2+ dass21_t2
dass_dep_t3 =~ dass3_t3+ dass5_t3+ dass10_t3+ dass13_t3+ dass16_t3+ dass17_t3+ dass21_t3
#latent growth
int =~ 1*dass_dep_t1 + 1*dass_dep_t2 + 1*dass_dep_t3
slp =~ 0*dass_dep_t1 + 1*dass_dep_t2 + 2*dass_dep_t3

int ~ 0*1

dass_dep_t1| a*t1
dass_dep_t2| a*t1
dass_dep_t3| a*t1


  dass_dep_t1 ~*~ 1*t1
   dass_dep_t2 ~*~ NA*t2
   dass_dep_t3 ~*~ NA*t3
    "

fit_dep<-growth(model_dep, dass_data_growth, ordered = c("dass3_t1","dass5_t1","dass10_t1", "dass13_t1", "dass16_t1", "dass17_t1", "dass21_t1",
                                                                  "dass3_t2","dass5_t2","dass10_t2", "dass13_t2", "dass16_t2", "dass17_t2", "dass21_t2",
                                                                  "dass3_t3","dass5_t3","dass10_t3", "dass13_t3", "dass16_t3", "dass17_t3", "dass21_t3",
                                                "dass_dep_t1", "dass_dep_t2", "dass_dep_t3" ))
summary(fit_dep, standardized = TRUE, fit.measures = TRUE)


Are there any mistakes?
Please find the output attached.
Kind regards, Angela
lgm_linear.csv

yrosseel

unread,
Mar 26, 2015, 3:54:34 AM3/26/15
to lav...@googlegroups.com
On 03/25/2015 04:08 PM, A.Bi. wrote:
> Dear all,
> I am also trying to fit a latent growth model with ordinal data.
> I used the syntax recommended above

> dass_dep_t1| a*t1
> dass_dep_t2| a*t1
> dass_dep_t3| a*t1

My apologies. I now see that these are your latent variables. These
thresholds are only for the *observed* indicators:

dass3_t1 | a*t1
dass3_t2 | a*t1
dass3_t3 | a*t1

dass5_t1 | b*t1
dass5_t2 | b*t1
dass5_t3 | b*t1

and so on.

There also no need for the scaling factors:

> dass_dep_t2 ~*~ NA*t2
> dass_dep_t3 ~*~ NA*t3

leave them alone.

Yves.

paul.tho...@gmail.com

unread,
Oct 1, 2020, 3:34:08 AM10/1/20
to lavaan
I am looking to fit a similar growth model but I have proportion as outcome. This is from a number correct items/total items for each individual, per session (so say 20 items per individual per session). I'm not sure that using the 'ordered=' command is the correct way forward? Currently, my model looks like the following:

model <- ' i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
           s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4 
# regressions
 # i ~ Condition
  s ~ Condition'  

model.s.eq <- ' i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
           s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4 
# regressions
 # i ~ Condition  #
  s ~ c(a2, a2)*Condition' #equate average effect of Condition on slope between groups

model.si.eq <- ' i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
           s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4 
# regressions
 # i ~ c(a1, a1)*Condition  #equate average effect of Condition on intercept between groups
  s ~ c(a2, a2)*Condition' #equate average effect of Condition on slope between groups


# Now fit all three models, starting with basic 2 group model with no constraints      
      fit.model <- growth(model, data=spreaddat,group='Group',verbose=F,ordered=c("t1","t2","t3","t4"))
      s<-summary(fit.model)
      fm<-fitMeasures(fit.model)
      
      fit.model.s.eq <- growth(model.s.eq, data=spreaddat,group='Group',verbose=F,ordered=c("t1","t2","t3","t4"))
      s1<-summary(fit.model.s.eq)
      fs.eq<-fitMeasures(fit.model.s.eq) #here we specify that slopes are equal across groups
      
      fit.model.si.eq <- growth(model.si.eq, data=spreaddat,group='Group',verbose=F,ordered=c("t1","t2","t3","t4"))
      s2<-summary(fit.model.si.eq)
      fsi.eq<-fitMeasures(fit.model.si.eq) #here we specify that slopes and intercepts are equal across groups


Thanks for any advice.

Paul

Patrick (Malone Quantitative)

unread,
Oct 1, 2020, 8:28:17 AM10/1/20
to lav...@googlegroups.com
Not clear what your question is?

Treating these data as ordered will probably not work well because there are too many values. A beta regression would probably be ideal, but I don't know of any SEM package that accommodates that. You'll probably need to bin responses into a smaller number of ordered groupings.

Pat

--
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+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/lavaan/cf36b8e3-d092-4b98-b7df-463d3b12759bn%40googlegroups.com.


--
Patrick S. Malone, Ph.D., Malone Quantitative
NEW Service Models: http://malonequantitative.com

He/Him/His

paul.tho...@gmail.com

unread,
Oct 1, 2020, 9:51:58 AM10/1/20
to lavaan
Dear Pat,

Thanks for your response. 

As far as I understand, in a logistic or profit model, the dependent measure does not have to be a list of 0 and 1s, it can fit the model if the data are a vector of proportions. I realise that the "ordered" command is not right for this as it's bounded continuous variable between bounds 0 and 1, not binary or categorical, but its in the right direction. I don't necessarily want to use a beta regression if possible. I have tried running this in R lme4 using a Generalised linear mixed model (adding weights as the number of items to create the proportions) and it works well, but I would like to do this with SEM if I can. My question is, can Lavaan do this? I thought that as you should be able to do categorical and binary growth curves based on the discussion above?

Thanks again for any advice.

Best,

Paul

Terrence Jorgensen

unread,
Oct 2, 2020, 2:59:34 PM10/2/20
to lavaan
I realise that the "ordered" command is not right for this as it's bounded continuous variable between bounds 0 and 1, not binary or categorical... can Lavaan do this? I thought that as you should be able to do categorical and binary growth curves based on the discussion above?

But as you said, you do not have binary or categorical data.  You have continuous (bounded) data.  A logistic model could be fitted directly to the proportions using nlme::nlme() or lme4::nlmer(), as demonstrated here:  https://stats.idre.ucla.edu/r/examples/alda/r-applied-longitudinal-data-analysis-ch-6/ .  The logistic regression you seem to be thinking of (a generalized linear model) for binary data use a logit link function, but the logistic curve is only implied by the inverse-logit function after fitting the linear model.

But lavaan can be made to fit nonlinear (growth) curves for continuous bounded outcomes (not only logistic curves) using some phantom variables and model constraints, as described by Grimm & Ram (2009):  https://doi.org/10.1080/10705510903206055

Here is a previous post describing how to accomplish this in lavaan:  https://groups.google.com/d/msg/lavaan/_-4zClO8oj8/wT3gaK_sBgAJ

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

Reply all
Reply to author
Forward
0 new messages