Migrating from Facets to mfrmr

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.

Mental model

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)
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()

A one-shot legacy-compatible call

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.

Translating the specification file

The mapping below covers the most common Facets specification keywords.

Facets and labels

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).

Anchoring

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.

Bias and interaction

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.

Wright map / variable map

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.

Fit df and ZSTD review

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.

Group anchoring and DFF

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")

Reviewing output contracts and fit tables

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().

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.

Producing Facets-style output files

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.

mirror server hosted at Truenetwork, Russian Federation.