3  ✏️ Esercizi

3.1 Ottimizzazione dello scoring per questionari a dati ordinali

Nel precedente esercizio, i punteggi del Questionario sulle Difficoltà e Risorse (SDQ) sono stati calcolati mediante scalatura Likert tradizionale, assegnando in modo sequenziale i valori 0, 1 e 2 alle categorie “Non vero”, “Parzialmente vero” e “Assolutamente vero”. Sebbene questa assegnazione rifletta intuitivamente una progressione nell’accordo con le affermazioni, la scelta degli interi specifici (0-1-2 anziché 1-2-3 o altri) è puramente arbitraria, un metodo noto in letteratura come “misurazione per decreto” (measurement by fiat).

In questo secondo esercizio, proponiamo un approccio alternativo volto a determinare punteggi ottimali per le risposte ordinali dell’SDQ, dove “ottimalità” è definita in base a criteri statistici rigorosi. L’obiettivo è superare l’arbitrarietà della scalatura tradizionale, identificando valori che massimizzino la coerenza psicometrica della scala.

3.2 Obiettivo di ottimizzazione

Trovare i punteggi \(s_j\) (pesi numerici) assegnati alle categorie ordinali degli item che massimizzino la funzione:

\[ \mathcal{R} = \frac{\text{Var}(T)}{\sum_{i=1}^k \text{Var}(X_i)} , \]

dove:

  • \(T = \sum_{i=1}^k X_i\) è il punteggio totale del test (\(k =\) numero di item),
  • \(\text{Var}(T)\) è la varianza del punteggio totale,
  • \(\text{Var}(X_i)\) è la varianza del punteggio dell’\(i\)-esimo item.

Relazione con l’affidabilità:
Il rapporto \(\mathcal{R}\) è legato all’alfa di Cronbach (\(\alpha\)) dalla relazione:
\[ \alpha = \frac{k}{k-1} \left( 1 - \frac{\sum_{i=1}^k \text{Var}(X_i)}{\text{Var}(T)} \right) = \frac{k}{k-1} \left( 1 - \frac{1}{\mathcal{R}} \right) . \]
Massimizzare \(\mathcal{R}\) equivale quindi a massimizzare \(\alpha\), garantendo che gli item siano coerenti nel misurare il costrutto latente.

Interpretazione:

  • Un \(\mathcal{R}\) elevato implica che la varianza totale \(\text{Var}(T)\) è spiegata principalmente dalle covarianze tra gli item (segnale condiviso), anziché dalla varianza individuale (rumore).
  • Ciò ottimizza la validità di costrutto e la sensibilità discriminante del test.

3.3 Implementazione con il pacchetto aspect

Per illustrare il metodo, focalizziamoci sulla sottoscala Sintomi Emotivi dell’SDQ. Utilizzeremo il pacchetto R aspect, progettato per ottimizzare le scalature attraverso algoritmi flessibili e strumenti visuali integrati. Questo strumento permette di:

  1. Assegnare punteggi non lineari alle categorie ordinali, adattandosi alla struttura latente dei dati.
  2. Visualizzare graficamente l’impatto delle diverse scalature sull’affidabilità del test.
  3. Confrontare soluzioni alternative in modo efficiente.

L’approccio ottimizzato non solo migliora la qualità metriche della scala, ma fornisce anche informazioni sulla risonanza psicologica degli item, rendendo i punteggi totali più rappresentativi del costrutto misurato.

source("../../code/_common.R")
library("aspect")

Importiamo i dati del Strengths and Difficulties Questionnaire (SDQ).

