Skip to contents

Latent Growth Modeling

  • Measuring change over repeated time measurements
  • Gives you more information than a traditional repeated measures ANOVA or regression line by itself.
  • Advantages:
    • Estimate means and covariances separately
    • Estimating observed values and unobserved values separately

Assumptions

  • Continuous measurement of the DVs
  • This assumption is true for many structural models though!
  • Time spacing is the same across people
  • NOT across measurements, but people need to be spaced the same
  • At least three time points per person (otherwise, you are doing a dependent t-test)
  • Large samples!

Before you start

  • You have to know the expected type of change before you start
  • Generally it’s linear, however, you can do curvilinear or power functions

Example model

  • We will set the regression loading values to specific numbers to be able to estimate intercept and slope.

Understand the idea

  • Intercept
    • You set these values to 1 indicating that you do NOT want to estimate them
  • Basically, that gives you a starting value for the first time point, which is the average point where people start (y-intercept)

Understand the idea

  • Slope – represents the change over time
    • You can set these values to anything you want
    • Usually the first time is indicated by a 0
    • There’s no slope for time 1, just an intercept
    • Then the paths are set based on the time differences between them

Understand the idea

  • Setting the parameters this way:
    • Helps with identification
    • Is theoretical to match the concept of slope and intercept estimation
    • Allows you to not have to set the variances, so you can look at them

Questions

  • What is the average change for each person?
  • Where do they start?
  • How does it change over time?
  • What is the form of the change?

Questions

  • Is the average slope and intercept a good fit for all participants?
  • Or should we include a variance term to account for the differences between people?
  • Models are called random effects if you add the random variances (similar to multilevel models)
  • If we have a random effects model – is there another variable that explains those random effects?

Understanding the Output

  • Intercept mean: the average starting point for time 1
  • Slope mean: the average increment across time points

Understanding the Output

  • Intercept variance: the spread around the average start point
    • Large scores indicate a lot of spread – meaning people start in a lot of different places
    • Small scores indicate a small spread – everyone starts about the same place

Understanding the Output

  • Slope variance – the range of increments across time points
    • Small variances mean that everyone is going up/down about the same amount
    • Large variances mean that people scores are going up/down differently (almost like an interaction)

Understanding the Output

  • Factor covariance – examines the relationship between intercept and slope
  • Positive covariance - positive slope
    • People who start with higher intercepts have higher positive slopes
  • Positive covariance - negative slope
    • People who start higher go down faster (larger negative slopes)
  • Negative covariance - positive slope
    • People who start with higher intercepts go up slower (smaller slopes)
  • Negative covariance - negative slope
    • People who start with higher intercepts go down slower

Example: Crime Data across time

##load the data
crime.cov <- lav_matrix_lower2full(c(.63, 
                                    .50, .60, 
                                    .48, .48, .58, 
                                    .47, .48, .51, .67))

crime.mean <- c(5.17, 5.32, 5.40, 5.52)

names(crime.mean) <- 
  rownames(crime.cov) <- 
  colnames(crime.cov) <- c("Time1", "Time2", "Time3", "Time4")

New Functions

  • growth(): This function helps with the set up for a LCM, fixes the parameters correctly for you.
  • We will use the data as covariances and means, but you can import the data just like cfa()

How to Test

  • This procedure is very similar to multigroup model testing, only in reverse.
  • We are going to start with the most constrained model and slowly let parameters free to see which is the best.
  • We would prefer the unconstrained model be the best:
    • The slope and intercept are useful
    • But the variances to be low indicating everyone has the same pattern of slope and intercept

Intercept Only Model

  • Use only the intercept (mean of the data) to estimate
  • You want this model to be bad, otherwise you are saying that one average score is the best for all the time points
  • Intercept variance is constrained to 0, so you only get a mean.
  • Residuals are forced to be the same for each time point, so the variance is the same across all time points
