Item selection functions, as you may have guessed, implement the
rules that determine which item is administered next to each respondent.
They are one of the three core swappable components of a
meow simulation. For the full module contract, see
vignette("extending-meow"); this vignette focuses on the
bundled selectors and how to write your own.
Every item selection function has the signature
select_fun <- function(pers, item, R, admin, adj_mat, ...) {
# ... decide which items to administer ...
return(admin)
}Any select_fun() receives the current person parameter
estimates (pers), item parameter estimates
(item), the full respondent-by-item response matrix
(R), the integer administration matrix
(admin), and the item co-exposure matrix
(adj_mat). It then returns an administration matrix with
the newly chosen cells marked with a non-zero integer value. The
simulation harness itself will correctly record the order of
administration, so you only need to mark newly administered items with
non-zero values.
The default behavior when early stopping rules are not implemented in
selection functions is run the simulation until it has administered
every item to every respondent. The unadministered items for respondent
i can be retrieved using
which(admin[i, ] == 0), and the respondents who still have
items to respond to can be found using
which(rowSums(admin == 0) > 0).
select_sequential() administers the lowest-numbered
remaining item to each respondent, producing a fixed linear form.
select_sequential <- function(pers, item, R, admin, adj_mat = NULL) {
if (!any(admin != 0)) {
admin[, seq_len(min(5, ncol(admin)))] <- 1L # seed the first five items
return(admin)
}
unadmin <- admin == 0
has <- which(rowSums(unadmin) > 0)
nextcol <- max.col(unadmin[has, , drop = FALSE] + 0, ties.method = "first")
admin[cbind(has, nextcol)] <- 1L
admin
}select_random() draws one remaining item per respondent
at random. It accepts a select_seed for reproducibility,
which it clears after use.
select_max_info() administers the remaining item with
the greatest 2PL Fisher information, \(I(\theta) = a^2 P(\theta)(1 - P(\theta))\),
evaluated at each respondent’s current ability estimate. The information
for every respondent-by-item combination is computed as a single matrix,
and the maximum is taken per row.
select_max_dist() and
select_max_dist_enhanced() treat the item pool as a network
whose edge weights are derived from the co-exposure adjacency matrix
adj_mat. They administer the item farthest (by
shortest-path distance) from the items a respondent has already seen,
breaking ties using maximum information. See
vignette("network-item-selection").
A custom selector need only follow the prescribed function signature
and return an updated admin matrix. Here we administer the
item whose difficulty is closest to a respondent’s current ability,
which can be seen as maximizing information when all items are assumed
to be equally discriminating.
select_targeted <- function(pers, item, R, admin, adj_mat = NULL) {
if (!any(admin != 0)) {
admin[, seq_len(min(5, ncol(admin)))] <- 1L
return(admin)
}
for (i in which(rowSums(admin == 0) > 0)) {
remaining <- which(admin[i, ] == 0)
gap <- abs(item$b[remaining] - pers$theta[i])
admin[i, remaining[which.min(gap)]] <- 1L
}
admin
}Use it in a simulation by passing it as select_fun:
if (!any(admin != 0)) to seed an initial set of items so
that you have baseline parameter estimates to start from.admin unchanged when you want to end the simulation.meow_long() only when long data
is genuinely more convenient.