mirt example for multidimensional discrete latent traits

mirt example for multidimensional discrete latent traits

Adapted from the sirt package examples (data.read).

library(mirt)
library(sirt)
library(mvtnorm)

data( data.read )
dat <- data.read

Q <- matrix( 0 , nrow=12,ncol=3)
Q[1:4,1] <- 1
Q[5:8,2] <- 1
Q[9:12,3] <- 1

# define discrete theta distribution with 3 dimensions
Theta <- matrix(c(0,0,0, 1,0,0, 0,1,0, 0,0,1, 1,1,0, 1,0,1, 0,1,1, 1,1,1), ncol=3, byrow=TRUE)
Theta
##      [,1] [,2] [,3]
## [1,]    0    0    0
## [2,]    1    0    0
## [3,]    0    1    0
## [4,]    0    0    1
## [5,]    1    1    0
## [6,]    1    0    1
## [7,]    0    1    1
## [8,]    1    1    1
#-- Model 13a: din (in CDM)
mod13a <- CDM::din( dat , q.matrix=Q , rule="DINA", progress = FALSE)
summary(mod13a)
## CDM 8.2-6 (Built 2022-08-25 15:43:23)
## Call:
##  CDM::din(data = dat, q.matrix = Q, rule = "DINA", progress = FALSE) 
## 
## Date of Analysis: 2023-07-05 16:12:07 
## Time difference of 0.03082705 secs
## Computation Time: 0.03082705 
## 
## 
## Deviance = 3847.473  |   Log-Likelihood= -1923.737 
## 
## Number of iterations: 40 
## 
## Number of item parameters: 24 
## Number of skill class parameters: 7 
## 
## Information criteria: 
##   AIC = 3909.473 
##   BIC = 4027.056 
## 
## Mean of RMSEA item fit: 0.053 
## 
## Item parameters
##    item guess  slip   IDI rmsea
## 1    A1 0.691 0.000 0.309 0.024
## 2    A2 0.491 0.031 0.478 0.040
## 3    A3 0.302 0.184 0.514 0.016
## 4    A4 0.244 0.337 0.419 0.049
## 5    B1 0.568 0.163 0.269 0.042
## 6    B2 0.329 0.344 0.327 0.071
## 7    B3 0.817 0.014 0.170 0.091
## 8    B4 0.431 0.104 0.465 0.044
## 9    C1 0.188 0.013 0.799 0.023
## 10   C2 0.050 0.239 0.711 0.140
## 11   C3 0.000 0.065 0.935 0.028
## 12   C4 0.000 0.212 0.788 0.070
## 
## Marginal skill probabilities:
##        skill.prob
## Skill1     0.5159
## Skill2     0.5421
## Skill3     0.9326
## 
## Tetrachoric correlations among skill dimensions
##        Skill1 Skill2 Skill3
## Skill1 1.0000 0.9727 0.3044
## Skill2 0.9727 1.0000 0.3129
## Skill3 0.3044 0.3129 1.0000
## 
## Skill Pattern Probabilities 
## 
##     000     100     010     001     110     101     011     111 
## 0.03879 0.00832 0.00948 0.39361 0.01080 0.01720 0.04220 0.47959
# compare used Theta distributions
cbind( Theta , mod13a$attribute.patt.splitted)
##            Skill1 Skill2 Skill3
## [1,] 0 0 0      0      0      0
## [2,] 1 0 0      1      0      0
## [3,] 0 1 0      0      1      0
## [4,] 0 0 1      0      0      1
## [5,] 1 1 0      1      1      0
## [6,] 1 0 1      1      0      1
## [7,] 0 1 1      0      1      1
## [8,] 1 1 1      1      1      1
#-- Model 13b: gdm (in CDM)
mod13b <- CDM::gdm( dat , Qmatrix=Q , theta.k=Theta , skillspace="full", progress=FALSE)
summary(mod13b)
## -----------------------------------------------------------------------------
## CDM 8.2-6 (2022-08-25 15:43:23) 
## 
## Date of Analysis: 2023-07-05 16:12:10 
## Time difference of 3.131896 secs
## Computation Time: 3.131896 
## 
## General Diagnostic Model 
## 
## Call:
## CDM::gdm(data = dat, theta.k = Theta, Qmatrix = Q, skillspace = "full", 
##     progress = FALSE)
## 
##     328 Cases,  12 Items,  1 Group(s) , 3 Dimension(s)
##     Saturated skill space
## 
## -----------------------------------------------------------------------------
## Number of iterations= 1000
## Maximum number of iterations was reached.
## 
## Deviance = 3865.63 | Log Likelihood = -1932.82
## Number of persons = 328
## Number of estimated parameters = 31
##   Number of estimated item parameters = 24
##       12 Intercepts and 12 Slopes 
##       0 centered intercepts and 0 centered slopes 
##   Number of estimated distribution parameters = 7
## 
## AIC = 3928  | penalty = 62    | AIC = -2*LL + 2*p  
## AICc = 3934  | penalty = 68.7    | AICc = -2*LL + 2*p + 2*p*(p+1)/(n-p-1)  (bias corrected AIC)  
## BIC = 4045  | penalty = 179.58    | BIC = -2*LL + log(n)*p   
## CAIC = 4076  | penalty = 210.58    | CAIC = -2*LL + [log(n)+1]*p  (consistent AIC)   
## 
## -----------------------------------------------------------------------------
## Trait Distribution
## 
## M Trait:
##           F1    F2    F3
## Group1 0.524 0.507 0.816
## 
## SD Trait:
##           F1  F2    F3
## Group1 0.499 0.5 0.387
## 
## Skewness Trait:
##            F1     F2     F3
## Group1 -0.098 -0.028 -1.633
## 
## Correlations Trait: 
## Group 1 
##       F1    F2    F3
## F1 1.000 0.861 0.368
## F2 0.861 1.000 0.349
## F3 0.368 0.349 1.000
## 
## EAP Reliability:
##         F1    F2    F3
## [1,] 0.709 0.651 0.657
## -----------------------------------------------------------------------------
## Item Parameters 
##    item   N     M b.Cat1   a.F1  a.F2   a.F3 itemfit.rmsea
## 1    A1 328 0.851  0.781 33.673 0.000  0.000         0.039
## 2    A2 328 0.738 -0.062  3.456 0.000  0.000         0.020
## 3    A3 328 0.567 -0.843  2.283 0.000  0.000         0.031
## 4    A4 328 0.460 -1.160  1.830 0.000  0.000         0.018
## 5    B1 328 0.713  0.273  0.000 1.500  0.000         0.045
## 6    B2 328 0.506 -0.657  0.000 1.348  0.000         0.054
## 7    B3 328 0.909  1.551  0.000 3.022  0.000         0.041
## 8    B4 328 0.683 -0.169  0.000 2.388  0.000         0.044
## 9    C1 328 0.933  0.887  0.000 0.000 33.995         0.066
## 10   C2 328 0.713 -0.685  0.000 0.000  2.079         0.103
## 11   C3 328 0.872 -0.104  0.000 0.000  3.323         0.049
## 12   C4 328 0.735 -1.040  0.000 0.000  2.709         0.033
## 
## Mean of RMSEA item fit: 0.045
#-- Model 13c: mirt (in mirt)
# define mirt model
I <- ncol(dat) # I = 12
mirtmodel <- mirt::mirt.model("
F1 = 1-4
F2 = 5-8
F3 = 9-12
")

# get parameters
mod.pars <- mirt(dat, model=mirtmodel , pars = "values")

# starting values d parameters (transformed guessing parameters)
ind <- which( mod.pars$name == "d" )
mod.pars[ind,"value"] <- qlogis(.2)

# starting values transformed slipping parameters
ind <- which( ( mod.pars$name %in% paste0("a",1:3) ) & ( mod.pars$est ) )
mod.pars[ind,"value"] <- qlogis(.8) - qlogis(.2)
mod.pars
##    group  item     class   name parnum     value lbound ubound   est prior.type
## 1    all    A1      dich     a1      1  2.772589   -Inf    Inf  TRUE       none
## 2    all    A1      dich     a2      2  0.000000   -Inf    Inf FALSE       none
## 3    all    A1      dich     a3      3  0.000000   -Inf    Inf FALSE       none
## 4    all    A1      dich      d      4 -1.386294   -Inf    Inf  TRUE       none
## 5    all    A1      dich      g      5  0.000000  0e+00      1 FALSE       none
## 6    all    A1      dich      u      6  1.000000  0e+00      1 FALSE       none
## 7    all    A2      dich     a1      7  2.772589   -Inf    Inf  TRUE       none
## 8    all    A2      dich     a2      8  0.000000   -Inf    Inf FALSE       none
## 9    all    A2      dich     a3      9  0.000000   -Inf    Inf FALSE       none
## 10   all    A2      dich      d     10 -1.386294   -Inf    Inf  TRUE       none
## 11   all    A2      dich      g     11  0.000000  0e+00      1 FALSE       none
## 12   all    A2      dich      u     12  1.000000  0e+00      1 FALSE       none
## 13   all    A3      dich     a1     13  2.772589   -Inf    Inf  TRUE       none
## 14   all    A3      dich     a2     14  0.000000   -Inf    Inf FALSE       none
## 15   all    A3      dich     a3     15  0.000000   -Inf    Inf FALSE       none
## 16   all    A3      dich      d     16 -1.386294   -Inf    Inf  TRUE       none
## 17   all    A3      dich      g     17  0.000000  0e+00      1 FALSE       none
## 18   all    A3      dich      u     18  1.000000  0e+00      1 FALSE       none
## 19   all    A4      dich     a1     19  2.772589   -Inf    Inf  TRUE       none
## 20   all    A4      dich     a2     20  0.000000   -Inf    Inf FALSE       none
## 21   all    A4      dich     a3     21  0.000000   -Inf    Inf FALSE       none
## 22   all    A4      dich      d     22 -1.386294   -Inf    Inf  TRUE       none
## 23   all    A4      dich      g     23  0.000000  0e+00      1 FALSE       none
## 24   all    A4      dich      u     24  1.000000  0e+00      1 FALSE       none
## 25   all    B1      dich     a1     25  0.000000   -Inf    Inf FALSE       none
## 26   all    B1      dich     a2     26  2.772589   -Inf    Inf  TRUE       none
## 27   all    B1      dich     a3     27  0.000000   -Inf    Inf FALSE       none
## 28   all    B1      dich      d     28 -1.386294   -Inf    Inf  TRUE       none
## 29   all    B1      dich      g     29  0.000000  0e+00      1 FALSE       none
## 30   all    B1      dich      u     30  1.000000  0e+00      1 FALSE       none
## 31   all    B2      dich     a1     31  0.000000   -Inf    Inf FALSE       none
## 32   all    B2      dich     a2     32  2.772589   -Inf    Inf  TRUE       none
## 33   all    B2      dich     a3     33  0.000000   -Inf    Inf FALSE       none
## 34   all    B2      dich      d     34 -1.386294   -Inf    Inf  TRUE       none
## 35   all    B2      dich      g     35  0.000000  0e+00      1 FALSE       none
## 36   all    B2      dich      u     36  1.000000  0e+00      1 FALSE       none
## 37   all    B3      dich     a1     37  0.000000   -Inf    Inf FALSE       none
## 38   all    B3      dich     a2     38  2.772589   -Inf    Inf  TRUE       none
## 39   all    B3      dich     a3     39  0.000000   -Inf    Inf FALSE       none
## 40   all    B3      dich      d     40 -1.386294   -Inf    Inf  TRUE       none
## 41   all    B3      dich      g     41  0.000000  0e+00      1 FALSE       none
## 42   all    B3      dich      u     42  1.000000  0e+00      1 FALSE       none
## 43   all    B4      dich     a1     43  0.000000   -Inf    Inf FALSE       none
## 44   all    B4      dich     a2     44  2.772589   -Inf    Inf  TRUE       none
## 45   all    B4      dich     a3     45  0.000000   -Inf    Inf FALSE       none
## 46   all    B4      dich      d     46 -1.386294   -Inf    Inf  TRUE       none
## 47   all    B4      dich      g     47  0.000000  0e+00      1 FALSE       none
## 48   all    B4      dich      u     48  1.000000  0e+00      1 FALSE       none
## 49   all    C1      dich     a1     49  0.000000   -Inf    Inf FALSE       none
## 50   all    C1      dich     a2     50  0.000000   -Inf    Inf FALSE       none
## 51   all    C1      dich     a3     51  2.772589   -Inf    Inf  TRUE       none
## 52   all    C1      dich      d     52 -1.386294   -Inf    Inf  TRUE       none
## 53   all    C1      dich      g     53  0.000000  0e+00      1 FALSE       none
## 54   all    C1      dich      u     54  1.000000  0e+00      1 FALSE       none
## 55   all    C2      dich     a1     55  0.000000   -Inf    Inf FALSE       none
## 56   all    C2      dich     a2     56  0.000000   -Inf    Inf FALSE       none
## 57   all    C2      dich     a3     57  2.772589   -Inf    Inf  TRUE       none
## 58   all    C2      dich      d     58 -1.386294   -Inf    Inf  TRUE       none
## 59   all    C2      dich      g     59  0.000000  0e+00      1 FALSE       none
## 60   all    C2      dich      u     60  1.000000  0e+00      1 FALSE       none
## 61   all    C3      dich     a1     61  0.000000   -Inf    Inf FALSE       none
## 62   all    C3      dich     a2     62  0.000000   -Inf    Inf FALSE       none
## 63   all    C3      dich     a3     63  2.772589   -Inf    Inf  TRUE       none
## 64   all    C3      dich      d     64 -1.386294   -Inf    Inf  TRUE       none
## 65   all    C3      dich      g     65  0.000000  0e+00      1 FALSE       none
## 66   all    C3      dich      u     66  1.000000  0e+00      1 FALSE       none
## 67   all    C4      dich     a1     67  0.000000   -Inf    Inf FALSE       none
## 68   all    C4      dich     a2     68  0.000000   -Inf    Inf FALSE       none
## 69   all    C4      dich     a3     69  2.772589   -Inf    Inf  TRUE       none
## 70   all    C4      dich      d     70 -1.386294   -Inf    Inf  TRUE       none
## 71   all    C4      dich      g     71  0.000000  0e+00      1 FALSE       none
## 72   all    C4      dich      u     72  1.000000  0e+00      1 FALSE       none
## 73   all GROUP GroupPars MEAN_1     73  0.000000   -Inf    Inf FALSE       none
## 74   all GROUP GroupPars MEAN_2     74  0.000000   -Inf    Inf FALSE       none
## 75   all GROUP GroupPars MEAN_3     75  0.000000   -Inf    Inf FALSE       none
## 76   all GROUP GroupPars COV_11     76  1.000000  1e-04    Inf FALSE       none
## 77   all GROUP GroupPars COV_21     77  0.000000   -Inf    Inf FALSE       none
## 78   all GROUP GroupPars COV_31     78  0.000000   -Inf    Inf FALSE       none
## 79   all GROUP GroupPars COV_22     79  1.000000  1e-04    Inf FALSE       none
## 80   all GROUP GroupPars COV_32     80  0.000000   -Inf    Inf FALSE       none
## 81   all GROUP GroupPars COV_33     81  1.000000  1e-04    Inf FALSE       none
##    prior_1 prior_2
## 1      NaN     NaN
## 2      NaN     NaN
## 3      NaN     NaN
## 4      NaN     NaN
## 5      NaN     NaN
## 6      NaN     NaN
## 7      NaN     NaN
## 8      NaN     NaN
## 9      NaN     NaN
## 10     NaN     NaN
## 11     NaN     NaN
## 12     NaN     NaN
## 13     NaN     NaN
## 14     NaN     NaN
## 15     NaN     NaN
## 16     NaN     NaN
## 17     NaN     NaN
## 18     NaN     NaN
## 19     NaN     NaN
## 20     NaN     NaN
## 21     NaN     NaN
## 22     NaN     NaN
## 23     NaN     NaN
## 24     NaN     NaN
## 25     NaN     NaN
## 26     NaN     NaN
## 27     NaN     NaN
## 28     NaN     NaN
## 29     NaN     NaN
## 30     NaN     NaN
## 31     NaN     NaN
## 32     NaN     NaN
## 33     NaN     NaN
## 34     NaN     NaN
## 35     NaN     NaN
## 36     NaN     NaN
## 37     NaN     NaN
## 38     NaN     NaN
## 39     NaN     NaN
## 40     NaN     NaN
## 41     NaN     NaN
## 42     NaN     NaN
## 43     NaN     NaN
## 44     NaN     NaN
## 45     NaN     NaN
## 46     NaN     NaN
## 47     NaN     NaN
## 48     NaN     NaN
## 49     NaN     NaN
## 50     NaN     NaN
## 51     NaN     NaN
## 52     NaN     NaN
## 53     NaN     NaN
## 54     NaN     NaN
## 55     NaN     NaN
## 56     NaN     NaN
## 57     NaN     NaN
## 58     NaN     NaN
## 59     NaN     NaN
## 60     NaN     NaN
## 61     NaN     NaN
## 62     NaN     NaN
## 63     NaN     NaN
## 64     NaN     NaN
## 65     NaN     NaN
## 66     NaN     NaN
## 67     NaN     NaN
## 68     NaN     NaN
## 69     NaN     NaN
## 70     NaN     NaN
## 71     NaN     NaN
## 72     NaN     NaN
## 73     NaN     NaN
## 74     NaN     NaN
## 75     NaN     NaN
## 76     NaN     NaN
## 77     NaN     NaN
## 78     NaN     NaN
## 79     NaN     NaN
## 80     NaN     NaN
## 81     NaN     NaN
#* define prior for latent class analysis
lca_prior <- function(Theta,Etable){
    TP <- nrow(Theta)
    if ( is.null(Etable) ){
    prior <- rep( 1/TP , TP )
    }
    if ( ! is.null(Etable) ){
    prior <- ( rowSums( Etable[ , seq(1,2*I,2)] ) + rowSums( Etable[ , seq(2,2*I,2)]))}
    prior <- prior / sum(prior)
    return(prior)
}

#* estimate model
mod13c <- mirt(dat, mirtmodel , technical = list(
customTheta=Theta , customPriorFun = lca_prior) ,
pars = mod.pars , verbose=FALSE)

# estimated parameters from the user customized prior distribution.
nest <- as.integer(sum(mod.pars$est) + 2)

#* extract item parameters
coef13c <- coef(mod13c, simplify=TRUE)$items

#* inspect estimated distribution
cbind(Theta, extract.mirt(mod13c, "Prior")[[1]])
##      [,1] [,2] [,3]        [,4]
## [1,]    0    0    0 0.040517629
## [2,]    1    0    0 0.008688677
## [3,]    0    1    0 0.007497199
## [4,]    0    0    1 0.415904645
## [5,]    1    1    0 0.010802342
## [6,]    1    0    1 0.041193529
## [7,]    0    1    1 0.009551117
## [8,]    1    1    1 0.465844863
#-* comparisons of estimated parameters
# extract guessing and slipping parameters from din
dfr <- as.data.frame(matrix(coef(mod13a)[1:24], ncol=2))
colnames(dfr) <- paste0("din.",c("guess","slip") )

# estimated parameters from gdm
dfr$gdm.guess <- plogis(mod13b$item$b)
dfr$gdm.slip <- 1 - plogis( rowSums(mod13b$item[,c("b.Cat1","a.F1","a.F2","a.F3")]))# estimated parameters from mirt
dfr$mirt.guess <- plogis( coef13c[,'d'] )
dfr$mirt.slip <- 1 - plogis( rowSums(coef13c[,c("d","a1","a2","a3")]) )

# comparison
round(dfr[, c(1,3,5,2,4,6)],3)
##    din.guess gdm.guess mirt.guess din.slip gdm.slip mirt.slip
## 1      0.691     0.686      0.685    0.817    0.000     0.000
## 2      0.000     0.485      0.488    0.014    0.032     0.038
## 3      0.491     0.301      0.300    0.431    0.192     0.193
## 4      0.031     0.239      0.239    0.104    0.338     0.340
## 5      0.302     0.568      0.579    0.188    0.145     0.148
## 6      0.184     0.341      0.343    0.013    0.334     0.327
## 7      0.244     0.825      0.827    0.050    0.010     0.008
## 8      0.337     0.458      0.461    0.239    0.098     0.090
## 9      0.568     0.708      0.189    0.000    0.000     0.013
## 10     0.163     0.335      0.050    0.065    0.199     0.239
## 11     0.329     0.474      0.001    0.000    0.038     0.065
## 12     0.344     0.261      0.000    0.212    0.159     0.212
# estimated class sizes
dfr <- data.frame( "Theta" = Theta , "din"=mod13a$attribute.patt$class.prob ,
"gdm"=mod13b$pi.k , "mirt" = extract.mirt(mod13c, "Prior")[[1]])
# comparison
round(dfr,3)
##   Theta.1 Theta.2 Theta.3   din   gdm  mirt
## 1       0       0       0 0.039 0.147 0.041
## 2       1       0       0 0.008 0.011 0.009
## 3       0       1       0 0.009 0.011 0.007
## 4       0       0       1 0.394 0.302 0.416
## 5       1       1       0 0.011 0.014 0.011
## 6       1       0       1 0.017 0.033 0.041
## 7       0       1       1 0.042 0.015 0.010
## 8       1       1       1 0.480 0.467 0.466
sessionInfo()
## R version 4.2.2 Patched (2022-11-10 r83330)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] CDM_8.2-6       mvtnorm_1.2-2   sirt_3.12-66    ltm_1.2-0      
##  [5] polycor_0.8-1   msm_1.7         MASS_7.3-58.2   mirt_1.39.4    
##  [9] lattice_0.20-45 knitr_1.43     
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.10          highr_0.10           compiler_4.2.2      
##  [4] dcurver_0.9.2        tools_4.2.2          evaluate_0.21       
##  [7] lifecycle_1.0.3      gtable_0.3.3         nlme_3.1-162        
## [10] mgcv_1.8-41          rlang_1.1.1          Matrix_1.5-3        
## [13] cli_3.6.1            rstudioapi_0.14      commonmark_1.9.0    
## [16] parallel_4.2.2       TAM_4.1-4            expm_0.999-7        
## [19] xfun_0.39            gridExtra_2.3        cluster_2.1.4       
## [22] admisc_0.32          grid_4.2.2           glue_1.6.2          
## [25] pbapply_1.7-0        survival_3.4-0       GPArotation_2023.3-1
## [28] splines_4.2.2        mime_0.12            permute_0.9-7       
## [31] Deriv_4.1.3          markdown_1.7         vegan_2.6-4