crime.model1 <- '
# intercept
i =~ 1*Time1 + 1*Time2 + 1*Time3 + 1*Time4
i~~0*i
# residual variances
Time1~~r*Time1
Time2~~r*Time2
Time3~~r*Time3
Time4~~r*Time4
'

Intercept Only Model

crime.fit1 <- growth(crime.model1,
                    sample.cov=crime.cov, 
                    sample.mean=crime.mean, 
                    sample.nobs=952)
summary(crime.fit1,
        standardized = TRUE,
        fit.measures = TRUE,
        rsquare = TRUE)
#> lavaan 0.6-19 ended normally after 19 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         5
#>   Number of equality constraints                     3
#> 
#>   Number of observations                           952
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                              3461.981
#>   Degrees of freedom                                12
#>   P-value (Chi-square)                           0.000
#> 
#> Model Test Baseline Model:
#> 
#>   Test statistic                              3358.206
#>   Degrees of freedom                                 6
#>   P-value                                        0.000
#> 
#> User Model versus Baseline Model:
#> 
#>   Comparative Fit Index (CFI)                    0.000
#>   Tucker-Lewis Index (TLI)                       0.485
#> 
#> Loglikelihood and Information Criteria:
#> 
#>   Loglikelihood user model (H0)              -4540.205
#>   Loglikelihood unrestricted model (H1)      -2809.215
#>                                                       
#>   Akaike (AIC)                                9084.410
#>   Bayesian (BIC)                              9094.127
#>   Sample-size adjusted Bayesian (SABIC)       9087.775
#> 
#> Root Mean Square Error of Approximation:
#> 
#>   RMSEA                                          0.550
#>   90 Percent confidence interval - lower         0.534
#>   90 Percent confidence interval - upper         0.565
#>   P-value H_0: RMSEA <= 0.050                    0.000
#>   P-value H_0: RMSEA >= 0.080                    1.000
#> 
#> Standardized Root Mean Square Residual:
#> 
#>   SRMR                                           0.523
#> 
#> Parameter Estimates:
#> 
#>   Standard errors                             Standard
#>   Information                                 Expected
#>   Information saturated (h1) model          Structured
#> 
#> Latent Variables:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i =~                                                                  
#>     Time1             1.000                               0.000    0.000
#>     Time2             1.000                               0.000    0.000
#>     Time3             1.000                               0.000    0.000
#>     Time4             1.000                               0.000    0.000
#> 
#> Intercepts:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     i                 5.352    0.013  414.325    0.000      Inf      Inf
#> 
#> Variances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     i                 0.000                                 NaN      NaN
#>    .Time1      (r)    0.636    0.015   43.635    0.000    0.636    1.000
#>    .Time2      (r)    0.636    0.015   43.635    0.000    0.636    1.000
#>    .Time3      (r)    0.636    0.015   43.635    0.000    0.636    1.000
#>    .Time4      (r)    0.636    0.015   43.635    0.000    0.636    1.000
#> 
#> R-Square:
#>                    Estimate
#>     Time1             0.000
#>     Time2             0.000
#>     Time3             0.000
#>     Time4             0.000

Intercept Only Model

semPaths(crime.fit1,
         whatLabels = "par",
         edge.label.cex = 1,
         layout = "tree")

Comparison Table Fit Indices

library(knitr)
fit.table <- matrix(NA, nrow = 5, ncol = 6)
colnames(fit.table) <- c("Model", "X2", "df", "RMSEA", "SRMR", "CFI")
fit.table[1, ] <- c("Intercept Only", round(fitmeasures(crime.fit1, c("chisq", "df", "rmsea", "srmr", "cfi")),3))
kable(fit.table)
Model X2 df RMSEA SRMR CFI
Intercept Only 3461.981 12 0.55 0.523 0
NA NA NA NA NA NA
NA NA NA NA NA NA
NA NA NA NA NA NA
NA NA NA NA NA NA

Comparison Table Parameters

#save the parameter estimates
crime.fit1.par <- parameterestimates(crime.fit1)

