One booklet 2PL items - tmatta/lsasim GitHub Wiki


We examined item parameter recovery under the following conditions: 1 (IRT model) x 3 (IRT R packages) x 3 (sample sizes) x 4 (test lengths) x 1 (test booklet)


  • One IRT model was included: 2PL model
    • Item parameters were randomly generated
    • The bounds of the item difficulty parameter, b, are constrained to b_bounds = (-2, 2) where -2 is the lowest generating value and 2 is the highest generating value
    • The bounds of the item discrimination parameter, a, are constrained to a_bounds = (0.75, 1.25) where 0.75 is the lowest generating value and 1.25 is the highest generating value
  • Three IRT R packages were evaluated: TAM (version 2.4-9), mirt (version 1.25), and ltm (version, 1.0-0)
  • Three sample sizes were used: 500, 1000, and 5000
    • Simulated samples were based on one ability level from distribution N(0, 1)
  • Four test lengths were used: 40, 60, 80, and 100
  • A single booklet was used.

  • One hundred replications were used for each condition for the calibration

  • Summary of item parameter recovery:
    • TAM, mirt, and ltm demonstrated a similar level of accuracy
    • b-parameter recovered well, with correlation ranging from 0.989 to 0.999, with bias ranging from -0.013 to -0.001, and with RMSE ranging from 0.057 to 0.206
    • a-parameter recovered moderately, with correlation ranging from 0.699 to 0.962, with bias ranging from -0.035 to 0.015, and with RMSE ranging from 0.044 to 0.152
    • For b-parameter, sample sizes of 5000 consistently produced the most accurate results
    • For a-parameter, when sample size increased, recovery accuracy improved further
    • For b- and a-parameters, the four levels of test lengths performed very similarly

 

# Load libraries
if(!require(lsasim)){  
  install.packages("lsasim")
  library(lsasim) #version 1.0.1
}

if(!require(mirt)){  
  install.packages("mirt")
  library(mirt) #version 1.25
}

if(!require(TAM)){
  install.packages("TAM")
  library(TAM) #version 2.4-9
}

if(!require(ltm)){
  install.packages("ltm")
  library(ltm) #version 1.0-0
}
# Set up conditions
N.cond <- c(500, 1000, 5000) #number of sample sizes
I.cond <- c(40, 60, 80, 100) #number of items 
K.cond  <- 1                 #number of booklets  

# Set up number of replications
reps <- 100

# Create space for outputs
results <- NULL
#==============================================================================#
# START SIMULATION
#==============================================================================#