load("../../data/data_sdq/SDQ.RData")
glimpse(SDQ)
#> Rows: 228
#> Columns: 51
#> $ Gender   <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
#> $ consid   <dbl> 1, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, …
#> $ restles  <dbl> 2, 0, 0, 0, 1, 0, 2, 1, 2, 0, 1, 1, 0, 1, 0, 2, 0, 1, 1, …
#> $ somatic  <dbl> 2, 2, 0, 0, 2, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 2, 1, 1, 1, …
#> $ shares   <dbl> 1, 1, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, …
#> $ tantrum  <dbl> 0, 0, 0, 0, 1, 0, 2, 0, 2, 0, 0, 1, 0, 1, 1, 2, 0, 1, 1, …
#> $ loner    <dbl> 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 2, …
#> $ obeys    <dbl> 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, …
#> $ worries  <dbl> 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 2, 0, 1, 2, 0, 1, 1, 2, …
#> $ caring   <dbl> 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 1, 2, 2, …
#> $ fidgety  <dbl> 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, …
#> $ friend   <dbl> 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, …
#> $ fights   <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ unhappy  <dbl> 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 2, …
#> $ popular  <dbl> 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 2, 1, …
#> $ distrac  <dbl> 0, 1, 0, 0, 1, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 2, 0, 0, 1, …
#> $ clingy   <dbl> 1, 1, 0, 1, 1, 1, 2, 0, 0, 0, 0, 1, 0, 2, 2, 1, 2, 0, 2, …
#> $ kind     <dbl> 1, 2, 2, 2, 1, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 1, 2, 2, …
#> $ lies     <dbl> 0, 0, 0, 0, 2, 0, 1, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, …
#> $ bullied  <dbl> 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, …
#> $ helpout  <dbl> 2, 1, 2, 2, 0, 2, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, …
#> $ reflect  <dbl> 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, …
#> $ steals   <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, …
#> $ oldbest  <dbl> 1, 0, 2, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 2, …
#> $ afraid   <dbl> 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1, 1, 1, 0, 1, …
#> $ attends  <dbl> 2, 2, 1, 2, 0, 2, 2, 2, 2, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, …
#> $ consid2  <dbl> 1, 2, 2, 2, NA, 2, 2, 2, 2, 2, NA, 1, NA, 2, 2, NA, 1, 2,…
#> $ restles2 <dbl> 0, 1, 2, 1, NA, 0, 1, 1, 0, 0, NA, 2, NA, 0, 1, NA, 1, 1,…
#> $ somatic2 <dbl> 0, 1, 1, 0, NA, 0, 0, 0, 0, 0, NA, 2, NA, 0, 1, NA, 0, 1,…
#> $ shares2  <dbl> 1, 2, 2, 1, NA, 2, 1, 2, 2, 2, NA, 2, NA, 2, 2, NA, 1, 2,…
#> $ tantrum2 <dbl> 0, 1, 2, 0, NA, 0, 2, 0, 0, 0, NA, 2, NA, 0, 1, NA, 1, 0,…
#> $ loner2   <dbl> 0, 0, 1, 0, NA, 0, 0, 0, 0, 0, NA, 1, NA, 1, 0, NA, 0, 0,…
#> $ obeys2   <dbl> 2, 1, 2, 1, NA, 2, 2, 2, 2, 1, NA, 1, NA, 2, 1, NA, 1, 2,…
#> $ worries2 <dbl> 0, 0, 1, 0, NA, NA, 1, 0, 0, 0, NA, 1, NA, 1, 2, NA, 0, 0…
#> $ caring2  <dbl> 2, 2, 1, 2, NA, 2, 2, 2, 2, 2, NA, 2, NA, 2, 2, NA, 1, 2,…
#> $ fidgety2 <dbl> 0, 1, 0, 0, NA, 0, 1, 0, 0, 0, NA, 2, NA, 0, 0, NA, 1, 0,…
#> $ friend2  <dbl> 2, 2, 1, 2, NA, 2, 2, 2, 2, 2, NA, 2, NA, 1, 2, NA, 2, 2,…
#> $ fights2  <dbl> 0, 0, 0, 0, NA, 0, 0, 0, 0, 0, NA, 2, NA, 0, 0, NA, 0, 0,…
#> $ unhappy2 <dbl> 0, 0, 1, 0, NA, 0, 0, 0, 0, 0, NA, 1, NA, 0, 0, NA, 0, 0,…
#> $ popular2 <dbl> 2, 1, 1, 2, NA, 2, 1, 2, 2, 2, NA, 2, NA, 2, 2, NA, 1, 2,…
#> $ distrac2 <dbl> 0, 0, 0, 2, NA, 0, 2, 1, 0, 0, NA, 1, NA, 0, 1, NA, 1, 0,…
#> $ clingy2  <dbl> 1, 1, 1, 0, NA, 1, 1, 1, 0, 0, NA, 1, NA, 0, 0, NA, 2, 0,…
#> $ kind2    <dbl> 2, 2, 2, 2, NA, 2, 2, 2, 2, 2, NA, 2, NA, 2, 2, NA, 1, 2,…
#> $ lies2    <dbl> 1, 0, 0, 0, NA, 0, 1, 0, 1, 0, NA, 1, NA, 0, 0, NA, 1, 0,…
#> $ bullied2 <dbl> 0, 0, 0, 0, NA, 0, 2, 0, 0, 0, NA, 0, NA, 0, 0, NA, 0, 0,…
#> $ helpout2 <dbl> 1, 1, 1, 2, NA, 2, 2, 1, 2, 1, NA, 2, NA, 2, 1, NA, 0, 2,…
#> $ reflect2 <dbl> 1, 1, 2, 1, NA, 2, 1, 2, 1, 2, NA, 1, NA, 2, 1, NA, 1, 2,…
#> $ steals2  <dbl> 0, 0, 0, 0, NA, 0, 0, 0, 0, 0, NA, 2, NA, 0, 0, NA, 0, 0,…
#> $ oldbest2 <dbl> 0, 0, 1, 0, NA, 1, 0, 1, 1, 0, NA, 1, NA, 0, 0, NA, 0, 0,…
#> $ afraid2  <dbl> 0, 1, 0, 0, NA, 0, 0, 0, 0, 0, NA, 2, NA, 0, 0, NA, 0, 0,…
#> $ attends2 <dbl> 1, 1, 2, 0, NA, 2, 2, 2, 2, 1, NA, 1, NA, 2, 2, NA, 1, 1,…

