--- title: "Migrating from FACETS to mfrmr" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Migrating from FACETS to mfrmr} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} 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 closest `mfrmr` workflow: preparing data, fitting an `RSM`/`PCM` many-facet Rasch-family model with FACETS-oriented settings, generating related diagnostic and reporting tables, 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 software reference target for this migration boundary is FACETS 64-bit 4.5.1 (July 2026). A cited manual may retain its published 4.5.0 edition; software version and documentation edition are recorded separately. The coverage described here is not an external numerical-parity result. ## 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: ```r facets_feature_coverage() facets_feature_coverage("not_implemented") ``` This matrix describes the availability of package-native output surfaces. `implemented` does not by itself mean that the two programs use the same estimand, conditioning, extreme-score rule, degrees of freedom, or numerical contract. | Concept | FACETS (Linacre 2026) | mfrmr | |---|---|---| | Input | Specification file plus data file | `data.frame` in long format | | Estimation | JMLE by default | `MML` by default; `JML` is the closest estimation route for a JMLE-oriented comparison | | 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 | Multiple model statements, rating scales, partial credit, and other response families can coexist | One response-model family per fit: `RSM`, `PCM`, or bounded `GPCM` | | Output | Tables 0-30 plus graphic files | Returned R objects with `summary()` and `plot()` methods | | Anchoring | Element/group anchors, rating-scale calibration, and reusable starting values | Element and group anchors; no general threshold/scale anchors or fixed-calibration starting-value bundle | | Repeated cells | Multiple observations may be represented within a design cell | Exact Person-by-facet duplicates are retained but force Data review; distinguish legitimate repeats with an event/occasion facet | | 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, input data, FACETS version, and recorded run environment/settings | `build_mfrm_manifest()` plus `build_mfrm_replay_script()` | ## A one-shot legacy-compatible call If the goal is to translate a FACETS-style script with minimal R-side plumbing, use `run_mfrm_facets()` (alias `mfrmRFacets()`): ```{r facets-mode} library(mfrmr) data("mfrmr_example_operational", package = "mfrmr") run <- run_mfrm_facets( data = mfrmr_example_operational, 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: ```{r facets-mode-summary} jml_status <- summary(run$fit, profile = "fit", detail = "brief") jml_status$overview[, c( "Model", "Method", "Converged", "InferenceReady", "ConvergenceSeverity" )] jml_status$readiness head(run$fair_average) ``` `method = "JML"` is shown here for a JMLE-oriented migration comparison. Do not infer readiness from `Converged` alone: require `InferenceReady = TRUE` for the numerical gate and review the terminal-gradient guidance when severity is `"review"` or `"fail"`. Numerical readiness does not override a Data, Design, or Stability hold; use the readiness table before interpreting or reporting the fit. 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: ```r 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: ```r 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 For FACETS Table 14 bias output between Rater and Criterion, the closest mfrmr screening route is: ```r 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, first create the FACETS-organized summary and retain its result object: ```r review <- summary(fit, profile = "facets", detail = "brief") res <- review$results # Primary final-scale figure: all locations and available facet uncertainty. plot(res, type = "wright", renderer = "native", show_ci = TRUE, top_n = Inf, preset = "publication") ``` `plot_wright_unified()` is the corresponding explicit helper when the Wright map is the main figure. For readers who expect the FACETS Table 6-style asterisk ruler and horizontal, rubric-labelled category transitions, define one label for every retained original score: ```r rubric_labels <- setNames( your_rubric_labels, fit$prep$score_map$OriginalScore ) plot(res, type = "wright", renderer = "facets", show_ci = FALSE, category_labels = rubric_labels, preset = "publication") ``` `show_ci = FALSE` is the closest FACETS-style visual grammar. Setting `show_ci = TRUE` deliberately creates a hybrid display: the ruler is FACETS-style, but the intervals are mfrmr uncertainty estimates. Neither renderer implies that FACETS performed the estimation or that the two programs are numerically equivalent. For the Bond-and-Fox-style follow-up requested by many FACETS users, put Infit on the horizontal axis and the measure on the vertical axis. Person rows remain opt-in: ```r plot(res, type = "fit_pathway", fit_stat = "Infit", include_person = TRUE, top_n_person = 12, person_labels = "none", facet_labels = "flagged") ``` 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: ```r 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: ```r 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: ```r 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. ```r 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: ```r 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 documented limitation. ## Recommended next steps After a FACETS-oriented package-native fit is in hand, the recommended mfrmr reporting workflow 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 alongside the FACETS-style handoff out of the box. The `mfrmr-workflow` vignette covers the full sequence end to end; the `mfrmr-reporting-and-apa` vignette focuses on manuscript preparation; the `mfrmr-linking-and-dff` vignette covers anchoring, drift, and DFF in detail.