for (N in N.cond) { #sample size
  
  for (I in I.cond) { #number of items
    
    # generate item parameters for a 2PL model
    set.seed(4364) # fix item parameters across replications
    item_pool <- lsasim::item_gen(n_2pl = I, 
                                  thresholds = 1, 
                                  b_bounds = c(-2, 2), 
                                  a_bounds = c(0.75, 1.25))
    
    for (K in K.cond) { #number of booklets
      
      for (r in 1:reps) { #replication
        
        #------------------------------------------------------------------------------#
        # Data simulation
        #------------------------------------------------------------------------------#
        
        set.seed(8088*(r+2))
        
        # generate thetas
        theta <- rnorm(N, mean=0, sd=1)
        
        # assign items to block
        block_bk1 <- lsasim::block_design(n_blocks = K, 
                                          item_parameters = item_pool)
        
        #assign block to booklet
        book_bk1 <- lsasim::booklet_design(item_block_assignment = 
                                             block_bk1$block_assignment,
                                           book_design = matrix(K))
        #assign booklet to subjects
        book_samp <- lsasim::booklet_sample(n_subj = N, 
                                            book_item_design = book_bk1, 
                                            book_prob = NULL)
        
        # generate item responses 
        cog <- lsasim::response_gen(subject = book_samp$subject, 
                                    item = book_samp$item, 
                                    theta = theta, 
                                    b_par = item_pool$b,
                                    a_par = item_pool$a)
        
        # extract item responses (excluding "subject" column)
        resp <- cog[, c(1:I)]
        
        #------------------------------------------------------------------------------#
        # Item calibration
        #------------------------------------------------------------------------------#
        
        # fit 2PL model using mirt package
        mirt.mod <- NULL
        mirt.mod <- mirt::mirt(resp, 1, itemtype = '2PL', verbose = F)
        
        # fit 2PL model using TAM package
        tam.mod <- NULL
        tam.mod <- TAM::tam.mml.2pl(resp)
        
        # fit 2PL model using ltm package 
        ltm.mod <- NULL
        ltm.mod <- ltm::ltm(resp ~ z1, IRT.param=T)
        
        #------------------------------------------------------------------------------#
        # Item parameter extraction
        #------------------------------------------------------------------------------#
        
        # extract b and a in mirt package
        mirt_b <- coef(mirt.mod, IRTpars = TRUE, simplify=TRUE)$items[,"b"]
        mirt_a <- coef(mirt.mod, IRTpars = TRUE, simplify=TRUE)$items[,"a"]
        
        # convert TAM output into 2PL parametrization
        tam_b <- (tam.mod$item$AXsi_.Cat1/tam.mod$item$B.Cat1.Dim1)
        tam_a <- (tam.mod$item$B.Cat1.Dim1) 
        
        # extract Dffclt and Dscrmn in ltm package
        ltm_b <- data.frame(coef(ltm.mod))$Dffclt
        ltm_a <- data.frame(coef(ltm.mod))$Dscrmn
        
        #------------------------------------------------------------------------------#
        # Item parameter recovery
        #------------------------------------------------------------------------------#
        
        # summarize results
        itempars <- data.frame(matrix(c(N, I, K, r), nrow=1))
        colnames(itempars) <- c("N", "I", "K", "rep")
        
        # calculate corr, bias, RMSE for item parameters in mirt pacakge
        itempars$corr_mirt_b <- cor( item_pool$b, mirt_b)
        itempars$bias_mirt_b <- mean( mirt_b - item_pool$b )
        itempars$RMSE_mirt_b <- sqrt(mean( ( mirt_b - item_pool$b)^2 )) 
        
        itempars$corr_mirt_a <- cor( item_pool$a, mirt_a)
        itempars$bias_mirt_a <- mean( mirt_a - item_pool$a )
        itempars$RMSE_mirt_a <- sqrt(mean( ( mirt_a - item_pool$a)^2 )) 
        
        # calculate corr, bias, RMSE for item parameters in TAM pacakge
        itempars$corr_tam_b <- cor( item_pool$b, tam_b)
        itempars$bias_tam_b <- mean( tam_b - item_pool$b )
        itempars$RMSE_tam_b <- sqrt(mean( ( tam_b - item_pool$b)^2 )) 
        
        itempars$corr_tam_a <- cor( item_pool$a, tam_a)
        itempars$bias_tam_a <- mean( tam_a - item_pool$a )
        itempars$RMSE_tam_a <- sqrt(mean( ( tam_a - item_pool$a)^2 )) 
        
        # calculate corr, bias, RMSE for item parameters in ltm pacakge
        itempars$corr_ltm_b <- cor( item_pool$b, ltm_b)
        itempars$bias_ltm_b <- mean( ltm_b - item_pool$b )
        itempars$RMSE_ltm_b <- sqrt(mean( ( ltm_b - item_pool$b)^2 )) 
        
        itempars$corr_ltm_a <- cor( item_pool$a, ltm_a)
        itempars$bias_ltm_a <- mean( ltm_a - item_pool$a )
        itempars$RMSE_ltm_a <- sqrt(mean( ( ltm_a - item_pool$a)^2 )) 
        
        # combine results
        results <- rbind(results, itempars)
        
      }
    }
  }
}

 

  • Correlation, bias, and RMSE for item parameter recovery in mirt package

 

mirt_recovery <- aggregate(cbind(corr_mirt_b, bias_mirt_b, RMSE_mirt_b,
                                 corr_mirt_a, bias_mirt_a, RMSE_mirt_a) ~ N + I, 
                            data=results, mean, na.rm=TRUE)
names(mirt_recovery) <- c("Sample Size", "Test Length", 
                         "corr_b", "bias_b", "RMSE_b",
                         "corr_a", "bias_a", "RMSE_a")
