mirt example grade of membership model

mirt example grade of membership model

Adapted from the sirt package examples (gom.em).

library(mirt)
library(sirt)

# define a 2-class model with graded membership
set.seed(8765)
I <- 10
prob.class1 <- runif( I , 0 , .35 )
prob.class2 <- runif( I , .70 , .95 )
prob.class3 <- .5*prob.class1+.5*prob.class2 # probabilities for fuzzy class
probs <- cbind( prob.class1 , prob.class2 , prob.class3)

# define classes
N <- 1000
latent.class <- c( rep(1,round(1/3*N)),rep(2,round(1/2*N)),rep(3,round(1/6*N)))

# simulate item responses
dat <- matrix( NA , nrow=N , ncol=I )
for (ii in 1:I){
dat[,ii] <- probs[ ii , latent.class ]
dat[,ii] <- 1 * ( runif(N) < dat[,ii] )
}
colnames(dat) <- paste0( "I" , 1:I)

#** Model 1: estimate latent class model
mod1 <- gom.em(dat, K=2, problevels= c(0,1) , model="GOM", progress = FALSE )
##   0:     11146.239: -1.57117 -1.95874 -1.86628 -2.42936 -1.58315 -0.951507 -1.25370 -1.11453 -2.51371 -0.764076  1.72480  1.36079  1.21448  1.22730 0.797383 0.959441  1.03650  1.16077 0.880661  2.23621 -0.319547
##   2:     11146.238: -1.57244 -1.95979 -1.86756 -2.43043 -1.58377 -0.952397 -1.25449 -1.11554 -2.51460 -0.764964  1.72362  1.35952  1.21355  1.22600 0.796431 0.958887  1.03563  1.16006 0.879537  2.23494 -0.320130
##   4:     11146.238: -1.57311 -1.96067 -1.86857 -2.43175 -1.58413 -0.952546 -1.25474 -1.11577 -2.51579 -0.765009  1.72335  1.35944  1.21355  1.22595 0.796369 0.958880  1.03561  1.16006 0.879502  2.23409 -0.320567
##   6:     11146.238: -1.57306 -1.96072 -1.86858 -2.43207 -1.58408 -0.952594 -1.25473 -1.11580 -2.51612 -0.765084  1.72331  1.35934  1.21347  1.22587 0.796436 0.958884  1.03557  1.16000 0.879528  2.23407 -0.320859
##   8:     11146.238: -1.57309 -1.96074 -1.86861 -2.43214 -1.58410 -0.952587 -1.25475 -1.11580 -2.51620 -0.765061  1.72330  1.35937  1.21350  1.22592 0.796417 0.958894  1.03560  1.16004 0.879539  2.23404 -0.320916
##  10:     11146.238: -1.57309 -1.96074 -1.86861 -2.43214 -1.58410 -0.952587 -1.25475 -1.11580 -2.51620 -0.765061  1.72330  1.35937  1.21350  1.22592 0.796417 0.958894  1.03560  1.16004 0.879539  2.23404 -0.320916
##  12:     11146.238: -1.57309 -1.96074 -1.86861 -2.43214 -1.58410 -0.952587 -1.25475 -1.11580 -2.51620 -0.765061  1.72330  1.35937  1.21350  1.22592 0.796417 0.958894  1.03560  1.16004 0.879539  2.23404 -0.320916
summary(mod1)
## -----------------------------------------------------------------
## sirt 3.12-66 (2022-05-16 12:27:54) 
## R version 4.2.2 Patched (2022-11-10 r83330) x86_64, linux-gnu | nodename=cruncher | login=unknown 
## 
## Call:
## gom.em(dat = dat, K = 2, problevels = c(0, 1), model = "GOM", 
##     progress = FALSE)
## 
## Date of Analysis: 2023-07-05 16:11:52 
## Time difference of 0.1742499 secs
## Computation Time: 0.1742499 
## 
##   Function 'gom.em' 
## 
## --- Information about Newton-Raphson algorithm of optimization ---
## 
## Optimizer = nlminb 
## Converged = FALSE 
## Optimization Function Value = 11146.24 
## Number of iterations = 12 
## Elapsed time =  Time difference of 0.1551373 secs
## 
##    Discrete Grade of Membership Model
## 
##     1000 Cases,  10 Items,  2 Classes , 2 Discrete Integration Points
## -----------------------------------------------------------------
## Number of EM iterations = 9 
## Deviance = 11146.24  | Log Likelihood = -5573.12 
## Number of persons = 1000 
## Number of estimated parameters = 21 
##   Number of estimated item parameters = 20 
##   Number of estimated distribution parameters = 1 
## 
## AIC  =  11188  | penalty = 42    | AIC=-2*LL + 2*p    
## AICc =  11189  | penalty = 42.94    | AICc=-2*LL + 2*p + 2*p*(p+1)/(n-p-1)  (bias corrected AIC)   
## BIC  =  11291  | penalty = 145.06    | BIC=-2*LL + log(n)*p    
## CAIC =  11312  | penalty = 166.06    | CAIC=-2*LL + [log(n)+1]*p  (consistent AIC)   
## 
## -----------------------------------------------------------------
## Membership Function Descriptives 
##                    Class1 Class2
## p.Class             0.420  0.580
## p.problevel0.class  0.580  0.420
## p.problevel1.class  0.420  0.580
## M.problevel0.class  0.771  0.182
## M.problevel1.class  0.182  0.771
## -----------------------------------------------------------------
## Item Parameters 
##    item    N     p lam.Cl1 lam.Cl2
## 1    I1 1000 0.564   0.172   0.849
## 2    I2 1000 0.513   0.123   0.796
## 3    I3 1000 0.503   0.134   0.771
## 4    I4 1000 0.482   0.081   0.773
## 5    I5 1000 0.471   0.170   0.689
## 6    I6 1000 0.536   0.278   0.723
## 7    I7 1000 0.521   0.222   0.738
## 8    I8 1000 0.545   0.247   0.761
## 9    I9 1000 0.441   0.075   0.707
## 10  I10 1000 0.657   0.318   0.903
#** Model 2: estimate GOM
mod2 <- gom.em(dat, K=2, problevels= seq(0,1,0.5) , model="GOM", progress = FALSE )
##   0:     11077.788: -2.02255 -2.44497 -2.50845 -3.15900 -1.91699 -1.19362 -1.45189 -1.39108 -3.27716 -0.944203  2.13281  1.63345  1.49626  1.44908 0.968595  1.13970  1.18468  1.37809  1.07529  2.72903 -0.368931 -0.987892
##   2:     11077.783: -2.01990 -2.44305 -2.50605 -3.15713 -1.91497 -1.19179 -1.45059 -1.38901 -3.27577 -0.942890  2.12930  1.63078  1.49346  1.44675 0.966666  1.13765  1.18304  1.37551  1.07314  2.72658 -0.373459 -0.998178
##   4:     11077.777: -2.01739 -2.44058 -2.50267 -3.15389 -1.91342 -1.19143 -1.45029 -1.38823 -3.27324 -0.942927  2.12565  1.62945  1.49247  1.44606 0.966344  1.13738  1.18283  1.37488  1.07288  2.72244 -0.369974 -1.00208
##   6:     11077.776: -2.01639 -2.43906 -2.50022 -3.14952 -1.91292 -1.19076 -1.44981 -1.38775 -3.26993 -0.942559  2.12662  1.62924  1.49194  1.44558 0.966699  1.13734  1.18270  1.37449  1.07290  2.72144 -0.369053 -1.00086
##   8:     11077.776: -2.01641 -2.43902 -2.50010 -3.14772 -1.91251 -1.19080 -1.44971 -1.38738 -3.26860 -0.942503  2.12564  1.62914  1.49211  1.44567 0.966136  1.13704  1.18255  1.37450  1.07244  2.72238 -0.368994 -1.00285
##  10:     11077.776: -2.01620 -2.43894 -2.50005 -3.14769 -1.91259 -1.19085 -1.44981 -1.38757 -3.26856 -0.942342  2.12577  1.62936  1.49217  1.44572 0.966141  1.13711  1.18264  1.37460  1.07252  2.72202 -0.368923 -1.00269
##  12:     11077.776: -2.01609 -2.43891 -2.50005 -3.14770 -1.91264 -1.19086 -1.44983 -1.38761 -3.26857 -0.942332  2.12588  1.62939  1.49222  1.44578 0.966182  1.13714  1.18266  1.37465  1.07255  2.72172 -0.368749 -1.00264
##  14:     11077.776: -2.01610 -2.43888 -2.50001 -3.14765 -1.91263 -1.19087 -1.44983 -1.38761 -3.26852 -0.942267  2.12586  1.62943  1.49221  1.44576 0.966181  1.13714  1.18265  1.37462  1.07255  2.72168 -0.368707 -1.00271
##  16:     11077.776: -2.01593 -2.43880 -2.49987 -3.14743 -1.91263 -1.19082 -1.44983 -1.38763 -3.26835 -0.942274  2.12591  1.62937  1.49221  1.44576 0.966158  1.13718  1.18266  1.37460  1.07263  2.72156 -0.368693 -1.00267
##  18:     11077.776: -2.01598 -2.43879 -2.49987 -3.14736 -1.91260 -1.19084 -1.44981 -1.38757 -3.26830 -0.942231  2.12587  1.62944  1.49220  1.44576 0.966198  1.13713  1.18267  1.37464  1.07254  2.72160 -0.368685 -1.00277
##  20:     11077.776: -2.01599 -2.43879 -2.49987 -3.14737 -1.91260 -1.19084 -1.44981 -1.38758 -3.26830 -0.942241  2.12586  1.62943  1.49220  1.44576 0.966187  1.13713  1.18266  1.37464  1.07254  2.72160 -0.368681 -1.00276
summary(mod2)
## -----------------------------------------------------------------
## sirt 3.12-66 (2022-05-16 12:27:54) 
## R version 4.2.2 Patched (2022-11-10 r83330) x86_64, linux-gnu | nodename=cruncher | login=unknown 
## 
## Call:
## gom.em(dat = dat, K = 2, problevels = seq(0, 1, 0.5), model = "GOM", 
##     progress = FALSE)
## 
## Date of Analysis: 2023-07-05 16:11:52 
## Time difference of 0.2007856 secs
## Computation Time: 0.2007856 
## 
##   Function 'gom.em' 
## 
## --- Information about Newton-Raphson algorithm of optimization ---
## 
## Optimizer = nlminb 
## Converged = FALSE 
## Optimization Function Value = 11077.78 
## Number of iterations = 21 
## Elapsed time =  Time difference of 0.1802695 secs
## 
##    Discrete Grade of Membership Model
## 
##     1000 Cases,  10 Items,  2 Classes , 3 Discrete Integration Points
## -----------------------------------------------------------------
## Number of EM iterations = 13 
## Deviance = 11077.78  | Log Likelihood = -5538.89 
## Number of persons = 1000 
## Number of estimated parameters = 22 
##   Number of estimated item parameters = 20 
##   Number of estimated distribution parameters = 2 
## 
## AIC  =  11122  | penalty = 44    | AIC=-2*LL + 2*p    
## AICc =  11123  | penalty = 45.04    | AICc=-2*LL + 2*p + 2*p*(p+1)/(n-p-1)  (bias corrected AIC)   
## BIC  =  11230  | penalty = 151.97    | BIC=-2*LL + log(n)*p    
## CAIC =  11252  | penalty = 173.97    | CAIC=-2*LL + [log(n)+1]*p  (consistent AIC)   
## 
## -----------------------------------------------------------------
## Membership Function Descriptives 
##                      Class1 Class2
## p.Class               0.425  0.575
## p.problevel0.class    0.486  0.336
## p.problevel0.5.class  0.178  0.178
## p.problevel1.class    0.336  0.486
## M.problevel0.class    0.808  0.138
## M.problevel0.5.class  0.473  0.473
## M.problevel1.class    0.138  0.808
## -----------------------------------------------------------------
## Item Parameters 
##    item    N     p lam.Cl1 lam.Cl2
## 1    I1 1000 0.564   0.118   0.893
## 2    I2 1000 0.513   0.080   0.836
## 3    I3 1000 0.503   0.076   0.816
## 4    I4 1000 0.482   0.041   0.809
## 5    I5 1000 0.471   0.129   0.724
## 6    I6 1000 0.536   0.233   0.757
## 7    I7 1000 0.521   0.190   0.765
## 8    I8 1000 0.545   0.200   0.798
## 9    I9 1000 0.441   0.037   0.745
## 10  I10 1000 0.657   0.280   0.938
# inspect distribution
cbind( mod2$theta.k , mod2$pi.k )
##      [,1] [,2]      [,3]
## [1,]  1.0  0.0 0.3359931
## [2,]  0.5  0.5 0.1782190
## [3,]  0.0  1.0 0.4857879
#***
# Model2m: estimate discrete GOM in mirt
# define latent classes
Theta <- c(1, 0, .5, .5, 0, 1)
Theta <- matrix( Theta , nrow=3 , ncol=2,byrow=TRUE)