Per analizzare solo gli item che misurano i Sintomi Emotivi, è conveniente creare un nuovo data frame.

items_emotion <- c("somatic", "worries", "unhappy", "clingy", "afraid")
sdq_emo <- SDQ[, items_emotion]
sdq_emo |>
    head()
#> # A tibble: 6 × 5
#>   somatic worries unhappy clingy afraid
#>     <dbl>   <dbl>   <dbl>  <dbl>  <dbl>
#> 1       2       1       0      1      0
#> 2       2       0       0      1      0
#> 3       0       0       0      0      1
#> 4       0       0       0      1      1
#> 5       2       1       0      1      0
#> 6       1       0       0      1      0

Affrontiamo il problema dei dati mancanti come discusso in precedenza.

sdq_emo <- sdq_emo %>%
    mutate_at(
      vars(somatic:afraid), ~ ifelse(is.na(.), mean(., na.rm = TRUE), .)
      ) |> 
  round()

Esaminiamo le modalità di ciascun item:

emotional_symptoms <- c("somatic", "worries", "unhappy", "clingy", "afraid")
result <- lapply(emotional_symptoms, function(x) sort(unique(sdq_emo[[x]])))
result |> 
  print()
#> [[1]]
#> [1] 0 1 2
#> 
#> [[2]]
#> [1] 0 1 2
#> 
#> [[3]]
#> [1] 0 1 2
#> 
#> [[4]]
#> [1] 0 1 2
#> 
#> [[5]]
#> [1] 0 1 2

Trasformiamo il data frame in una matrice.

M <- sdq_emo |> 
  as.matrix()

Implementiamo lo scaling ottimale con la funzione corAspect().

opt <- corAspect(M, aspect = "aspectSum", level = "ordinal")

Parametri principali della funzione:

  1. data
    Questo argomento rappresenta il data frame che contiene i dati da analizzare. Nel nostro caso, si tratta degli item relativi alla scala che stiamo studiando (ad esempio, quelli che misurano i Sintomi Emotivi).

  2. aspect
    Questo parametro specifica il criterio da ottimizzare. Per impostazione predefinita, aspect="aspectSum" massimizza la somma delle correlazioni tra gli item. Questo criterio è utile per migliorare la consistenza interna della scala, ad esempio incrementando l’alfa di Cronbach. Nel nostro caso, utilizziamo questa impostazione predefinita.

  3. level
    Questo argomento definisce il livello di misura delle variabili analizzate:

    • nominal (impostazione predefinita): suppone che le variabili rappresentino categorie nominali. In questo caso, non vi sono restrizioni sui punteggi risultanti.
    • ordinal: richiede che l’ordine dei punteggi venga preservato.
    • numerical: oltre a preservare l’ordine, richiede che le distanze tra i punteggi siano uguali.
      Nel caso delle categorie di risposta del SDQ (“non vero”, “parzialmente vero”, “assolutamente vero”), queste riflettono chiaramente un ordine crescente di accordo. Vogliamo preservare questo ordine durante l’ottimizzazione, quindi impostiamo level="ordinal".

Esaminiamo il risultato ottenuto.

