# easyRasch2

<!-- badges: start -->
[![CRAN Version](https://www.r-pkg.org/badges/version/easyRasch2)](https://cran.r-project.org/package=easyRasch2)
[![Downloads](https://cranlogs.r-pkg.org/badges/easyRasch2?color=brightgreen)](https://CRAN.R-project.org/package=easyRasch2)
![Downloads Status](https://cranlogs.r-pkg.org/badges/grand-total/easyRasch2)
[![R-CMD-check](https://github.com/pgmj/easyRasch2/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/pgmj/easyRasch2/actions/workflows/R-CMD-check.yaml)
[![Codecov test coverage](https://codecov.io/gh/pgmj/easyRasch2/graph/badge.svg)](https://app.codecov.io/gh/pgmj/easyRasch2)
<a href="https://buymeacoffee.com/pgmj" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
<!-- badges: end -->

`easyRasch2` is an R package for Rasch measurement theory analysis workflows. It
is the successor to [`easyRasch`](https://pgmj.github.io/easyRasch/), offering a
lightweight and consistent structure with proper namespacing and minimal
dependencies.

A central design choice is **simulation-based critical values** for various fit
statistics. Rather than relying on rule-of-thumb cutoffs, most diagnostics
are paired with a parametric-bootstrap function that generates an empirical
null distribution from the fitted Rasch / PCM model and the observed
sample (Johansson, 2025, 2026).

The [Get Started](https://pgmj.github.io/easyRasch2/articles/easyRasch2.html)
link above contains a short introduction. For broader Rasch-analysis tutorials,
see the [vignette](https://pgmj.github.io/raschrvignette/RaschRvign.html) for
the archived sibling package [`easyRasch`](https://pgmj.github.io/easyRasch/).

## Statement of need

A complete Rasch analysis requires many separate procedures: item fit, local
dependence, dimensionality, differential item functioning, reliability,
targeting, and more. In R these are spread across packages with differing data
formats, argument conventions, and output objects, which raises the barrier to
entry and can make analyses hard to reproduce. A further problem is that fit
statistics (item fit MSQ, Yen's $Q_3$ residuals, the first residual-PCA
contrast, CFA fit indices) are usually judged against fixed rule-of-thumb
cutoffs that are known to depend on sample size, number of items and other
factors such as targeting, and the number of response categories.

`easyRasch2` targets applied researchers and students validating rating scales
and tests in health, education, and psychology using modern psychometric
methods. It provides a single, consistently named interface across the whole
workflow with publication-ready output. Its distinguishing feature is that it
replaces rule-of-thumb cutoffs with sample-specific critical values obtained by
parametric bootstrap from the fitted Rasch/PCM model (Johansson, 2025, 2026).
Item fit and local dependence flag on a multiplicity-corrected bootstrap
*p*-value. Several methods, including the polytomous Martin-Löf test with Monte
Carlo *p*-values (Christensen & Kreiner, 2007) and the bootstrap item-restscore
test, are not available in other R packages.

## Installation

Install from CRAN:

```r
install.packages("easyRasch2")
```

Install the development version from GitHub:

```r
# install.packages("remotes") # if needed
remotes::install_github("pgmj/easyRasch2")
```

## Key design principles

- **Estimation**: a single engine across the package — Conditional Maximum
  Likelihood (CML) item parameters via `psychotools` and Warm's Weighted
  Likelihood Estimation (WLE) for person parameters. `eRm` is used for
  Andersen's LR test (`RMdifLR()`); `mirt` (MML) is available as an optional
  engine (`estimator = "MML"` in `RMlocdepQ3()` and `RMitemParameters()`)
  and for the plausible values behind the RMU reliability metric.
- **Inference**: simulation-based cutoffs throughout. Item fit and local
  dependence flag on multiplicity-corrected bootstrap *p*-values whenever the
  full cutoff object is supplied, using Westfall–Young family-wise correction
  (default) or FDR alternatives (Ferreira, 2024; Johansson, 2026). Pass
  `p_value = FALSE` to flag against the simulated interval instead, which
  tests every item or pair at once and so sets a family-wise error rate of
  `1 - width^m` implicitly.
- **Output**: `knitr::kable()` for tables (Quarto-friendly), `ggplot2`
  for figures, and `"dataframe"` output options for downstream use.
  Every caption reports the estimation sample size and missing-data policy.
- **Naming**: Functions use the `RM` prefix (e.g., `RMlocdepQ3()`).

## Functions by domain

### Item fit

- `RMitemInfit()` — conditional infit MSQ, flagged on multiplicity-corrected
  bootstrap *p*-values with family-wise (Westfall–Young) or FDR
  multiple-comparison correction
- `RMitemInfitCutoff()` + `RMitemInfitPlot()` — simulation-based cutoffs and
  plot
- `RMitemInfitMI()` + `RMitemInfitCutoffMI()` — multiple-imputation variants
- `RMitemRestscore()` — item-restscore with Goodman-Kruskal's $\gamma$ (gamma)
- `RMitemRestscoreBoot()` — non-parametric bootstrap of item-restscore fit
- `RMitemICCPlot()` - conditional item characteristic curves

### Local dependence

- `RMlocdepQ3()` + `RMlocdepQ3Cutoff()` + `RMlocdepQ3Plot()` — Yen's $Q_3$
  residual correlations (CML/WLE by default, `estimator = "MML"` optional);
  table and plot share a `$matrix` ($Q_3$ heatmap) / `$pairs` (per-pair
  observed-vs-simulated) structure; per-pair bootstrap *p*-values
- `RMlocdepGamma()` + `RMlocdepGammaCutoff()` + `RMlocdepGammaPlot()` —
  partial-γ local dependence; per-pair bootstrap *p*-values

### Dimensionality / unidimensionality

- `RMdimResidualPCA()` + `RMdimResidualPCACutoff()` — PCA of standardized
  residuals, with simulation-based first-contrast cutoff (Chou & Wang, 2010) and
  an optional bootstrap *p*-value
- `RMdimMartinLof()` + `RMdimMartinLofResiduals()` — Martin-Löf LR test
  (Christensen & Kreiner, 2007), supports polytomous data with Monte Carlo
  *p*-values
- `RMdimCFACutoff()` + `RMdimCFA()` + `RMdimCFAPlot()` — posterior-predictive
  CFA fit-index and per-item loading checks under PCM unidimensionality (via
  `lavaan` WLSMV) with simulation-based cutoffs and optional bootstrap
  *p*-values

### Differential item functioning

- `RMdifLR()` — Andersen's likelihood-ratio test (`eRm::LRtest`)
- `RMdifTree()` — Rasch / partial-credit trees (`psychotree`) with
  Mantel-Haenszel or partial-γ effect sizes per split, optional
  iterative purification, and `stablelearner`-based stability assessment
- `RMdifGamma()` + `RMdifGammaCutoff()` + `RMdifGammaPlot()` — partial-γ
  DIF; optional bootstrap *p*-values calibrated against the simulated Rasch null
- `RMitemICCPlot()` - evaluates DIF across class intervals

### Item category threshold ordering

- `RMitemCatProb()` for classic item probability function trace plots.
- `RMitemHierarchy()` renders a plot sorting items based on difficulty, showing
  item threshold locations and confidence intervals.

### Reliability, targeting, score conversion

- `RMreliability()` + `RMUreliability()` — Cronbach's α, PSI, marginal
  reliability, and Relative Measurement Uncertainty from plausible values
- `RMtargeting()` — Wright-map style person-item targeting plot
- `RMscoreSE()` — raw-score → logit transformation table (WLE / EAP)

### Item & person parameters

- `RMitemParameters()` — item difficulty / threshold locations in long or wide
  format, with optional standard errors and confidence intervals (CML via
  `psychotools`, or MML via `mirt` for sparse data)
- `RMpersonParameters()` — per-respondent person locations (WLE or EAP),
  estimated on each response pattern so partial missingness is handled directly

### Person fit

- `RMpersonFit()` — per-respondent conditional infit / outfit MSQ and the
  standardized log-likelihood $\ell_z$, with resampling-based *p*-values rather
  than unreliable asymptotic nulls (Sinharay, 2016; Müller, 2020)

### Data visualization

- `RMplotTile()` — response-distribution heatmap with optional group faceting
- `RMplotBar()` & `RMplotStackedbar()`


## Example

```r
library(easyRasch2)
data("pcmdat2", package = "eRm")
options(mc.cores = 4)
set.seed(42)

# Conditional item infit with simulation-based cutoffs
simfit <- RMitemInfitCutoff(pcmdat2, iterations = 400)
# Passing the full cutoff object flags on the Westfall-Young corrected
# p-value. The 95% interval is reported alongside as a description.
RMitemInfit(pcmdat2, cutoff = simfit)

# Test of unidimensionality via posterior-predictive ordinal CFA
cfa_sim <- RMdimCFACutoff(pcmdat2, iterations = 250)   # simulated reference
tabs <- RMdimCFA(pcmdat2, cutoff = cfa_sim)            # observed vs expected
tabs$fit                                                # fit-index table
tabs$loadings                                           # per-item loading table
plots <- RMdimCFAPlot(cfa_sim, data = pcmdat2)          # list of 2 ggplots
plots$loadings                                          # observed vs expected loadings
plots$fit                                               # fit-index distributions

# DIF analysis via Andersen's LR test
grp <- factor(sample(c("A", "B"), nrow(pcmdat2), replace = TRUE))
RMdifLR(pcmdat2, dif_var = grp)

# Rasch-tree DIF with effect-size classification on continuous +
# categorical covariates simultaneously
covs <- data.frame(
  group = grp,
  band  = sample(c("low", "high"), nrow(pcmdat2), replace = TRUE)
)
RMdifTree(pcmdat2, covariates = covs)
```

## References

- Bjorner, J. B., Kreiner, S., Ware, J. E., Damsgaard, M. T., & Bech, P. (1998).
  Differential item functioning in the Danish translation of the SF-36.
  *Journal of Clinical Epidemiology, 51*(11), 1189–1202.
  <https://doi.org/10.1016/S0895-4356(98)00111-5>
- Chou, Y.-T., & Wang, W.-C. (2010). Checking dimensionality in item-response
  models with principal component analysis on standardized residuals.
  *Educational and Psychological Measurement, 70*(5), 717–731.
  <https://doi.org/10.1177/0013164410379322>
- Christensen, K. B., & Kreiner, S. (2007). A Monte Carlo approach to
  unidimensionality testing in polytomous Rasch models.
  *Applied Psychological Measurement, 31*(1), 20–30.
  <https://doi.org/10.1177/0146621605286204>
- Christensen, K. B., Makransky, G., & Horton, M. (2017). Critical values for
  Yen's Q3: Identification of local dependence in the Rasch model using
  residual correlations. *Applied Psychological Measurement, 41*(3), 178–194.
  <https://doi.org/10.1177/0146621616677520>
- Ferreira, J. A. (2024). Methods of testing a 'small' or 'moderate' number of
  hypotheses simultaneously: An account focusing on the control of the
  probability of at least one incorrect rejection and of the false discovery
  rate. *Journal of Statistical Theory and Practice, 19*(6).
  <https://doi.org/10.1007/s42519-024-00412-4>
- Henninger, M., Debelak, R., & Strobl, C. (2023). A new stopping criterion
  for Rasch trees based on the Mantel-Haenszel effect size measure for DIF.
  *Educational and Psychological Measurement, 83*, 181–212.
  <https://doi.org/10.1177/00131644221077135>
- Henninger, M., Radek, J., Debelak, R., & Strobl, C. (2025). Partial credit
  trees meet the partial gamma coefficient for quantifying DIF and DSF in
  polytomous items. *Behaviormetrika, 52*, 221–257.
  <https://doi.org/10.1007/s41237-024-00252-3>
- Johansson, M. (2025). Detecting item misfit in Rasch models.
  *Educational Methods & Psychometrics, 3*(18).
  <https://doi.org/10.61186/emp.2025.5>
- Johansson, M. (2026). Simulation-based cutoffs for conditional item fit in
  Rasch models: Iterations, multiplicity correction, and decision stability.
  *PsyArXiv*. <https://doi.org/10.31234/osf.io/7pqz4_v2>
- Kreiner, S. (2011). A note on item-restscore association in Rasch models.
  *Applied Psychological Measurement, 35*(7), 557–561.
  <https://doi.org/10.1177/0146621611410227>
- Müller, M. (2020). Item fit statistics for Rasch analysis: Can we trust them?
  *Journal of Statistical Distributions and Applications, 7*(1), 5.
  <https://doi.org/10.1186/s40488-020-00108-7>
- Philipp, M., Rusch, T., Hornik, K., & Strobl, C. (2018). Measuring the
  stability of results from supervised statistical learning. *Journal of
  Computational and Graphical Statistics, 27*, 685–700.
  <https://doi.org/10.1080/10618600.2018.1473779>
- Rosseel, Y. (2012). lavaan: An R package for structural equation modeling.
  *Journal of Statistical Software, 48*(2), 1–36.
  <https://doi.org/10.18637/jss.v048.i02>
- Sinharay, S. (2016). Assessment of person fit using resampling-based
  approaches. *Journal of Educational Measurement, 53*(1), 63–85.
  <https://doi.org/10.1111/jedm.12101>
- Strobl, C., Kopf, J., & Zeileis, A. (2015). Rasch trees: A new method for
  detecting DIF in the Rasch model. *Psychometrika, 80*, 289–316.
  <https://doi.org/10.1007/s11336-013-9388-3>


## Credits

As mentioned earlier, this is based on my `easyRasch` package, and I am using
Claude Opus/Fable to rewrite functions to this more properly formatted package.
While it uses my earlier code, most of the code in this package is produced by
the LLM and tested and bug fixed by me.

`RMdifTree()` adapts MIT-licensed code from Mirka Henninger and Jan Radek's
[`raschtreeMH`](https://github.com/mirka-henninger/raschtreeMH) and
[`effecttree`](https://github.com/mirka-henninger/effecttree) packages for
the effect-size and ETS-classification algorithms.

[Magnus Johansson](https://ki.se/en/people/magnus-johansson-3) is a licensed
psychologist with a PhD in behavior analysis. He works as a research specialist
focused on psychometrics and statistics at Karolinska Institutet, Department of
Clinical Neuroscience, Center for Psychiatry Research.

- ORCID: [0000-0003-1669-592X](https://orcid.org/0000-0003-1669-592X)
- Bluesky: [@pgmj.bsky.social](https://bsky.app/profile/pgmj.bsky.social) 

## License

GPL (>= 3)
