55  Modelli di Equazioni Strutturali Multilivello

I modelli di equazioni strutturali multilivello (multilevel SEM) sono una metodologia statistica avanzata utilizzata per analizzare dati organizzati su più livelli gerarchici. Questa tecnica consente di modellare simultaneamente le variazioni sia tra gruppi (variazione inter-cluster) che all’interno dei gruppi (variazione intra-cluster).

Prerequisiti

Concetti e Competenze Chiave

Preparazione del Notebook

here::here("code", "_common.R") |> source()

# Load packages
if (!requireNamespace("pacman")) install.packages("pacman")
pacman::p_load(lavaan, semTools, semPlot, patchwork, lme4)

55.1 Introduzione

Nella ricerca psicologica e nelle scienze sociali, i dati raccolti spesso mostrano strutture complesse a più livelli, in cui le informazioni sono organizzate in gruppi o cluster, e le osservazioni all’interno di ogni cluster tendono a essere correlate tra loro. Questo fenomeno rappresenta un aspetto cruciale poiché, in molte situazioni, i modelli classici di equazioni strutturali (SEM) non sono adatti per analizzare dati di questo tipo. La principale limitazione dei modelli SEM tradizionali risiede nella loro incapacità di tenere conto delle correlazioni intrinseche che caratterizzano i dati strutturati su più livelli, il che può portare a stime distorte e inefficaci.

Di conseguenza, per analizzare i dati a struttura multilivello, è necessario estendere l’approccio SEM classico integrando una modellazione adatta a tale struttura. La modellazione delle equazioni strutturali multilivello (multilevel SEM) introduce variabili latenti progettate per catturare sia la variazione tra i cluster sia quella all’interno dei cluster. In questo modo, le variabili osservate sono influenzate da fattori latenti che operano sia a livello individuale che a livello di gruppo.

Questa strategia consente di distinguere tra la variazione sistematica tra i gruppi (variazione tra i cluster) e la variazione individuale all’interno dei gruppi (variazione intra-cluster), permettendo un’analisi più precisa e rappresentativa dei dati complessi tipici delle scienze sociali e psicologiche. Adottando questo approccio, si ottiene una visione più completa e informativa delle dinamiche che sottostanno ai fenomeni studiati, in quanto il modello multilevel SEM è in grado di cogliere sia le differenze tra gruppi sia le specificità individuali all’interno dei gruppi.

55.2 Concetto Generale per la Modellazione a Equazioni Strutturali Multilivello

La tipica implementazione della modellazione a equazioni strutturali multilivello (SEM) prevede la scomposizione dell’outcome osservato in due componenti, una per descrivere la varianza a livello “within” (all’interno) e l’altra a livello “between” (tra gruppi), come segue:

\[ \bar{y}_{ij} - \bar{y}_{..} = (\bar{y}_{ij} - \bar{y}_{.j}) + (\bar{y}_{.j} - \bar{y}_{..}), \]

dove \(j\) è l’indicatore del cluster \(j\)-esimo (ad esempio, una scuola, come nell’esempio sopra), con \(j = 1, \dots, J\) e \(i\) rappresenta l’indicatore dell’individuo \(i\)-esimo all’interno del cluster, con \(i = 1, \dots, n_j\). Il termine \(\bar{y}_{.j}\) indica la media a livello di cluster per il cluster \(j\), mentre \(\bar{y}_{..}\) rappresenta la media complessiva.

Questa equazione corrisponde alla scomposizione della matrice di covarianza della popolazione in componenti “within” e “between”:

\[ \text{Cov}(y) = \Sigma_T = \Sigma_W + \Sigma_B. \]

Basandosi su questa scomposizione, è possibile costruire una funzione di verosimiglianza per stimare i parametri associati ai pesi fattoriali, ai coefficienti di percorso e alle varianze residue nei modelli di equazioni strutturali. Gli errori standard e gli intervalli di credibilità si possono ottenere grazie alla teoria della stima di massima verosimiglianza per l’inferenza statistica. Questa stima viene implementata in lavaan basandosi sui metodi descritti da McDonald e Goldstein (1989).

55.3 Un Esempio Pratico

