Adapted from the sirt
package examples (data.read
).
library(mirt)
library(sirt)
## - sirt 3.12-66 (2022-05-16 12:27:54)
library(mvtnorm)
data(data.read)
dat <- data.read
I <- ncol(dat)
# define some simple Q-Matrix (does not really make in this application)
Q <- matrix( 0 , nrow=12,ncol=2)
Q[1:4,1] <- 1
Q[5:8,2] <- 1
Q[9:12,1:2] <- 1
# define discrete theta distribution with 3 dimensions
Theta <- matrix(c(0,0, 1,0, 0,1, 1,1), byrow=TRUE, ncol=2)
Theta
## [,1] [,2]
## [1,] 0 0
## [2,] 1 0
## [3,] 0 1
## [4,] 1 1
#-- Model 14a: din (in CDM)
mod14a <- CDM::din( dat , q.matrix=Q , rule="DINA", progress = FALSE)
summary(mod14a)
## 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:11:51
## Time difference of 0.02211118 secs
## Computation Time: 0.02211118
##
##
## Deviance = 3915.31 | Log-Likelihood= -1957.655
##
## Number of iterations: 41
##
## Number of item parameters: 24
## Number of skill class parameters: 3
##
## Information criteria:
## AIC = 3969.31
## BIC = 4071.722
##
## Mean of RMSEA item fit: 0.028
##
## Item parameters
## item guess slip IDI rmsea
## 1 A1 0.674 0.030 0.295 0.011
## 2 A2 0.423 0.049 0.527 0.017
## 3 A3 0.258 0.224 0.519 0.012
## 4 A4 0.245 0.394 0.361 0.018
## 5 B1 0.534 0.166 0.300 0.026
## 6 B2 0.338 0.382 0.280 0.025
## 7 B3 0.796 0.016 0.188 0.019
## 8 B4 0.421 0.142 0.436 0.033
## 9 C1 0.850 0.000 0.150 0.043
## 10 C2 0.480 0.097 0.423 0.037
## 11 C3 0.746 0.026 0.228 0.054
## 12 C4 0.575 0.136 0.289 0.038
##
## Marginal skill probabilities:
## skill.prob
## Skill1 0.5964
## Skill2 0.5996
##
## Tetrachoric correlations among skill dimensions
## Skill1 Skill2
## Skill1 1.0000 0.9572
## Skill2 0.9572 1.0000
##
## Skill Pattern Probabilities
##
## 00 10 01 11
## 0.35663 0.04377 0.04693 0.55267
# compare used Theta distributions
cbind( Theta , mod14a$attribute.patt.splitted)
## Skill1 Skill2
## [1,] 0 0 0 0
## [2,] 1 0 1 0
## [3,] 0 1 0 1
## [4,] 1 1 1 1
#-- Model 14b: mirt (in mirt)
# define mirt model
I <- ncol(dat) # I = 12
mirtmodel <- mirt::mirt.model("
F1 = 1-4
F2 = 5-8
(F1*F2) = 9-12
")
#-> constructions like (F1*F2*F3) are also allowed in mirt.model
# 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 COV_11 75 1.000000 1e-04 Inf FALSE none
## 76 all GROUP GroupPars COV_21 76 0.000000 -Inf Inf FALSE none
## 77 all GROUP GroupPars COV_22 77 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
lca_prior <- function(Theta,Etable){
# number of latent Theta classes
TP <- nrow(Theta)
# prior in initial iteration
if ( is.null(Etable) ){
prior <- rep( 1/TP , TP )
}
# process Etable (this is correct for datasets without missing data)
if ( ! is.null(Etable) ){
# sum over correct and incorrect expected responses
prior <- ( rowSums( Etable[ , seq(1,2*I,2)] ) + rowSums( Etable[ , seq(2,2*I,2)]))}
prior <- prior / sum(prior)
return(prior)
}
#* estimate model
mod14b <- 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
coef14b <- coef(mod14b, simplify=TRUE)$items
#-* comparisons of estimated parameters
# extract guessing and slipping parameters from din
dfr <- data.frame(matrix(coef(mod14a)[1:24], ncol=2, byrow=T))
colnames(dfr) <- paste0("din.",c("guess","slip") )
# estimated parameters from mirt
dfr$mirt.guess <- plogis( coef14b[,'d'] )
dfr$mirt.slip <- 1 - plogis( rowSums(coef14b[,c("d","a1","a2","a3")]) )
# comparison
round(dfr[, c(1,3,2,4)],3)
## din.guess mirt.guess din.slip mirt.slip
## 1 0.674 0.671 0.030 0.030
## 2 0.423 0.420 0.049 0.050
## 3 0.258 0.254 0.224 0.225
## 4 0.245 0.243 0.394 0.395
## 5 0.534 0.543 0.166 0.164
## 6 0.338 0.348 0.382 0.380
## 7 0.796 0.803 0.016 0.015
## 8 0.421 0.437 0.142 0.140
## 9 0.850 0.851 0.000 0.000
## 10 0.480 0.480 0.097 0.097
## 11 0.746 0.746 0.026 0.026
## 12 0.575 0.577 0.136 0.137
dfr <- data.frame( "Theta" = Theta , "din"=mod14a$attribute.patt$class.prob ,
"mirt" = extract.mirt(mod14b, "Prior")[[1]])
# comparison
round(dfr,3)
## Theta.1 Theta.2 din mirt
## 1 0 0 0.357 0.370
## 2 1 0 0.044 0.049
## 3 0 1 0.047 0.030
## 4 1 1 0.553 0.551
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] mvtnorm_1.2-2 sirt_3.12-66 ltm_1.2-0 polycor_0.8-1
## [5] msm_1.7 MASS_7.3-58.2 mirt_1.39.4 lattice_0.20-45
## [9] 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 CDM_8.2-6
## [28] GPArotation_2023.3-1 splines_4.2.2 mime_0.12
## [31] permute_0.9-7 Deriv_4.1.3 markdown_1.7
## [34] vegan_2.6-4