35. Modelli di crescita latenti bivariati#

I ricercatori spesso studiano più costrutti nel tempo per comprendere il loro sviluppo congiunto e le correlazioni tra di loro. Esistono diversi modelli statistici per studiare il cambiamento in più entità contemporaneamente. In questo capitolo ne copriamo due: il modello di crescita multivariato (MGM), che esamina le interrelazioni tra due processi di crescita distinti, e il modello di crescita con covariata variabile nel tempo (TVC), che stima l’effetto di una variabile che cambia nel tempo sui punteggi mentre modella contemporaneamente il cambiamento in quei punteggi con un modello di crescita. Questi modelli sono comuni nella ricerca sullo sviluppo e rispondono a domande specifiche riguardo le associazioni tra entità che cambiano simultaneamente e tra i cambiamenti per individui correlati.

Carichiamo i pacchetti necessari.

Questo tutorial mostra come adattare modelli di crescita lineare multivariati (bivariati) nel framework SEM in R. I dati sono tratti dal Capitolo 8 Grimm et al. [GRE16]. In particolare, utilizzando il set di dati NLSY-CYA, esaminiamo come le differenze individuali nella variazione del rendimento in matematica dei bambini durante la scuola siano correlate alle differenze individuali nella variazione dell’iperattività dei bambini (valutata dagli insegnanti). Iniziamo a leggere i dati.

# set filepath for data file
filepath <- "https://raw.githubusercontent.com/LRI-2/Data/main/GrowthModeling/nlsy_math_hyp_long_R.dat"
# read in the text data file using the url() function
dat <- read.table(
  file = url(filepath),
  na.strings = "."
) # indicates the missing data designator
# copy data with new name
nlsy_math_hyp_long <- dat

# Add names the columns of the data set
names(nlsy_math_hyp_long) <- c(
  "id", "female", "lb_wght", "anti_k1",
  "math", "comp", "rec", "bpi", "as", "anx", "hd",
  "hyp", "dp", "wd",
  "grade", "occ", "age", "men", "spring", "anti"
)

# reducing to variables of interest
nlsy_math_hyp_long <- nlsy_math_hyp_long[, c("id", "grade", "math", "hyp")]

# view the first few observations in the data set
head(nlsy_math_hyp_long, 10)
A data.frame: 10 × 4
idgrademathhyp
<int><int><int><int>
1 2013380
2 2015550
3 3032261
4 3035331
527022562
627024583
727028803
843033411
943034581
1050024463

Il nostro interesse specifico è il cambiamento intra-individuale nelle misure ripetute di matematica e iperattività durante il periodo scolastico (grade).

# intraindividual change trajetories
ggplot(
  data = nlsy_math_hyp_long, # data set
  aes(x = grade, y = math, group = id)
) + # setting variables
  geom_point(size = .5) + # adding points to plot
  geom_line() + # adding lines to plot
  # setting the x-axis with breaks and labels
  scale_x_continuous(
    limits = c(2, 8),
    breaks = c(2, 3, 4, 5, 6, 7, 8),
    name = "Grade at Testing"
  ) +
  # setting the y-axis with limits breaks and labels
  scale_y_continuous(
    limits = c(10, 90),
    breaks = c(10, 30, 50, 70, 90),
    name = "PIAT Mathematics"
  )
_images/6799d5acae8fdf0782a6ea5a3bc6adfa02d223cd759e183ac4a8e43e082828be.png

Esaminiamo i punteggi di iperattività in funzione di grade.

# intraindividual change trajetories
ggplot(
  data = nlsy_math_hyp_long, # data set
  aes(x = grade, y = hyp, group = id)
) + # setting variables
  geom_point(size = .5) + # adding points to plot
  geom_line() + # adding lines to plot
  # setting the x-axis with breaks and labels
  scale_x_continuous(
    limits = c(2, 8),
    breaks = c(2, 3, 4, 5, 6, 7, 8),
    name = "Grade at Testing"
  ) +
  # setting the y-axis with limits breaks and labels
  scale_y_continuous(
    limits = c(0, 5),
    breaks = c(0, 1, 2, 3, 4, 5),
    name = "Hyperactivity"
  )
Warning message:
“Removed 51 rows containing missing values (`geom_point()`).”
Warning message:
“Removed 44 rows containing missing values (`geom_line()`).”
_images/6e976a47f0687538f25b94e59db6dc58068356d058ad1d6c0bd507c6b7606677.png

Per semplicità, leggiamo i dati in formato long da file.