#make table
par.table <- matrix(NA, nrow = 5, ncol = 7)
colnames(par.table) <- c("Model", "Intercept Mean", "Intercept Variance", "Residual Variance", "Slope Mean", "Slope Variance", "Covariance")

#put data in table
par.table[1, ] <- c("Intercept Only", 
                    round(crime.fit1.par$est[crime.fit1.par$lhs == "i" & crime.fit1.par$op == "~1"], 3),
                    "X", 
                    round(crime.fit1.par$est[crime.fit1.par$lhs == "Time1" & crime.fit1.par$op == "~~"], 3),
                    "X", 
                    "X", 
                    "X")
kable(par.table)
Model Intercept Mean Intercept Variance Residual Variance Slope Mean Slope Variance Covariance
Intercept Only 5.352 X 0.636 X X X
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA

Random Intercept Only Model

  • We allow the intercept variance to be > 0
  • People can start at different places
  • Residual variances are still the same
  • Still no slope
crime.model2 <- '
# intercept
i =~ 1*Time1 + 1*Time2 + 1*Time3 + 1*Time4
# residual variances
Time1~~r*Time1
Time2~~r*Time2
Time3~~r*Time3
Time4~~r*Time4
'

Random Intercept Only Model

crime.fit2 <- growth(crime.model2,
                    sample.cov=crime.cov, 
                    sample.mean=crime.mean, 
                    sample.nobs=952)
summary(crime.fit2,
        standardized = TRUE,
        fit.measures = TRUE,
        rsquare = TRUE)
#> lavaan 0.6-19 ended normally after 19 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         6
#>   Number of equality constraints                     3
#> 
#>   Number of observations                           952
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                               555.313
#>   Degrees of freedom                                11
#>   P-value (Chi-square)                           0.000
#> 
#> Model Test Baseline Model:
#> 
#>   Test statistic                              3358.206
#>   Degrees of freedom                                 6
#>   P-value                                        0.000
#> 
#> User Model versus Baseline Model:
#> 
#>   Comparative Fit Index (CFI)                    0.838
#>   Tucker-Lewis Index (TLI)                       0.911
#> 
#> Loglikelihood and Information Criteria:
#> 
#>   Loglikelihood user model (H0)              -3086.871
#>   Loglikelihood unrestricted model (H1)      -2809.215
#>                                                       
#>   Akaike (AIC)                                6179.742
#>   Bayesian (BIC)                              6194.318
#>   Sample-size adjusted Bayesian (SABIC)       6184.790
#> 
#> Root Mean Square Error of Approximation:
#> 
#>   RMSEA                                          0.228
#>   90 Percent confidence interval - lower         0.212
#>   90 Percent confidence interval - upper         0.244
#>   P-value H_0: RMSEA <= 0.050                    0.000
#>   P-value H_0: RMSEA >= 0.080                    1.000
#> 
#> Standardized Root Mean Square Residual:
#> 
#>   SRMR                                           0.092
#> 
#> Parameter Estimates:
#> 
#>   Standard errors                             Standard
#>   Information                                 Expected
#>   Information saturated (h1) model          Structured
#> 
#> Latent Variables:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i =~                                                                  
#>     Time1             1.000                               0.693    0.870
#>     Time2             1.000                               0.693    0.870
#>     Time3             1.000                               0.693    0.870
#>     Time4             1.000                               0.693    0.870
#> 
#> Intercepts:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     i                 5.352    0.023  229.140    0.000    7.720    7.720
#> 
#> Variances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>    .Time1      (r)    0.155    0.004   37.789    0.000    0.155    0.244
#>    .Time2      (r)    0.155    0.004   37.789    0.000    0.155    0.244
#>    .Time3      (r)    0.155    0.004   37.789    0.000    0.155    0.244
#>    .Time4      (r)    0.155    0.004   37.789    0.000    0.155    0.244
#>     i                 0.481    0.024   20.174    0.000    1.000    1.000
#> 
#> R-Square:
#>                    Estimate
#>     Time1             0.756
#>     Time2             0.756
#>     Time3             0.756
#>     Time4             0.756