In questo capitolo introdurremo l’implementazione in lavaan per l’analisi SEM multilivello seguendo il tutorial fornito sul sito di lavaan.org.

Utilizzeremo un set di dati artificiali ricavato dal sito di MPlus.

dat <- read.table("http://statmodel.com/usersguide/chap9/ex9.6.dat")
names(dat) <- c("y1", "y2", "y3", "y4", "x1", "x2", "w", "clus")
head(dat)
A data.frame: 6 x 8
y1 y2 y3 y4 x1 x2 w clus
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
1 2.2033 1.859 1.7385 2.245 1.143 -0.797 -0.150 1
2 1.9349 2.128 0.0831 2.509 1.949 -0.123 -0.150 1
3 0.3220 0.977 -0.8354 0.558 -0.716 -0.767 -0.150 1
4 0.0732 -1.743 -2.3103 -1.514 -2.649 0.638 -0.150 1
5 -1.2149 0.453 0.3726 -1.790 -0.263 0.303 -0.150 1
6 0.2983 -1.820 0.5613 -2.091 -0.945 1.363 0.319 2

Il data frame è costituito da 1000 righe:

dim(dat) |> print()
[1] 1000    8


Ci sono 110 cluster (clus), il che significa che ci sono misure ripetute per ciascun cluster (possiamo immaginare che i cluster corrispondano ai soggetti):

length(unique(dat$clus))
110


Analizzeremo questi dati mediante un modello di equazioni strutturali multilivello. Iniziamo a definire il modello SEM appropriato per questi dati.

model <- "
    level: 1
        fw =~ y1 + y2 + y3 + y4
        fw ~ x1 + x2

    level: 2
        fb =~ y1 + y2 + y3 + y4

    # optional
    y1 ~~ 0*y1
    y2 ~~ 0*y2
    y3 ~~ 0*y3
    y4 ~~ 0*y4
    fb ~ w
"

Questa sintassi del modello è strutturata su due livelli, uno per il livello 1 (intra-cluster) e uno per il livello 2 (inter-cluster). All’interno di ciascun livello, è possibile specificare un modello come nel caso a livello singolo, ma con una distinzione importante: ogni livello rappresenta fonti di variabilità differenti, in questo caso tra le misurazioni individuali (livello 1) e le differenze tra gruppi o cluster (livello 2).

55.3.1 Spiegazione dei livelli

  1. Livello 1 (Intra-cluster):

    • Fattore latente fw: Al livello individuale, fw è un fattore latente che riflette la variazione tra le quattro variabili osservate y1, y2, y3 e y4. La sintassi fw =~ y1 + y2 + y3 + y4 indica che fw è il costrutto latente che sottende questi indicatori osservabili.
    • Effetto dei predittori x1 e x2: Al livello individuale, fw è modellato in funzione dei predittori x1 e x2 (fw ~ x1 + x2), che rappresentano variabili a livello intra-cluster che possono influenzare il fattore latente fw. Questo permette di catturare come i predittori influenzano la variabilità nelle risposte individuali.
  2. Livello 2 (Inter-cluster):

    • Fattore latente fb: Al livello del cluster, fb rappresenta un secondo fattore latente che viene definito anch’esso dalle variabili y1, y2, y3 e y4, ma con una prospettiva inter-cluster. Questo livello considera quindi la variazione nei punteggi medi del cluster piuttosto che nelle risposte individuali.
    • Effetto del predittore w: Al livello di cluster, fb è modellato come funzione del predittore w (fb ~ w), che rappresenta una variabile esplicativa per le differenze tra cluster.

55.3.2 Parte opzionale

La sezione opzionale, che include espressioni come y1 ~~ 0*y1, specifica la varianza residua delle variabili osservate y1, y2, y3 e y4 al livello di cluster. L’uso di 0*y1, 0*y2, etc., indica che la varianza residua a livello inter-cluster viene fissata a zero, assumendo che tutta la varianza tra cluster sia spiegata da fb.

In sintesi, questo modello permette di distinguere come i fattori latenti (fw e fb) siano influenzati rispettivamente da variabili a livello individuale e cluster, consentendo di modellare simultaneamente la variabilità intra- e inter-cluster.

55.3.3 Coefficiente di Correlazione Intraclasse (ICC)