# define mirt model
I <- ncol(dat)

#*** create customized item for mirt model
name <- "icc_gom"
par <- c("a1" = -1 , "a2" = 1 )
est <- c(TRUE, TRUE)
P.gom <- function(par,Theta,ncat){    
    # GOM for two extremal classes
    pext1 <- plogis(par[1])
    pext2 <- plogis(par[2])
    P1 <- Theta[,1]*pext1 + Theta[,2]*pext2
    cbind(1-P1, P1)
}

# create item response function
icc_gom <- mirt::createItem(name, par=par, est=est, P=P.gom)

#** define prior for latent class analysis
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)]) )/I
    }
    prior <- prior / sum(prior)
    return(prior)
}

#*** estimate discrete GOM in mirt package
mod2m <- mirt::mirt(dat, 1, rep( "icc_gom",I) , customItems=list("icc_gom"=icc_gom),
    technical = list( customTheta=Theta , customPriorFun = lca_prior), verbose=FALSE )

# correct number of estimated parameters
nest <- as.integer(sum(mod2values(mod2m)$est) + nrow(Theta)-1 )

# extract log-likelihood and compute AIC and BIC
(LL <- extract.mirt(mod2m, 'logLik'))
## [1] -5538.888
( AIC <- -2*LL + 2*nest )
## [1] 11121.78
( BIC <- -2*LL + log(mod2m@Data$N)*nest )
## [1] 11229.75
# extract coefficients
( cmod2m <- coef(mod2m, simplify=TRUE) )
## $items
##         a1    a2
## I1  -2.016 2.126
## I2  -2.439 1.630
## I3  -2.500 1.492
## I4  -3.147 1.446
## I5  -1.912 0.966
## I6  -1.191 1.137
## I7  -1.450 1.183
## I8  -1.387 1.375
## I9  -3.268 1.073
## I10 -0.942 2.722
## 
## $means
## F1 
##  0 
## 
## $cov
##    F1
## F1  1
# compare estimated distributions
round( cbind( "sirt" = mod2$pi.k , "mirt" = extract.mirt(mod2m, "Prior")[[1]] ) , 5 )
##         sirt    mirt
## [1,] 0.33599 0.33603
## [2,] 0.17822 0.17820
## [3,] 0.48579 0.48577
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