All Code & Exploration were inspired by :
Paek, I., & Cole, K. (2019). Using R for Item Response Theory Model Applications (1st ed.). Routledge. https://doi.org/10.4324/9781351008167
term & (term, exam) :## [1] 149 34
eRm package uses : conditional maximum likelihood
(CML) estimation. Therefore it does not require the specification of the
latent trait distribution ( \(\theta\)
).
Above I fit a Rasch Model
Here we assume \(\sum_{i=1}^{34}b_i=0\) ; meaning, the sum of item difficulty is 0.
Typical item Difficulty
Easy Item Difficulty
Hard Item Difficulty
## [1] 1
item_difficulty <- cbind(-Rasch_CML$betapar, Rasch_CML$se.beta)
colnames(item_difficulty) <- c("est", "SE")
item_difficulty <- item_difficulty |> as.data.frame(); item_difficultydifficulty_CI <- cbind(-confint(Rasch_CML)[,2], -confint(Rasch_CML)[,1])
colnames(difficulty_CI) <- c("Low", "High")
difficulty_CI <- difficulty_CI |> as.data.frame(); difficulty_CIdifficulty <- cbind(difficulty_CI, est=item_difficulty[,1], item = names(Rasch_CML$betapar))
# summary(Rasch_CML)
# better method^difficulty |>
ggplot(aes(
x = est,
y = reorder(item, est),
xmin = Low,
xmax = High
)) +
geom_pointrange() +
geom_vline(
xintercept = 0,
linetype = "dashed"
) +
labs(
title = "Item Difficulty CI-95%",
x = "Item Difficulty",
y = "Item"
) +
theme_minimal()# set margin sizes
par(
mfrow = c(2, 1),
mar = c(3, 4, 2, 1)
)
hist(
item_difficulty$est,
main = "Item Difficulty Dist"
)
boxplot(
item_difficulty$est,
horizontal = TRUE
)As we can see difficulty is normally distributed
percent_correct <- S24MQ |>
summarize(
across(
everything(),
~ mean(.x, na.rm = TRUE) * 100
)
)
percent_correctpercent_correct_long <- percent_correct |>
pivot_longer(
cols = everything(),
names_to = "item",
values_to = "percent_correct"
)
difficulty_percent <- difficulty |>
mutate(
item = str_remove(item, "^beta ")
) |>
left_join(
percent_correct_long,
by = "item"
)difficulty_percent |>
ggplot(aes(
x = est,
y = percent_correct
)) +
geom_point() +
geom_text(
aes(label = item),
nudge_y = 2,
size = 3
) +
labs(
title = "Item Difficulty vs. Percent Correct",
x = "Item Difficulty",
y = "Percent Correct"
) +
theme_minimal()## [1] 0.9154302
about 92% of variation can be described via regressing
est and percent_correct
##
## Person Parameters:
##
## Raw Score Estimate Std.Error
## 13 -0.673932618 0.4142515
## 15 -0.336827848 0.4077035
## 16 -0.171289853 0.4062696
## 17 -0.006417529 0.4059753
## 18 0.158657094 0.4067802
## 19 0.324814862 0.4087111
## 20 0.493033349 0.4117921
## 21 0.664332670 0.4161253
## 22 0.839784956 0.4218264
## 23 1.020667015 0.4290985
## 24 1.208586752 0.4382412
## 25 1.405523315 0.4496752
## 26 1.613988463 0.4640264
## 27 1.837478272 0.4822548
## 28 2.081086749 0.5059079
## 29 2.352518700 0.5375958
## 30 2.664566213 0.5821649
## 31 3.041136296 0.6498887
## 32 3.535665240 0.7677078
## 33 4.318303521 1.0440940
## 34 5.170735537 NA
?person.parameter
cohorts |>
filter(exam=="Midterm", term=="S24") |>
select(student_id,Total.Score) |>
arrange(Total.Score) |>
head(3)cohorts |>
filter(exam=="Midterm", term=="S24") |>
select(student_id,Total.Score) |>
arrange(desc(Total.Score)) |>
head(3)Here we see for a given raw score the est. ability ( \(\theta\) ) score and its estimated Standard error.
Notice our estimates are only from the lowest to highest possible score obtained by students in data (S24M)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 13.00 23.00 27.00 26.33 30.00 34.00
Bottom 25% of class (Quartile 1) have an ability of 1.02 or below
Mean Equates to an ability of 1.61
Median Equates to 1.84
Score of 20 equates to 0.49 ability
Person_ability <- coef(Person.Rasch_CML) |> as.data.frame()
colnames(Person_ability) <- c("ability")
Person_ability |> arrange(ability)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -0.6739 1.0207 1.8375 1.8939 2.6646 5.1707
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 13.00 23.00 27.00 26.33 30.00 34.00
Estimated Person specific ability
Based on Raw Score – same score means same ability
Here we see the typical ability to actually be centered around 2 (1.89) – that would equate to about a raw score of 27, which from earlier makes more sense on the true typical behavior of students
mean_raw <- mean(S24M$Total.Score, na.rm = TRUE)
mean_ability <- mean(
Person_ability$ability,
na.rm = TRUE
)
plot(
Person.Rasch_CML,
main = "Raw Score vs. Person Ability"
)
abline(
v = mean_raw,
lty = 2
)
abline(
h = mean_ability,
lty = 2
)average Score, Ability Cross is provided alongside the equated Score, Ability Graph
Graph extends from worse to best score obtained in data
notice person ability are highly varied – maybe there is some multiple modality
Dots are item difficulty
GOF : Goodness of Fit
## Warning in LRtest.Rm(Rasch_CML):
## The following items were excluded due to inappropriate response patterns within
## subgroups:
## Q1 Q2 Q7 Q9 Q20 Q25
##
## Full and subgroup models are estimated without these items!
##
## Andersen LR-test:
## LR-value: 49.913
## Chi-square df: 27
## p-value: 0.005
This test basically asks : After splitting by median raw score values, are the item-difficulty parameters essentially the same for the lower-scoring and higher-scoring groups?
Therefore, you reject the null hypothesis of parameter invariance. Item difficulties are not sufficiently similar across the two ability groups, so there is evidence that the simple Rasch model does NOT fit the data well globally
##
## Martin-Loef-Test (split criterion: median)
## LR-value: 72.539
## Chi-square df: 288
## p-value: 1
splits data based on subgroups of items and conducts a test of a homogeneous item set
according to this test, the items are consistent with measuring a single latent trait
In other words :
The exam may still be measuring one main ability.
But some items may function differently across student subgroups.
##
## Andersen LR-test:
## LR-value: 49.913
## Chi-square df: 27
## p-value: 0.005
## Warning in Waldtest.Rm(Rasch_CML):
## The following items were excluded due to inappropriate response patterns within
## subgroups:
## Q1 Q2 Q7 Q9 Q20 Q25
##
## Subgroup models are estimated without these items!
##
## Wald test on item level (z-values):
##
## z-statistic p-value
## beta Q3 -0.273 0.785
## beta Q4 1.658 0.097
## beta Q5 0.799 0.424
## beta Q6 -0.766 0.444
## beta Q8 -0.126 0.900
## beta Q10 -1.141 0.254
## beta Q11 0.805 0.421
## beta Q12 -0.567 0.571
## beta Q13 0.247 0.805
## beta Q14 1.141 0.254
## beta Q15 0.778 0.437
## beta Q16 0.389 0.697
## beta Q17 -0.143 0.886
## beta Q18 -0.839 0.402
## beta Q19 -1.191 0.234
## beta Q21 0.576 0.564
## beta Q22 3.589 0.000
## beta Q23 -0.352 0.725
## beta Q24 4.259 0.000
## beta Q26 -0.135 0.892
## beta Q27 -1.114 0.265
## beta Q28 -1.114 0.265
## beta Q29 -0.396 0.692
## beta Q30 0.112 0.911
## beta Q31 -1.192 0.233
## beta Q32 0.713 0.476
## beta Q33 -1.737 0.082
## beta Q34 0.087 0.931
Q22, 24 have unusual z scores
It is suggested to use Bonferroni correction because of the possible family-wise Type I error rate
## Warning in Waldtest.Rm(Rasch_CML):
## The following items were excluded due to inappropriate response patterns within
## subgroups:
## Q1 Q2 Q7 Q9 Q20 Q25
##
## Subgroup models are estimated without these items!
wald$coef.table <- cbind(
wald$coef.table,
p.adj.bonf = p.adjust(wald$coef.table[, "p-value"],
method = "bonferroni")
)
# look at only sign.
wald$coef.table[
wald$coef.table[, "p.adj.bonf"] < 0.05,
,
drop = FALSE
]## z-statistic p-value p.adj.bonf
## beta Q22 3.588739 3.322818e-04 0.0093038901
## beta Q24 4.259341 2.050308e-05 0.0005740863
Item difficulty estimates from the two groups are plotted with their confidence ellipses
##
## Itemfit Statistics:
## Chisq df p-value Outfit MSQ Infit MSQ Outfit t Infit t Discrim
## Q1 103.937 147 0.997 0.702 0.988 -0.353 0.069 0.185
## Q2 74.320 147 1.000 0.502 0.943 -0.719 -0.048 0.261
## Q3 93.158 147 1.000 0.629 0.894 -0.711 -0.347 0.354
## Q4 176.393 147 0.049 1.192 1.158 1.807 2.054 0.258
## Q5 144.118 147 0.552 0.974 0.995 -0.210 -0.034 0.356
## Q6 185.745 147 0.017 1.255 1.017 1.936 0.247 0.374
## Q7 113.789 147 0.981 0.769 0.957 0.179 0.150 0.064
## Q8 141.447 147 0.614 0.956 1.012 -0.199 0.161 0.383
## Q9 72.886 147 1.000 0.492 0.908 -0.498 -0.066 0.268
## Q10 121.417 147 0.939 0.820 0.882 -1.581 -1.623 0.528
## Q11 168.023 147 0.113 1.135 0.925 0.431 -0.025 0.192
## Q12 106.208 147 0.995 0.718 0.848 -0.892 -0.961 0.469
## Q13 223.114 147 0.000 1.508 0.902 1.191 -0.399 0.296
## Q14 153.111 147 0.348 1.035 1.013 0.214 0.137 0.263
## Q15 188.145 147 0.012 1.271 1.097 0.898 0.648 0.112
## Q16 122.490 147 0.930 0.828 0.926 -0.271 -0.258 0.312
## Q17 115.728 147 0.973 0.782 1.002 -0.279 0.099 0.180
## Q18 104.386 147 0.997 0.705 0.879 -1.305 -1.017 0.486
## Q19 90.399 147 1.000 0.611 0.890 -1.339 -0.670 0.444
## Q20 43.721 147 1.000 0.295 0.842 -1.139 -0.299 0.394
## Q21 129.776 147 0.843 0.877 0.973 -0.566 -0.233 0.345
## Q22 207.358 147 0.001 1.401 1.225 2.509 2.315 0.096
## Q23 122.152 147 0.933 0.825 0.919 -1.058 -0.909 0.428
## Q24 241.299 147 0.000 1.630 1.396 4.379 4.708 0.013
## Q25 97.628 147 0.999 0.660 0.921 -1.034 -0.421 0.362
## Q26 133.376 147 0.783 0.901 1.037 -0.157 0.258 0.195
## Q27 89.453 147 1.000 0.604 0.831 -1.699 -1.341 0.527
## Q28 116.760 147 0.969 0.789 0.991 -0.792 -0.026 0.314
## Q29 139.757 147 0.652 0.944 0.990 -0.387 -0.107 0.388
## Q30 97.341 147 0.999 0.658 0.945 -0.847 -0.209 0.343
## Q31 103.137 147 0.998 0.697 0.866 -1.746 -1.411 0.503
## Q32 142.973 147 0.578 0.966 1.057 -0.162 0.677 0.296
## Q33 125.845 147 0.896 0.850 0.880 -1.523 -1.679 0.470
## Q34 151.668 147 0.379 1.025 1.009 0.274 0.150 0.366