L’analisi SEM Multilivello permette di calcolare il Coefficiente di Correlazione Intraclasse (ICC), una misura statistica utile in studi dove i dati sono raggruppati in cluster o gruppi (come ad esempio soggetti all’interno di classi o pazienti all’interno di ospedali). L’ICC valuta il grado di somiglianza o omogeneità delle misurazioni all’interno di ciascun gruppo rispetto alla variazione totale nei dati.

Più precisamente, l’ICC quantifica la proporzione della varianza totale che può essere attribuita alle differenze tra i gruppi piuttosto che a quelle all’interno dei gruppi. Quando l’ICC è elevato, significa che una parte rilevante della varianza osservata nei dati deriva dalle differenze tra i gruppi. In questo caso, le misurazioni all’interno dello stesso gruppo tendono a essere più simili tra loro rispetto a quelle di gruppi differenti. Al contrario, un ICC basso indica che la varianza è in gran parte dovuta alle differenze individuali all’interno dei gruppi, suggerendo una scarsa influenza del raggruppamento.

In sintesi, l’ICC è un indice di quanto “forte” sia l’effetto del raggruppamento sulle misurazioni, informando sulla necessità di considerare la struttura multilivello dei dati nell’analisi.

55.3.4 ICC nei Modelli SEM Multilivello

Il calcolo dell’ICC (Intra-Class Correlation) per ciascuna variabile osservata in un modello SEM multilivello è essenziale per comprendere la struttura gerarchica dei dati e la variabilità tra i gruppi. L’ICC quantifica la proporzione della varianza totale di una variabile che può essere attribuita a differenze tra gruppi piuttosto che a variazioni individuali all’interno dei gruppi. Un ICC elevato per una variabile suggerisce che l’appartenenza a un determinato gruppo (come una scuola, una famiglia o un ospedale) ha un’influenza rilevante su quella variabile, indicando che una parte consistente della variazione osservata è dovuta alle differenze tra gruppi piuttosto che alle differenze tra individui all’interno di ciascun gruppo.

In pratica, l’ICC è un criterio utile per determinare l’adeguatezza di un modello multilivello. Un ICC basso suggerisce che la variabilità tra i gruppi è limitata e che potrebbe non essere necessario utilizzare un modello multilivello complesso, in quanto le differenze individuali rappresentano la maggior parte della variabilità. Al contrario, un ICC alto indica che la struttura a cluster dei dati è rilevante e che ignorarla potrebbe portare a stime distorte e a conclusioni potenzialmente errate.

Utilizzando l’ICC come guida, i ricercatori possono decidere se e in che misura adottare un approccio multilivello per rappresentare in modo accurato e affidabile la varianza attribuibile a fattori tra e intra-gruppo, ottenendo così una comprensione più precisa dei fenomeni studiati.

55.4 Calcolo dell’ICC con lmer

Per iniziare, poniamoci il problema di calcolare l’ICC mediante un modello lineare multilivello. Svolgeremo questi calcoli con lmer. Il modello lmer considera ogni variabile osservata separatamente, fornendo un’analisi indipendente per ciascuna. Per y1, per esempio, abbiamo:

model_lmer <- lmer(y1 ~ 1 + (1 | clus), data = dat)

Calcoliamo l’ICC:

 varianza_cluster <- VarCorr(model_lmer)$clus[1]
 varianza_residua <- attr(VarCorr(model_lmer), "sc")^2
 ICC <- varianza_cluster / (varianza_cluster + varianza_residua)
 ICC
0.129536196128802


Il Coefficiente di Correlazione Intraclass (ICC) di 0.129 per la variabile y1 significa che circa il 12.9% della variazione totale in y1 è ascrivibile alle differenze tra gli studenti, considerati come unità separate o cluster individuali. Questa percentuale relativamente modesta della variazione totale suggerisce che le caratteristiche o i comportamenti individuali degli studenti spiegano solo una piccola parte della variazione osservata in y1.

