@@ -374,6 +374,249 @@ ggplot(
374374 linewidth = 2)
375375```
376376
377+ # Latent Basis Growth Curve Model {#lbgcm}
378+
379+ ## Model Syntax
380+
381+ ### Abbreviated
382+
383+ ``` {r}
384+ lbgcm1_syntax <- '
385+ # Intercept and slope
386+ intercept =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
387+ slope =~ 0*t1 + a*t2 + b*t3 + 3*t4 # freely estimate the loadings for t2 and t3
388+
389+ # Regression paths
390+ intercept ~ x1 + x2
391+ slope ~ x1 + x2
392+
393+ # Time-varying covariates
394+ t1 ~ c1
395+ t2 ~ c2
396+ t3 ~ c3
397+ t4 ~ c4
398+ '
399+ ```
400+
401+ ### Full
402+
403+ ``` {r}
404+ lbgcm2_syntax <- '
405+ # Intercept and slope
406+ intercept =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
407+ slope =~ 0*t1 + a*t2 + b*t3 + 3*t4 # freely estimate the loadings for t2 and t3
408+
409+ # Regression paths
410+ intercept ~ x1 + x2
411+ slope ~ x1 + x2
412+
413+ # Time-varying covariates
414+ t1 ~ c1
415+ t2 ~ c2
416+ t3 ~ c3
417+ t4 ~ c4
418+
419+ # Constrain observed intercepts to zero
420+ t1 ~ 0
421+ t2 ~ 0
422+ t3 ~ 0
423+ t4 ~ 0
424+
425+ # Estimate mean of intercept and slope
426+ intercept ~ 1
427+ slope ~ 1
428+ '
429+ ```
430+
431+ ## Fit the Model
432+
433+ ### Abbreviated
434+
435+ ``` {r}
436+ lbgcm1_fit <- growth(
437+ lbgcm1_syntax,
438+ data = Demo.growth,
439+ missing = "ML",
440+ estimator = "MLR",
441+ meanstructure = TRUE,
442+ int.ov.free = FALSE,
443+ int.lv.free = TRUE,
444+ fixed.x = FALSE,
445+ em.h1.iter.max = 100000)
446+ ```
447+
448+ ### Full
449+
450+ ``` {r}
451+ lbgcm2_fit <- sem(
452+ lbgcm2_syntax,
453+ data = Demo.growth,
454+ missing = "ML",
455+ estimator = "MLR",
456+ meanstructure = TRUE,
457+ fixed.x = FALSE,
458+ em.h1.iter.max = 100000)
459+ ```
460+
461+ ## Summary Output
462+
463+ ### Abbreviated
464+
465+ ``` {r}
466+ summary(
467+ lbgcm1_fit,
468+ fit.measures = TRUE,
469+ standardized = TRUE,
470+ rsquare = TRUE)
471+ ```
472+
473+ ### Full
474+
475+ ``` {r}
476+ summary(
477+ lbgcm2_fit,
478+ fit.measures = TRUE,
479+ standardized = TRUE,
480+ rsquare = TRUE)
481+ ```
482+
483+ ## Estimates of Model Fit
484+
485+ ``` {r}
486+ fitMeasures(
487+ lbgcm1_fit,
488+ fit.measures = c(
489+ "chisq", "df", "pvalue",
490+ "chisq.scaled", "df.scaled", "pvalue.scaled",
491+ "chisq.scaling.factor",
492+ "baseline.chisq","baseline.df","baseline.pvalue",
493+ "rmsea", "cfi", "tli", "srmr",
494+ "rmsea.robust", "cfi.robust", "tli.robust"))
495+ ```
496+
497+ ## Residuals of Observed vs. Model-Implied Correlation Matrix
498+
499+ ``` {r}
500+ residuals(
501+ lbgcm1_fit,
502+ type = "cor")
503+ ```
504+
505+ ## Modification Indices
506+
507+ ``` {r}
508+ modificationindices(
509+ lbgcm1_fit,
510+ sort. = TRUE)
511+ ```
512+
513+ ## Internal Consistency Reliability
514+
515+ ``` {r}
516+ compRelSEM(lbgcm1_fit)
517+ ```
518+
519+ ## Path Diagram
520+
521+ ``` {r}
522+ semPaths(
523+ lbgcm1_fit,
524+ what = "Std.all",
525+ layout = "tree2",
526+ edge.label.cex = 1.5)
527+ ```
528+
529+ ## Plot Trajectories
530+
531+ ### Protoypical Growth Curve
532+
533+ ``` {r}
534+ lbgcm1_intercept <- coef(lbgcm1_fit)["intercept~1"]
535+ lbgcm1_slope <- coef(lbgcm1_fit)["slope~1"]
536+ lbgcm1_slopeloadingt2 <- coef(lbgcm1_fit)["a"]
537+ lbgcm1_slopeloadingt3 <- coef(lbgcm1_fit)["b"]
538+
539+ timepoints <- 4
540+
541+ newData <- data.frame(
542+ time = 1:4,
543+ slopeloading = c(0, lbgcm1_slopeloadingt2, lbgcm1_slopeloadingt3, 3)
544+ )
545+
546+ newData$predictedValue <- NA
547+ newData$predictedValue <- lbgcm1_intercept + lbgcm1_slope * newData$slopeloading
548+
549+ ggplot(
550+ data = newData,
551+ mapping = aes(x = time, y = predictedValue)) +
552+ xlab("Timepoint") +
553+ ylab("Score") +
554+ scale_y_continuous(
555+ limits = c(0, 5)) +
556+ geom_line()
557+ ```
558+
559+ ### Individuals' Growth Curves
560+
561+ ``` {r}
562+ person_factors <- as.data.frame(predict(lbgcm1_fit))
563+ person_factors$id <- rownames(person_factors)
564+
565+ slope_loadings <- c(0, lbgcm1_slopeloadingt2, lbgcm1_slopeloadingt3, 3)
566+
567+ # Compute model-implied values for each person at each time point
568+ individual_trajectories <- person_factors %>%
569+ rowwise() %>%
570+ mutate(
571+ t1 = intercept + slope * slope_loadings[1],
572+ t2 = intercept + slope * slope_loadings[2],
573+ t3 = intercept + slope * slope_loadings[3],
574+ t4 = intercept + slope * slope_loadings[4]
575+ ) %>%
576+ ungroup() %>%
577+ select(id, t1, t2, t3, t4) %>%
578+ pivot_longer(
579+ cols = starts_with("t"),
580+ names_to = "timepoint",
581+ values_to = "value") %>%
582+ mutate(
583+ time = as.integer(substr(timepoint, 2, 2)) # extract number from "t1", "t2", etc.
584+ )
585+
586+ ggplot(
587+ data = individual_trajectories,
588+ mapping = aes(x = time, y = value, group = factor(id))) +
589+ xlab("Timepoint") +
590+ ylab("Score") +
591+ scale_y_continuous(
592+ limits = c(-10, 20)) +
593+ geom_line()
594+ ```
595+
596+ ### Individuals' Trajectories Overlaid with Prototypical Trajectory
597+
598+ ``` {r}
599+ #ggplot(
600+ # data = newData) +
601+ # xlab("Timepoint") +
602+ # ylab("Score") +
603+ # scale_x_continuous(
604+ # limits = c(0, 3),
605+ # labels = 1:4) +
606+ # scale_y_continuous(
607+ # limits = c(-10, 20)) +
608+ # geom_abline(
609+ # mapping = aes(
610+ # slope = slope,
611+ # intercept = intercept)) +
612+ # geom_abline(
613+ # mapping = aes(
614+ # slope = lgcm1_slope,
615+ # intercept = lgcm1_intercept),
616+ # color = "blue",
617+ # linewidth = 2)
618+ ```
619+
377620# Latent Change Score Model {#lcsm}
378621
379622## Model Syntax
0 commit comments