Comparison Table Fit Indices

  • This model is much better than model 1
  • Implies that people all start in different places
fit.table[2, ] <- c("Random Intercept", round(fitmeasures(crime.fit2, c("chisq", "df", "rmsea", "srmr", "cfi")),3))
kable(fit.table)
Model X2 df RMSEA SRMR CFI
Intercept Only 3461.981 12 0.55 0.523 0
Random Intercept 555.313 11 0.228 0.092 0.838
NA NA NA NA NA NA
NA NA NA NA NA NA
NA NA NA NA NA NA

Comparison Table Parameters

#save the parameter estimates
crime.fit2.par <- parameterestimates(crime.fit2)

#put data in table
par.table[2, ] <- c("Random Intercept", 
                    round(crime.fit2.par$est[crime.fit2.par$lhs == "i" & crime.fit2.par$op == "~1"], 3),
                    round(crime.fit2.par$est[crime.fit2.par$lhs == "i" & crime.fit2.par$op == "~~"], 3), 
                    round(crime.fit2.par$est[crime.fit2.par$lhs == "Time1" & crime.fit2.par$op == "~~"], 3),
                    "X", 
                    "X", 
                    "X")
kable(par.table)
Model Intercept Mean Intercept Variance Residual Variance Slope Mean Slope Variance Covariance
Intercept Only 5.352 X 0.636 X X X
Random Intercept 5.352 0.481 0.155 X X X
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA

Random Slope + Intercepts

  • Add in the random slope
    • s~0*1 (makes the average slope 0)
    • s~~0*i (uncorrelated slope/intercept)
  • Leave the intercept and its variance in the model
  • Keep the residuals constrained to be the same
crime.model3 <- '
# intercept
i =~ 1*Time1 + 1*Time2 + 1*Time3 + 1*Time4
# slope
s =~ 0*Time1 + 1*Time2 + 2*Time3 + 3*Time4
s~0*1
s~~0*i
# residual variances
Time1~~r*Time1
Time2~~r*Time2
Time3~~r*Time3
Time4~~r*Time4
'

Random Slope + Intercepts

crime.fit3 <- growth(crime.model3,
                    sample.cov=crime.cov, 
                    sample.mean=crime.mean, 
                    sample.nobs=952)
summary(crime.fit3,
        standardized = TRUE,
        fit.measures = TRUE,
        rsquare = TRUE)