attributes(opt) 
#> $names
#>  [1] "loss"      "catscores" "cormat"    "eigencor"  "indmat"    "scoremat" 
#>  [7] "data"      "burtmat"   "niter"     "call"     
#> 
#> $class
#> [1] "aspect"
summary(opt)
#> 
#> Correlation matrix of the scaled data:
#>         somatic worries unhappy clingy afraid
#> somatic   1.000   0.339   0.369  0.254  0.313
#> worries   0.339   1.000   0.468  0.397  0.378
#> unhappy   0.369   0.468   1.000  0.367  0.454
#> clingy    0.254   0.397   0.367  1.000  0.378
#> afraid    0.313   0.378   0.454  0.378  1.000
#> 
#> 
#> Eigenvalues of the correlation matrix:
#> [1] 2.497 0.757 0.634 0.610 0.502
#> 
#> Category scores:
#> somatic:
#>     score
#> 0 -0.901
#> 1  0.586
#> 2  1.972
#> 
#> worries:
#>     score
#> 0 -0.854
#> 1  0.445
#> 2  2.096
#> 
#> unhappy:
#>     score
#> 0 -0.601
#> 1  1.393
#> 2  2.659
#> 
#> clingy:
#>     score
#> 0 -1.199
#> 1  0.237
#> 2  1.617
#> 
#> afraid:
#>     score
#> 0 -0.769
#> 1  1.009
#> 2  1.951
  1. Punteggi ottimali per ogni item:
    La funzione calcola i punteggi “ottimali” per ogni item, ovvero valori che massimizzano la somma delle correlazioni tra gli item. Questo migliora la coerenza interna della scala.

  2. Preservazione dell’ordine delle risposte:
    Utilizzando level="ordinal", i punteggi ottimizzati mantengono l’ordine crescente delle categorie di risposta, come ad esempio:

    • “non vero” < “parzialmente vero” < “assolutamente vero”.

    Ciò assicura che la struttura ordinata delle risposte venga rispettata.

  3. Correlazioni e punteggi trasformati:
    L’output include:

    • La matrice di correlazione dei punteggi trasformati, ovvero le correlazioni tra gli item dopo la scalatura ottimale.
    • Le correlazioni possono essere confrontate con quelle calcolate sulle variabili originali utilizzando la funzione cor(items).
  4. Autovalori della matrice di correlazione:
    L’output mostra anche gli autovalori della matrice di correlazione, che rappresentano le varianze delle componenti principali (da un’Analisi delle Componenti Principali, PCA).

    • Gli autovalori sono utili per determinare il numero di dimensioni misurate dal set di item.
    • Ad esempio, se il primo autovalore è notevolmente più grande degli altri, e ciò suggerisce che gli item misurano una sola dimensione, come ci si aspettava.
  5. Punteggi delle categorie:
    La funzione mostra i punteggi assegnati a ciascuna categoria di risposta dopo la scalatura ottimale. Ad esempio, per l’item somatic, i risultati potrebbero indicare:

    • “non vero” → -0.886
    • “parzialmente vero” → 0.584
    • “assolutamente vero” → 2.045

    Questi punteggi sono scelti in modo da:

    • Avere una media pari a 0 nel campione analizzato.
    • Massimizzare le correlazioni tra gli item, migliorando la coerenza interna della scala.
  6. Grafici delle trasformazioni:
    Il pacchetto aspect offre grafici utili che mostrano visivamente l’assegnazione dei punteggi alle categorie. Questi grafici aiutano a interpretare il risultato della scalatura ottimale in modo intuitivo.

Questo approccio offre un metodo rigoroso per ottimizzare la misurazione degli item, migliorando la qualità psicometrica della scala e assicurando che l’interpretazione delle risposte rifletta al meglio la coerenza interna del test.

I punteggi ottenuti si ottengono nel modo seguente:

opt$scoremat |> head()
#>   somatic worries unhappy clingy afraid
#> 1   1.972   0.445  -0.601  0.237 -0.769
#> 2   1.972  -0.854  -0.601  0.237 -0.769
#> 3  -0.901  -0.854  -0.601 -1.199  1.009
#> 4  -0.901  -0.854  -0.601  0.237  1.009
#> 5   1.972   0.445  -0.601  0.237 -0.769
#> 6   0.586  -0.854  -0.601  0.237 -0.769

Esaminiamo la relazione tra lo scoring basato sul metodo Likert con lo scoring ottimale.

plot(opt$scoremat[, 1], sdq_emo$somatic)

plot(opt$scoremat[, 4], sdq_emo$clingy)

plot(opt$scoremat[, 3], sdq_emo$unhappy)

plot(opt$scoremat[, 2], sdq_emo$worries)