Un ICC di questo livello, che si può considerare relativamente basso, implica che la maggior parte della variazione nella variabile non è legata in modo sostanziale alle differenze tra gli studenti. Questo può essere indicativo del fatto che altri fattori, esterni alle caratteristiche individuali degli studenti, giocano un ruolo più determinante. In un contesto educativo, ad esempio, questo potrebbe suggerire che elementi come l’ambiente scolastico, le metodologie didattiche impiegate, o le specificità del programma di studi, hanno un impatto maggiore sulla variazione di y1 rispetto alle differenze individuali tra gli studenti.

55.5 Calcolo dell’ICC con sem

Ora calcoliamo l’ICC utilizzando un modello SEM multilivello. Per adattare questo modello, è necessario aggiungere l’argomento cluster= nella chiamata alla funzione sem di lavaan. Questo argomento specifica la variabile di raggruppamento, permettendo al modello di tenere conto della struttura a cluster dei dati e di stimare l’ICC per ciascuna variabile osservata.

 fit <- sem(model,
     data = dat,
     cluster = "clus",
     fixed.x = FALSE
 )

L’argomento fixed.x controlla come vengono trattate le variabili predittive (o esogene) all’interno del modello.

Quando fixed.x = FALSE, si sta indicando a lavaan di trattare le variabili esogene non come valori fissi, ma come variabili aleatorie con una propria varianza e covarianza da stimare nel modello. Questo significa che le variabili esogene non sono considerate “date” o senza errore, ma il modello tiene conto della loro variabilità.

  • fixed.x = TRUE (impostazione predefinita in lavaan):
    • Le variabili esogene sono considerate senza errore (cioè come dati “fissi”).
    • Non si stima la varianza delle variabili esogene.
    • Questo approccio è tipico nei modelli di regressione classici, dove le variabili predittive sono trattate come note e prive di errore.
  • fixed.x = FALSE:
    • Le variabili esogene sono considerate come variabili aleatorie con errori di misurazione, quindi la loro varianza e covarianza vengono stimate.
    • Questo approccio è più realistico in molti contesti psicologici e sociali, dove è ragionevole assumere che anche le variabili esogene possano contenere errori.

In molte situazioni di ricerca, le variabili esogene (come i punteggi dei questionari o le misure di osservazione) non sono perfettamente accurate e possono contenere errore. Impostare fixed.x = FALSE consente di modellare questa incertezza, offrendo una rappresentazione più realistica dei dati.

summary(fit) |>
    print()
lavaan 0.6-19 ended normally after 27 iterations

  Estimator                                         ML
  Optimization method                           NLMINB
  Number of model parameters                        26

  Number of observations                          1000
  Number of clusters [clus]                        110

Model Test User Model:
                                                      
  Test statistic                                 3.863
  Degrees of freedom                                17
  P-value (Chi-square)                           1.000

Parameter Estimates:

  Standard errors                             Standard
  Information                                 Observed
  Observed information based on                Hessian


Level 1 [within]:

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)
  fw =~                                               
    y1                1.000                           
    y2                0.999    0.033   30.735    0.000
    y3                0.995    0.033   29.804    0.000
    y4                1.017    0.033   30.364    0.000

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)
  fw ~                                                
    x1                0.973    0.042   23.287    0.000
    x2                0.510    0.038   13.422    0.000

Covariances:
                   Estimate  Std.Err  z-value  P(>|z|)
  x1 ~~                                               
    x2                0.032    0.032    1.014    0.311

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)
    x1                0.007    0.031    0.222    0.825
    x2                0.014    0.032    0.440    0.660

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
   .y1                0.981    0.057   17.151    0.000
   .y2                0.948    0.056   17.015    0.000
   .y3                1.070    0.060   17.700    0.000
   .y4                1.014    0.059   17.182    0.000
   .fw                0.980    0.071   13.888    0.000
    x1                0.985    0.044   22.361    0.000
    x2                1.017    0.045   22.361    0.000


Level 2 [clus]:

Latent Variables:
                   Estimate  Std.Err  z-value  P(>|z|)
  fb =~                                               
    y1                1.000                           
    y2                0.960    0.073   13.078    0.000
    y3                0.924    0.074   12.452    0.000
    y4                0.949    0.075   12.631    0.000

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)
  fb ~                                                
    w                 0.344    0.078    4.429    0.000