#> lavaan 0.6-19 ended normally after 38 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         7
#>   Number of equality constraints                     3
#> 
#>   Number of observations                           952
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                               339.586
#>   Degrees of freedom                                10
#>   P-value (Chi-square)                           0.000
#> 
#> Model Test Baseline Model:
#> 
#>   Test statistic                              3358.206
#>   Degrees of freedom                                 6
#>   P-value                                        0.000
#> 
#> User Model versus Baseline Model:
#> 
#>   Comparative Fit Index (CFI)                    0.902
#>   Tucker-Lewis Index (TLI)                       0.941
#> 
#> Loglikelihood and Information Criteria:
#> 
#>   Loglikelihood user model (H0)              -2979.008
#>   Loglikelihood unrestricted model (H1)      -2809.215
#>                                                       
#>   Akaike (AIC)                                5966.015
#>   Bayesian (BIC)                              5985.450
#>   Sample-size adjusted Bayesian (SABIC)       5972.746
#> 
#> Root Mean Square Error of Approximation:
#> 
#>   RMSEA                                          0.186
#>   90 Percent confidence interval - lower         0.169
#>   90 Percent confidence interval - upper         0.203
#>   P-value H_0: RMSEA <= 0.050                    0.000
#>   P-value H_0: RMSEA >= 0.080                    1.000
#> 
#> Standardized Root Mean Square Residual:
#> 
#>   SRMR                                           0.149
#> 
#> Parameter Estimates:
#> 
#>   Standard errors                             Standard
#>   Information                                 Expected
#>   Information saturated (h1) model          Structured
#> 
#> Latent Variables:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i =~                                                                  
#>     Time1             1.000                               0.696    0.903
#>     Time2             1.000                               0.696    0.884
#>     Time3             1.000                               0.696    0.835
#>     Time4             1.000                               0.696    0.768
#>   s =~                                                                  
#>     Time1             0.000                               0.000    0.000
#>     Time2             1.000                               0.158    0.201
#>     Time3             2.000                               0.317    0.380
#>     Time4             3.000                               0.475    0.525
#> 
#> Covariances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i ~~                                                                  
#>     s                 0.000                               0.000    0.000
#> 
#> Intercepts:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     s                 0.000                               0.000    0.000
#>     i                 5.262    0.024  221.401    0.000    7.565    7.565
#> 
#> Variances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>    .Time1      (r)    0.110    0.003   31.461    0.000    0.110    0.185
#>    .Time2      (r)    0.110    0.003   31.461    0.000    0.110    0.178
#>    .Time3      (r)    0.110    0.003   31.461    0.000    0.110    0.159
#>    .Time4      (r)    0.110    0.003   31.461    0.000    0.110    0.134
#>     i                 0.484    0.025   19.597    0.000    1.000    1.000
#>     s                 0.025    0.002   11.651    0.000    1.000    1.000
#> 
#> R-Square:
#>                    Estimate
#>     Time1             0.815
#>     Time2             0.822
#>     Time3             0.841
#>     Time4             0.866

Comparison Table Fit Indices

  • This model is much better than model 2
  • Implies that having at least a random slope is better than no slope
fit.table[3, ] <- c("Random Slope", round(fitmeasures(crime.fit3, c("chisq", "df", "rmsea", "srmr", "cfi")),3))
kable(fit.table)
Model X2 df RMSEA SRMR CFI
Intercept Only 3461.981 12 0.55 0.523 0
Random Intercept 555.313 11 0.228 0.092 0.838
Random Slope 339.586 10 0.186 0.149 0.902
NA NA NA NA NA NA
NA NA NA NA NA NA

Comparison Table Parameters

#save the parameter estimates
crime.fit3.par <- parameterestimates(crime.fit3)

#put data in table
par.table[3, ] <- c("Random Slope", 
                    round(crime.fit3.par$est[crime.fit3.par$lhs == "i" & crime.fit3.par$op == "~1"], 3),
                    round(crime.fit3.par$est[crime.fit3.par$lhs == "i" & crime.fit3.par$op == "~~" & crime.fit3.par$rhs == "i"], 3), 
                    round(crime.fit3.par$est[crime.fit3.par$lhs == "Time1" & crime.fit3.par$op == "~~"], 3),
                    "X", 
                    round(crime.fit3.par$est[crime.fit3.par$lhs == "s" & crime.fit3.par$op == "~~"], 3), 
                    "X")
kable(par.table)
Model Intercept Mean Intercept Variance Residual Variance Slope Mean Slope Variance Covariance
Intercept Only 5.352 X 0.636 X X X
Random Intercept 5.352 0.481 0.155 X X X
Random Slope 5.262 0.484 0.11 X 0.025 X
NA NA NA NA NA NA NA
NA NA NA NA NA NA NA

Full Slopes + Intercept

  • Allow the slope and intercept to vary
  • Constrain the residual variances
crime.model4 <- '
# intercept
i =~ 1*Time1 + 1*Time2 + 1*Time3 + 1*Time4
# slope
s =~ 0*Time1 + 1*Time2 + 2*Time3 + 3*Time4
# residual variances
Time1~~r*Time1
Time2~~r*Time2
Time3~~r*Time3
Time4~~r*Time4
'

Full Slopes + Intercept

  • Significant covariance – negative covariance with positive slope
    • People who start high go up slower than people who start lower.
