setwd("/Users/isaiahmireles/Desktop/Misconceptions")
cohorts <- read.csv("cohorts.csv")

Primary Reference : https://philippmasur.de/2022/05/13/how-to-run-irt-analyses-in-r/

S24 M)

library(tidyverse)
## Warning: package 'dplyr' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.0     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   4.0.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
cohorts |> filter(term=="S24") |> 
  group_by(exam) |> 
  count(version)
MS24 <- cohorts |> filter(
  term=="S24", 
  exam=="Midterm")
FS24 <- cohorts |> filter(
  term=="S24", 
  exam=="Final")
FS24
MS24_items <- MS24 |>
  select(matches("^Q\\d+$")) |>
  select(where(\(x) any(!is.na(x))))

FS24_items <- FS24 |>
  select(matches("^Q\\d+$")) |>
  select(where(\(x) any(!is.na(x))))
library(mirt)
## Warning: package 'mirt' was built under R version 4.4.3
## Loading required package: stats4
## Loading required package: lattice
unimodel <- 'F1 = 1-34'

fit3PL <- mirt(data = MS24_items, 
               model = unimodel, 
               itemtype = "3PL", 
               verbose = FALSE)
fit3PL
## 
## Call:
## mirt(data = MS24_items, model = unimodel, itemtype = "3PL", verbose = FALSE)
## 
## Full-information item factor analysis with 1 factor(s).
## Converged within 1e-04 tolerance after 14 EM iterations.
## mirt version: 1.46.1 
## M-step optimizer: BFGS 
## EM acceleration: Ramsay 
## Number of rectangular quadrature: 61
## Latent density type: Gaussian 
## 
## Log-likelihood = -1996.793
## Estimated parameters: 102 
## AIC = 4197.586
## BIC = 4503.989; SABIC = 4181.187
## G2 (1e+10) = 2532.91, p = 1
## RMSEA = 0, CFI = NaN, TLI = NaN

Item Param (discrimination, difficulty and guessing probability)

params3PL <- coef(fit3PL, IRTpars = TRUE, simplify = TRUE)
round(params3PL$items, 2)
##         a      b    g u
## Q1   5.86   0.06 0.91 1
## Q2   9.48  -0.40 0.89 1
## Q3   2.81  -1.01 0.67 1
## Q4   0.50   0.27 0.01 1
## Q5   3.90   0.95 0.28 1
## Q6   0.88  -0.35 0.13 1
## Q7   0.41 -10.27 0.15 1
## Q8   1.00  -1.20 0.01 1
## Q9   1.62  -2.95 0.07 1
## Q10  1.55  -0.38 0.01 1
## Q11  1.33  -3.34 0.04 1
## Q12  1.37  -1.79 0.01 1
## Q13  0.90  -2.99 0.03 1
## Q14  0.46  -3.72 0.09 1
## Q15  1.21   0.75 0.81 1
## Q16  1.07  -2.70 0.05 1
## Q17  7.77  -0.16 0.87 1
## Q18  1.71  -1.25 0.00 1
## Q19  1.86  -1.57 0.01 1
## Q20  7.15  -1.97 0.01 1
## Q21  0.88  -1.10 0.25 1
## Q22  5.42   1.59 0.26 1
## Q23  1.40  -0.46 0.27 1
## Q24 -0.11   2.27 0.12 1
## Q25  1.40  -1.89 0.03 1
## Q26  1.96  -0.41 0.73 1
## Q27  3.97  -0.65 0.38 1
## Q28  3.20  -0.07 0.64 1
## Q29  1.57   0.13 0.35 1
## Q30  7.24  -0.38 0.75 1
## Q31  2.71  -0.38 0.34 1
## Q32 12.03   0.35 0.53 1
## Q33 10.40   0.43 0.24 1
## Q34  0.85   0.25 0.02 1
round(params3PL$items, 2) |> summary()
##        a                b                  g                u    
##  Min.   :-0.110   Min.   :-10.2700   Min.   :0.0000   Min.   :1  
##  1st Qu.: 1.018   1st Qu.: -1.7350   1st Qu.:0.0300   1st Qu.:1  
##  Median : 1.595   Median : -0.4050   Median :0.1950   Median :1  
##  Mean   : 3.111   Mean   : -1.0100   Mean   :0.2929   Mean   :1  
##  3rd Qu.: 3.953   3rd Qu.:  0.1125   3rd Qu.:0.4925   3rd Qu.:1  
##  Max.   :12.030   Max.   :  2.2700   Max.   :0.9100   Max.   :1
M2(fit3PL)
itemfit(fit3PL)
itemfit(fit3PL, fit_stats = "infit")
library(ggmirt)
itemfitPlot(fit3PL)
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?

head(personfit(fit3PL))
personfitPlot(fit3PL)
## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.

itempersonMap(fit3PL)
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

tracePlot(fit3PL)

tracePlot(fit3PL, facet = F, legend = T) + scale_color_brewer(palette = "Set3")
## Scale for colour is already present.
## Adding another scale for colour, which will replace the existing scale.
## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Set3 is 12
## Returning the palette you asked for with that many colors
## Warning: Removed 17622 rows containing missing values or values outside the scale range
## (`geom_line()`).

itemInfoPlot(fit3PL) + scale_color_brewer(palette = "Set3")
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## ℹ The deprecated feature was likely used in the ggmirt package.
##   Please report the issue to the authors.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Scale for colour is already present.
## Adding another scale for colour, which will replace the existing scale.
## Warning in RColorBrewer::brewer.pal(n, pal): n too large, allowed maximum for palette Set3 is 12
## Returning the palette you asked for with that many colors
## Warning: Removed 17622 rows containing missing values or values outside the scale range
## (`geom_line()`).

itemInfoPlot(fit3PL, facet = T)

testInfoPlot(fit3PL, adj_factor = 2)