In the two-parameter partial credit model (TPPCM) of Yu (1991)
< https://hdl.handle.net/2142/21459>, every step of a polytomous item has
its own discrimination. Estimation is left to existing software: 'TAM'
(tam.mml.3pl() with a step design matrix, tam.mml.2pl(), tam.mml()) and,
for the bilinear product form and non-negativity constraints, 'sirt'
(xxirt()). The package adds what these packages do not provide: standard
errors of step discriminations based on the full observed information,
step-level Wald tests, and score tests with modification indices and
expected parameter changes for moving down the model ladder saturated
TPPCM -> product form -> generalized partial credit model -> partial
credit model, limited-information fit statistics (the M2 statistic, root
mean square error of approximation, standardized root mean squared
residual, comparative fit index and Tucker-Lewis index; Maydeu-Olivares
and Joe (2006)
Boundary-level diagnostics for the two-parameter partial credit model (TPPCM; Yu, 1991), built on top of TAM.
In the TPPCM every step l of item i has its own discrimination ail:
P(Xi = k | θ) ∝ exp( Σl ≤ k (ail θ − dil) ), k = 0, …, K−1.
The generalized partial credit model (GPCM; Muraki, 1992) is the special case with equal steps within items, and the partial credit model (PCM; Masters, 1982) has one common discrimination.
Estimation stays in TAM (and in sirt for the product form). tppcm takes the fitted object and adds what TAM does not report at the level of category boundaries: step discriminations (CBD), locations (CBL) and intercepts (IBD) with standard errors, tests between models, fit statistics, step information and tests of differential step functioning (DSF) across groups.
# install.packages("remotes")
remotes::install_github("jiewenTsai/tppcm", build_vignettes = TRUE)
tppcm(dat, design = ) builds the design array
for tam.mml.3pl(). The design formula reads the step discriminations as
an item x step layout on the log scale: ~ item * step (saturated TPPCM,
default), ~ item (GPCM), ~ step (steps common to the items), ~ 1
(PCM); ~ item + step (Rank-1, ail = αi
γl) is fitted with xxirt_tppcm(). Any other equality pattern
goes through index.irt_pars() prints step discriminations and
difficulties with standard errors and a model-fit header.m2() gives limited-information fit statistics (M2, RMSEA,
SRMSR, CFI, TLI) for any step design.step_info() splits item information into the
contributions of each step.wald_test() tests equal steps within an item.
score_test(), item_test() and mi() test a fitted model against a
larger one without fitting it.xxirt_tppcm(dat, design = ~ item + step) fits
ail = αi γl and non-negativity bounds
through sirt::xxirt().dif_test(fit, g, by = "step") screens every category boundary with a
joint score (LM) test; dsf() decomposes flagged steps into CBD and
location differences by likelihood ratio tests; dsf_design() builds
configural, metric, scalar and partial multigroup models (lavaan-style
group.equal / group.partial). tppcmtree() grows DIF trees when the
grouping is unknown. Tables of tests report Holm-adjusted p_adj.get_parts() extracts the quantities behind these
functions (scores, information, Jacobian, posterior and more).sim_tppcm() generates responses from a TPPCM.Fit with TAM, then pass the fitted object to tppcm.
library(tppcm) # attaches TAM
set.seed(1)
a <- outer(c(0.8, 1.0, 1.2, 1.5, 1.0), c(1, 1.5, 0.7))
b <- matrix(c(-1, 0, 1), 5, 3, byrow = TRUE)
dat <- sim_tppcm(1000, disc = a, diff = b)
# 1. Fit the models with TAM
m_gpcm <- tam.mml.2pl(dat, irtmodel = "GPCM", verbose = FALSE)
m_tppcm <- tam.mml.3pl(dat, E = tppcm(dat), est.variance = FALSE, verbose = FALSE,
control = list(maxiter = 3000, max.increment = 0.1))
# 2. Parameters, fit and step-level tests
irt_pars(m_tppcm)
m2(m_tppcm)
wald_test(m_tppcm)
# 3. Where does the GPCM fail?
sp <- get_parts(m_gpcm)
score_test(sp)
item_test(sp)
mi(sp)
# 4. Rank-1: is there a step effect, an item x step interaction?
score_test(sp, against = ~ item + step) # GPCM vs Rank-1
m_rank1 <- xxirt_tppcm(dat, design = ~ item + step)
irt_pars(m_rank1, restricted = TRUE)
score_test(m_rank1) # Rank-1 vs saturated
# 5. Differential step functioning between two groups
g <- rep(c("A", "B"), each = 500)
m_mg <- tam.mml.3pl(dat, E = tppcm(dat), group = g, est.variance = FALSE, verbose = FALSE)
st <- dif_test(m_mg, g, by = "step") # screen every step (2 df)
flagged <- st$unit[st$p_adj < 0.05]
if (length(flagged)) dsf(m_mg, items = flagged) # CBD vs location of flagged steps
Two vignettes:
vignette("tppcm-tutorial", package = "tppcm"): the model and the package.vignette("tppcm-recipes", package = "tppcm"): short recipes for every
function.Traditional Chinese versions, with the same code, are in the GitHub
repository (not in the CRAN package):
tppcm-tutorial-zh
and
tppcm-recipes-zh.
Cite with citation("tppcm").