crime.fit4 <- growth(crime.model4,
                    sample.cov=crime.cov, 
                    sample.mean=crime.mean, 
                    sample.nobs=952)
summary(crime.fit4,
        standardized = TRUE,
        fit.measures = TRUE,
        rsquare = TRUE)
#> lavaan 0.6-19 ended normally after 33 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         9
#>   Number of equality constraints                     3
#> 
#>   Number of observations                           952
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                                24.001
#>   Degrees of freedom                                 8
#>   P-value (Chi-square)                           0.002
#> 
#> Model Test Baseline Model:
#> 
#>   Test statistic                              3358.206
#>   Degrees of freedom                                 6
#>   P-value                                        0.000
#> 
#> User Model versus Baseline Model:
#> 
#>   Comparative Fit Index (CFI)                    0.995
#>   Tucker-Lewis Index (TLI)                       0.996
#> 
#> Loglikelihood and Information Criteria:
#> 
#>   Loglikelihood user model (H0)              -2821.215
#>   Loglikelihood unrestricted model (H1)      -2809.215
#>                                                       
#>   Akaike (AIC)                                5654.431
#>   Bayesian (BIC)                              5683.582
#>   Sample-size adjusted Bayesian (SABIC)       5664.526
#> 
#> Root Mean Square Error of Approximation:
#> 
#>   RMSEA                                          0.046
#>   90 Percent confidence interval - lower         0.025
#>   90 Percent confidence interval - upper         0.067
#>   P-value H_0: RMSEA <= 0.050                    0.589
#>   P-value H_0: RMSEA >= 0.080                    0.004
#> 
#> Standardized Root Mean Square Residual:
#> 
#>   SRMR                                           0.020
#> 
#> Parameter Estimates:
#> 
#>   Standard errors                             Standard
#>   Information                                 Expected
#>   Information saturated (h1) model          Structured
#> 
#> Latent Variables:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i =~                                                                  
#>     Time1             1.000                               0.720    0.911
#>     Time2             1.000                               0.720    0.930
#>     Time3             1.000                               0.720    0.925
#>     Time4             1.000                               0.720    0.896
#>   s =~                                                                  
#>     Time1             0.000                               0.000    0.000
#>     Time2             1.000                               0.128    0.165
#>     Time3             2.000                               0.255    0.328
#>     Time4             3.000                               0.383    0.476
#> 
#> Covariances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i ~~                                                                  
#>     s                -0.021    0.005   -4.002    0.000   -0.228   -0.228
#> 
#> Intercepts:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     i                 5.183    0.025  207.586    0.000    7.194    7.194
#>     s                 0.113    0.006   17.990    0.000    0.885    0.885
#> 
#> Variances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>    .Time1      (r)    0.106    0.003   30.854    0.000    0.106    0.170
#>    .Time2      (r)    0.106    0.003   30.854    0.000    0.106    0.177
#>    .Time3      (r)    0.106    0.003   30.854    0.000    0.106    0.175
#>    .Time4      (r)    0.106    0.003   30.854    0.000    0.106    0.164
#>     i                 0.519    0.027   19.007    0.000    1.000    1.000
#>     s                 0.016    0.002    8.790    0.000    1.000    1.000
#> 
#> R-Square:
#>                    Estimate
#>     Time1             0.830
#>     Time2             0.823
#>     Time3             0.825
#>     Time4             0.836

Comparison Table Fit Indices

  • This model is much better than model 3
  • Implies that having a single slope is better than just random slopes
fit.table[4, ] <- c("Full Slope", round(fitmeasures(crime.fit4, c("chisq", "df", "rmsea", "srmr", "cfi")),3))
kable(fit.table)
Model X2 df RMSEA SRMR CFI
Intercept Only 3461.981 12 0.55 0.523 0
Random Intercept 555.313 11 0.228 0.092 0.838
Random Slope 339.586 10 0.186 0.149 0.902
Full Slope 24.001 8 0.046 0.02 0.995
NA NA NA NA NA NA

Comparison Table Parameters