plot(opt$scoremat[, 5], sdq_emo$afraid)

Guardando ai grafici ottenuti, si può notare che 1) i punteggi per le categorie successive aumentano quasi linearmente; 2) le categorie sono approssimativamente equidistanti. Concludiamo che per la valutazione degli item ordinali nella scala dei Sintomi Emotivi del SDQ, la scala Likert è appropriata, e l’ottimizzazione della scala rispetto alla semplice scala Likert di base produce cambiamenti minimi. Per altri dati, comunque, la situazione potrebbe essere molto diversa.

In conclusione, l’ottimizzazione dello scoring dei dati di questionari ordinali offre un metodo rigoroso per ottimizzare la misurazione degli item, migliorando la qualità psicometrica della scala e assicurando che l’interpretazione delle risposte rifletta al meglio la coerenza interna del test.

3.4 Session Info

sessionInfo()
#> R version 4.4.2 (2024-10-31)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.3.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/UTF-8/C/C/C/C
#> 
#> time zone: Europe/Rome
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#>  [1] aspect_1.0-6      ggokabeito_0.1.0  see_0.10.0        MASS_7.3-65      
#>  [5] viridis_0.6.5     viridisLite_0.4.2 ggpubr_0.6.0      ggExtra_0.10.1   
#>  [9] gridExtra_2.3     patchwork_1.3.0   bayesplot_1.11.1  semTools_0.5-6   
#> [13] semPlot_1.1.6     lavaan_0.6-19     psych_2.4.12      scales_1.3.0     
#> [17] markdown_1.13     knitr_1.49        lubridate_1.9.4   forcats_1.0.0    
#> [21] stringr_1.5.1     dplyr_1.1.4       purrr_1.0.4       readr_2.1.5      
#> [25] tidyr_1.3.1       tibble_3.2.1      ggplot2_3.5.1     tidyverse_2.0.0  
#> [29] here_1.0.1       
#> 
#> loaded via a namespace (and not attached):
#>   [1] rstudioapi_0.17.1   jsonlite_1.9.1      magrittr_2.0.3     
#>   [4] TH.data_1.1-3       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.7      zoo_1.8-13          igraph_2.1.4       
#>  [22] mime_0.12           lifecycle_1.0.4     pkgconfig_2.0.3    
#>  [25] Matrix_1.7-2        R6_2.6.1            fastmap_1.2.0      
#>  [28] rbibutils_2.3       shiny_1.10.0        digest_0.6.37      
#>  [31] OpenMx_2.21.13      fdrtool_1.2.18      colorspace_2.1-1   
#>  [34] rprojroot_2.0.4     Hmisc_5.2-2         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-88      zip_2.3.2           httpuv_1.6.15      
#>  [52] nnet_7.3-20         glue_1.8.0          quadprog_1.5-8     
#>  [55] nlme_3.1-167        promises_1.3.2      lisrelToR_0.3      
#>  [58] grid_4.4.2          checkmate_2.3.2     cluster_2.1.8      
#>  [61] reshape2_1.4.4      generics_0.1.3      gtable_0.3.6       
#>  [64] tzdb_0.4.0          data.table_1.17.0   hms_1.1.3          
#>  [67] car_3.1-3           sem_3.1-16          pillar_1.10.1      
#>  [70] rockchalk_1.8.157   later_1.4.1         splines_4.4.2      
#>  [73] lattice_0.22-6      survival_3.8-3      kutils_1.73        
#>  [76] tidyselect_1.2.1    miniUI_0.1.1.1      pbapply_1.7-2      
#>  [79] reformulas_0.4.0    stats4_4.4.2        xfun_0.51          
#>  [82] qgraph_1.9.8        arm_1.14-4          stringi_1.8.4      
#>  [85] yaml_2.3.10         pacman_0.5.1        boot_1.3-31        
#>  [88] evaluate_1.0.3      codetools_0.2-20    mi_1.1             
#>  [91] cli_3.6.4           RcppParallel_5.1.10 rpart_4.1.24       
#>  [94] xtable_1.8-4        Rdpack_2.6.2        munsell_0.5.1      
#>  [97] Rcpp_1.0.14         coda_0.19-4.1       png_0.1-8          
#> [100] XML_3.99-0.18       parallel_4.4.2      jpeg_0.1-10        
#> [103] lme4_1.1-36         mvtnorm_1.3-3       openxlsx_4.2.8     
#> [106] rlang_1.1.5         multcomp_1.4-28     mnormt_2.1.1