
rasch is a Rasch Measurement Theory engine for R, built entirely from published measurement theory. Items are estimated by pairwise conditional maximum likelihood (Andrich & Luo 2003; Zwinderman 1995) with Godambe sandwich standard errors, persons by Warm’s (1989) weighted likelihood, and the complete diagnostic apparatus follows the conventions of Andrich & Marais (2019). A modern Shiny interface exposes every analysis with the reproducing R code attached to every output.
Documentation: https://drjoshmcgrane.github.io/rasch/
# install.packages("remotes")
remotes::install_github("drjoshmcgrane/rasch")The analysis engine is base R only (stats,
graphics, grDevices, utils). The
Shiny interface additionally uses shiny,
bslib, and DT.
library(rasch)
# a persons-by-items data frame; item names, an ID column, and person
# factors carry through the whole analysis
fit <- rasch(responses, model = "PCM",
id = "person_id", factors = c("gender", "site"))
summary(fit) # the full test-of-fit report
fit$items # locations, SEs, fit residuals, infit/outfit, chi-square
fit$person # WLE person measures with SEs and person fit
score_table(fit) # raw score to measure conversion
plot_icc(fit, "Q05", group = "gender") # the graphical DIF display
plot_pimap(fit) # person-item threshold distribution
dif_anova(fit) # DIF over all factors jointly (main effects;
# effects = "factorial" adds interactions)
dimensionality_test(fit) # Smith's residual-component t-test
residual_correlations(fit) # Yen's Q3 / Q3* local dependence
save_outputs(fit, "results/") # every table and plot, in one callMissing data is allowed everywhere: estimation is pairwise and person measures use each person’s observed items. Empty categories are collapsed and constant items dropped, with notes recorded on the fit.
rasch::run_app()A guided bslib (Bootstrap 5) workflow: one-tap example datasets,
grouped run settings, status badges once a fit exists, and dark mode.
Every plot and table lives in a full-screen-capable card with downloads;
master–detail explorers drive the item, person, and DIF pages; dynamic
notes summarise each table’s verdict in words; and an “R code for this
analysis” panel shows the exact rasch call reproducing the
current run. Wide, long (rated), frame-of-reference, and
paired-comparison data layouts are all supported, including anchor
upload for equating and one-click structural remedies (subtests, item
splitting, automatic DIF resolution) with in-place reset.
# ------------------------------------------------------------- test of fit --
chisq_detail(fit, "Q05") # per-class-interval chi-square detail
ctt_table(fit) # classical companions: facility, item-total r,
# discrimination index, alpha, classical SEM
lr_test(fit) # PCM vs RSM: raw + Kent-calibrated composite LR
compare_fits(PCM = fit, RSM = rasch(responses, model = "RSM"))
guttman_table(fit) # scalogram with coefficient of reproducibility
# ---------------------------------------------------------------- persons --
score_table(fit, method = "mle", extremes = "extrapolated")
person_extrapolated(fit) # geometric extreme-score extrapolation
# (Andrich & Marais 2019, ch. 10)
# ----------------------------------------------------------- independence --
dimensionality_test(fit, component = 2) # any residual component's split
dimensionality_magnitude(fit, list(setA, setB))
# Andrich (2016): c, rho = 1/(1+c^2), and A
plot_pca_biplot(fit) # PC1 x PC2 loadings biplot
plot_scree(fit) # eigenvalues with a model-simulated reference
plot_resid_cor(fit) # Q3* heatmap (stat = "q3" for raw Q3)
dependence_magnitude(fit, dependent = "Q05", independent = "Q04")
# Andrich & Kreiner d in logits, with SE
fit2 <- combine_items(fit, list(c("Q04", "Q05"))) # subtest remedy
spread_test(fit2) # spread against the binomial least upper bound
# ------------------------------------------------------------- invariance --
dif_anova(fit, sizes = TRUE) # all factors jointly; within-subject factors
# get proper repeated-measures error strata
dif_contrasts(fit) # planned one-df questions with familywise control
dif_size(fit, "Q05", by = "gender") # DIF magnitude in logits
fit3 <- split_items(fit, "Q05", by = "gender") # resolve one item
resolve_dif(fit) # split iteratively, largest effect first,
# until no significant DIF remains
tailored_analysis(fit, chance = 0.25) # four-step guessing procedure
# ---------------------------------------------------------------- equating --
fit_eq <- rasch(responses, anchors = data.frame(item = c("Q01", "Q10"),
k = c(1, NA), tau = c(-1.2, 0.9)))
eq <- equate_tests(fit, fit_eq) # drift tests through the common items
plot_equate(fit, fit_eq)
# -------------------------------------------------------------- many-facet --
mf <- rasch_mfrm(ratings, person = "person", item = "criterion",
score = "score", facets = "rater")
mf$facet_effects$rater # severities with SEs and pooled fit
plot_facets(mf)
mfi <- rasch_mfrm(ratings, person = "person", item = "criterion",
score = "score", facets = "rater", interaction = "rater")
mfi$interaction_effects # item-by-rater interactions
# --------------------------------------------------------- multiple choice --
mc <- rasch(responses_raw, key = c(Q1 = "A", Q2 = "C", Q3 = "B"))
distractor_analysis(mc) # per option: n, proportion, location, pt-biserial
prop <- distractor_rescore(mc) # propose polytomous option scores
mc2 <- rasch(responses_raw, key = prop$option_scores)
# ------------------------------------------------- extended frame of reference --
ef <- rasch_efrm(responses, item_sets = list(numeracy = num_items,
literacy = lit_items),
groups = "year_group")
ef$phi_table; ef$alpha_table # group and item-set units with SEs
plot_frames(ef); plot_icc_frames(ef, "Q07")
# ------------------------------------------------------- paired comparisons --
bt <- btl(comparisons, object_a = "left", object_b = "right",
winner = "preferred", judge = "judge", order = "sequence")
bt$objects; bt$judges # locations + fit; erratic judges flag
bt$dependence # within-judge exposure and carry-over, in logits
plot_btl_dependence(bt, "carry_over") # interrogate a dependence effect
btl_transitivity(bt) # preference loops: is one scale enough?
btl_dimensionality(bt) # residual "swirl" (bimensions) = a 2nd attribute?
plot_btl_scree(btl_dimensionality(bt)) # bimension scree vs a noise reference
btl(comparisons, ..., position = TRUE) # first-position (order) advantage
btl(comparisons, ..., anchors = c(S07 = 0.42)) # anchored, for equating
btl_equate(bt, bt_lastyear) # common-object drift tests across panels/years
btl_information(bt) # design information per object (targeting)
btl_next_pairs(bt) # adaptive next comparisons (Pollitt 2012)
btl_dif(bt, list(panel = panel_map, experience = exp_map))
# judge-group DIF, factors modelled jointly
plot_btl_icc(bt, "E04", group = panel_map) # curves by judge group
# ------------------------------------------------------- repeated measures --
stack_data(t1, t2) # change-in-persons: DIF over time
rack_data(t1, t2) # change-in-itemspcml). Standard errors come from a Godambe sandwich
estimator, which corrects the over-optimism of the naive pairwise
information.pcml_pc)
reparameterises each item’s thresholds as Andrich’s (1978, 1985)
orthogonal-polynomial principal components — location, spread, skewness,
kurtosis (Pedler
rasch_efrm;
Humphry 2005; Humphry & Andrich 2008): frames are item-set by
person-group cells with units rho = alpha_set × phi_group; person-group
units come from person-free within-frame pairwise conditioning, item-set
units from persons common to the sets, reconciled over the linking
graph.btl) as a member of the
same family — btl() on the pair-conditional comparisons
extracted from Rasch data reproduces pcml()’s item
locations to solver tolerance. Dichotomous or graded preferences
(symmetric thresholds, so the model is invariant to presentation order),
judge-clustered sandwich standard errors, object and judge fit, pairwise
goodness of fit, within-judge dependence effects (exposure and
carry-over, estimated jointly with the locations, with a per-comparison
audit trail and a partial-residual display), and judge-group DIF with
factors modelled jointly and resolved magnitudes in logits.Everything in the package that claims to be Rasch measurement is conditionally estimated, sufficiency-respecting, and invariance-preserving: item comparisons are person-free by pairwise conditioning, and the partial credit, rating scale, and additive many-facet models are members of the Rasch class. The diagnostics (fit residuals, item-trait chi-square, residual components, DIF analysis of variance, threshold ordering) exist to police the theory’s requirements, and the structural remedies (subtests, item splitting, anchoring) are orthodox practice that restore rather than parameterise away invariance.
Departures from the classical model are deliberate and labelled. Warm’s weighted likelihood adds a penalty beyond the conditional likelihood (this is what makes extreme-score estimates finite). Cronbach’s alpha and the distractor point-biserials are classical-test-theory companions, reported as descriptives only. Interactive facet mode remains in the Rasch class but a significant item-by-facet interaction qualifies specific objectivity in practice. The extended frame of reference model is strictly Rasch within every frame; across frames it is an argued extension of the theory of the unit (Humphry 2005; Humphry & Andrich 2008) whose status the literature still debates, and its item-set units are necessarily identified from the person side — a departure from purely distribution-free comparison that belongs to the model, not the implementation.
inst/casestudies/wording_units_selfesteem.R applies the
extended frame of reference model to the public Rosenberg Self-Esteem
Scale dataset of the Open Source Psychometrics Project, treating
positively and negatively worded items as two item sets. The positively
worded items carry a unit about 27 per cent larger than the
reverse-scored negative items (alpha ratio 1.266, 95% CI 1.246–1.286),
and it matters: persons with identical raw scores differ by up to 0.85
logits once the wording units are modelled, and the male–female gap is
understated by about 10 per cent under equal units. A free-slope model
agrees at the set level, and sensitivity analysis localises part of the
effect to the scale’s well-known ambivalent item.
inst/casestudies/party_blocs_crisis.R applies the
paired-comparison form to the Tübingen 2009 party-preference data
shipped with psychotools (Strobl, Wickelmaier & Zeileis
2011): ideological blocs as object sets, concern about the 2008–9
economic crisis as judge panels. Crisis-affected respondents judge party
contests with a unit about a third smaller than the unaffected — less
decisively, not more — though the contrast stays short of significance
by either standard error, and it exceeds anything gender or education
produces; the right bloc’s origin sits firmly below the left’s in this
university-town sample. The study doubles as a design clinic: a
two-object set whose single internal pair splits nearly evenly
identifies neither its panel-ratio contribution nor its own unit, and
the fit now screens the set out of the unit reconciliation, reports the
boundary-unstable set unit as NA, and names both in notes
rather than diverging — the honest answers to questions such a design
cannot support.
Every estimation and diagnostic component is validated against
simulated data with known parameters in tests/testthat
(990+ tests): parameter recovery for every model; sandwich standard
errors against empirical sampling variability; DIF detected on planted
items only — including within-subject, factorial, and judge-group
designs — with adversarial cases for artificial DIF; dimensionality
verdicts on one- and two-dimensional data with a calibrated null;
dependence effects recovered from sequentially simulated judgments; and
the published conventions reproduced directly against the worked
examples they come from. R CMD check runs clean.
The estimators are also cross-validated against independent
implementations, at the level of agreement each comparison licenses:
sirt::rasch.pairwise estimates the same pairwise
conditional family and agrees to near-identity; eRm fits
the full Andersen conditional likelihood — a different consistent
estimator of the same parameters — and agrees to sampling precision,
with our judge-robust sandwich standard errors sitting just above eRm’s
CML errors (the documented efficiency price of pairwise conditioning);
and btl() reproduces psychotools::btmodel to
machine precision, as it must (same likelihood). These checks run in the
test suite whenever the packages are installed.
Andrich & Luo (2003) and Zwinderman (1995), conditional pairwise estimation; Andrich (1978, 1985) and Pedler (1987), principal-components thresholds; Warm (1989), weighted likelihood; Smith (2002), residual-component dimensionality; Raiche (2005) and Chou & Wang (2010), eigenvalue references; Andrich (2016), multidimensionality magnitude; Yen (1984, 1993) and Christensen, Makransky & Horton (2017), residual correlations; Andrich & Kreiner (2010) and Andrich, Humphry & Marais (2012), response dependence; Waller (1989) and Andrich, Marais & Humphry (2012), tailored analysis of guessing; Hagquist & Andrich (2017), DIF by residual ANOVA; Andrich & Hagquist (2012, 2015), artificial DIF; Maxwell & Delaney (2004), planned contrasts; Linacre (1989), many-facet measurement; Humphry (2005) and Humphry & Andrich (2008), the unit and the extended frame of reference; Bradley & Terry (1952), Luce (1959), and Davidson & Beaver (1977), paired comparisons and order effects; Dittrich, Hatzinger & Katzenbeisser (1998), judge covariates; Kent (1982) and Varin, Reid & Firth (2011), composite likelihood-ratio calibration; Andrich & Marais (2019), the output conventions followed throughout.