#save the parameter estimates
crime.fit4.par <- parameterestimates(crime.fit4)

#put data in table
par.table[4, ] <- c("Full Slope", 
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "i" & crime.fit4.par$op == "~1"], 3),
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "i" & crime.fit4.par$op == "~~" & crime.fit4.par$rhs == "i"], 3), 
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "Time1" & crime.fit4.par$op == "~~"], 3),
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "s" & crime.fit4.par$op == "~1"], 3), 
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "s" & crime.fit4.par$op == "~~"], 3), 
                    round(crime.fit4.par$est[crime.fit4.par$lhs == "i" & crime.fit4.par$op == "~~" & crime.fit4.par$rhs == "s"], 3))
kable(par.table)
Model Intercept Mean Intercept Variance Residual Variance Slope Mean Slope Variance Covariance
Intercept Only 5.352 X 0.636 X X X
Random Intercept 5.352 0.481 0.155 X X X
Random Slope 5.262 0.484 0.11 X 0.025 X
Full Slope 5.183 0.519 0.106 0.113 0.016 -0.021
NA NA NA NA NA NA NA

Totally Unconstrained Model

  • Now the residuals are free to vary
  • You want the residuals to be small and roughly equal, so this model shouldn’t be any different than the previous model
crime.model5 <- '
# intercept
i =~ 1*Time1 + 1*Time2 + 1*Time3 + 1*Time4
# slope
s =~ 0*Time1 + 1*Time2 + 2*Time3 + 3*Time4
'

Totally Unconstrained Model

crime.fit5 <- growth(crime.model5,
                    sample.cov=crime.cov, 
                    sample.mean=crime.mean, 
                    sample.nobs=952)
summary(crime.fit5,
        standardized = TRUE,
        fit.measures = TRUE,
        rsquare = TRUE)
#> lavaan 0.6-19 ended normally after 48 iterations
#> 
#>   Estimator                                         ML
#>   Optimization method                           NLMINB
#>   Number of model parameters                         9
#> 
#>   Number of observations                           952
#> 
#> Model Test User Model:
#>                                                       
#>   Test statistic                                 9.137
#>   Degrees of freedom                                 5
#>   P-value (Chi-square)                           0.104
#> 
#> Model Test Baseline Model:
#> 
#>   Test statistic                              3358.206
#>   Degrees of freedom                                 6
#>   P-value                                        0.000
#> 
#> User Model versus Baseline Model:
#> 
#>   Comparative Fit Index (CFI)                    0.999
#>   Tucker-Lewis Index (TLI)                       0.999
#> 
#> Loglikelihood and Information Criteria:
#> 
#>   Loglikelihood user model (H0)              -2813.783
#>   Loglikelihood unrestricted model (H1)      -2809.215
#>                                                       
#>   Akaike (AIC)                                5645.567
#>   Bayesian (BIC)                              5689.294
#>   Sample-size adjusted Bayesian (SABIC)       5660.710
#> 
#> Root Mean Square Error of Approximation:
#> 
#>   RMSEA                                          0.029
#>   90 Percent confidence interval - lower         0.000
#>   90 Percent confidence interval - upper         0.059
#>   P-value H_0: RMSEA <= 0.050                    0.854
#>   P-value H_0: RMSEA >= 0.080                    0.001
#> 
#> Standardized Root Mean Square Residual:
#> 
#>   SRMR                                           0.014
#> 
#> Parameter Estimates:
#> 
#>   Standard errors                             Standard
#>   Information                                 Expected
#>   Information saturated (h1) model          Structured
#> 
#> Latent Variables:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i =~                                                                  
#>     Time1             1.000                               0.721    0.914
#>     Time2             1.000                               0.721    0.922
#>     Time3             1.000                               0.721    0.943
#>     Time4             1.000                               0.721    0.887
#>   s =~                                                                  
#>     Time1             0.000                               0.000    0.000
#>     Time2             1.000                               0.121    0.155
#>     Time3             2.000                               0.242    0.316
#>     Time4             3.000                               0.363    0.446
#> 
#> Covariances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>   i ~~                                                                  
#>     s                -0.020    0.006   -3.517    0.000   -0.231   -0.231
#> 
#> Intercepts:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>     i                 5.182    0.025  207.459    0.000    7.190    7.190
#>     s                 0.113    0.006   18.106    0.000    0.937    0.937
#> 
#> Variances:
#>                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
#>    .Time1             0.102    0.010   10.029    0.000    0.102    0.165
#>    .Time2             0.117    0.007   16.319    0.000    0.117    0.191
#>    .Time3             0.087    0.006   14.268    0.000    0.087    0.149
#>    .Time4             0.131    0.011   12.138    0.000    0.131    0.198
#>     i                 0.519    0.028   18.712    0.000    1.000    1.000
#>     s                 0.015    0.002    6.558    0.000    1.000    1.000
#> 
#> R-Square:
#>                    Estimate
#>     Time1             0.835
#>     Time2             0.809
#>     Time3             0.851
#>     Time4             0.802