# set filepath for data file
filepath <- "https://raw.githubusercontent.com/LRI-2/Data/main/GrowthModeling/nlsy_math_hyp_wide_R.dat"
# read in the text data file using the url() function
dat <- read.table(
  file = url(filepath),
  na.strings = "."
) # indicates the missing data designator
# copy data with new name
nlsy_math_hyp_wide <- dat

# Add names the columns of the data set
# Give the variable names
names(nlsy_math_hyp_wide) <- c(
  "id", "female", "lb_wght", "anti_k1",
  "math2", "math3", "math4", "math5", "math6", "math7", "math8",
  "comp2", "comp3", "comp4", "comp5", "comp6", "comp7", "comp8",
  "rec2", "rec3", "rec4", "rec5", "rec6", "rec7", "rec8",
  "bpi2", "bpi3", "bpi4", "bpi5", "bpi6", "bpi7", "bpi8",
  "asl2", "asl3", "asl4", "asl5", "asl6", "asl7", "asl8",
  "ax2", "ax3", "ax4", "ax5", "ax6", "ax7", "ax8",
  "hds2", "hds3", "hds4", "hds5", "hds6", "hds7", "hds8",
  "hyp2", "hyp3", "hyp4", "hyp5", "hyp6", "hyp7", "hyp8",
  "dpn2", "dpn3", "dpn4", "dpn5", "dpn6", "dpn7", "dpn8",
  "wdn2", "wdn3", "wdn4", "wdn5", "wdn6", "wdn7", "wdn8",
  "age2", "age3", "age4", "age5", "age6", "age7", "age8",
  "men2", "men3", "men4", "men5", "men6", "men7", "men8",
  "spring2", "spring3", "spring4", "spring5", "spring6", "spring7", "spring8",
  "anti2", "anti3", "anti4", "anti5", "anti6", "anti7", "anti8"
)

# reducing to variables of interest
nlsy_multi_wide <- nlsy_math_hyp_wide[, c(
  "id",
  "math2", "math3", "math4", "math5", "math6", "math7", "math8",
  "hyp2", "hyp3", "hyp4", "hyp5", "hyp6", "hyp7", "hyp8"
)]

# view the first few observations in the data set
head(nlsy_multi_wide, 10)
A data.frame: 10 × 15
idmath2math3math4math5math6math7math8hyp2hyp3hyp4hyp5hyp6hyp7hyp8
<int><int><int><int><int><int><int><int><int><int><int><int><int><int><int>
1 201NA38NA55NANANANA 0NA 0NANANA
2 30326NANA33NANANA 1NANA 1NANANA
3270256NA58NANANA80 2NA 3NANANA 3
44303NA4158NANANANANA 1 1NANANANA
55002NANA46NA54NA66NANA 3NA 2NA 3
6500535NA50NA60NA59 0NA 3NA 0NA 1
75701NA6261NANANANANA 4 3NANANANA
86102NANA5567NA81NANANA 2 0NA 0NA
96801NA54NA62NA66NANA 0NA 1NA 1NA
106802NA55NA66NA68NANA 0NA 0NA 0NA

Per l’implementazione SEM, utilizziamo i punteggi di rendimento in matematica e iperattività e le covariate invarianti nel tempo dai dati Wide. Specifichiamo un modello di crescita lineare bivariato usando la sintassi lavaan.

