Introduction to ForceChoice

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5
)

Overview

The ForceChoice package provides a unified framework for fitting, simulating, and evaluating forced-choice and traditional item response theory (IRT) models. It supports eight model families with full Bayesian estimation via Stan (Hamiltonian Monte Carlo), a fast iterative stochastic EM (iStEM) algorithm, and a deterministic EM backend for FCGDINA.

This vignette is intended as a reproducible user guide rather than a complete methodological review. Model formulas and object components are described using the parameterization implemented in ForceChoice. The reference list gives DOI links where available so readers can verify the underlying psychometric sources.

Model Families

| Family | Description | Data Type | |---|---|---| | MIRT | Multidimensional IRT (1PL--4PL) | Binary | | MGPCM | Multidimensional Generalized Partial Credit | Polytomous | | MGGUM | Multidimensional Generalized Graded Unfolding | Polytomous | | FCMIRT | Forced-Choice MIRT with Luce--Plackett ranking | Ranking/MOLE/PICK | | FCGGUM | Forced-Choice GGUM with ranking | Ranking/MOLE/PICK | | TIRT | Thurstonian IRT with pairwise probit | Ranking/MOLE/PICK | | FCDCM | Forced-Choice Diagnostic Classification | Paired comparison | | FCGDINA | Forced-Choice GDINA diagnostic model | Ranking/MOLE/PICK |

The forced-choice families accept full ranking ("RANK"), most-least ("MOLE"), or best-only ("PICK") data when supported by the corresponding fitting function. FCDCM is the exception: it is a paired-comparison model where every block contains exactly two statements.

Model and Backend Coverage

| Family | Main response process | Backends | Primary outputs | |---|---|---|---| | MIRT | Dominance IRT for binary items | Stan, iStEM | item parameters, $\theta$, factor correlations | | MGPCM | Dominance IRT for ordered categories | Stan, iStEM | category intercepts, $\theta$, factor correlations | | MGGUM | Ideal-point/unfolding IRT | Stan, iStEM | slopes, locations, thresholds, $\theta$ | | FCMIRT | MIRT endorsement plus Luce--Plackett ranking | Stan, iStEM | item parameters, $\theta$, block fit | | FCGGUM | GGUM endorsement plus Luce--Plackett ranking | Stan, iStEM | unfolding item parameters, $\theta$, block fit | | TIRT | Pairwise Thurstonian probit comparisons | Stan, iStEM | loadings, uniquenesses, $\theta$ | | FCDCM | Higher-order DCM for two-statement FC blocks | Stan, iStEM | attribute profiles, higher-order parameters | | FCGDINA | GDINA/DINA/DINO/ACDM plus FC ranking | Stan, iStEM, EM | attribute profiles, CDM item parameters |

Estimation Backends

Installation

# Development version from GitHub
remotes::install_github("Naidantu/ForceChoice")

Quick Start: Traditional IRT

Simulating and Fitting a MIRT Model

library(ForceChoice)

# Simulate binary response data
sim <- sim.data.MIRT(N = 20, I = 6, D = 2, model = "2PL")

# Fit via iStEM
fit <- fit.MIRT(sim$data, model = "2PL", D = 2,
                method = "iStEM",
                control.method = list(
                  vis = FALSE, seed = 123,
                  M = 2, B = 2, burnin.maxitr = 2,
                  maxitr = 3, eps1 = 10, eps2 = 10,
                  estimate.se = FALSE))

# Examine results
print(fit)
summary(fit)

# Item parameter estimates (first 6 items)
head(coef(fit))

# Factor correlation matrix
fit$Corr$est

# Trait recovery
diag(cor(fit$theta$est, sim$theta))

Goodness-of-Fit Evaluation

# Compute comprehensive fit indices
gof <- get.fit.index(fit)

# Summary of fit indices
summary(gof)

# Extract specific indices
gof$M2       # Limited-information M2 statistic
gof$RMSEA    # RMSEA with 90% CI
gof$CFI      # Comparative Fit Index
gof$TLI      # Tucker-Lewis Index
gof$SRMSR    # Standardized Root Mean Square Residual
gof$AIC      # Akaike Information Criterion
gof$BIC      # Bayesian Information Criterion

Forced-Choice Modeling

Simulating and Fitting FCMIRT

Forced-choice data uses ranking strings (e.g., "2>1>3" means item 2 is preferred over item 1 over item 3).

# Simulate forced-choice ranking data
sim <- sim.data.FCMIRT(N.person = 20, N.block = 3, I.block = 2,
                       D = 2, model = "2PL", fc.type = "RANK")

# The data contains ranking strings
head(sim$data)

# Fit: block.items and fc.type are auto-detected
fit <- fit.FCMIRT(sim$data, model = "2PL", D = 2,
                  method = "iStEM",
                  control.method = list(
                    vis = FALSE, seed = 123,
                    M = 2, B = 2, burnin.maxitr = 2,
                    maxitr = 3, eps1 = 10, eps2 = 10,
                    estimate.se = FALSE))

# Trait recovery
cor(fit$theta$est, sim$theta)

# Goodness-of-fit (uses nominal binary expansion)
gof <- get.fit.index(fit)
summary(gof)

Thurstonian IRT (TIRT)

