Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
WrightMap Tutorial 4 – More Flexibility
Using the person and item side functions
Introduction
Version 1.2 of the WrightMap
package allows you to directly access the functions used for drawing the person and item sides of the map in order to allow more flexible item person maps. The parts can be put together on the same plot using the split.screen
function.
Calling the functions
Let’s start by installing the latest version of the package from CRAN.
install.packages('WrightMap') library(WrightMap)
And set up some item data.
items.loc <- sort( rnorm( 20)) thresholds <- data.frame( l1 = items.loc - 0.5 , l2 = items.loc - 0.25, l3 = items.loc + 0.25, l4 = items.loc + 0.5)
We can draw a simple item map by calling one of the item side functions. Currently there are three: itemModern
, itemClassic
, and itemHist
.
The itemModern
function is the default called by wrightMap
.
itemModern(thresholds)
itemClassic
function creates item sides inspired by text-based Wright Maps.
itemClassic(thresholds)
itemHist
function plots the items as a histogram.
itemHist(thresholds)
Similarly, the person side functions allow you to graph the person parameters. There are two, personHist
and personDens
.
## Mock results multi.proficiency <- data.frame( d1 = rnorm(1000, mean = -0.5, sd = .5), d2 = rnorm(1000, mean = 0.0, sd = 1), d3 = rnorm(1000, mean = +0.5, sd = 1), d4 = rnorm(1000, mean = 0.0, sd = .5), d5 = rnorm(1000, mean = -0.5, sd = .75)) personHist(multi.proficiency)
personDens(multi.proficiency)
item.side
and person.side
parameters.
wrightMap(multi.proficiency,thresholds,item.side = itemClassic,person.side = personDens)
Use with CQmodel: The personData and itemData functions
The person side and item side functions are expecting data in the form of matrices. They do not recognize CQmodel objects. When a CQModel object is sent to wrightMap
, it first extracts the necessary data, and then sends the data to the plotting functions. In 1.2, the data processing functions have also been made directly accessible to users in the form of the personData
and itemData
functions. These are fast ways to pull the data out of a CQmodel object in such a way that it is ready to be sent to wrightMap
or any of the item and person plotting functions.
The personData
function is very simple. It can take either a CQmodel object or a string containing the name of a ConQuest person parameter file. It extracts the person estimates as a matrix.
fpath <- system.file("extdata", package="WrightMap") model1 <- CQmodel(file.path(fpath,"ex7a.eap"), file.path(fpath,"ex7a.shw")) head(model1$p.est) ## casenum est (d1) error (d1) pop (d1) est (d2) error (d2) pop (d2) ## 1 1 1.37364 0.70308 0.60309 1.73654 0.60556 0.52928 ## 2 2 -0.17097 0.64866 0.66216 0.75620 0.54852 0.61379 ## 3 3 0.46677 0.64837 0.66246 0.85146 0.55129 0.60987 ## 4 4 0.67448 0.66017 0.65006 1.16098 0.56368 0.59214 ## 5 5 0.89717 0.67704 0.63195 1.49079 0.58539 0.56012 ## 6 6 1.64704 0.72529 0.57762 2.11784 0.62916 0.49188 m1.person <- personData(model1) head(m1.person) ## d1 d2 ## 1 1.37364 1.73654 ## 2 -0.17097 0.75620 ## 3 0.46677 0.85146 ## 4 0.67448 1.16098 ## 5 0.89717 1.49079 ## 6 1.64704 2.11784 personHist(m1.person,dim.lab.side = 1)
The itemData
function uses the GIN table (Thurstonian thresholds) if it is there, and otherwise tries to create delta parameters out of the RMP tables. You can also specify tables to use as items, steps, and interactions, and it will add them together appropriately to create delta parameters.
model2 <- CQmodel(file.path(fpath,"ex4a.mle"), file.path(fpath,"ex4a.shw")) names(model2$RMP) ## [1] "rater" "topic" ## [3] "criteria" "rater*topic" ## [5] "rater*criteria" "topic*criteria" ## [7] "rater*topic*criteria*step" m2.item <- itemData(model2,item.table = "topic", interactions = "rater*topic", step.table = "rater") itemModern(m2.item)
See Tutorial 3 for details on specifying tables from CQmodel objects.
Having these data functions pulled out also makes it easier to combine parameters from different models onto a single plot (when appropriate).
wrightMap(m1.person,m2.item)
Putting it all together with split.screen
By calling these functions directly and using, we can make Wright Maps with other arrangements of persons and items. The item side functions can be combined using any of the base graphics options for combining plots (layout, par(mfrow)), but the person side functions are based on split.screen
, which is incompatible with those options. We will be combining item and person maps, so we need to use split.screen
.
The first step of combining these functions is to set up the screens. Details for screen functions are in the documentation for split.screen
. The function takes as a parameter a 4-column matrix, in which each row is a screen, and the columns represent the left, bottom, right, and top of the screens respectively. Each value is expressed as a number from 0 to 1, where 0 is the left/bottom of the current device and 1 is the right/top.
To make a Wright Map with the items on the left and the persons on the right, we will set up two screens, with 80% of the width on the left and 20% on the right.
split.screen(figs = matrix(c(0,.8,0,1 ,.8,1,0,1),ncol = 4, byrow = TRUE)))
Next, we’ll draw the item side. IMPORTANT NOTE: Make sure to explicitly set the yRange
variable when combining plots to ensure they are on the same scale. We can also adjust some of the other parameters to work better with a left-side item plot. We’ll move the logit axis to the left with the show.axis.logit
parameter, and set the righthand outer margin to 2 to give us a space between the plots.
itemModern(thresholds, yRange = c(-3,4), show.axis.logits = "L", oma = c(0,0,0,2))
We can also add a title at this time.
mtext("Wright Map", side = 3, = 2, line = 1)
Finally, we will move to screen 2 and draw the person side. This plot will be adjusted to move the persons label and remove the axis.
screen(2) personHist(multi.proficiency, axis.persons = "",yRange = c(-3,4) , axis.logits = "Persons", show.axis.logits = FALSE)
The last thing to do is to close all the screens to prevent them from getting in the way of any future plotting.
close.screen(all.screens = TRUE)
Here is the complete plot:
split.screen(figs = matrix(c(0,.8,0,1,.8,1,0,1),ncol = 4, byrow = TRUE)) itemModern(thresholds, yRange = c(-3,4), show.axis.logits = "L", oma = c(0,0,0,2)) mtext("Wright Map", side = 3, = 2, line = 1) screen(2) personHist(multi.proficiency, axis.persons = "",yRange = c(-3,4) , axis.logits = "Persons", show.axis.logits = FALSE) close.screen(all.screens = TRUE)
Countless arrangements are possible. As one last example, here are two ways to put two dimensions put side by side in separate Wright Maps.
Explicitly splitting the device into four screens:
d1 = rnorm(1000, mean = -0.5, sd = 1) d2 = rnorm(1000, mean = 0.0, sd = 1) dim1.diff <- rnorm(5) dim2.diff <- rnorm(5) split.screen(figs = matrix(c(0,.09,0,1, .11,.58,0,1, .5,.59,0,1, .51,1,0,1),ncol = 4,byrow = TRUE)) personDens(d1,yRange = c(-3,3),show.axis.logits = FALSE , axis.logits = "") screen(2) itemModern(dim1.diff,yRange = c(-3,3),show.axis.logits = FALSE) mtext("Wright Map", side = 3, = 2, line = 1) screen(3) personDens(d2,yRange = c(-3,3),show.axis.logits = FALSE , axis.logits = "" , axis.persons = "",dim.names = "Dim2") screen(4) itemModern(dim2.diff,yRange = c(-3,3),show.axis.logits = FALSE , label.items = paste("Item",6:10))
close.screen(all.screens = TRUE)
Splitting the device into two screens with a Wright Map on each:
split.screen(figs = matrix(c(0,.5,0,1, .5,1,0,1),ncol = 4,byrow = TRUE)) wrightMap(d1,dim1.diff,person.side = personDens,show.axis.logits = FALSE) screen(2) wrightMap(d2,dim2.diff,person.side = personDens,show.axis.logits = FALSE) close.screen(all.screens = TRUE)
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.