Initializes the proper S4 class and methods necessary for mirt
functions to use in estimation. To use the defined objects pass to the
mirt(..., customItems = list())
command, and
ensure that the classes are properly labelled and unique in the list.
Additionally, the input mirt(..., customItemsData = list())
can
also be included to specify additional item-level information to better
recycle custom-item definitions (e.g., for supplying varying
Q-matrices), where the list
input must have the same length as the
number of items. For further examples regarding how this function can be
used for fitting unfolding-type models see Liu and Chalmers (2018).
Usage
createItem(
name,
par,
est,
P,
gr = NULL,
hss = NULL,
gen = NULL,
lbound = NULL,
ubound = NULL,
derivType = "Richardson",
derivType.hss = "Richardson",
bytecompile = TRUE
)
Arguments
- name
a character indicating the item class name to be defined
- par
a named vector of the starting values for the parameters
- est
a logical vector indicating which parameters should be freely estimated by default
- P
the probability trace function for all categories (first column is category 1, second category two, etc). First input contains a vector of all the item parameters, the second input must be a matrix called
Theta
, the third input must be the number of categories calledncat
, and (optionally) a fourth argument termeditemdata
may be included containing further users specification information. The last optional input is to be utilized within the estimation functions such asmirt
via the list inputcustomItemsData
to more naturally recycle custom-item definitions. Therefore, these inputs must be of the formfunction(par, Theta, ncat){...}
or
function(par, Theta, ncat, itemdata){...}
to be valid; however, the names of the arguements is not relavent.
Finally, this function must return a
matrix
object of category probabilities, where the columns represent each respective category- gr
gradient function (vector of first derivatives) of the log-likelihood used in estimation. The function must be of the form
gr(x, Theta)
, wherex
is the object defined bycreateItem()
andTheta
is a matrix of latent trait parameters. Tabulated (EM) or raw (MHRM) data are located in thex@dat
slot, and are used to form the complete data log-likelihood. If not specified a numeric approximation will be used- hss
Hessian function (matrix of second derivatives) of the log-likelihood used in estimation. If not specified a numeric approximation will be used (required for the MH-RM algorithm only). The input is identical to the
gr
argument- gen
a function used when
GenRandomPars = TRUE
is passed to the estimation function to generate random starting values. Function must be of the formfunction(object) ...
and must return a vector with properties equivalent to thepar
object. If NULL, parameters will remain at the defined starting values by default- lbound
optional vector indicating the lower bounds of the parameters. If not specified then the bounds will be set to -Inf
- ubound
optional vector indicating the lower bounds of the parameters. If not specified then the bounds will be set to Inf
- derivType
if the
gr
term is not specified this type will be used to obtain the gradient numerically or symbolically. Default is the 'Richardson' extrapolation method; seenumerical_deriv
for details and other options. If'symbolic'
is supplied then the gradient will be computed using a symbolical approach (potentially the most accurate method, though may fail depending on how theP
function was defined)- derivType.hss
if the
hss
term is not specified this type will be used to obtain the Hessian numerically. Default is the 'Richardson' extrapolation method; seenumerical_deriv
for details and other options. If'symbolic'
is supplied then the Hessian will be computed using a symbolical approach (potentially the most accurate method, though may fail depending on how theP
function was defined)- bytecompile
logical; where applicable, byte compile the functions provided? Default is
TRUE
to provide
Details
The summary()
function will not return proper standardized loadings
since the function is not sure how to handle them (no slopes could be
defined at all!). Instead loadings of .001 are filled in as place-holders.
References
Chalmers, R., P. (2012). mirt: A Multidimensional Item Response Theory Package for the R Environment. Journal of Statistical Software, 48(6), 1-29. doi:10.18637/jss.v048.i06
Liu, C.-W. and Chalmers, R. P. (2018). Fitting item response unfolding models to Likert-scale data using mirt in R. PLoS ONE, 13, 5. doi:10.1371/journal.pone.0196292
Author
Phil Chalmers rphilip.chalmers@gmail.com
Examples
if (FALSE) { # \dontrun{
name <- 'old2PL'
par <- c(a = .5, b = -2)
est <- c(TRUE, TRUE)
P.old2PL <- function(par,Theta, ncat){
a <- par[1]
b <- par[2]
P1 <- 1 / (1 + exp(-1*a*(Theta - b)))
cbind(1-P1, P1)
}
x <- createItem(name, par=par, est=est, P=P.old2PL)
# So, let's estimate it!
dat <- expand.table(LSAT7)
sv <- mirt(dat, 1, c(rep('2PL',4), 'old2PL'), customItems=list(old2PL=x), pars = 'values')
tail(sv) #looks good
mod <- mirt(dat, 1, c(rep('2PL',4), 'old2PL'), customItems=list(old2PL=x))
coef(mod)
mod2 <- mirt(dat, 1, c(rep('2PL',4), 'old2PL'), customItems=list(old2PL=x), method = 'MHRM')
coef(mod2)
# same definition as above, but using symbolic derivative computations
# (can be more accurate/stable)
xs <- createItem(name, par=par, est=est, P=P.old2PL, derivType = 'symbolic')
mod <- mirt(dat, 1, c(rep('2PL',4), 'old2PL'), customItems=list(old2PL=xs))
coef(mod, simplify=TRUE)
# several secondary functions supported
M2(mod, calcNull=FALSE)
itemfit(mod)
fscores(mod, full.scores=FALSE)
plot(mod)
# fit the same model, but specify gradient function explicitly (use of a browser() may be helpful)
gr <- function(x, Theta){
# browser()
a <- x@par[1]
b <- x@par[2]
P <- probtrace(x, Theta)
PQ <- apply(P, 1, prod)
r_P <- x@dat / P
grad <- numeric(2)
grad[2] <- sum(-a * PQ * (r_P[,2] - r_P[,1]))
grad[1] <- sum((Theta - b) * PQ * (r_P[,2] - r_P[,1]))
## check with internal numerical form to be safe
# numerical_deriv(x@par[x@est], mirt:::EML, obj=x, Theta=Theta)
grad
}
x <- createItem(name, par=par, est=est, P=P.old2PL, gr=gr)
mod <- mirt(dat, 1, c(rep('2PL',4), 'old2PL'), customItems=list(old2PL=x))
coef(mod, simplify=TRUE)
### non-linear
name <- 'nonlin'
par <- c(a1 = .5, a2 = .1, d = 0)
est <- c(TRUE, TRUE, TRUE)
P.nonlin <- function(par,Theta, ncat=2){
a1 <- par[1]
a2 <- par[2]
d <- par[3]
P1 <- 1 / (1 + exp(-1*(a1*Theta + a2*Theta^2 + d)))
cbind(1-P1, P1)
}
x2 <- createItem(name, par=par, est=est, P=P.nonlin)
mod <- mirt(dat, 1, c(rep('2PL',4), 'nonlin'), customItems=list(nonlin=x2))
coef(mod)
### nominal response model (Bock 1972 version)
Tnom.dev <- function(ncat) {
T <- matrix(1/ncat, ncat, ncat - 1)
diag(T[-1, ]) <- diag(T[-1, ]) - 1
return(T)
}
name <- 'nom'
par <- c(alp=c(3,0,-3),gam=rep(.4,3))
est <- rep(TRUE, length(par))
P.nom <- function(par, Theta, ncat){
alp <- par[1:(ncat-1)]
gam <- par[ncat:length(par)]
a <- Tnom.dev(ncat) %*% alp
c <- Tnom.dev(ncat) %*% gam
z <- matrix(0, nrow(Theta), ncat)
for(i in 1:ncat)
z[,i] <- a[i] * Theta + c[i]
P <- exp(z) / rowSums(exp(z))
P
}
nom1 <- createItem(name, par=par, est=est, P=P.nom)
nommod <- mirt(Science, 1, 'nom1', customItems=list(nom1=nom1))
coef(nommod)
Tnom.dev(4) %*% coef(nommod)[[1]][1:3] #a
Tnom.dev(4) %*% coef(nommod)[[1]][4:6] #d
} # }