is_cran_check <- !isTRUE(as.logical(Sys.getenv("NOT_CRAN", "false"))) knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5, eval = !is_cran_check )
This vignette walks Facets users through the equivalent mfrmr workflow:
preparing data, fitting an RSM/PCM many-facet Rasch-family model with
Facets-compatible defaults, generating the diagnostic and reporting tables that
the canonical Facets output stack provides, and reviewing the output-contract
boundary between the two systems. Bounded GPCM can be fit in mfrmr, but its slope-aware
score semantics are intentionally outside the score-side Facets output-contract
route.
The two stacks share the same psychometric framework but differ in operating model.
Before treating a legacy workflow as covered, inspect the public coverage boundary:
facets_feature_coverage() facets_feature_coverage("not_implemented")
| Concept | Facets (Linacre 2026) | mfrmr |
|---|---|---|
| Input | Specification file plus data file | data.frame in long format |
| Estimation | JMLE by default | JML (legacy default) or MML (recommended for new analyses) |
| Fit-statistic basis | Residuals at JMLE estimates | Residuals at EAP person measures under MML (shrunken toward the mean); refit with method = "JML" for a JMLE-style residual basis |
| Models | Rating-scale, partial-credit, polytomous step models | RSM, PCM, bounded GPCM |
| Output | Tables 0-30 plus graphic files | Returned R objects with summary() and plot() methods |
| Anchoring | D=, A= fields in the specification | anchors and group_anchors arguments to fit_mfrm() |
| Bias / interaction | Table 14 | estimate_bias() and bias_interaction_report() |
| Wright map / variable map | Graphic variable-map output | plot(fit, type = "wright") and plot_wright_unified() |
| Fair average | Table 7 fair-M average | fair_average_table() |
| Reproducibility | Specification file is the manifest | build_mfrm_manifest() plus build_mfrm_replay_script() |
If the goal is to reproduce a Facets-style script with minimal R-side
plumbing, use run_mfrm_facets() (alias mfrmRFacets()):
library(mfrmr) data("ej2021_study1", package = "mfrmr") run <- run_mfrm_facets( data = ej2021_study1, person = "Person", facets = c("Rater", "Criterion"), score = "Score", model = "RSM", method = "JML" ) names(run)
The wrapper returns the same fit_mfrm() and diagnose_mfrm() objects that
a step-by-step pipeline produces, plus the iteration log, fair-average
table, and rating-scale table:
summary(run$fit) head(run$fair_average)
For new analysis scripts, prefer fit_mfrm(method = "MML") directly. MML
integrates over the person distribution under an N(0, 1) prior and exposes
per-person posterior SEs that JML cannot produce.
The mapping below covers the most common Facets specification keywords.
Facets = 3
Models = ?,?,?,R5
Labels =
1, Examinee
1 = P01
...
2, Rater
1 = R1
...
3, Criterion
1 = Content
...
translates to:
fit_mfrm( data = examinee_long, person = "Examinee", facets = c("Rater", "Criterion"), score = "Score", rating_min = 1, rating_max = 5, model = "RSM" )
Models = ?,?,?,R5 becomes model = "RSM" and the R5 rating-scale
declaration becomes rating_min = 1, rating_max = 5. For a partial-credit
specification, pass model = "PCM" and identify the facet that carries the
step thresholds with step_facet = "Rater" (or the appropriate facet
name).
A Facets D = 2, A = block:
D = 2
A = 1, 0.0
2, 0.5
becomes an anchors data frame:
anchors <- data.frame( facet = "Rater", level = c("R1", "R2"), estimate = c(0.0, 0.5), stringsAsFactors = FALSE ) fit <- fit_mfrm(..., anchors = anchors)
review_mfrm_anchors() validates and reports on the anchor block before the
fit runs, surfacing connectivity, overlap, and minimum-sample issues.
Facets Table 14 bias output between Rater and Criterion has a direct equivalent:
diag <- diagnose_mfrm(fit) bias <- estimate_bias(fit, diag, facet_a = "Rater", facet_b = "Criterion") summary(bias)
estimate_all_bias() enumerates every non-person facet pair in one call.
For a shared-logit visual display of persons, facet levels, and step thresholds, use the Wright map route:
plot(fit, type = "wright", preset = "publication", show_ci = TRUE)
plot_wright_unified() is the corresponding explicit helper when the Wright
map is the main figure rather than one panel in a larger visual workflow.
Use draw = FALSE or plot_data(fit, type = "wright") when you need the
underlying coordinates for a custom ggplot2, base-R, or Quarto graphic.
Facets users often compare Infit/Outfit MnSq together with ZStd columns.
In mfrmr, treat MnSq as the primary fit statistic and use the df/ZSTD
columns to explain how the same MnSq values were standardized. The direct
review path is:
diag <- diagnose_mfrm(fit, residual_pca = "none", fit_df_method = "both") fm <- fit_measures_table(fit, diagnostics = diag, facet = "Rater", fit_df_method = "both") fm$facets_table fm$df_sensitive plot(fm, type = "df_sensitivity")
df_sensitivity reports the engine-vs-FACETS-style df comparison row by row;
df_sensitive keeps only rows where the df convention changes the |ZSTD| flag
or materially changes the ZSTD interpretation. The same status taxonomy is
used by facets_fit_review(), so a table-oriented review and an external
FACETS comparison use the same language.
Facets D = ..., G = group-anchor blocks for differential facet
functioning translate to the group_anchors argument and the
analyze_dff() follow-up:
group_anchors <- data.frame( facet = "Criterion", level = "Content", group = c("Native", "Non-native"), estimate = c(0.0, 0.0), stringsAsFactors = FALSE ) fit_g <- fit_mfrm(..., group_anchors = group_anchors) dff <- analyze_dff(fit_g, diag, facet = "Criterion", group = "FirstLanguage", method = "refit")
When migrating an existing study, facets_output_contract_review() checks
whether the package-generated report components satisfy the FACETS-style
output contract encoded in the package:
contract_review <- facets_output_contract_review( fit, diagnostics = diag, branch = "facets" ) summary(contract_review) contract_review$missing_preview contract_review$metric_checks
The resulting object reviews column coverage and package-native metric checks.
It is not a claim that mfrmr has reproduced FACETS estimates numerically.
For external numerical comparison, use an exported FACETS fit table and
facets_fit_review().
When that comparison involves an MML fit, remember that mfrmr evaluates
residual-based fit statistics at shrunken EAP person measures while FACETS
uses JMLE estimates, so MnSq differences can reflect the residual basis
rather than a fit-computation difference; refit with method = "JML"
before attributing such gaps. See facets_fit_df_guide() for this
boundary and for the separate df/ZSTD standardization conventions.
If you already have a FACETS fit table on disk, read it first and then run the fit review. This does not run FACETS; it consumes an exported or otherwise harmonized table.
facets_fit <- read_facets_fit_table( "score.2.txt", facet_map = c("1" = "Person", "2" = "Rater", "3" = "Criterion") ) review <- facets_fit_review( fit, diagnostics = diag, facets_fit = facets_fit, external_zstd_tolerance = 0.05 ) review$df_sensitivity review$df_sensitive review$external_table_quality review$external_comparison plot(review, type = "df_sensitivity")
Use external_comparison for the supplied FACETS table and df_sensitivity
for the engine-vs-FACETS-style df convention check. This separation keeps
external numerical differences distinct from ZSTD differences caused by df
standardization. external_table_quality is the first place to look if the
FACETS export only contains ZStd and T.Count columns, or if duplicate
Facet x Level rows were supplied.
For traceability or downstream tools that expect Facets output files,
facets_output_file_bundle() writes a parallel set of fixed-width or CSV
exports:
files <- facets_output_file_bundle( fit, diagnostics = diag, out_dir = tempdir(), include = c("graph", "score") )
For RSM and PCM the score-side helpers are available. Under bounded GPCM
the score-side bundle is intentionally restricted; see
?gpcm_capability_matrix and the mfrmr-gpcm-scope vignette for the
binding contract.
After a Facets-equivalent fit is in hand, the canonical mfrmr reporting route extends the analysis with:
review_mfrm_anchors() before anchored fitting, and
detect_anchor_drift() / plot_anchor_drift() when common elements define
a cross-form or cross-wave link.diagnose_mfrm(diagnostic_mode = "both") for the strict marginal screen
alongside the residual stack.rating_scale_table(), category_structure_report(), and
category_curves_report() for category-functioning evidence.fair_average_table() when FACETS Table 12-style fair-average review is
needed.plot(fit, type = "wright") or plot_wright_unified() for a variable-map
view of targeting and threshold placement.estimate_bias(), bias_interaction_report(), and
bias_pairwise_report() when FACETS Table 14-style local interaction
screening is substantively relevant.reporting_checklist() for a manuscript-readiness summary.build_apa_outputs() for Method and Results paragraphs and APA tables.build_mfrm_manifest() and build_mfrm_replay_script() for the
reproducibility bundle that Facets specifications cannot produce
out of the box.The mfrmr-workflow vignette covers the full sequence end to end; the
mfrmr-reporting-and-apa vignette focuses on the manuscript surface; the
mfrmr-linking-and-dff vignette covers anchoring, drift, and DFF in
detail.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.