philchalmers / mirt

Multidimensional item response theory
https://philchalmers.github.io/mirt/
201 stars 75 forks source link

Seed doesn't seem to pass into mirt? #198

Closed noahdasanaike closed 3 years ago

noahdasanaike commented 3 years ago

Hey! I set a seed before my package function call, which uses mirt; however, I don't believe that the set.seed() call is being passed into mirt. Is there a way to seed my mirt calls? Basically, I'm doing as follows:

set.seed(123) function()

and in function.R, I call mirt::mirt, mirt::fscores, and mirt::coef.

philchalmers commented 3 years ago

I don't quite follow why you would need this at all. mirt's estimation information is generally of a fixed nature (excluding the MHRM estimation criteria). What are you trying to do, and can you demonstrate what you mean by mirt not being reproducible?

noahdasanaike commented 3 years ago

I'm calling MIRT to produce initial values for my IRT model if not provided (apologies for the original lack of specificity):

cl <- match.call()
mIRT <- match(c("formulaIRT", "dataIRT"), names(cl), 0L)
mfIRT <- cl[c(1L, mIRT)]
mfIRT$drop.unused.levels <- TRUE
names(mfIRT)[c(2,3)] <- c("formula","data")
mfIRT[[1L]] <- quote(stats::model.frame)
mfIRT <- eval(mfIRT, parent.frame())
mtIRT <- attr(mfIRT, "terms")
y <- stats::model.response(mfIRT, "numeric")
mlm <- is.matrix(y)
if(!mlm)
  stop("LHS of `formulaIRT` must be cbind'ed item names; single-item models not supported.")
wIRT <- stats::model.matrix(mtIRT, mfIRT)[,-1]

## Get initial values, if not provided
  if(is.null(ctrl$theta_init) | is.null(ctrl$beta_init)){
    if(ctrl$verbose){
      cat("Finding initial values...\n")
    }
    tmp <- (y/wIRT > 0.5) * 1.0
    tmp <- apply(tmp, 2,
                 function(yj){
                   if(all(yj==yj[1]))
                     yj[sample(1:length(yj), 1)] <- 1-yj[1]
                   return(yj)
                 })
    tmp_res <- mirt::mirt(tmp, 1, verbose = FALSE)
philchalmers commented 3 years ago

Thanks. In this case mirt will always give the same answer given the data (e.g., in your code

tmp <- apply(tmp, 2,
                 function(yj){
                   if(all(yj==yj[1]))
                     yj[sample(1:length(yj), 1)] <- 1-yj[1]
                   return(yj)
                 })
    tmp_res <- mirt::mirt(tmp, 1, verbose = FALSE)
    tmp_res2 <- mirt::mirt(tmp, 1, verbose = FALSE)

will be the same. Perhaps you should avoid using yj[sample(1:length(yj), 1)] <- 1-yj[1] as this is introducing some stochastic component that's no doubt changing the tmp data being passed to mirt in an unusual way.

noahdasanaike commented 3 years ago

Thanks! Does this also apply to the code that proceeds the above? When lrho_init is null and either theta_init and beta_init aren't, my results aren't seeding properly.

 if(is.null(ctrl$theta_init))
    ctrl$theta_init <- mirt::fscores(tmp_res)
  if(is.null(ctrl$beta_init))
    ctrl$beta_init <- t(mirt::coef(tmp_res, simplify=TRUE, verbose=FALSE)$item)[c(1,2),]
  }
  if(is.null(ctrl$lrho_init))
    ctrl$lrho_init <- rnorm(ncol(xIRT), 0, 1)