Code
library(mirt)Loading required package: stats4
Loading required package: lattice
Code
d<-read.csv("/Users/briggsd/Library/CloudStorage/Dropbox/Courses/EDUC 8720/Data Sets/Misc/ER_data_tab7-2.csv")The purpose of this tutorial test is to show how to fix the values of various item parameters in mirt. There are lots of reasons you might want to do this–I’ll get into this later.
For now let me just show how this works using the data from Embretson & Reise, Chapter 7, Tables 7.2 & 7.3.
library(mirt)Loading required package: stats4
Loading required package: lattice
d<-read.csv("/Users/briggsd/Library/CloudStorage/Dropbox/Courses/EDUC 8720/Data Sets/Misc/ER_data_tab7-2.csv")Instead of calibrating the 2PL, I’m going to run the following command
sv1 <- mirt(d, 1, itemtype='2PL', pars = 'values',verbose=FALSE)Let’s have a look at the first 8 rows this object
sv1[1:8,] group item class name parnum value lbound ubound est const nconst
1 all it1 dich a1 1 0.851 -Inf Inf TRUE none none
2 all it1 dich d 2 -0.107 -Inf Inf TRUE none none
3 all it1 dich g 3 0.000 0 1 FALSE none none
4 all it1 dich u 4 1.000 0 1 FALSE none none
5 all it2 dich a1 5 0.851 -Inf Inf TRUE none none
6 all it2 dich d 6 0.107 -Inf Inf TRUE none none
7 all it2 dich g 7 0.000 0 1 FALSE none none
8 all it2 dich u 8 1.000 0 1 FALSE none none
prior.type prior_1 prior_2
1 none NaN NaN
2 none NaN NaN
3 none NaN NaN
4 none NaN NaN
5 none NaN NaN
6 none NaN NaN
7 none NaN NaN
8 none NaN NaN
I also want to show you the last two rows of sv1
sv1[41:42,] group item class name parnum value lbound ubound est const nconst
41 all GROUP GroupPars MEAN_1 41 0 -Inf Inf FALSE none none
42 all GROUP GroupPars COV_11 42 1 0 Inf FALSE none none
prior.type prior_1 prior_2
41 none NaN NaN
42 none NaN NaN
These are the only rows in the table that don’t correspond to item parameters. Instead, these are the mean and variance of the person ability distribution (assuming we are specifying a normal distribution for ability, which is the default in mirt). Note that these values, by default are set to 0 and 1 to identify the scale.
Now, in the ER example in Table 7.3, they are getting ML estimates (“scores”) of theta for their 23 examinees after imposing different assumptions about the true values of the item parameters in each case. I want to show you how to replicate what they did with mirt by fixing values in the sv1 object I just introduced.
For the first column, all discrimination values are fixed at 1 and item difficulty goes -2 to 2 mostly in increments of .5 logits.
#custom discrimination and easiness values
sv1$value[sv1$name == 'a1'] <- rep(1,10) #Creates a vector of 10 1's
sv1$value[sv1$name == 'd'] <- -1*c(-2,-1.5,-1,-.5,0,0,.5,1,1.5,2) #Multiplied by -1 to make this easiness
#Fix the parameters to these starting values, do NOT estimate them
sv1$est <- FALSE
#Set pop mean and variance as free to be estimated
sv1$est[sv1$name == 'MEAN_1'] <- TRUE
sv1$est[sv1$name == 'COV_11'] <- TRUEDo you see what we are doing here? Let’s see how the sv1 object has changed
sv1[1:8,] group item class name parnum value lbound ubound est const nconst
1 all it1 dich a1 1 1.0 -Inf Inf FALSE none none
2 all it1 dich d 2 2.0 -Inf Inf FALSE none none
3 all it1 dich g 3 0.0 0 1 FALSE none none
4 all it1 dich u 4 1.0 0 1 FALSE none none
5 all it2 dich a1 5 1.0 -Inf Inf FALSE none none
6 all it2 dich d 6 1.5 -Inf Inf FALSE none none
7 all it2 dich g 7 0.0 0 1 FALSE none none
8 all it2 dich u 8 1.0 0 1 FALSE none none
prior.type prior_1 prior_2
1 none NaN NaN
2 none NaN NaN
3 none NaN NaN
4 none NaN NaN
5 none NaN NaN
6 none NaN NaN
7 none NaN NaN
8 none NaN NaN
sv1[41:42,] group item class name parnum value lbound ubound est const nconst
41 all GROUP GroupPars MEAN_1 41 0 -Inf Inf TRUE none none
42 all GROUP GroupPars COV_11 42 1 0 Inf TRUE none none
prior.type prior_1 prior_2
41 none NaN NaN
42 none NaN NaN
When we fit the 2PL under these constraints, we aren’t estimating any item parameters. The only thing getting estimated is the population mean and variance. Then we go straight to estimating thetas.
fit.1 <- mirt(d, 1, pars = sv1,verbose= FALSE)
coef.fit.1 <- coef(fit.1, IRTpars=TRUE, simplify=TRUE)
coef.fit.1$items
a b g u
it1 1 -2.0 0 1
it2 1 -1.5 0 1
it3 1 -1.0 0 1
it4 1 -0.5 0 1
it5 1 0.0 0 1
it6 1 0.0 0 1
it7 1 0.5 0 1
it8 1 1.0 0 1
it9 1 1.5 0 1
it10 1 2.0 0 1
$means
F1
-0.393
$cov
F1
F1 0.578
# The estimated population SD
sqrt(coef(fit.1)$GroupPars[2]) [1] 0.7600784
theta_mle1 <- fscores(fit.1, method = "ML", full.scores.SE = TRUE)
round(theta_mle1,2) F1 SE_F1
[1,] 0.00 0.73
[2,] 0.00 0.73
[3,] 0.00 0.73
[4,] 0.00 0.73
[5,] 0.00 0.73
[6,] 0.00 0.73
[7,] -1.12 0.78
[8,] -1.12 0.78
[9,] -1.12 0.78
[10,] -1.12 0.78
[11,] -1.12 0.78
[12,] -1.12 0.78
[13,] -1.12 0.78
[14,] -1.12 0.78
[15,] -2.74 1.12
[16,] -1.79 0.87
[17,] -1.12 0.78
[18,] -0.54 0.74
[19,] 0.00 0.73
[20,] 0.54 0.74
[21,] 1.12 0.78
[22,] 1.79 0.87
[23,] 2.74 1.12
#An estimate of marginal reliability
num <- mean(theta_mle1[,2]) #Average SEM of thetas
den <- var(theta_mle1[,1]) #Total observed Variance of estimated thetas
1 - num/den #Proportion of Observed Variance that is "real" variance[1] 0.4322175
sqrt((1 - num/den)*den) #The "true" standard deviation of theta (compare to estimated pop SD)[1] 0.7803674
You can see that this replicates the results in columns for \(\alpha=1\) in ER’s Table 7.3.
For you to try on your own: use the approach above to replicate ER’s columns for \(\alpha=2\) and \(\alpha=2.5\).
There is an even nicer way to specify these values using a function in the package sirt called mirt.specify.partable. To see how it works, first install the sirt package, load it, and then type ?mirt.specify.partable for an illustration of how this works.