round(mirt_recovery, 3)
##    Sample Size Test Length corr_b bias_b RMSE_b corr_a bias_a RMSE_a
## 1          500          40  0.990 -0.005  0.199  0.699  0.013  0.152
## 2         1000          40  0.995 -0.009  0.133  0.814  0.005  0.106
## 3         5000          40  0.999 -0.002  0.058  0.952  0.000  0.047
## 4          500          60  0.990 -0.004  0.203  0.703  0.014  0.152
## 5         1000          60  0.995 -0.008  0.139  0.814  0.007  0.107
## 6         5000          60  0.999 -0.002  0.062  0.952  0.001  0.047
## 7          500          80  0.990 -0.003  0.192  0.728  0.012  0.146
## 8         1000          80  0.995 -0.004  0.131  0.838  0.006  0.101
## 9         5000          80  0.999 -0.002  0.057  0.960  0.000  0.045
## 10         500         100  0.989 -0.004  0.191  0.739  0.014  0.143
## 11        1000         100  0.995 -0.010  0.132  0.848  0.006  0.099
## 12        5000         100  0.999 -0.002  0.058  0.962  0.000  0.044

 

  • Correlation, bias, and RMSE for item parameter recovery in TAM package

 

tam_recovery <- aggregate(cbind(corr_tam_b, bias_tam_b, RMSE_tam_b,
                                corr_tam_a, bias_tam_a, RMSE_tam_a) ~ N + I, 
                           data=results, mean, na.rm=TRUE)
names(tam_recovery) <- c("Sample Size", "Test Length", 
                         "corr_b", "bias_b", "RMSE_b",
                         "corr_a", "bias_a", "RMSE_a")
round(tam_recovery, 3)
##    Sample Size Test Length corr_b bias_b RMSE_b corr_a bias_a RMSE_a
## 1          500          40  0.990 -0.004  0.199  0.699  0.013  0.152
## 2         1000          40  0.995 -0.008  0.133  0.814  0.005  0.106
## 3         5000          40  0.999 -0.001  0.058  0.952  0.000  0.047
## 4          500          60  0.990 -0.004  0.203  0.703  0.015  0.152
## 5         1000          60  0.995 -0.008  0.139  0.814  0.007  0.107
## 6         5000          60  0.999 -0.001  0.062  0.952  0.000  0.047
## 7          500          80  0.990 -0.005  0.194  0.728  0.010  0.145
## 8         1000          80  0.995 -0.003  0.132  0.839  0.002  0.100
## 9         5000          80  0.999 -0.003  0.058  0.960 -0.004  0.045
## 10         500         100  0.989 -0.005  0.196  0.738  0.002  0.141
## 11        1000         100  0.995 -0.012  0.135  0.848 -0.006  0.097
## 12        5000         100  0.999 -0.003  0.062  0.962 -0.013  0.045

 

  • Correlation, bias, and RMSE for item parameter recovery in ltm package

 

ltm_recovery <- aggregate(cbind(corr_ltm_b, bias_ltm_b, RMSE_ltm_b,
                                corr_ltm_a, bias_ltm_a, RMSE_ltm_a) ~ N + I, 
                          data=results, mean, na.rm=TRUE)
names(ltm_recovery) <- c("Sample Size", "Test Length", 
                         "corr_b", "bias_b", "RMSE_b",
                         "corr_a", "bias_a", "RMSE_a")
round(ltm_recovery, 3)
##    Sample Size Test Length corr_b bias_b RMSE_b corr_a bias_a RMSE_a
## 1          500          40  0.990 -0.005  0.199  0.699  0.013  0.152
## 2         1000          40  0.995 -0.009  0.133  0.814  0.005  0.106
## 3         5000          40  0.999 -0.002  0.058  0.952  0.000  0.047
## 4          500          60  0.990 -0.004  0.205  0.703  0.012  0.151
## 5         1000          60  0.995 -0.009  0.140  0.814  0.005  0.106
## 6         5000          60  0.999 -0.002  0.062  0.952 -0.003  0.047
## 7          500          80  0.990 -0.008  0.199  0.727 -0.001  0.143
## 8         1000          80  0.995 -0.001  0.136  0.838 -0.010  0.099
## 9         5000          80  0.999 -0.002  0.063  0.960 -0.016  0.047
## 10         500         100  0.989 -0.012  0.206  0.737 -0.018  0.140
## 11        1000         100  0.995 -0.013  0.145  0.847 -0.027  0.098
## 12        5000         100  0.999 -0.002  0.075  0.962 -0.035  0.055