Intercepts:
                   Estimate  Std.Err  z-value  P(>|z|)
   .y1               -0.083    0.074   -1.128    0.259
   .y2               -0.077    0.071   -1.081    0.280
   .y3               -0.045    0.071   -0.637    0.524
   .y4               -0.030    0.072   -0.418    0.676
    w                 0.006    0.086    0.070    0.944

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
   .y1                0.000                           
   .y2                0.000                           
   .y3                0.000                           
   .y4                0.000                           
   .fb                0.361    0.078    4.643    0.000
    w                 0.815    0.110    7.416    0.000
fitMeasures(fit) |>
    print()
                 npar                  fmin                 chisq 
               26.000                 3.913                 3.863 
                   df                pvalue        baseline.chisq 
               17.000                 1.000              3283.563 
          baseline.df       baseline.pvalue                   cfi 
               24.000                 0.000                 1.000 
                  tli                  nnfi                   rfi 
                1.006                 1.006                 0.998 
                  nfi                  pnfi                   ifi 
                0.999                 0.707                 1.004 
                  rni                  logl     unrestricted.logl 
                1.004             -9527.429             -9525.497 
                  aic                   bic                ntotal 
            19106.857             19234.459              1000.000 
                 bic2                 rmsea        rmsea.ci.lower 
            19151.882                 0.000                 0.000 
       rmsea.ci.upper        rmsea.ci.level          rmsea.pvalue 
                0.000                 0.900                 1.000 
       rmsea.close.h0 rmsea.notclose.pvalue     rmsea.notclose.h0 
                0.050                 0.000                 0.080 
                 srmr           srmr_within          srmr_between 
                0.022                 0.004                 0.017 

Quando calcoliamo l’ICC utilizzando lavaan, otteniamo valori distinti per ciascuna variabile osservata all’interno del modello multilivello. Questi valori rappresentano la proporzione di varianza in ogni variabile che è attribuibile alle differenze tra i gruppi, considerando le relazioni specificate nel modello SEM. In altre parole, l’ICC di ciascun item riflette quanto le differenze tra i gruppi influenzano quella particolare variabile, nel contesto delle dipendenze definite dal modello.

Per ottenere l’ICC per ciascuno dei quattro item, è possibile utilizzare il comando:

lavInspect(fit, "icc") |>
    print()
   y1    y2    y3    y4    x1    x2 
0.125 0.121 0.106 0.115 0.000 0.000 


Nel caso di y1, la stima di ICC fornita dal modello SEM multilivello è molto simile al risultato ottenuto con lmer.

55.6 Riflessioni Conclusive

In questo capitolo, abbiamo illustrato il modello di equazioni strutturali multilivello utilizzando lavaan. Come evidenziato dall’esempio, l’implementazione in lavaan è molto diretta, richiedendo solo l’inclusione dell’opzione cluster nella funzione sem. È importante sottolineare che, al momento, lavaan supporta solo modelli SEM a due livelli.

Nell’ambito dei modelli SEM multilivello, abbiamo visto come l’interpretazione dei coefficienti di correlazione intra-classe (ICC) possa fornire intuizioni significative sulla variazione dei dati all’interno di gruppi o cluster. Un ICC basso, come quello osservato nell’esempio (0.129), indica che una porzione minore della variazione totale è attribuibile alle differenze tra i cluster. Nel contesto specifico dei nostri dati, dove ogni studente è considerato un cluster individuale, ciò suggerisce che fattori esterni agli studenti stessi potrebbero giocare un ruolo più significativo nella variazione osservata rispetto alle caratteristiche individuali degli studenti.

In conclusione, la modellazione di equazioni strutturali multilivello è uno strumento potente e flessibile nell’analisi di dati strutturati gerarchicamente. lavaan, sebbene limitato ai modelli a due livelli, fornisce un approccio accessibile e diretto per questi tipi di analisi. Per modelli più complessi e a più livelli, Mplus offre soluzioni alternative che possono gestire una gamma più ampia di esigenze analitiche.

Informazioni sull’Ambiente di Sviluppo

sessionInfo()
R version 4.4.2 (2024-10-31)
Platform: aarch64-apple-darwin20
Running under: macOS Sequoia 15.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] C

