This example demonstrates how to collect response data with mirtCAT
for new items in an item bank. The constraint is that these new items should not influence the selection process, nor should they influence the computation of the latent trait estimates. Hence, we will require setting up a customized item selection function in order to control how many/when these unscored items should be administered, and also include various flags to mirtCAT()
to indicate that they should not be included in the scoring process.
Say that we had the following bank of existing items.
library('mirtCAT')
## Loading required package: mirt
## Loading required package: stats4
## Loading required package: lattice
## Loading required package: shiny
options(stringsAsFactors = FALSE)
# define population IRT parameters
set.seed(1234)
nitems <- 100
itemnames <- paste0('Item.', 1:nitems)
a <- matrix(rlnorm(nitems, .2, .3))
d <- matrix(rnorm(nitems))
pars <- data.frame(a1=a, d=d, g=0.2)
mod <- generate.mirt_object(pars, '3PL')
# math items definitions
# addition for one factor and multiplication for the other
questions <- answers <- character(nitems)
choices <- matrix('a', nitems, 5)
spacing <- floor(d - min(d)) + 1 #easier items have more variation
for(i in 1:nitems){
n1 <- sample(1:100, 1)
n2 <- sample(101:200, 1)
ans <- n1 + n2
questions[i] <- paste0(n1, ' + ', n2, ' = ?')
answers[i] <- as.character(ans)
ch <- ans + sample(c(-5:-1, 1:5) * spacing[i,], 5)
ch[sample(1:5, 1)] <- ans
choices[i,] <- as.character(ch)
}
df <- data.frame(Questions=questions, Answer=answers, Option=choices, Type='radio')
head(df)
## Questions Answer Option.1 Option.2 Option.3 Option.4 Option.5 Type
## 1 69 + 142 = ? 211 211 219 207 195 231 radio
## 2 63 + 142 = ? 205 214 220 205 193 208 radio
## 3 52 + 178 = ? 230 230 224 233 236 221 radio
## 4 94 + 120 = ? 214 199 208 205 214 226 radio
## 5 44 + 194 = ? 238 229 241 238 244 235 radio
## 6 82 + 189 = ? 271 283 267 291 271 275 radio
Now, we wish to collect responses for two new items.
new2 <- data.frame(Questions = c('Q101', 'Q102'),
Answer = c('A1', 'A1'),
Option = matrix(paste0('A', 1:5), nrow=2, ncol=5),
Type = 'radio')
new2
## Questions Answer Option.1 Option.2 Option.3 Option.4 Option.5 Type
## 1 Q101 A1 A1 A3 A5 A2 A4 radio
## 2 Q102 A1 A2 A4 A1 A3 A5 radio
The conceptual difficulty here is that there are no estimated IRT parameters available for these items yet, because we have no sample data from which to estimate them.
In order to include these two new items in the CAT we must include some provisional IRT parameters to, in essence, ‘trick’ mirtCAT()
into thinking these items have valid IRT parameters. However, we’ll flag which of these items contain arbitrary parameters by using the constrain
input list, specifically the not_scored
element.
First, let’s build our new objects to crate a CAT bank with 102 items (compared to the original 100 items above).
# new stimuli information
newdf <- rbind(df, new2)
tail(newdf)
## Questions Answer Option.1 Option.2 Option.3 Option.4 Option.5 Type
## 97 71 + 147 = ? 218 206 198 234 202 218 radio
## 98 41 + 116 = ? 157 153 167 155 151 157 radio
## 99 67 + 131 = ? 198 214 202 194 198 182 radio
## 100 39 + 160 = ? 199 202 199 195 197 204 radio
## 101 Q101 A1 A1 A3 A5 A2 A4 radio
## 102 Q102 A1 A2 A4 A1 A3 A5 radio
# new parameter data.frame and model
newpars <- rbind(pars, data.frame(a1 = c(0,0), d = c(0,0), g=0))
tail(newpars) # parameter values for unscored items are arbitrary
## a1 d g
## 97 0.8690258 0.6202102 0.2
## 98 1.5895661 -0.9659032 0.2
## 99 1.6353797 0.1626547 0.2
## 100 2.3078933 -2.0782375 0.2
## 101 0.0000000 0.0000000 0.0
## 102 0.0000000 0.0000000 0.0
newmod <- generate.mirt_object(newpars, '3PL')
coef(newmod, simplify=TRUE)
## $items
## a1 d g u
## Item.1 0.850 0.415 0.2 1
## Item.2 1.327 -0.475 0.2 1
## Item.3 1.691 0.066 0.2 1
## Item.4 0.604 -0.502 0.2 1
## Item.5 1.389 -0.826 0.2 1
## Item.6 1.422 0.167 0.2 1
## Item.7 1.028 -0.896 0.2 1
## Item.8 1.037 0.168 0.2 1
## Item.9 1.031 0.355 0.2 1
## Item.10 0.935 -0.052 0.2 1
## Item.11 1.058 -0.196 0.2 1
## Item.12 0.905 -0.649 0.2 1
## Item.13 0.968 -1.110 0.2 1
## Item.14 1.245 0.849 0.2 1
## Item.15 1.629 0.022 0.2 1
## Item.16 1.182 0.831 0.2 1
## Item.17 1.048 -1.244 0.2 1
## Item.18 0.929 0.169 0.2 1
## Item.19 0.950 0.673 0.2 1
## Item.20 2.521 -0.026 0.2 1
## Item.21 1.272 -0.191 0.2 1
## Item.22 1.054 -0.782 0.2 1
## Item.23 1.070 2.058 0.2 1
## Item.24 1.402 0.751 0.2 1
## Item.25 0.992 1.824 0.2 1
## Item.26 0.791 0.080 0.2 1
## Item.27 1.451 -0.631 0.2 1
## Item.28 0.898 -1.513 0.2 1
## Item.29 1.216 -0.636 0.2 1
## Item.30 0.922 0.226 0.2 1
## Item.31 1.700 1.014 0.2 1
## Item.32 1.059 0.253 0.2 1
## Item.33 0.987 -1.172 0.2 1
## Item.34 1.051 0.669 0.2 1
## Item.35 0.749 -1.650 0.2 1
## Item.36 0.860 -0.366 0.2 1
## Item.37 0.635 -0.316 0.2 1
## Item.38 0.817 -1.948 0.2 1
## Item.39 1.118 0.920 0.2 1
## Item.40 1.062 -0.623 0.2 1
## Item.41 1.887 -0.334 0.2 1
## Item.42 0.886 1.395 0.2 1
## Item.43 0.945 0.637 0.2 1
## Item.44 1.123 -0.108 0.2 1
## Item.45 0.906 0.514 0.2 1
## Item.46 0.913 0.399 0.2 1
## Item.47 0.876 1.663 0.2 1
## Item.48 0.839 0.276 0.2 1
## Item.49 1.044 0.506 0.2 1
## Item.50 1.052 0.348 0.2 1
## Item.51 0.710 -0.377 0.2 1
## Item.52 1.026 0.098 0.2 1
## Item.53 0.876 1.639 0.2 1
## Item.54 0.901 -0.876 0.2 1
## Item.55 1.163 0.122 0.2 1
## Item.56 1.446 1.362 0.2 1
## Item.57 2.002 -0.235 0.2 1
## Item.58 0.969 -1.053 0.2 1
## Item.59 1.977 -0.870 0.2 1
## Item.60 0.863 -0.390 0.2 1
## Item.61 1.487 -0.847 0.2 1
## Item.62 2.624 -0.261 0.2 1
## Item.63 1.209 -0.414 0.2 1
## Item.64 0.999 -0.183 0.2 1
## Item.65 1.219 0.407 0.2 1
## Item.66 2.082 0.625 0.2 1
## Item.67 0.868 1.678 0.2 1
## Item.68 1.841 -0.069 0.2 1
## Item.69 1.820 -0.321 0.2 1
## Item.70 1.351 1.471 0.2 1
## Item.71 1.224 1.704 0.2 1
## Item.72 1.065 0.043 0.2 1
## Item.73 1.094 -0.333 0.2 1
## Item.74 1.484 -1.822 0.2 1
## Item.75 2.273 1.411 0.2 1
## Item.76 1.166 -0.838 0.2 1
## Item.77 0.805 -1.124 0.2 1
## Item.78 0.983 3.044 0.2 1
## Item.79 1.320 0.235 0.2 1
## Item.80 1.111 -0.033 0.2 1
## Item.81 1.158 -2.732 0.2 1
## Item.82 1.161 -0.100 0.2 1
## Item.83 0.809 0.976 0.2 1
## Item.84 1.159 0.414 0.2 1
## Item.85 1.576 0.912 0.2 1
## Item.86 1.506 1.984 0.2 1
## Item.87 1.441 1.169 0.2 1
## Item.88 1.082 -0.509 0.2 1
## Item.89 1.153 0.704 0.2 1
## Item.90 0.854 -0.198 0.2 1
## Item.91 1.202 -0.538 0.2 1
## Item.92 1.319 -2.856 0.2 1
## Item.93 2.038 -0.790 0.2 1
## Item.94 1.649 0.488 0.2 1
## Item.95 1.053 2.168 0.2 1
## Item.96 1.359 0.501 0.2 1
## Item.97 0.869 0.620 0.2 1
## Item.98 1.590 -0.966 0.2 1
## Item.99 1.635 0.163 0.2 1
## Item.100 2.308 -2.078 0.2 1
## Item.101 0.000 0.000 0.0 1
## Item.102 0.000 0.000 0.0 1
##
## $means
## F1
## 0
##
## $cov
## F1
## F1 1
With these new objects built, we’re ready to use mirtCAT()
. However, if we are interested in how/when these new items should be administered, while also desiring that the items selection method remain adaptive (e.g., with the maximum-information criteria), then we’ll need a customNextItem()
function definition to control the item selection scheme.
In the following code, items are selected using the MI
criteria. However, if item 101 or 102 have not yet been administered then they will be randomly selected 10% of the time until both of these items appear in the CAT session. To get a better feel for how this item selection function behaves, simply uncomment the browser()
line and walk-through the object state using R’s internal debugging functions.
customNextItem <- function(design, person, test){
# browser()
items_answered <- na.omit(extract.mirtCAT(person, 'items_answered'))
new_items <- 101:102
# if there are any new items to be administered, then 10% of
# the time randomly pick one to administer
if(!all(new_items %in% items_answered) && runif(1) < .1){
pick <- new_items[!(new_items %in% items_answered)]
if(length(pick) == 1) return(pick)
else return(sample(pick, 1))
}
# othewise, pick item with the MI criteria
item <- findNextItem(person=person, design=design, test=test,
criteria = 'MI')
item
}
Finally, we have all the components necessary to pass to mirtCAT()
in order to control these new special items. Let’s run a test case to see how this selection process behaves.
# test pattern
set.seed(1)
pat <- generate_pattern(newmod, Theta = 0, df = newdf)
result <- mirtCAT(newdf, newmod, method = 'EAP', criteria = 'MI', start_item = 'random',
local_pattern = pat,
design = list(min_SEM = .2,
# ensure these items are not scored
constraints = list(not_scored = 101:102),
customNextItem=customNextItem))
Notice the design
element constraints = list(not_scored = 101:102)
. This tells mirtCAT()
to never use item 101 or 102 in the scoring of the \(\theta\) components. Hence, when you inspect the properties of the CAT session whenever item’s 101 and 102 are administered the previous \(\hat{\theta}\) and associated \(SE\) estimates are identical to the previous response pattern state (because nothing changes in the CAT, only the responses for these items are collected). For ease of location, the thetas_SE_history
retains the labels of these new items so that they can be easily located from the estimation history.
summary(result)
## $final_estimates
## Theta_1
## Estimates 0.2653299
## SEs 0.2062403
##
## $raw_responses
## [1] "3" "1" "3" "5" "2" "4" "2" "2" "5" "5" "1" "5" "4" "1" "1" "5" "3"
## [18] "3" "5" "3" "4" "1" "1" "1" "5" "3" "3" "4" "2" "1" "5" "1" "4" "1"
## [35] "4" "4" "4" "2" "2" "1" "4" "4" "2" "4" "5" "3" "5" "5" "3" "2" "5"
## [52] "3" "2" "3" "1" "5" "1" "1" "3" "5" "2" "1" "5" "2" "2" "1" "3" "4"
## [69] "4" "2" "2" "1" "1" "2" "5" "2" "4" "2" "5" "2" "2" "5" "4" "1" "4"
## [86] "4" "3" "1" "2" "2" "2" "3" "3" "2" "1" "5" "1" "5" "4" "2" "5" "2"
##
## $scored_responses
## [1] 0 1 1 1 0 1 1 0 1 1 1 0 1 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 0 0 0 1 1 1 0
## [36] 1 0 0 0 1 1 0 1 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 0
## [71] 1 1 1 1 1 1 0 0 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 0 1 1 0
##
## $items_answered
## [1] 62 75 66 20 57 68 41 93 69 59 3 15 99 94 98 31 6
## [18] 85 27 96 79 61 101 100 24 2 5 21 63 65 102 87 55 82
## [35] 91 29 84 14 56 16 44 80 89 70 72 32 73 50 8 11 39
## [52] 49 9 52 86 34 88 76 64 40 18 10 22 30 19 43 46 45
## [69] 7 74 71 90 1 48 97 12 36 60 17 58 26 54 13 33 83
## [86] 42 51 25 23 53 77 47 67 37 95 28 4 35 38 92 78 81
##
## $thetas_history
## Theta_1
## [1,] 0.000000000
## [2,] -0.621601266
## [3,] -0.342265947
## [4,] -0.118285616
## [5,] 0.102724421
## [6,] -0.135465547
## [7,] 0.008212652
## [8,] 0.136972907
## [9,] 0.001182598
## [10,] 0.105174077
## [11,] 0.210466438
## [12,] 0.277790579
## [13,] 0.148955294
## [14,] 0.207744033
## [15,] 0.252736938
## [16,] 0.188257534
## [17,] 0.219217501
## [18,] 0.131153423
## [19,] 0.161702056
## [20,] 0.106440907
## [21,] 0.138421785
## [22,] 0.171682003
## [23,] 0.219261742
## [24,] 0.219261742
## [25,] 0.183569627
## [26,] 0.206654703
## [27,] 0.242809368
## [28,] 0.281827318
## [29,] 0.311662347
## [30,] 0.268141771
## [31,] 0.208929062
## [32,] 0.208929062
## [33,] 0.224342399
## [34,] 0.247698177
## [35,] 0.272508684
## [36,] 0.236214508
## [37,] 0.265525880
## [38,] 0.214491607
## [39,] 0.153452211
## [40,] 0.074913641
## [41,] 0.091802979
## [42,] 0.116034705
## [43,] 0.079182758
## [44,] 0.096112970
## [45,] 0.107416764
## [46,] 0.072421562
## [47,] 0.091370485
## [48,] 0.062493345
## [49,] 0.080219017
## [50,] 0.098632514
## [51,] 0.069619542
## [52,] 0.082721445
## [53,] 0.044771085
## [54,] 0.061379026
## [55,] 0.030552600
## [56,] 0.038610997
## [57,] 0.052614177
## [58,] 0.074249070
## [59,] 0.097818342
## [60,] 0.115862508
## [61,] 0.094931858
## [62,] 0.109541845
## [63,] 0.125264161
## [64,] 0.106894040
## [65,] 0.120577481
## [66,] 0.131543415
## [67,] 0.142506114
## [68,] 0.154417124
## [69,] 0.165376377
## [70,] 0.184139426
## [71,] 0.171511459
## [72,] 0.177453615
## [73,] 0.190990271
## [74,] 0.201273803
## [75,] 0.212047771
## [76,] 0.221158527
## [77,] 0.236472671
## [78,] 0.218526347
## [79,] 0.200999609
## [80,] 0.218728012
## [81,] 0.205606477
## [82,] 0.216344370
## [83,] 0.231506489
## [84,] 0.218992510
## [85,] 0.235413584
## [86,] 0.208344145
## [87,] 0.213512216
## [88,] 0.224539671
## [89,] 0.228458296
## [90,] 0.231838799
## [91,] 0.235902510
## [92,] 0.249043580
## [93,] 0.252956537
## [94,] 0.256764797
## [95,] 0.244258409
## [96,] 0.247181607
## [97,] 0.260998833
## [98,] 0.250361679
## [99,] 0.261425644
## [100,] 0.256199405
## [101,] 0.268225102
## [102,] 0.269427689
## [103,] 0.265329867
##
## $thetas_SE_history
## Theta_1
## 1.0000000
## 0.7548353
## 0.6722111
## 0.6085983
## 0.5589631
## 0.5253759
## 0.4842760
## 0.4549861
## 0.4345534
## 0.4086428
## 0.3933434
## 0.3764879
## 0.3696571
## 0.3545867
## 0.3431957
## 0.3349382
## 0.3258627
## 0.3226753
## 0.3146059
## 0.3103663
## 0.3039357
## 0.2986620
## 0.2957117
## theta_SE 0.2957117
## 0.2874217
## 0.2829751
## 0.2797514
## 0.2774798
## 0.2746487
## 0.2718502
## 0.2706582
## theta_SE 0.2706582
## 0.2675727
## 0.2649813
## 0.2627104
## 0.2604413
## 0.2586356
## 0.2576300
## 0.2578025
## 0.2599520
## 0.2569882
## 0.2542633
## 0.2539774
## 0.2512933
## 0.2490171
## 0.2488317
## 0.2464954
## 0.2459742
## 0.2437226
## 0.2416602
## 0.2412598
## 0.2392847
## 0.2397172
## 0.2376866
## 0.2377494
## 0.2359365
## 0.2340835
## 0.2325220
## 0.2313024
## 0.2298529
## 0.2291498
## 0.2277964
## 0.2265310
## 0.2258159
## 0.2245711
## 0.2234115
## 0.2223018
## 0.2212498
## 0.2202730
## 0.2196677
## 0.2183117
## 0.2174459
## 0.2166732
## 0.2158923
## 0.2151498
## 0.2144272
## 0.2139073
## 0.2133364
## 0.2128122
## 0.2126050
## 0.2119369
## 0.2112999
## 0.2109037
## 0.2102435
## 0.2100055
## 0.2100374
## 0.2095240
## 0.2090744
## 0.2086326
## 0.2082196
## 0.2078254
## 0.2076548
## 0.2072842
## 0.2069293
## 0.2066749
## 0.2063319
## 0.2063853
## 0.2061497
## 0.2062183
## 0.2059148
## 0.2067795
## 0.2066423
## 0.2062403