-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMAP_estimation_of_ability.R
145 lines (106 loc) · 5.33 KB
/
MAP_estimation_of_ability.R
1
#########################################################################This program calculates ability estimates given known item parameters ##using MAP ##########################################################################for this example, there are just two items, whose parameters are below:a <- c(1,2) # a parametersb <- c(0,1) # b parametersthetaList <- seq(-3,3,0.1) # list of theta values for plots and quadratureTlist <- list(0,0) # lists of values of the trace lines#Loops through all the items.for (i in 1:length(a)) {#compute probabilities of a correct responde using the 2 PL #given the theta parameters in thetaList#and the item parametrs in the a and b vectors. Tlist[[i]] <- 1/(1+exp(-a[i]*(thetaList-b[i])))}# draw something like Test Scoring's Figure 3.10, in three panels:#quartz(width=8, height=10) # note: quartz is specific to Mac OS X; omit on PCspar(mfrow=c(3,1), cex.axis=2, cex.lab=2, mar=(c(5, 8, 4, 2)+0.1), lab=c(7,3,7))#Plot the ICC of the first item.plot(thetaList,Tlist[[1]],type="l", xlab="Theta", ylab="T(u=1)", col="blue")#Plot the ICC of the second item.plot(thetaList,(1-Tlist[[2]]),type="l", xlab="Theta", ylab="T(u=0)", col="blue")#compute the likelihood = P(1-P)likelihood <- Tlist[[1]]*(1-Tlist[[2]])#plots the likelihood as a function of theta.plot(thetaList,likelihood,type="l", xlab="Theta", ylab="Likelihood", col="blue")#################################################################################vector of responses to the two items for a person.#this person responded the first item correctly and the second incorrectly.#for MLE, you need a minimum of 2 responses, one correct and one incorrect.u <- c(1,0) # responses, positive (right) and negative (wrong)# now draw the log of the likelihood:loglikelihood <- log(likelihood)plot(thetaList,loglikelihood,type="l", xlab="Theta", ylab="loglikelihood", col="blue")################################################################################ # MAXIMUM A POSTERIORI (MAP) ESTIMATION #Sets graphing mode#quartz(width=8, height=10)par(cex.axis=2, cex.lab=2, mar=(c(5, 8, 4, 2)+0.1), lab=c(7,3,7))#define a population distribution (prior distribution)popdist <- exp(-0.5*(thetaList^2)) # compute (proportional to) pop. dist.# re-compute and re-draw the log of the likelihood with the population distribution.loglikelihood <- log(likelihood) + log(popdist)plot(thetaList,loglikelihood,type="l", xlab="Theta", ylab="loglikelihood", col="blue")# Use the "fixed" version of the Newton-Raphson algorithm with stepsize limit and backtracking:thetahat <- 0 # thetahat starts at 0, all else same as aboveNiter <- 10StepLimit <- 0.5 # this will limit change to 0.5 per iterationLastdl <- 0 # and we need to know last direction (start with none)#loop through iterations for MAP estimation.for (iter in 1:Niter) { l <- 0 # initialize loglikelihood (don't need to compute this, dl <- 0 # except for the graphics), and first and second derivatives d2l <- 0 #loop through responses for (i in 1:length(u)) { #obtain the probability of correct response to an item given the 2PL and item parameters. T <- 1/(1+exp(-a[i]*(thetahat-b[i]))) #l is the loglikelihood of the response pattern. #it is obtained with a summation of l for a response with the l of previous responses. l <- l + u[i]*log(T) + (1-u[i])*log(1 - T) #dl is the first partial derivative of the loglikelihood with respect to theta #it is obtained with a summation of dl of a response to the dl of previous reponses dl <- dl + a[i] * (u[i]-T) #d2l is the second partial derivative of the loglikelihood with respect to theta. #it is obtained with a summation (of negative values) of d2l of a response to the d2l of previous responses. d2l <- d2l - (a[i]^2) * T * (1 - T) }# add population distribution terms into loglikelihood, first and second derivatives l <- l - 0.5*(T^2) # pop. dist. dl <- dl - thetahat d2l <- d2l - 1 #calculate the amount to update theta. correction <- dl/d2l#======================== # the twelve lines below are purely for graphics if((abs(correction) < 0.000001) | (iter == Niter)) lcolor <- "red" else lcolor <- "pink" ltheta <- thetahat - 0.5 # these lines draw the rtheta <- thetahat + 0.5 # straight lines that llinear <- l - dl*0.5 # illustrate the gradient rlinear <- l + dl*0.5 lines(c(ltheta,rtheta),c(llinear, rlinear), col=lcolor)# the five lines below compute and plot the "fitted" quadratic q2 <- d2l/2 q1 <- dl - (2*q2*thetahat) q0 <- l - q1*thetahat - q2*thetahat*thetahat quad <- q0 + q1*thetaList + q2*thetaList*thetaList lines(thetaList, quad, col=lcolor) #============================================================ print(c(thetahat, l, dl, d2l)) if (d2l > -0.01) d2l <- -0.01 # insurance: don't (get close to) zero divide # below impose stepsize limit if (abs(correction) > StepLimit) correction <- sign(correction)*StepLimit #updates theta. thetahat <- thetahat - correction #Compare the size of the update amount with the convergence criterion. if(abs(correction) < 0.000001) break if ((dl*Lastdl) < 0.0) { # if gradient has changed sign thetahat <- thetahat + (0.5*correction) # back up half way StepLimit <- 0.5*StepLimit # "split the difference" or dl <- 0 # "false position" } Lastdl <- dl}