--- title: "Exploring Differential Items Functioning with Dexter" author: "Timo Bechger" date: "`r Sys.Date()`" bibliography: dexter.bib output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Exploring Differential Items Functioning with Dexter} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} library(knitr) opts_chunk$set(echo = TRUE, fig.width=6, fig.height=5, message=FALSE) if (requireNamespace("Cairo", quietly = TRUE)) { opts_chunk$set(dev='CairoPNG') } RcppArmadillo::armadillo_throttle_cores(1) ``` ## Test Differential Items Functioning Based on the theory given in @BechgerMarisDIF, the function `DIF()` tests for differential items function (DIF) under an extended nominal response model. We illustrate how it works using the verbal aggression data. ```{r get_data, results='hide'} library(dexter) library(dplyr) db = start_new_project(verbAggrRules, ":memory:", person_properties=list(gender="")) add_booklet(db, verbAggrData, "data") add_item_properties(db, verbAggrProperties) ``` Gender is available as a discrete covariate which defines two groups: male and female respondents. We first test whether there is DIF. ```{r dif} dif_gender = DIF(db, "gender") dif_gender ``` The results shows that there is DIF hence it makes sense to look further to see what is going on. To this aim, we look at the relative positions of the item categories in the two groups. ## Exploring DIF Consider a plot of the ability scale in each of the two groups. ```{r abscale, echo=FALSE, results='hide', fig.height=3, fig.width=8, fig.align='center'} cc=fit_enorm(db,(gender=="Male")&(item_id%in%c("S1DoCurse","S1WantCurse","S3WantScold"))) plot(c(-2,3),c(0,1),xaxt='n',yaxt='n',bty='n',pch='',ylab='',xlab='ability-scale') lines(-1.4:2.6,rep(0,5),lty=2,col="gray") lines(-1.4:2.6,rep(0.8,5),lty=2,col="gray") cf=coef(cc) text(cf$beta,.8,paste(cf$item_id, cf$item_score),cex=0.6,adj=1,srt=90,pos = 3, xpd=NA) cc=fit_enorm(db,(gender=="Female")&(item_id%in%c("S1DoCurse","S1WantCurse","S3WantScold"))) cf=coef(cc) text(cf$beta,0,paste(cf$item_id, cf$item_score),cex=0.6,adj=1,srt=90,pos = 3, xpd=NA) text(-1.3,0.8,"Males", pos=2, xpd=NA) text(-1.3,0,"Females", pos=2, xpd=NA) ``` The plot show the ability scale in each group and the location of the score categories of three of the items indicated by their labels. The ability scale is drawn without numbers because the origin is arbitrary but it does show us the relative locations of the item categories as they are estimated from the data. On first sight, we would conclude that the locations of the item categories are clearly different. Now we wish to test whether, for each pair of locations whether this is indeed the case or whether the differences we see are due to chance. The simplest way to do this is to plot the output of the DIF function. ```{r plotdif,fig.align='center'} plot(dif_gender) ``` This produces an image plot of a matrix of differences between groups of the relative positions of each pair of item categories. The entries are standardized and can be interpreted as normally distributed test statistics such that significance can be judged from the colors. It is clear, for instance that, across male and female respondents, the second category of the item S3WantScold has a different position relative to most other item-categories. Note that the matrix can be found in the output of the DIF function. ## Limitations As usual in DIF testing, the procedure we have described is exploratory and there is no guarantee that the results can be interpreted easily. Furthermore, the plots will become cluttered when there are many items. If possible, we therefore recommend to use the profile plots. Note that with three groups, the procedure can be applied to each pair of groups but this will not take us very far when there are many more groups. Finally, note that sorting can help to make the picture more interpretable. For example, we can sort on 'want' versus 'do': ```{r sorting, fig.align='center'} items = get_items(db) |> arrange(mode, item_id) plot(dif_gender, items=items$item_id) ``` ```{r, include=FALSE} close_project(db) RcppArmadillo::armadillo_reset_cores() ``` Now, we see that the DO-items among themselves keep their relative positions in the two groups. ## References