#############################################################################
# EXAMPLE 1: data.Students | (Generalized) Partial Credit Model
#############################################################################
data(data.Students)
dat <- data.Students[ , c("mj1","mj2","mj3","mj4","sc1" , "sc2") ]
# define discretized ability
theta.k <- seq( -6 , 6 , len=21 )
#*** Model 1: Partial credit model
# define design matrix for lambda
I <- ncol(dat)
maxK <- 4
TP <- length(theta.k)
NXlam <- I*(maxK-1) + 1 # number of estimated parameters
# last parameter is joint slope parameter
Xdes <- array( 0 , dim=c(I , maxK , TP , NXlam ) )
# Item1Cat1 , ... , Item1Cat3, Item2Cat1, ...,
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[3]] <- paste0("Class" , 1:TP )
v2 <- unlist( sapply( 1:I , FUN = function(ii){ # ii
paste0( paste0( colnames(dat)[ii] , "_b" ) , "Cat" , 1:(maxK-1) )
} , simplify=FALSE) )
dimnames(Xdes)[[4]] <- c( v2 , "a" )
# define theta design and item discriminations
for (ii in 1:I){
for (hh in 1:(maxK-1) ){
Xdes[ii , hh + 1 , , NXlam ] <- hh * theta.k
}
}
# item intercepts
for (ii in 1:I){
for (hh in 1:(maxK-1) ){
# ii <- 1 # Item # hh <- 1 # category
Xdes[ii,hh+1,, ( ii - 1)*(maxK-1) + hh] <- 1
}
}
#****
# skill space designmatrix
TP <- length(theta.k)
w1 <- stats::dnorm(theta.k)
w1 <- w1 / sum(w1)
delta.designmatrix <- matrix( 1 , nrow=TP , ncol=1 )
delta.designmatrix[,1] <- log(w1)
# initial lambda parameters
Xlambda.init <- c( stats::rnorm( dim(Xdes)[[4]] - 1 ) , 1 )
# fixed delta parameter
delta.fixed <- cbind( 1 , 1 ,1 )
# estimate model
mod1 <- slca( dat , Xdes=Xdes , delta.designmatrix=delta.designmatrix ,
Xlambda.init=Xlambda.init , delta.fixed = delta.fixed, maxiter = 70 )
summary(mod1)
plot(mod1, cex.names=.7 )
#*** Model 2: Partial credit model with some parameter constraints
# fixed lambda parameters
Xlambda.fixed <- cbind( c(1,19) , c(3.2,1.52 ) )
# 1st parameter = 3.2
# 19th parameter = 1.52 (joint item slope)
mod2 <- slca( dat , Xdes=Xdes , delta.designmatrix=delta.designmatrix ,
delta.init=delta.init, Xlambda.init=Xlambda.init, delta.fixed=delta.fixed,
Xlambda.fixed=Xlambda.fixed , maxiter = 70 )
#*** Model 3: Partial credit model with non-normal distribution
Xlambda.fixed <- cbind( c(1,19) , c(3.2,1) ) # fix item slope to one
delta.designmatrix <- cbind( 1 , theta.k , theta.k^2 , theta.k^3 )
mod3 <- slca( dat , Xdes=Xdes, delta.designmatrix=delta.designmatrix ,
Xlambda.fixed=Xlambda.fixed , maxiter = 200 )
summary(mod3)
# non-normal distribution with convergence regularizing factor oldfac
mod3a <- slca( dat , Xdes=Xdes, delta.designmatrix=delta.designmatrix ,
Xlambda.fixed=Xlambda.fixed, maxiter = 500 , oldfac=.95 )
summary(mod3a)
#*** Model 4: Generalized Partial Credit Model
# estimate generalized partial credit model without restrictions on trait
# distribution and item parameters to ensure better convergence behavior
# Note that two parameters are not identifiable and information criteria
# have to be adapted.
#---
# define design matrix for lambda
I <- ncol(dat)
maxK <- 4
TP <- length(theta.k)
NXlam <- I*(maxK-1) + I # number of estimated parameters
Xdes <- array( 0 , dim=c(I , maxK , TP , NXlam ) )
# Item1Cat1 , ... , Item1Cat3, Item2Cat1, ...,
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[3]] <- paste0("Class" , 1:TP )
v2 <- unlist( sapply( 1:I , FUN = function(ii){ # ii
paste0( paste0( colnames(dat)[ii] , "_b" ) , "Cat" , 1:(maxK-1) )
} , simplify=FALSE) )
dimnames(Xdes)[[4]] <- c( v2 , paste0( colnames(dat),"_a") )
dimnames(Xdes)
# define theta design and item discriminations
for (ii in 1:I){
for (hh in 1:(maxK-1) ){
Xdes[ii , hh + 1 , , I*(maxK-1) + ii ] <- hh * theta.k
}
}
# item intercepts
for (ii in 1:I){
for (hh in 1:(maxK-1) ){
Xdes[ii,hh+1,, ( ii - 1)*(maxK-1) + hh] <- 1
}
}
#****
# skill space designmatrix
delta.designmatrix <- cbind( 1, theta.k,theta.k^2 )
# initial lambda parameters from partial credit model
Xlambda.init <- mod1$Xlambda
Xlambda.init <- c( mod1$Xlambda[ - length(Xlambda.init) ] ,
rep( Xlambda.init[ length(Xlambda.init) ] ,I) )
# estimate model
mod4 <- slca( dat , Xdes=Xdes , Xlambda.init=Xlambda.init ,
delta.designmatrix=delta.designmatrix , decrease.increments=TRUE ,
maxiter = 300 )
#############################################################################
# SIMULATED EXAMPLE 2: Latent class model with two classes
#############################################################################
set.seed(9876)
I <- 7 # number of items
# simulate response probabilities
a1 <- stats::runif(I, 0 , .4 )
a2 <- stats::runif(I, .6 , 1 )
N <- 1000 # sample size
# simulate data in two classes of proportions .3 and .7
N1 <- round(.3*N)
dat1 <- 1 * ( matrix(a1,N1,I,byrow=TRUE) > matrix( stats::runif( N1 * I) , N1 , I ) )
N2 <- round(.7*N)
dat2 <- 1 * ( matrix(a2,N2,I,byrow=TRUE) > matrix( stats::runif( N2 * I) , N2 , I ) )
dat <- rbind( dat1 , dat2 )
colnames(dat) <- paste0("I" , 1:I)
# define design matrices
TP <- 2 # two classes
# The idea is that latent classes refer to two different "dimensions".
# Items load on latent class indicators 1 and 2, see below.
Xdes <- array(0 , dim=c(I,2,2,2*I) )
items <- colnames(dat)
dimnames(Xdes)[[4]] <- c(paste0( colnames(dat) , "Class" , 1),
paste0( colnames(dat) , "Class" , 2) )
# items, categories , classes , parameters
# probabilities for correct solution
for (ii in 1:I){
Xdes[ ii , 2 , 1 , ii ] <- 1 # probabilities class 1
Xdes[ ii , 2 , 2 , ii+I ] <- 1 # probabilities class 2
}
# estimate model
mod1 <- slca( dat , Xdes=Xdes )
summary(mod1)
#############################################################################
# SIMULATED EXAMPLE 3: Mixed Rasch model with two classes
#############################################################################
set.seed(987)
library(sirt)
# simulate two latent classes of Rasch populations
I <- 15 # 6 items
b1 <- seq( -1.5 , 1.5 , len=I) # difficulties latent class 1
b2 <- b1 # difficulties latent class 2
b2[ c(4,7, 9 , 11 , 12, 13) ] <- c(1 , -.5 , -.5 , .33 , .33 , -.66 )
N <- 3000 # number of persons
wgt <- .25 # class probability for class 1
# class 1
dat1 <- sirt::sim.raschtype( stats::rnorm( wgt*N ) , b1 )
# class 2
dat2 <- sirt::sim.raschtype( stats::rnorm( (1-wgt)*N , mean= 1 , sd=1.7) , b2 )
dat <- rbind( dat1 , dat2 )
# theta grid
theta.k <- seq( -5 , 5 , len=9 )
TP <- length(theta.k)
#*** Model 1: Rasch model with normal distribution
maxK <- 2
NXlam <- I +1
Xdes <- array( 0 , dim=c(I , maxK , TP , NXlam ) )
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[4]] <- c( paste0( "b_" , colnames(dat)[1:I] ) , "a" )
# define item difficulties
for (ii in 1:I){ Xdes[ii , 2 , , ii ] <- -1 }
# theta design
for (tt in 1:TP){ Xdes[1:I , 2 , tt , I + 1] <- theta.k[tt] }
# skill space definition
delta.designmatrix <- cbind( 1 , theta.k^2 )
delta.fixed <- NULL
Xlambda.init <- c( stats::runif( I , -.8 , .8 ) , 1 )
Xlambda.fixed <- cbind( I+1 , 1 )
# estimate model
mod1 <- slca( dat , Xdes=Xdes , delta.designmatrix=delta.designmatrix ,
delta.fixed=delta.fixed , Xlambda.fixed=Xlambda.fixed ,
Xlambda.init=Xlambda.init, decrease.increments=TRUE , maxiter = 200 )
summary(mod1)
#*** Model 1b: Constraint the sum of item difficulties to zero
# change skill space definition
delta.designmatrix <- cbind( 1 , theta.k , theta.k^2 )
delta.fixed <- NULL
# constrain sum of difficulties Xlambda parameters to zero
Xlambda.constr.V <- matrix( 1 , nrow=I+1 , ncol=1 )
Xlambda.constr.V[I+1,1] <- 0
Xlambda.constr.c <- c(0)
# estimate model
mod1b <- slca( dat , Xdes=Xdes , delta.designmatrix=delta.designmatrix ,
Xlambda.fixed=Xlambda.fixed , Xlambda.constr.V=Xlambda.constr.V ,
Xlambda.constr.c=Xlambda.constr.c )
summary(mod1b)
#*** Model 2: Mixed Rasch model with two latent classes
NXlam <- 2*I +2
Xdes <- array( 0 , dim=c(I , maxK , 2*TP , NXlam ) )
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[4]] <- c( paste0( "bClass1_" , colnames(dat)[1:I] ) ,
paste0( "bClass2_" , colnames(dat)[1:I] ) , "aClass1" , "aClass2" )
# define item difficulties
for (ii in 1:I){
Xdes[ii , 2 , 1:TP , ii ] <- -1 # first class
Xdes[ii , 2 , TP + 1:TP , I+ii ] <- -1 # second class
}
# theta design
for (tt in 1:TP){
Xdes[1:I , 2 , tt , 2*I+1 ] <- theta.k[tt]
Xdes[1:I , 2 , TP+tt , 2*I+2 ] <- theta.k[tt]
}
# skill space definition
delta.designmatrix <- matrix( 0 , nrow=2*TP , ncol=4 )
delta.designmatrix[1:TP,1] <- 1
delta.designmatrix[1:TP,2] <- theta.k^2
delta.designmatrix[TP + 1:TP,3] <- 1
delta.designmatrix[TP+ 1:TP,4] <- theta.k^2
b1 <- stats::qnorm( colMeans( dat ) )
Xlambda.init <- c( b1 , b1 + stats::runif(I, -2 , 2 ) , 1 , 1 )
Xlambda.init <- c( stats::runif( 2*I , -1.8 , 1.8 ) , 1 ,1 )
Xlambda.fixed <- cbind( c(2*I+1 , 2*I+2) , 1 )
# estimate model
mod2 <- slca( dat, Xdes=Xdes, delta.designmatrix=delta.designmatrix ,
Xlambda.fixed=Xlambda.fixed, decrease.increments=TRUE ,
Xlambda.init=Xlambda.init, maxiter = 1000 )
summary(mod2)
summary(mod1)
# latent class proportions
stats::aggregate( mod2$pi.k , list( rep(1:2, each=TP)) , sum )
#*** Model 2b: Different parametrization with sum constraint on item difficulties
# skill space definition
delta.designmatrix <- matrix( 0 , nrow=2*TP , ncol=6 )
delta.designmatrix[1:TP,1] <- 1
delta.designmatrix[1:TP,2] <- theta.k
delta.designmatrix[1:TP,3] <- theta.k^2
delta.designmatrix[TP+ 1:TP,4] <- 1
delta.designmatrix[TP+ 1:TP,5] <- theta.k
delta.designmatrix[TP+ 1:TP,6] <- theta.k^2
Xlambda.fixed <- cbind( c(2*I+1,2*I+2) , c(1,1) )
b1 <- stats::qnorm( colMeans( dat ) )
Xlambda.init <- c( b1 , b1 + stats::runif(I, -1 , 1 ) , 1 , 1 )
# constraints on item difficulties
Xlambda.constr.V <- matrix( 0 , nrow=NXlam , ncol=2)
Xlambda.constr.V[1:I , 1 ] <- 1
Xlambda.constr.V[I + 1:I , 2 ] <- 1
Xlambda.constr.c <- c(0,0)
# estimate model
mod2b <- slca( dat, Xdes=Xdes, delta.designmatrix=delta.designmatrix ,
Xlambda.fixed=Xlambda.fixed , Xlambda.init=Xlambda.init ,
Xlambda.constr.V=Xlambda.constr.V , Xlambda.constr.c=Xlambda.constr.c ,
decrease.increments=TRUE , maxiter = 1000 )
summary(mod2b)
stats::aggregate( mod2b$pi.k , list( rep(1:2, each=TP)) , sum )
#*** Model 2c: Estimation with mRm package
library(mRm)
mod2c <- mRm::mrm(data.matrix=dat, cl=2)
plot(mod2c)
print(mod2c)
#*** Model 2d: Estimation with psychomix package
library(psychomix)
mod2d <- psychomix::raschmix(data=dat, k=2 , verbose=TRUE )
summary(mod2d)
plot(mod2d)
#############################################################################
# SIMULATED EXAMPLE 4: Located latent class model, Rasch model
#############################################################################
set.seed(487)
library(sirt)
I <- 15 # I items
b1 <- seq( -2 , 2 , len=I) # item difficulties
N <- 4000 # number of persons
# simulate 4 theta classes
theta0 <- c( -2.5 , -1 , 0.3 , 1.3 ) # skill classes
probs0 <- c( .1 , .4 , .2 , .3 )
TP <- length(theta0)
theta <- theta0[ rep(1:TP, round(probs0*N) ) ]
dat <- sirt::sim.raschtype( theta , b1 )
#*** Model 1: Located latent class model with 4 classes
maxK <- 2
NXlam <- I + TP
Xdes <- array( 0 , dim=c(I , maxK , TP , NXlam ) )
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[3]] <- paste0("Class" , 1:TP )
dimnames(Xdes)[[4]] <- c( paste0( "b_" , colnames(dat)[1:I] ) , paste0("theta" , 1:TP) )
# define item difficulties
for (ii in 1:I){ Xdes[ii , 2 , , ii ] <- -1 }
# theta design
for (tt in 1:TP){ Xdes[1:I , 2 , tt , I + tt] <- 1 }
# skill space definition
delta.designmatrix <- diag(TP)
Xlambda.init <- c( - stats::qnorm( colMeans(dat) ) , seq(-2,1,len=TP) )
# constraint on item difficulties
Xlambda.constr.V <- matrix( 0 , nrow=NXlam , ncol=1)
Xlambda.constr.V[1:I,1] <- 1
Xlambda.constr.c <- c(0)
delta.init <- matrix( c(1,1,1,1) , TP , 1 )
# estimate model
mod1 <- slca( dat , Xdes=Xdes, delta.designmatrix=delta.designmatrix,
delta.init=delta.init, Xlambda.init=Xlambda.init ,
Xlambda.constr.V=Xlambda.constr.V , Xlambda.constr.c=Xlambda.constr.c ,
decrease.increments=TRUE, maxiter = 400 )
summary(mod1)
# compare estimated and simulated theta class locations
cbind( mod1$Xlambda[ - c(1:I) ] , theta0 )
# compare estimated and simulated latent class proportions
cbind( mod1$pi.k , probs0 )
#############################################################################
# SIMULATED EXAMPLE 5: DINA model with two skills
#############################################################################
set.seed(487)
N <- 3000 # number of persons
# define Q-matrix
I <- 9 # 9 items
NS <- 2 # 2 skills
TP <- 4 # number of skill classes
Q <- scan( nlines=3)
1 0 1 0 1 0
0 1 0 1 0 1
1 1 1 1 1 1
Q <- matrix(Q , I, ncol=NS,byrow=TRUE)
# define skill distribution
alpha0 <- matrix( c(0,0,1,0,0,1,1,1) , nrow=4,ncol=2,byrow=TRUE)
prob0 <- c( .2 , .4 , .1 , .3 )
alpha <- alpha0[ rep( 1:TP , prob0*N) ,]
# define guessing and slipping parameters
guess <- round( stats::runif(I, 0 , .4 ) , 2 )
slip <- round( stats::runif(I , 0 , .3 ) , 2 )
# simulate data according to the DINA model
dat <- CDM::sim.din( q.matrix=Q , alpha=alpha , slip=slip , guess=guess )$dat
# define Xlambda design matrix
maxK <- 2
NXlam <- 2*I
Xdes <- array( 0 , dim=c(I , maxK , TP , NXlam ) )
dimnames(Xdes)[[1]] <- colnames(dat)
dimnames(Xdes)[[2]] <- paste0("Cat" , 1:(maxK) )
dimnames(Xdes)[[3]] <- c("S00","S10","S01","S11")
dimnames(Xdes)[[4]] <- c( paste0("guess",1:I ) , paste0( "antislip" , 1:I ) )
dimnames(Xdes)
# define item difficulties
for (ii in 1:I){
# define latent responses
latresp <- 1*( alpha0 %*% Q[ii,] == sum(Q[ii,]) )[,1]
# model slipping parameters
Xdes[ii , 2 , latresp == 1, I+ii ] <- 1
# guessing parameters
Xdes[ii , 2 , latresp == 0, ii ] <- 1
}
Xdes[1,2,,] ; Xdes[7,2,,]
# skill space definition
delta.designmatrix <- diag(TP)
Xlambda.init <- c( rep( stats::qlogis( .2 ) , I ) , rep( stats::qlogis( .8 ) , I ) )
# estimate DINA model with slca function
mod1 <- slca( dat , Xdes=Xdes, delta.designmatrix=delta.designmatrix ,
Xlambda.init=Xlambda.init, decrease.increments=TRUE, maxiter=400 )
summary(mod1)
# compare estimated and simulated latent class proportions
cbind( mod1$pi.k , probs0 )
# compare estimated and simulated guessing parameters
cbind( mod1$pjk[1,,2] , guess )
# compare estimated and simulated slipping parameters
cbind( 1 - mod1$pjk[4,,2] , slip )
Run the code above in your browser using DataLab