### Install and load the packages:
# eRm:
citation("eRm")
## The original JSS article: Article about CML Estimation in eRm: Article
## about LLRAs in eRm: Book Chapter about LLRAs: Article about the
## performance of Quasi-Exact Tests in eRm: Article about the Performance
## of the nonparametric Q3 Tests in eRm:
##
## Mair P, Hatzinger R (2007). "Extended Rasch modeling: The eRm package
## for the application of IRT models in R." _Journal of Statistical
## Software_, *20*. doi:10.18637/jss.v020.i09
## <https://doi.org/10.18637/jss.v020.i09>.
##
## Mair P, Hatzinger R (2007). "CML based estimation of extended Rasch
## models with the eRm package in R." _Psychology Science_, *49*.
## doi:10.18637/jss.v020.i09 <https://doi.org/10.18637/jss.v020.i09>.
##
## Hatzinger R, Rusch T (2009). "IRT models with relaxed assumptions in
## eRm: A manual-like instruction." _Psychology Science Quarterly_,
## *51*.
##
## Rusch T, Maier M, Hatzinger R (2013). "Linear logistic models with
## relaxed assumptions in R." In Lausen B, van den Poel D, Ultsch A
## (eds.), _Algorithms from and for Nature and Life_, series Studies in
## Classification, Data Analysis, and Knowledge Organization.
## doi:10.1007/978-3-319-00035-0_34
## <https://doi.org/10.1007/978-3-319-00035-0_34>.
##
## Koller I, Maier M, Hatzinger R (2015). "An empirical power analysis
## of quasi-exact tests for the Rasch model: Measurement invariance in
## small samples." _Methodology_, *11*. doi:10.1027/1614-2241/a000090
## <https://doi.org/10.1027/1614-2241/a000090>.
##
## Debelak R, Koller I (2019). "Testing the local independence
## assumption of the Rasch model with Q3-based nonparametric model
## tests." _Applied Psychological Measurement_, *44*.
## doi:10.1177/0146621619835501
## <https://doi.org/10.1177/0146621619835501>.
##
## To see these entries in BibTeX format, use 'print(<citation>,
## bibtex=TRUE)', 'toBibtex(.)', or set
## 'options(citation.bibtex.max=999)'.
#install.packages("eRm")
library("eRm")
## Warning: package 'eRm' was built under R version 4.4.1
transreas <- read.csv("transreas.csv")
summary(transreas)
## Student Grade task_01 task_02
## Min. : 1 Min. :2.000 Min. :0.0000 Min. :0.0000
## 1st Qu.:107 1st Qu.:3.000 1st Qu.:1.0000 1st Qu.:1.0000
## Median :213 Median :4.000 Median :1.0000 Median :1.0000
## Mean :213 Mean :4.005 Mean :0.9412 Mean :0.8094
## 3rd Qu.:319 3rd Qu.:5.000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :425 Max. :6.000 Max. :1.0000 Max. :1.0000
## task_03 task_04 task_05 task_06
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000
## Median :1.0000 Median :1.0000 Median :1.0000 Median :1.0000
## Mean :0.8847 Mean :0.7835 Mean :0.8024 Mean :0.9741
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## task_07 task_08 task_09 task_10
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.00
## 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:0.0000 1st Qu.:0.00
## Median :1.0000 Median :1.0000 Median :0.0000 Median :1.00
## Mean :0.8447 Mean :0.9671 Mean :0.3012 Mean :0.52
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.00
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.00
# Remove the Student ID and Grade level variables:
transreas.responses <- subset(transreas, select = -c(Student, Grade))
# Check descriptive statistics:
summary(transreas.responses)
## task_01 task_02 task_03 task_04
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000
## Median :1.0000 Median :1.0000 Median :1.0000 Median :1.0000
## Mean :0.9412 Mean :0.8094 Mean :0.8847 Mean :0.7835
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## task_05 task_06 task_07 task_08
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000 1st Qu.:1.0000
## Median :1.0000 Median :1.0000 Median :1.0000 Median :1.0000
## Mean :0.8024 Mean :0.9741 Mean :0.8447 Mean :0.9671
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## task_09 task_10
## Min. :0.0000 Min. :0.00
## 1st Qu.:0.0000 1st Qu.:0.00
## Median :0.0000 Median :1.00
## Mean :0.3012 Mean :0.52
## 3rd Qu.:1.0000 3rd Qu.:1.00
## Max. :1.0000 Max. :1.00
# Run the Dichotomous Rasch Model
dichot.transreas <- RM(transreas.responses)
# Request a summary of the model results
summary(dichot.transreas)
##
## Results of RM estimation:
##
## Call: RM(X = transreas.responses)
##
## Conditional log-likelihood: -921.3465
## Number of iterations: 18
## Number of parameters: 9
##
## Item (Category) Difficulty Parameters (eta): with 0.95 CI:
## Estimate Std. Error lower CI upper CI
## task_02 0.258 0.133 -0.003 0.518
## task_03 -0.416 0.157 -0.723 -0.109
## task_04 0.441 0.128 0.190 0.692
## task_05 0.309 0.131 0.052 0.567
## task_06 -2.175 0.292 -2.747 -1.604
## task_07 -0.025 0.141 -0.302 0.252
## task_08 -1.909 0.262 -2.423 -1.395
## task_09 2.923 0.130 2.668 3.179
## task_10 1.836 0.115 1.610 2.062
##
## Item Easiness Parameters (beta) with 0.95 CI:
## Estimate Std. Error lower CI upper CI
## beta task_01 1.243 0.204 0.842 1.643
## beta task_02 -0.258 0.133 -0.518 0.003
## beta task_03 0.416 0.157 0.109 0.723
## beta task_04 -0.441 0.128 -0.692 -0.190
## beta task_05 -0.309 0.131 -0.567 -0.052
## beta task_06 2.175 0.292 1.604 2.747
## beta task_07 0.025 0.141 -0.252 0.302
## beta task_08 1.909 0.262 1.395 2.423
## beta task_09 -2.923 0.130 -3.179 -2.668
## beta task_10 -1.836 0.115 -2.062 -1.610
# Plot the Wright Map
plotPImap(dichot.transreas, main = "Transitive Reasoning Assessment Wright Map", irug = FALSE)