time zone: Europe/Rome
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] lme4_1.1-35.5     Matrix_1.7-1      MASS_7.3-61       viridis_0.6.5    
 [5] viridisLite_0.4.2 ggpubr_0.6.0      ggExtra_0.10.1    gridExtra_2.3    
 [9] patchwork_1.3.0   bayesplot_1.11.1  semTools_0.5-6    semPlot_1.1.6    
[13] lavaan_0.6-19     psych_2.4.6.26    scales_1.3.0      markdown_1.13    
[17] knitr_1.49        lubridate_1.9.3   forcats_1.0.0     stringr_1.5.1    
[21] dplyr_1.1.4       purrr_1.0.2       readr_2.1.5       tidyr_1.3.1      
[25] tibble_3.2.1      ggplot2_3.5.1     tidyverse_2.0.0   here_1.0.1       

loaded via a namespace (and not attached):
  [1] rstudioapi_0.17.1   jsonlite_1.8.9      magrittr_2.0.3     
  [4] TH.data_1.1-2       estimability_1.5.1  farver_2.1.2       
  [7] nloptr_2.1.1        rmarkdown_2.29      vctrs_0.6.5        
 [10] minqa_1.2.8         base64enc_0.1-3     rstatix_0.7.2      
 [13] htmltools_0.5.8.1   broom_1.0.7         Formula_1.2-5      
 [16] htmlwidgets_1.6.4   plyr_1.8.9          sandwich_3.1-1     
 [19] emmeans_1.10.5      zoo_1.8-12          uuid_1.2-1         
 [22] igraph_2.1.1        mime_0.12           lifecycle_1.0.4    
 [25] pkgconfig_2.0.3     R6_2.5.1            fastmap_1.2.0      
 [28] shiny_1.9.1         digest_0.6.37       OpenMx_2.21.13     
 [31] fdrtool_1.2.18      colorspace_2.1-1    rprojroot_2.0.4    
 [34] Hmisc_5.2-0         fansi_1.0.6         timechange_0.3.0   
 [37] abind_1.4-8         compiler_4.4.2      withr_3.0.2        
 [40] glasso_1.11         htmlTable_2.4.3     backports_1.5.0    
 [43] carData_3.0-5       ggsignif_0.6.4      corpcor_1.6.10     
 [46] gtools_3.9.5        tools_4.4.2         pbivnorm_0.6.0     
 [49] foreign_0.8-87      zip_2.3.1           httpuv_1.6.15      
 [52] nnet_7.3-19         glue_1.8.0          quadprog_1.5-8     
 [55] promises_1.3.0      nlme_3.1-166        lisrelToR_0.3      
 [58] grid_4.4.2          pbdZMQ_0.3-13       checkmate_2.3.2    
 [61] cluster_2.1.6       reshape2_1.4.4      generics_0.1.3     
 [64] gtable_0.3.6        tzdb_0.4.0          data.table_1.16.2  
 [67] hms_1.1.3           car_3.1-3           utf8_1.2.4         
 [70] sem_3.1-16          pillar_1.9.0        IRdisplay_1.1      
 [73] rockchalk_1.8.157   later_1.3.2         splines_4.4.2      
 [76] cherryblossom_0.1.0 lattice_0.22-6      survival_3.7-0     
 [79] kutils_1.73         tidyselect_1.2.1    miniUI_0.1.1.1     
 [82] pbapply_1.7-2       airports_0.1.0      stats4_4.4.2       
 [85] xfun_0.49           qgraph_1.9.8        arm_1.14-4         
 [88] stringi_1.8.4       pacman_0.5.1        boot_1.3-31        
 [91] evaluate_1.0.1      codetools_0.2-20    mi_1.1             
 [94] cli_3.6.3           RcppParallel_5.1.9  IRkernel_1.3.2     
 [97] rpart_4.1.23        xtable_1.8-4        repr_1.1.7         
[100] munsell_0.5.1       Rcpp_1.0.13-1       coda_0.19-4.1      
[103] png_0.1-8           XML_3.99-0.17       parallel_4.4.2     
[106] usdata_0.3.1        jpeg_0.1-10         mvtnorm_1.3-2      
[109] openxlsx_4.2.7.1    crayon_1.5.3        openintro_2.5.0    
[112] rlang_1.1.4         multcomp_1.4-26     mnormt_2.1.1