# writing out linear growth model in full SEM way
bivariate_lavaan_model <- "
  # latent variable definitions
      #intercept for math
      eta_1 =~ 1*math2
      eta_1 =~ 1*math3
      eta_1 =~ 1*math4
      eta_1 =~ 1*math5
      eta_1 =~ 1*math6
      eta_1 =~ 1*math7
      eta_1 =~ 1*math8

      #linear slope for math
      eta_2 =~ 0*math2
      eta_2 =~ 1*math3
      eta_2 =~ 2*math4
      eta_2 =~ 3*math5
      eta_2 =~ 4*math6
      eta_2 =~ 5*math7
      eta_2 =~ 6*math8

      #intercept for hyp
      eta_3 =~ 1*hyp2
      eta_3 =~ 1*hyp3
      eta_3 =~ 1*hyp4
      eta_3 =~ 1*hyp5
      eta_3 =~ 1*hyp6
      eta_3 =~ 1*hyp7
      eta_3 =~ 1*hyp8

      #linear slope for hyp
      eta_4 =~ 0*hyp2
      eta_4 =~ 1*hyp3
      eta_4 =~ 2*hyp4
      eta_4 =~ 3*hyp5
      eta_4 =~ 4*hyp6
      eta_4 =~ 5*hyp7
      eta_4 =~ 6*hyp8

  # factor variances
      eta_1 ~~ eta_1
      eta_2 ~~ eta_2
      eta_3 ~~ eta_3
      eta_4 ~~ eta_4

  # covariances among factors
      eta_1 ~~ eta_2 + eta_3 + eta_4
      eta_2 ~~ eta_3 + eta_4
      eta_3 ~~ eta_4

  # factor means
      eta_1 ~ start(35)*1
      eta_2 ~ start(4)*1
      eta_3 ~ start(2)*1
      eta_4 ~ start(.1)*1

  # manifest variances for math (made equivalent by naming theta1)
      math2 ~~ theta1*math2
      math3 ~~ theta1*math3
      math4 ~~ theta1*math4
      math5 ~~ theta1*math5
      math6 ~~ theta1*math6
      math7 ~~ theta1*math7
      math8 ~~ theta1*math8

  # manifest variances for hyp (made equivalent by naming theta2)
      hyp2 ~~ theta2*hyp2
      hyp3 ~~ theta2*hyp3
      hyp4 ~~ theta2*hyp4
      hyp5 ~~ theta2*hyp5
      hyp6 ~~ theta2*hyp6
      hyp7 ~~ theta2*hyp7
      hyp8 ~~ theta2*hyp8

  # residual covariances (made equivalent by naming theta12)
      math2 ~~ theta12*hyp2
      math3 ~~ theta12*hyp3
      math4 ~~ theta12*hyp4
      math5 ~~ theta12*hyp5
      math6 ~~ theta12*hyp6
      math7 ~~ theta12*hyp7
      math8 ~~ theta12*hyp8

  # manifest means for math (fixed at zero)
      math2 ~ 0*1
      math3 ~ 0*1
      math4 ~ 0*1
      math5 ~ 0*1
      math6 ~ 0*1
      math7 ~ 0*1
      math8 ~ 0*1

  # manifest means for hyp (fixed at zero)
      hyp2 ~ 0*1
      hyp3 ~ 0*1
      hyp4 ~ 0*1
      hyp5 ~ 0*1
      hyp6 ~ 0*1
      hyp7 ~ 0*1
      hyp8 ~ 0*1
" # end of model definition

Adattiamo il modello ai dati.