### Examine Item Parameters:
# View individual item parameters:
difficulty <- dichot.transreas$etapar
difficulty
## task_02 task_03 task_04 task_05 task_06 task_07
## 0.25775001 -0.41581384 0.44093780 0.30941151 -2.17531698 -0.02481129
## task_08 task_09 task_10
## -1.90884851 2.92343872 1.83581566
# Calculate the location for item 1:
n.items <- ncol(transreas.responses)
i1 <- 0 - sum(difficulty[1:(n.items - 1)])
difficulty.all <- c(i1, difficulty[c(1:(n.items - 1))])
difficulty.all
## task_02 task_03 task_04 task_05 task_06
## -1.24256308 0.25775001 -0.41581384 0.44093780 0.30941151 -2.17531698
## task_07 task_08 task_09 task_10
## -0.02481129 -1.90884851 2.92343872 1.83581566
# Alternative method to calculate item difficulty (easiness * -1):
difficulty2 <- dichot.transreas$betapar * -1
difficulty2
## beta task_01 beta task_02 beta task_03 beta task_04 beta task_05 beta task_06
## -1.24256308 0.25775001 -0.41581384 0.44093780 0.30941151 -2.17531698
## beta task_07 beta task_08 beta task_09 beta task_10
## -0.02481129 -1.90884851 2.92343872 1.83581566
# View standard errors for item parameters:
dichot.transreas$se.beta
## [1] 0.2043488 0.1327967 0.1566695 0.1282289 0.1314316 0.2916385 0.1413876
## [8] 0.2622114 0.1302767 0.1152173
# Examine descriptive statistics for item parameters:
summary(difficulty.all)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -2.1753 -1.0359 0.1165 0.0000 0.4081 2.9234
sd(difficulty.all)
## [1] 1.576441
summary(dichot.transreas$se.beta)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1152 0.1306 0.1371 0.1694 0.1924 0.2916
sd(dichot.transreas$se.beta)
## [1] 0.06206381
### This is what the book shows - 0.06206381
hist(difficulty.all, main = "Histogram of Item Difficulty Estimates for the Transitive Reasoning Data",
xlab = "Item Difficulty Estimates in Logits")