Comparison Table Fit Indices

  • This model is equal to model 4
  • This result implies that your residual variances are all approximately equal
fit.table[5, ] <- c("Unconstrained", round(fitmeasures(crime.fit5, c("chisq", "df", "rmsea", "srmr", "cfi")),3))
kable(fit.table)
Model X2 df RMSEA SRMR CFI
Intercept Only 3461.981 12 0.55 0.523 0
Random Intercept 555.313 11 0.228 0.092 0.838
Random Slope 339.586 10 0.186 0.149 0.902
Full Slope 24.001 8 0.046 0.02 0.995
Unconstrained 9.137 5 0.029 0.014 0.999

Comparison Table Parameters

#save the parameter estimates
crime.fit5.par <- parameterestimates(crime.fit5)

residual_numbers <- paste(round(crime.fit5.par$est[crime.fit5.par$lhs == "Time1" & crime.fit5.par$op == "~~"], 3), 
                          round(crime.fit5.par$est[crime.fit5.par$lhs == "Time2" & crime.fit5.par$op == "~~"], 3),
                          round(crime.fit5.par$est[crime.fit5.par$lhs == "Time3" & crime.fit5.par$op == "~~"], 3),
                          round(crime.fit5.par$est[crime.fit5.par$lhs == "Time4" & crime.fit5.par$op == "~~"], 3))

#put data in table
par.table[5, ] <- c("Unconstrained", 
                    round(crime.fit5.par$est[crime.fit5.par$lhs == "i" & crime.fit5.par$op == "~1"], 3),
                    round(crime.fit5.par$est[crime.fit5.par$lhs == "i" & crime.fit5.par$op == "~~" & crime.fit5.par$rhs == "i"], 3), 
                    residual_numbers,
                    round(crime.fit5.par$est[crime.fit5.par$lhs == "s" & crime.fit5.par$op == "~1"], 3), 
                    round(crime.fit5.par$est[crime.fit5.par$lhs == "s" & crime.fit5.par$op == "~~"], 3), 
                    round(crime.fit5.par$est[crime.fit5.par$lhs == "i" & crime.fit5.par$op == "~~" & crime.fit5.par$rhs == "s"], 3))
kable(par.table)
Model Intercept Mean Intercept Variance Residual Variance Slope Mean Slope Variance Covariance
Intercept Only 5.352 X 0.636 X X X
Random Intercept 5.352 0.481 0.155 X X X
Random Slope 5.262 0.484 0.11 X 0.025 X
Full Slope 5.183 0.519 0.106 0.113 0.016 -0.021
Unconstrained 5.182 0.519 0.102 0.117 0.087 0.131 0.113 0.015 -0.02

Summary

  • In this lecture you’ve learned:

    • Latent growth modeling
    • The idea of random intercepts and slopes to account for participant variability
    • How to test most to least restrictive models
    • How to compare parameters and models