bivariate_lavaan_fit <- sem(bivariate_lavaan_model,
  data = nlsy_multi_wide,
  meanstructure = TRUE,
  estimator = "ML",
  missing = "fiml"
)
Warning message in lav_data_full(data = data, group = group, cluster = cluster, :
“lavaan WARNING: some cases are empty and will be ignored:
  741”
Warning message in lav_data_full(data = data, group = group, cluster = cluster, :
“lavaan WARNING:
    due to missing values, some pairwise combinations have less than
    10% coverage; use lavInspect(fit, "coverage") to investigate.”
Warning message in lav_mvnorm_missing_h1_estimate_moments(Y = X[[g]], wt = WT[[g]], :
“lavaan WARNING:
    Maximum number of iterations reached when computing the sample
    moments using EM; use the em.h1.iter.max= argument to increase the
    number of iterations”

Esaminiamo i risultati.

out = summary(bivariate_lavaan_fit, fit.measures = TRUE)
print(out)
lavaan 0.6.15 ended normally after 67 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        35
  Number of equality constraints                    18

                                                  Used       Total
  Number of observations                           932         933
  Number of missing patterns                        96            

Model Test User Model:
                                                      
  Test statistic                               318.885
  Degrees of freedom                               102
  P-value (Chi-square)                           0.000

Model Test Baseline Model:

  Test statistic                              1532.041
  Degrees of freedom                                91
  P-value                                        0.000

User Model versus Baseline Model:

  Comparative Fit Index (CFI)                    0.849
  Tucker-Lewis Index (TLI)                       0.866
                                                      
  Robust Comparative Fit Index (CFI)             1.000
  Robust Tucker-Lewis Index (TLI)               -6.557

Loglikelihood and Information Criteria:

  Loglikelihood user model (H0)             -11790.476
  Loglikelihood unrestricted model (H1)     -11631.033
                                                      
  Akaike (AIC)                               23614.951
  Bayesian (BIC)                             23697.186
  Sample-size adjusted Bayesian (SABIC)      23643.196

Root Mean Square Error of Approximation:

  RMSEA                                          0.048
  90 Percent confidence interval - lower         0.042
  90 Percent confidence interval - upper         0.054
  P-value H_0: RMSEA <= 0.050                    0.724
  P-value H_0: RMSEA >= 0.080                    0.000
                                                      
  Robust RMSEA                                   0.000
  90 Percent confidence interval - lower         0.000
  90 Percent confidence interval - upper         0.000
  P-value H_0: Robust RMSEA <= 0.050             1.000
  P-value H_0: Robust RMSEA >= 0.080             0.000

Standardized Root Mean Square Residual:

  SRMR                                           0.097

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Observed
  Observed information based on                Hessian

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)
  eta_1 =~                                            
    math2             1.000                           
    math3             1.000                           
    math4             1.000                           
    math5             1.000                           
    math6             1.000                           
    math7             1.000                           
    math8             1.000                           
  eta_2 =~                                            
    math2             0.000                           
    math3             1.000                           
    math4             2.000                           
    math5             3.000                           
    math6             4.000                           
    math7             5.000                           
    math8             6.000                           
  eta_3 =~                                            
    hyp2              1.000                           
    hyp3              1.000                           
    hyp4              1.000                           
    hyp5              1.000                           
    hyp6              1.000                           
    hyp7              1.000                           
    hyp8              1.000                           
  eta_4 =~                                            
    hyp2              0.000                           
    hyp3              1.000                           
    hyp4              2.000                           
    hyp5              3.000                           
    hyp6              4.000                           
    hyp7              5.000                           
    hyp8              6.000                           

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)
  eta_1 ~~                                            
    eta_2            -0.204    1.154   -0.176    0.860
    eta_3            -2.979    0.673   -4.426    0.000
    eta_4             0.107    0.164    0.654    0.513
  eta_2 ~~                                            
    eta_3             0.098    0.161    0.608    0.543
    eta_4            -0.040    0.038   -1.061    0.289
  eta_3 ~~                                            
    eta_4            -0.020    0.031   -0.644    0.520
 .math2 ~~                                            
   .hyp2    (th12)   -0.011    0.233   -0.046    0.963
 .math3 ~~                                            
   .hyp3    (th12)   -0.011    0.233   -0.046    0.963
 .math4 ~~                                            
   .hyp4    (th12)   -0.011    0.233   -0.046    0.963
 .math5 ~~                                            
   .hyp5    (th12)   -0.011    0.233   -0.046    0.963
 .math6 ~~                                            
   .hyp6    (th12)   -0.011    0.233   -0.046    0.963
 .math7 ~~                                            
   .hyp7    (th12)   -0.011    0.233   -0.046    0.963
 .math8 ~~                                            
   .hyp8    (th12)   -0.011    0.233   -0.046    0.963

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)
    eta_1            35.259    0.356   99.178    0.000
    eta_2             4.343    0.088   49.139    0.000
    eta_3             1.903    0.058   32.702    0.000
    eta_4            -0.057    0.014   -3.950    0.000
   .math2             0.000                           
   .math3             0.000                           
   .math4             0.000                           
   .math5             0.000                           
   .math6             0.000                           
   .math7             0.000                           
   .math8             0.000                           
   .hyp2              0.000                           
   .hyp3              0.000                           
   .hyp4              0.000                           
   .hyp5              0.000                           
   .hyp6              0.000                           
   .hyp7              0.000                           
   .hyp8              0.000                           

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
    eta_1            64.711    5.664   11.424    0.000
    eta_2             0.752    0.329    2.282    0.023
    eta_3             1.542    0.156    9.908    0.000
    eta_4             0.005    0.009    0.539    0.590
   .math2   (tht1)   36.126    1.863   19.390    0.000
   .math3   (tht1)   36.126    1.863   19.390    0.000
   .math4   (tht1)   36.126    1.863   19.390    0.000
   .math5   (tht1)   36.126    1.863   19.390    0.000
   .math6   (tht1)   36.126    1.863   19.390    0.000
   .math7   (tht1)   36.126    1.863   19.390    0.000
   .math8   (tht1)   36.126    1.863   19.390    0.000
   .hyp2    (tht2)    1.104    0.057   19.404    0.000
   .hyp3    (tht2)    1.104    0.057   19.404    0.000
   .hyp4    (tht2)    1.104    0.057   19.404    0.000
   .hyp5    (tht2)    1.104    0.057   19.404    0.000
   .hyp6    (tht2)    1.104    0.057   19.404    0.000
   .hyp7    (tht2)    1.104    0.057   19.404    0.000
   .hyp8    (tht2)    1.104    0.057   19.404    0.000

Il parametro principale di interesse, la covarianza pendenza-pendenza eta_3 ~~eta_4, non è significativamente diverso da zero.

Generiamo un diagramma di percorso.

semPaths(bivariate_lavaan_fit, what = "path", whatLabels = "par")
_images/193858fbfd7d6a6753ef6c2593b702e18444f3d9f018a1d483fc096e6bfb042c.png

35.1. Conclusione#

Questo tutorial ha presentato come il modello di crescita multivariato possa essere utilizzato per esaminare le interrelazioni nel cambiamento - in particolare la correlazione between-person tra le pendenze.