###############################################################################
#' Plots the standard error of measurement or test information function.
#'
#' @param results The output from a run of \code{craschR}. (link?)
#' @param dim Specify which dimension(s) to create graphic/tables for. If
#' \code{NULL}, output and graphics for each dimension will be produced.
#' @param type \code{c("SEM", "TIC", "IIC")}
#' @param completeOnly A logical indicating whether to graph points for
#' respondents who received a score on all items or everyone, no matter what.
#' @param thetaGrid A vector containing the range of thetas for the function
#' graph.
#' @param palette A character string indicating the color scheme. Can be
#' "BASS", any RColorBrewer palette, or a vector containing 2 colors (the
#' first for the curve and the second for the points).
#' @param writeout A logical indicated whether the graph should be written to
#' your working directory as your indicated \code{imageType}. If \code{TRUE},
#' the file name will begin \code{SEM} or \code{TIC} and will include an index
#' (if more than one graph is produced) and the \code{fileSuffix} if provided.
#' @param imageType A character string indicating the format for graphics (if
#' \code{writeout = TRUE}). Supported types:
#' \code{c("pdf","svg","jpeg","bmp","tiff","png")}.
#' @param fileSuffix A character string that will be affixed to the end of each
#' file name (if \code{writeout = TRUE}). Use this if you are conducting
#' multiple analyses in the same working directory and do not wish for your
#' existing files to be overwritten.
#'
#' @return A plot of the specified test information curve. (Note: if you want
#' category information curves, you can use the TAM function
#' \code{IRT.informationCurves()} directly.)
#'
#' @export
info.graph <- function(results, dim = NULL, type = "SEM", completeOnly = TRUE,
palette = "BASS",
thetaGrid = seq(-6, 6, length.out = 100),
writeout = FALSE, imageType = "pdf", fileSuffix = NULL) {
if (results$estSummary$D > 1 & !results$estSummary$consecutive) {
stop('Information curves only available for unidimensional (or consecutive) analyses.')
}
checkResults(results)
checkWrite(writeout, fileSuffix)
checkImageType(imageType)
checkDim(dim, results$consInfo)
if (!type %in% c("SEM", "TIC", "IIC")) {
stop('Invalid type argument.')
}
if (!is.logical(completeOnly)) {
stop('Invalid completeOnly argument.')
}
if (!is.numeric(thetaGrid)) {
stop('Invalid thetaGrid argument.')
}
if (length(thetaGrid) < 100) {
warning('It is recommended that thetaGrid contains >= 100 values. Plot may look "choppy." Interpret with caution.')
}
origPar = par(no.readonly = TRUE) # so graphical parameters can be reset after
par(mai = c(1.36, 1.093333, 1.093333, 0.56),
mar = c(5.1, 4.1, 4.1, 2.1),
xpd = FALSE)
if (identical(palette, "BASS")) {
color <- c(curve = "gray52", points = "#80b1d3")
} else if (length(palette) == 1) {
if (palette %in% row.names(brewer.pal.info)) {
color <- brewer.pal(3, palette)
} else {
stop('Invalid palette argument.')
}
} else if (all(areColors(palette)) & length(palette) == 2) {
color <- palette
} else {
stop('Invalid palette argument.')
}
if (is.null(dim)) {
D <- 1:results$estSummary$D
} else if (is.numeric(dim)) {
D <- dim
}
K <- ncol(results$itemThres)
inclConsIDs <- results$consInfo$cons.ID[D]
inclItem <- results$itemInfo$cons.ID %in% inclConsIDs
itemInfo <- results$itemInfo[inclItem,]
# polytomous item information as in Muraki (1993)
probs <- lapply(thetaGrid, catProbs, itemThres = results$itemThres[inclItem,])
infoItem <- sapply(1:length(probs), function(x) {
expScore <- apply(probs[[x]], 2, function(y) {
sum(0:K * y, na.rm = TRUE)
})
colSums(sapply(1:length(expScore), function(y) {
((0:K - expScore[y]) ^ 2) * probs[[x]][,y]
}), na.rm = TRUE)
})
# information for each observed person
infoPers <- results$scoresRecoded[,inclItem]
if (type == "IIC") {
thetas <- results$scoresRecoded[,inclItem]
} else {
thetas <- results$persPars
}
for (d in D) {
fillCol <- which(results$itemInfo$cons.ID[inclItem] == results$consInfo$cons.ID[d])
for (n in 1:results$estSummary$N) {
infoPers[n, fillCol] <- infoItem[,which.min(abs(results$persPars[n, d] - thetaGrid))][fillCol]
if (type == "IIC") {
thetas[n, fillCol] = rep(thetaGrid[which.min(abs(results$persPars[n, d] - thetaGrid))], length(fillCol))
} else {
thetas[n, d] = thetaGrid[which.min(abs(results$persPars[n, d] - thetaGrid))]
}
}
}
# put NAs in if they didn't answer the question
infoPers[is.na(results$scoresRecoded[,inclItem])] <- NA
testInfo <- matrix(nrow = length(thetaGrid), ncol = results$estSummary$D)
testInfoPers <- matrix(nrow = results$estSummary$N, ncol = results$estSummary$D)
for (d in D) {
itemsD <- which(results$itemInfo$cons.ID[inclItem] == results$consInfo$cons.ID[d])
testInfo[,d] = colSums(infoItem[itemsD,])
testInfoPers[,d] = rowSums(infoPers[,itemsD], na.rm = TRUE)
}
# flag complete cases
if (completeOnly) {
persToPlot <- complete.cases(results$scoresRecoded)
} else {
persToPlot <- rep(TRUE, nrow(results$persPars))
}
if (type == "IIC") {
for (i in 1:sum(inclItem)) {
if(writeout) {
eval(parse(text = paste0(imageType, "('IIC-item", i, fileSuffix, ".",
imageType, "')")))
}
maxX <- 2 * ceiling(max(thetaGrid)/2)
minX <- 2 * floor(min(thetaGrid)/2)
plot(1, type = "n", xlim = c(min(thetaGrid), max(thetaGrid)),
ylim = c(0, max(infoItem[i,])), axes = FALSE,
xlab = "Logits", ylab = "Information",
main = "Item Information Curve")
mtext(as.character(itemInfo$item.name[i]))
lines(thetaGrid, infoItem[i,], lwd = 2, col = color[1])
axis(1, at = seq(minX, maxX, 2))
axis(1, at = seq(minX+1, maxX-1, 2), labels = FALSE)
axis(2)
points(thetas[,i], infoPers[,i], pch = 21, col = color[1], bg = color[2])
if (writeout) {
dev.off
}
}
} else if (type == "TIC") {
for (d in D) {
if(writeout) {
eval(parse(text = paste0(imageType, "('TIC-cons", d, fileSuffix, ".",
imageType, "')")))
}
maxX <- 2 * ceiling(max(thetaGrid)/2)
minX <- 2 * floor(min(thetaGrid)/2)
plot(1, type = "n", xlim = c(min(thetaGrid), max(thetaGrid)),
ylim = c(0, max(testInfo[,d])), axes = FALSE,
xlab = "Logits", ylab = "Information",
main = "Test Information Curve")
mtext(as.character(results$consInfo$short.name[d]))
lines(thetaGrid, testInfo[,d], lwd = 2, col = color[1])
axis(1, at = seq(minX, maxX, 2))
axis(1, at = seq(minX+1, maxX-1, 2), labels = FALSE)
axis(2)
points(thetas[persToPlot, d], testInfoPers[persToPlot, d], pch = 21, col = color[1], bg = color[2])
if (writeout) {
dev.off
}
}
} else if (type == "SEM") {
for (d in D) {
if(writeout) {
eval(parse(text = paste0(imageType, "('SEM-cons", d, fileSuffix, ".",
imageType, "')")))
}
maxX <- 2 * ceiling(max(thetaGrid)/2)
minX <- 2 * floor(min(thetaGrid)/2)
plot(1, type = "n", xlim = c(min(thetaGrid), max(thetaGrid)),
ylim = c(0, max(1 / testInfo[,d])), axes = FALSE,
xlab = "Logits", ylab = "SEM",
main = "Standard Error of Measurement")
mtext(as.character(results$consInfo$short.name[d]))
lines(thetaGrid, 1 / testInfo[,d], lwd = 2, col = color[1])
axis(1, at = seq(minX, maxX, 2))
axis(1, at = seq(minX+1, maxX-1, 2), labels = FALSE)
axis(2)
points(thetas[persToPlot, d], 1 / testInfoPers[persToPlot, d], pch = 21, col = color[1], bg = color[2])
if (writeout) {
dev.off
}
}
} else {
stop('Invalid type specified. Choose "SEM", "TIC", or "IIC".')
}
par(origPar)
}
################################################################################
#' A wrapper for the wrightMap() function from the WrightMap package.
#'
#' @param results The output from a run of \code{craschR}. (link?)
#' @param dim A numeric vector that specifies for which dimension(s) to create
#' graphic/tables. If \code{NULL}, output and graphics for each dimension
#' will be produced.
#' @param byCat A logical indicating if items/steps should be grouped by the
#' construct category. If \code{FALSE}, items will be plotted in the order
#' of \code{itemInfo}.
#' @param palette A character string indicating the color scheme. Can be "BASS"
#' or any RColorBrewer palette. If you want to customize further, the
#' \code{wrightMap()} function should be used directly, not this wrapper.
#' @param writeout A logical indicating whether the graphic should be written to
#' to your working directory as your specified \code{imageType}. If
#' \code{TRUE}, the file name will begin \code{WM} and will include an index
#' (if more than one graph is produced) and the \code{fileSuffix} if provided.
#' @param imageType A character string indicating the format for graphics (if
#' \code{writeout = TRUE}). Supported types:
#' \code{c("pdf","svg","jpeg","bmp","tiff","png")}.
#' @param fileSuffix A character string that will be affixed to the end of each
#' file name (if \code{writeout = TRUE}). Use this if you are conducting
#' multiple analyses in the same working directory and do not wish for your
#' existing files to be overwritten.
#'
#' @return Wright Map graphic (multiple graphics will be produced if you have
#' more than one dimension and ran consecutive analyses)
#'
#' @export
wm <- function(results, dim = NULL, byCat = FALSE, palette = "BASS",
writeout = FALSE, imageType = "pdf", fileSuffix = NULL) {
checkResults(results)
checkWrite(writeout, fileSuffix)
checkImageType(imageType)
checkDim(dim, results$consInfo)
if (!is.logical(byCat)) {
stop('Invalid byCat argument.')
}
origPar = par(no.readonly = TRUE) # to reset graphical parameters after
# if ordering by construct level, then the WMs MUST be consecutive (1 for each dim)
if (byCat) {
consecutive = TRUE
} else {
consecutive = results$estSummary$consecutive
}
if (is.null(dim)) {
D <- 1:results$estSummary$D
} else {
D <- dim
}
itemInfo <- results$itemInfo
itemThres <- results$itemThres
if (consecutive) {
thresList <- list()
for (i in 1:length(D)) {
# get the indices for the items on each construct
thresList[[i]] <- which(results$itemInfo$cons.ID == results$consInfo$cons.ID[D[i]])
}
} else {
thresList <- list(1:results$estSummary$I)
}
if (byCat) {
itemXcat <- as.matrix(results$itemInfo[,6:ncol(results$itemInfo)])
for (i in 1:results$estSummary$I) {
# "remove" the empty categories
if (length(results$empties[[i]]) > 0) {
itemXcat[i, results$empties[[i]]] = FALSE
}
# "remove" the first TRUE for each item
itemXcat[i, min(which(itemXcat[i,]))] = FALSE
}
# take the transpose
itemXcat = t(itemXcat[,-1])
# fill in the thresholds
toGraph <- matrix(nrow = nrow(itemXcat), ncol = ncol(itemXcat))
toGraph[itemXcat] <- c(t(results$itemThres))[!is.na(c(t(results$itemThres)))]
} else {
toGraph <- results$itemThres
}
if (length(palette) == 1) {
if (palette == "BASS") {
color <- c("black", "#80b1d3")
} else if (palette %in% row.names(brewer.pal.info)) {
color <- RColorBrewer::brewer.pal(3, palette)
} else {
stop('Invalid palette argument.')
}
} else {
stop('Invalide palette argument.')
}
thresReturn <- list()
for (i in 1:length(thresList)) {
if (consecutive) {
thetas <- results$persPars[,D[i]]
cons <- consLabel <- paste0(results$consInfo$short.name[D[i]], " ")
thresLabel <- matrix(rep(results$itemInfo$item.name, each = nrow(toGraph)),
nrow = nrow(toGraph), ncol = ncol(toGraph))
} else {
thetas <- results$persPars
cons <- thresLabel <- NULL
consLabel <- colnames(results$persPars)
}
if (byCat) {
thres <- toGraph[,thresList[[i]]]
xLabel <- results$consInfo[D[i], 5:ncol(results$consInfo)]
thresPos <- c(2,4)
} else {
thres <- toGraph[thresList[[i]],]
xLabel <- results$itemInfo$item.name[thresList[[i]]]
thresPos <- 2
}
if (writeout) {
if (!byCat) {
fileName <- "-itemorder"
if (imageType == "pdf" | imageType == "svg") {
imgWidth <- min(14, length(thresList[[i]]))
imgHeight <- 7
} else {
imgWidth <- min(480 * 2, 480/7 * length(thresList[[i]]))
imgHeight <- 480
}
} else {
fileName <- "-constructorder"
if (imageType == "pdf" | imageType == "svg") {
imgWidth <- max(6, 2*(ncol(results$consInfo) - 5))
imgHeight <- 7
} else {
imgWidth <- max(6 * 480 / 7, 2 * 480 / 7 *(ncol(results$consInfo) - 5))
imgHeight <- 480
}
}
if (length(thresList)==1) {
index <- NULL
} else {
index <- D[i]
}
eval(parse(text = paste0(imageType, "('WM", fileName, index, fileSuffix,
".", imageType, "', width = ", imgWidth,
" , height = ", imgHeight, ")")))
}
wrightMap(thetas = thetas,
thresholds = thres,
main.title = paste0(cons, "Wright Map"),
show.thr.lab = ncol(toGraph) > 1,
axis.items = "",
label.items = xLabel,
label.items.srt = 45,
dim.names = consLabel,
thr.sym.pch = 21,
thr.sym.col.fg = color[1],
thr.sym.col.bg = color[2],
thr.lab.pos = thresPos,
return.thresholds = FALSE)
thresReturn[[i]] <- thres
colnames(thresReturn[[i]]) = results$itemInfo$item.name[thresList[[i]]]
if (writeout) {
dev.off()
}
}
par(origPar)
return(thresReturn)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.