sim <- sim.data.TIRT(N.person = 20, N.block = 3, I.block = 2,
                     D = 2, fc.type = "RANK")

fit <- fit.TIRT(sim$data, Q.matrix = sim$Q.matrix,
                block.items = sim$block.items,
                method = "iStEM",
                control.method = list(
                  vis = FALSE, seed = 123,
                  M = 2, B = 2, burnin.maxitr = 2,
                  maxitr = 3, eps1 = 10, eps2 = 10,
                  estimate.se = FALSE))

# Structural parameters: loadings and uniquenesses
head(coef(fit))

# Gamma matrix (pairwise intercepts)
fit$gamma.matrix$est[1:5, 1:5]

Forced-Choice DCM (FCDCM)

sim <- sim.data.FCDCM(N.person = 20, N.block = 3, D = 2,
                      dcm.type = "DINA")

fit <- fit.FCDCM(sim$data, Q.matrix = sim$Q.matrix,
                 block.items = sim$block.items,
                 method = "iStEM",
                 control.method = list(
                   vis = FALSE, seed = 123,
                   M = 2, B = 2, burnin.maxitr = 2,
                   maxitr = 3, eps1 = 10, eps2 = 10,
                   estimate.se = FALSE))

# Posterior attribute mastery probabilities
head(fit$alpha$prob)

# Attribute mastery proportions
colMeans(fit$alpha$prob > 0.5)

# Higher-order IRT parameters
fit$delta$est

Forced-Choice GDINA (FCGDINA)

FCGDINA is the diagnostic-classification counterpart for multi-statement forced-choice blocks. Unlike FCDCM, which is restricted to paired comparisons under a higher-order DCM structure, FCGDINA supports "GDINA", "DINA", "DINO", and "ACDM" statement-level models and can be fitted to full ranking, most-least, or best-only forced-choice data.

sim <- sim.data.FCGDINA(N.person = 20, N.block = 2, I.block = 2,
                        D = 2, model = "GDINA", fc.type = "RANK")

fit <- fit.FCGDINA(sim$data, Q.matrix = sim$Q.matrix,
                   block.items = sim$block.items, model = "GDINA",
                   fc.type = sim$fc.type, method = "EM",
                   control.method = list(vis = FALSE, seed = 123,
                                         maxitr = 2,
                                         estimate.se = FALSE))

# Posterior attribute mastery probabilities
head(fit$alpha$est)

# CDM item-parameter estimates
coef(fit, type = "delta")

Working with Polytomous Data

MGPCM (Dominance)

sim <- sim.data.MGPCM(N = 20, I = 6, D = 2, length.poly = 4)

fit <- fit.MGPCM(sim$data, D = 2, method = "iStEM",
                 control.method = list(
                   vis = FALSE, seed = 123,
                   M = 2, B = 2, burnin.maxitr = 2,
                   maxitr = 3, eps1 = 10, eps2 = 10,
                   estimate.se = FALSE))

# Category threshold parameters
coef(fit)

MGGUM (Ideal-Point / Unfolding)

sim <- sim.data.MGGUM(N = 20, I = 6, D = 2, length.poly = 4)

fit <- fit.MGGUM(sim$data, D = 2, method = "iStEM",
                 control.method = list(
                   vis = FALSE, seed = 123,
                   M = 2, B = 2, burnin.maxitr = 2,
                   maxitr = 3, eps1 = 10, eps2 = 10,
                   estimate.se = FALSE))

coef(fit)

Solution Rotation

For exploratory MIRT analyses, post-hoc rotation helps achieve simple structure:

fit_rot <- rotate.MIRT(fit, rotate = "oblimin")

# Compare original and rotated loadings
head(coef(fit))
head(coef(fit_rot))

Diagnostic Plots

# Item characteristic curves (ICC)
plot(fit, type = "icc", items = 1:8)

# Person parameter distributions
plot(fit, type = "theta")

# iStEM convergence trace
plot(fit, type = "trace")

Estimation Control

Common Parameters (top-level)

fit <- fit.MIRT(data, model = "2PL", D = 2, method = "iStEM",
                control.method = list(
                  vis = FALSE,
                  seed = 123,      # Reproducibility
                  M = 2, B = 2, burnin.maxitr = 2,
                  maxitr = 3, eps1 = 10, eps2 = 10,
                  estimate.se = FALSE))

Stan-Specific Control

# stan code, long time
fit <- fit.MIRT(data, model = "2PL", D = 2, method = "stan",
                control.method = list(chains = 1, iter = 200,
                                      warmup = 100, cores = 1,
                                      seed = 123))

iStEM-Specific Control (advanced)

fit <- fit.MIRT(data, model = "2PL", D = 2, method = "iStEM",
                control.method = list(
                  seed = 123,
                  M = 2,            # Burn-in batches for convergence
                  B = 2,            # Iterations per batch
                  burnin.maxitr = 2,
                  maxitr = 3,
                  eps1 = 1.5,       # Geweke convergence threshold
                  eps2 = 0.4        # MC error tolerance
                ))

References



Try the ForceChoice package in your browser

Any scripts or data that you put into this service are public.

ForceChoice documentation built on Sept. 13, 2026, 1:06 a.m.