### Inserted this from the book page 17
###items.to.plot <- c(1:3)
plotICC(dichot.transreas, ask = FALSE)



### She missed the first line of code in this chunk.
student.locations <- person.parameter(dichot.transreas)
achievement <- student.locations$theta.table
achievement$id <-rownames(achievement)
# Add standard errors to person achievement object:
se <- as.data.frame(student.locations$se.theta$NAgroup1)
se$id <- rownames(se)
names(se) <- c("person_se", "id")
achievement.with.se <- merge(achievement, se, by = "id" )
# Examine descriptive statistics for person parameters:
summary(achievement)
## Person Parameter NAgroup Interpolated id
## Min. :-2.916 Min. :1 Mode :logical Length:425
## 1st Qu.: 1.160 1st Qu.:1 FALSE:374 Class :character
## Median : 1.932 Median :1 TRUE :51 Mode :character
## Mean : 2.010 Mean :1
## 3rd Qu.: 3.028 3rd Qu.:1
## Max. : 4.201 Max. :1
hist(achievement$`Person Parameter`, main = "Histogram of Person Achievement Estimates \nfor the Transitive Reasoning Data",
xlab = "Person Achievement Estimates in Logits")

# Examine item fit statistics:
student.locations <- person.parameter(dichot.transreas)
item.fit <- itemfit(student.locations)
item.fit
##
## Itemfit Statistics:
## Chisq df p-value Outfit MSQ Infit MSQ Outfit t Infit t Discrim
## task_01 245.550 373 1.000 0.657 0.761 -1.327 -1.622 0.455
## task_02 421.904 373 0.041 1.128 1.087 1.071 1.187 0.054
## task_03 218.155 373 1.000 0.583 0.745 -2.742 -2.688 0.583
## task_04 478.894 373 0.000 1.280 1.158 2.442 2.268 -0.014
## task_05 346.015 373 0.839 0.925 0.991 -0.625 -0.109 0.184
## task_06 65.613 373 1.000 0.175 0.611 -2.789 -1.781 0.620
## task_07 244.494 373 1.000 0.654 0.804 -2.768 -2.475 0.493
## task_08 125.478 373 1.000 0.336 0.687 -2.205 -1.569 0.563
## task_09 394.207 373 0.216 1.054 0.904 0.462 -1.727 0.022
## task_10 326.534 373 0.960 0.873 0.898 -1.822 -2.408 0.226
## Calculate reliability of item separation:
# Get Item scores
ItemScores <- colSums(transreas.responses)
ItemScores
## task_01 task_02 task_03 task_04 task_05 task_06 task_07 task_08 task_09 task_10
## 400 344 376 333 341 414 359 411 128 221
# Get Item SD
ItemSD <- apply(transreas.responses,2,sd)
ItemSD
## task_01 task_02 task_03 task_04 task_05 task_06 task_07 task_08
## 0.2355714 0.3932279 0.3197530 0.4123240 0.3986938 0.1589714 0.3626117 0.1786930
## task_09 task_10
## 0.4593099 0.5001886
# Calculate the se of the Item
ItemSE <- ItemSD/sqrt(length(ItemSD))
ItemSE
## task_01 task_02 task_03 task_04 task_05 task_06 task_07
## 0.07449423 0.12434958 0.10111476 0.13038830 0.12607804 0.05027118 0.11466788
## task_08 task_09 task_10
## 0.05650769 0.14524655 0.15817354
# compute the Observed Variance (also known as Total Person Variability or Squared Standard Deviation)
SSD.ItemScores <- var(ItemScores)
SSD.ItemScores
## [1] 8268.011
# compute the Mean Square Measurement error (also known as Model Error variance)
Item.MSE <- sum((ItemSE)^2) / length(ItemSE)
Item.MSE
## [1] 0.01291176
# compute the Item Separation Reliability
item.separation.reliability <- (SSD.ItemScores-Item.MSE) / SSD.ItemScores
item.separation.reliability
## [1] 0.9999984
###Item Separability matches the book.
## Examine person fit statistics:
person.fit <- personfit(student.locations)
summary(person.fit$p.infitMSQ)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.4060 0.4910 0.7487 0.9102 1.1740 2.2483
summary(person.fit$p.outfitMSQ)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1723 0.2399 0.5468 0.7665 0.9592 7.3060
# Examine person reliability:
person_rel <- SepRel(student.locations)
person_rel$sep.rel
## [1] 0.2061478
### person separation reliability matches the book.
### Summarize the results in tables
summary.table.statistics <- c("Logit Scale Location Mean",
"Logit Scale Location SD",
"Standard Error Mean",
"Standard Error SD",
"Outfit MSE Mean",
"Outfit MSE SD",
"Infit MSE Mean",
"Infit MSE SD",
"Std. Outfit Mean",
"Std. Outfit SD",
"Std. Infit Mean",
"Std. Infit SD",
"Reliability of Separation")
item.summary.results <- rbind(mean(difficulty.all),
sd(difficulty.all),
mean(dichot.transreas$se.beta),
sd(dichot.transreas$se.beta),
mean(item.fit$i.outfitMSQ),
sd(item.fit$i.outfitMSQ),
mean(item.fit$i.infitMSQ),
sd(item.fit$i.infitMSQ),
mean(item.fit$i.outfitZ),
sd(item.fit$i.outfitZ),
mean(item.fit$i.infitMSQ),
sd(item.fit$i.infitZ),
item.separation.reliability)
person.summary.results <- rbind(mean(achievement.with.se$`Person Parameter`),
sd(achievement.with.se$`Person Parameter`),
mean(achievement.with.se$person_se, na.rm = TRUE),
sd(achievement.with.se$person_se, na.rm = TRUE),
mean(person.fit$p.outfitMSQ),
sd(person.fit$p.outfitMSQ),
mean(person.fit$p.infitMSQ),
sd(person.fit$p.infitMSQ),
mean(person.fit$p.outfitZ),
sd(person.fit$p.outfitZ),
mean(person.fit$p.infitZ),
sd(person.fit$p.infitZ),
person_rel$sep.rel)
# Round the values for presentation in a table:
item.summary.results_rounded <- round(item.summary.results, digits = 2)
person.summary.results_rounded <- round(person.summary.results, digits = 2)
Table1 <- cbind.data.frame(summary.table.statistics,
item.summary.results_rounded,
person.summary.results_rounded)
### Tables 1 & 2
# add descriptive column labels:
names(Table1) <- c("Statistic", "Items", "Persons")
print.data.frame(Table1, row.names = FALSE)
## Statistic Items Persons
## Logit Scale Location Mean 0.00 1.71
## Logit Scale Location SD 1.58 1.09
## Standard Error Mean 0.17 0.95
## Standard Error SD 0.06 0.16
## Outfit MSE Mean 0.77 0.77
## Outfit MSE SD 0.35 0.80
## Infit MSE Mean 0.86 0.91
## Infit MSE SD 0.18 0.47
## Std. Outfit Mean -1.03 0.08
## Std. Outfit SD 1.83 0.64
## Std. Infit Mean 0.86 -0.12
## Std. Infit SD 1.67 0.91
## Reliability of Separation 1.00 0.21
## Item calibration table:
# Calculate the proportion correct for each task:
PropCorrect <- apply(transreas.responses, 2, mean)
# Calculate the proportion correct for each task:
PropCorrect <- apply(transreas.responses, 2, mean)
# Combine item calibration results in a table:
Table2 <- cbind.data.frame(colnames(transreas.responses),
PropCorrect,
difficulty.all,
dichot.transreas$se.beta,
item.fit$i.outfitMSQ,
item.fit$i.outfitZ,
item.fit$i.infitMSQ,
item.fit$i.infitZ)
names(Table2) <- c("Task ID", "Proportion Correct", "Item Location","Item SE","Outfit MSE","Std. Outfit", "Infit MSE","Std. Infit")
# Sort Table 2 by Item difficulty:
Table2 <- Table2[order(-Table2$`Item Location`),]
# Round the numeric values (all columns except the first one) to 2 digits:
Table2[, -1] <- round(Table2[,-1], digits = 2)
print.data.frame(Table2, row.names = FALSE)
## Task ID Proportion Correct Item Location Item SE Outfit MSE Std. Outfit
## task_09 0.30 2.92 0.13 1.05 0.46
## task_10 0.52 1.84 0.12 0.87 -1.82
## task_04 0.78 0.44 0.13 1.28 2.44
## task_05 0.80 0.31 0.13 0.93 -0.62
## task_02 0.81 0.26 0.13 1.13 1.07
## task_07 0.84 -0.02 0.14 0.65 -2.77
## task_03 0.88 -0.42 0.16 0.58 -2.74
## task_01 0.94 -1.24 0.20 0.66 -1.33
## task_08 0.97 -1.91 0.26 0.34 -2.21
## task_06 0.97 -2.18 0.29 0.18 -2.79
## Infit MSE Std. Infit
## 0.90 -1.73
## 0.90 -2.41
## 1.16 2.27
## 0.99 -0.11
## 1.09 1.19
## 0.80 -2.47
## 0.75 -2.69
## 0.76 -1.62
## 0.69 -1.57
## 0.61 -1.78
## Person calibration table:
# Calculate proportion correct for persons:
PersonPropCorrect <- apply(student.locations$X.ex, 1, mean)
# Combine person calibration results in a table:
PersonPropCorrect <- apply(student.locations$X.ex, 1, mean)
# Combine person calibration results in a table:
Table3 <- cbind.data.frame(achievement.with.se$id,
PersonPropCorrect,
achievement.with.se$`Person Parameter`,
achievement.with.se$person_se,
person.fit$p.outfitMSQ,
person.fit$p.outfitZ,
person.fit$p.infitMSQ,
person.fit$p.infitZ)
names(Table3) <- c("Person ID", "Proportion Correct", "Person Location","Person SE","Outfit MSE","Std. Outfit", "Infit MSE","Std. Infit")
# Round the numeric values (all columns except the first one) to 2 digits:
Table3[, -1] <- round(Table3[,-1], digits = 2)
print.data.frame(Table3 [1:6, ], row.names = FALSE)
## Person ID Proportion Correct Person Location Person SE Outfit MSE Std. Outfit
## 1 0.8 1.93 0.94 0.24 -0.52
## 10 0.6 1.16 0.83 1.10 0.36
## 100 0.4 1.16 0.83 5.62 3.50
## 101 0.7 3.03 1.19 0.56 -0.37
## 102 0.9 0.52 0.77 0.17 0.12
## 103 0.7 3.03 1.19 0.73 -0.09
## Infit MSE Std. Infit
## 0.41 -1.19
## 1.13 0.47
## 2.10 2.55
## 0.71 -0.57
## 0.49 -0.61
## 0.80 -0.34
write.csv(Table3, file = "Table3.CSV", row.names = FALSE)
write.csv(Table2, file = "Table2.CSV", row.names = FALSE)
write.csv(Table1, file = "Table1.CSV", row.names = FALSE)