Documentation accuracy pass plus research-grounded visualization and GPCM bias-inference refinements. Documentation, citations, and band attributions are corrected against primary sources, with mathematical screening-SE corrections, Snijders-corrected person-fit reporting where the assumptions are met, and clearer plot data for review.
This release keeps the 0.1.6 defaults, but it is not only an
infrastructure polish release. Public review helpers have been
consolidated on the *_review* names documented below, and
the former *_audit* public spellings, S3 compatibility
classes, and duplicate top-level fields have been removed as a
deliberate breaking cleanup.
For most users, the main changes in 0.2.0 are:
mfrmr is
positioned as a package-native MFRM workflow with FACETS-style tables,
review helpers, and migration routes, not as a promise to numerically
reproduce every FACETS estimate.draw = FALSE data, long-form
plot tables, annotations, and style metadata so users can rebuild
figures in ggplot2, plotly, Quarto, or other reporting workflows.Breaking changes in 0.2.0 are intentional and concentrated around
public naming clarity: former exported *_audit* helper
names and their compatibility S3 classes were removed in favour of the
canonical *_review* surface. Model defaults from 0.1.6 are
retained.
The detailed notes below are organized as follows:
facets_positioning_guide() makes the package boundary
explicit for reports and migration notes. mfrmr is not
presented as a FACETS numerical clone: estimates remain package-native
unless external FACETS output is supplied for comparison, while
FACETS-style wrappers, coverage tables, and output files serve
transition, handoff, and report-organization purposes.reporting_checklist() and
summary(reporting_checklist(...)) now carry a
facets_positioning table so Quarto, appendix, and handoff
workflows can quote the same boundary language used by
facets_positioning_guide().
build_summary_table_bundle() includes this table as
facets_relationship_wording.facets_output_contract_review() is now the sole public
helper for FACETS-style output-contract review. The returned bundle
class is mfrm_facets_contract_review; the helper checks
package output columns and derived metric consistency against the
FACETS-style contract, and does not claim numerical FACETS equivalence.
Public result components use column_review and
metric_checks, not older bookkeeping labels.mfrmr_output_guide("facets") now explicitly routes FACETS
users to Table 14-style bias/interactions via
estimate_bias(), bias_interaction_report(),
bias_pairwise_report(), and
plot_bias_interaction(), and to variable-map review via
plot(fit, type = "wright"),
plot_wright_unified(), and
plot_data(type = "wright").mfrmr_output_guide("facets") now also exposes direct/group
anchor routes through review_mfrm_anchors(),
make_anchor_table(), and
fit_mfrm(anchors = ..., group_anchors = ...), drift/linking
review through detect_anchor_drift() and
plot_anchor_drift(), and FACETS-style category and
fair-average routes through rating_scale_table(),
category_structure_report(),
category_curves_report(), and
fair_average_table().write_mfrm_residual_file() writes observation-level
observed, expected, residual, standardized residual, score-information,
and optional category probability columns to CSV/TSV. New
write_mfrm_subset_file() writes connected-subset summaries
plus node-membership files for external linking review, without forcing
users through the legacy graph/score output bundle.category_curves_report() now includes a
category_information table and
plot(..., type = "category_information"). Category
contributions use a^2 P_k(theta) (k - E[X | theta])^2 and
sum to the total curve information at each theta value; for PCM/RSM this
reduces to the unit-slope form.category_curves_report() now also includes
cumulative_probabilities and
cumulative_boundaries, with
plot(..., type = "cumulative") for the
FACETS/Winsteps-style accumulated category-probability view. Both
P(X <= k) and flipped P(X >= k)
directions are returned; boundary rows report approximate theta values
where P(X <= k) = .5, with crossing status columns so
out-of-range or multiple-crossing boundaries are not
over-interpreted.plot(category_curves_report(...)) now defaults to an
overview panel that shows category probabilities, cumulative
probabilities, total information, and category-specific information
together. Existing focused views remain available through
type = "ogive", "ccc",
"cumulative", "information", and
"category_information". The plot now also supports
preset = "monochrome" with line-type separation and
explicit cumulative .5 boundary-line control through
boundary_status = "in_range", "all", or
"none".
plot_data(..., component = "plot_long") returns a
ggplot2/plotly-friendly long table spanning ogive, category-probability,
cumulative-probability, total-information, and
category-specific-information series, with curve_style
carrying the resolved color/line-type mapping.fit_mfrm() no longer emits an informational message for
routine observed-score range inference by default. The same provenance
is retained in fit$prep,
summary(fit)$settings_overview, and
describe_mfrm_data() so users can still tell whether
rating_min / rating_max were declared or
inferred. Set
options(mfrmr.show_inferred_rating_range = TRUE) to restore
the one-time message during interactive checks.prepare_mfrm_data() now stores row retention and
preparation notes in fit$prep$row_retention and
fit$prep$preparation_notes. Row drops, whitespace trimming,
duplicate person-by-facet cells, and single-level facets are therefore
available to summary(fit) and
summary(describe_mfrm_data(...)) instead of existing only
as transient console messages. Routine row-drop, trim, and
single-level-facet messages are quiet by default; set
options(mfrmr.show_preparation_messages = TRUE) to show
them during interactive checks.plot(fit, type = "pathway", draw = FALSE) now returns
R-friendly pathway_long and
pathway_annotations tables alongside
fit_measures, fit_status,
curve_fit_status, and fit_measure_status. This
lets users rebuild FACETS-style pathway maps in ggplot2, plotly, or
Quarto while retaining the same underfit/overfit labels used by
fit_measures_table().plot_bias_interaction(..., draw = FALSE)
now exposes plot_long, plot_annotations,
flag_summary, and plot_settings across
scatter, ranked, heatmap, and facet-profile views.
compute_information() now stores
conditional_sem, information_long, and a
precision/SEM summary, and plot_information(type = "sem") /
"csem" are supported aliases for the conditional
standard-error-of-measurement curve.plot(category_curves_report(...), type = "category_probability")
and type = "conditional_probability" now route to the same
category-probability curves as type = "ccc", matching
FACETS/Winsteps terminology without changing the underlying probability
data. Draw-free plot data now include plot_annotations and
curve_summary alongside plot_long,
curve_style, boundary_lines, and
plot_settings.facets_feature_coverage() gives a public, release-scoped
map from the FACETS 64-bit output index to current mfrmr
routes, separating implemented, partial,
not_implemented, and not_targeted surfaces.
This makes unsupported FACETS-specific outputs such as Winsteps
control-file export, raw FACETS report parsing, and arbitrary Web/Excel
menu plots explicit rather than implicit.mfrm_d_study() extends mfrm_generalizability()
from observed variance-component review to planned design comparison. It
reports projected G and Phi under alternative
numbers of raters, criteria, or other random measurement facets, and
exposes residual-scaling sensitivity assumptions so simplified G-study
residuals are not silently over-interpreted.
plot(mfrm_d_study(...), draw = FALSE) and
plot_data() expose reusable coefficient/error-variance
series for custom design-planning visuals.
plot.mfrm_d_study() now supports line plots with
group_var, ggplot2-like panel_by /
panel_grid small multiples, and two-axis
heatmap / contour views for rater-by-task
design grids. An optional surface3d view is available for
exploration, while heatmap/contour remain the recommended reporting
displays. Plot data labels these coefficients as
MetricFamily = "G-theory" so they are not confused with IRT
or classical-test-theory reliability coefficients.subset_connectivity_report() now includes reusable
node/edge tables, and plot(..., type = "network") provides
an igraph-based co-observation graph when igraph is
installed. With draw = FALSE, the returned plot data
supports custom R visualization without depending on the base plotting
default.mfrm_network_analysis() treats the person/facet-level
observation design as an undirected weighted co-observation graph and
returns graph-level connectedness, node degree and strength,
betweenness/closeness, articulation points, bridge edges, and
facet-level vulnerability summaries. These are explicitly framed as
design linking diagnostics, not as person ability, rater quality, or
model-fit statistics. plot(..., type = "centrality"),
plot(..., type = "facet_summary"), and
plot(..., type = "network") provide immediate visual checks
with draw-free plot data.rater_network_analysis() adds a Lamprianou-style pairwise
rater network route separate from design connectedness. It supports
agreement, disagreement, and directed severity-direction networks,
returns rater-level in/out strength, betweenness/closeness, a finite
network severity index, retained edge tables, and all pairwise metrics
used before thresholding. The help page states that these indices are
descriptive network diagnostics rather than Rasch logit estimates or
formal fit statistics. plot(..., type = "network"),
"severity", "centrality", and
"matrix" provide immediate visual checks and reusable plot
data.rater_halo_network_analysis() reshapes observed ratings
into rater-by-criterion nodes, computes Spearman/Pearson/Kendall
node-pair correlations, labels same-rater cross-criterion edges as
halo, and contrasts halo-edge weights with non-halo edges.
The default Bonferroni-adjusted edge filter follows the conservative
network-screening strategy used in Lamprianou’s halo example. The
returned bundle includes summary,
node_metrics, edge_metrics,
pair_metrics, halo_summary_by_rater, and
caveats that the Welch halo/non-halo comparison is descriptive because
network edges are dependent. halo_summary_by_rater now
includes ReviewStatus and ReviewReason based
on same-rater cross-criterion mean weight, incident non-halo comparison
weight, and retained halo-edge count, with labels framed as screening
priorities rather than causal halo diagnoses.
plot(..., type = "edge_distribution"),
"halo_summary", "matrix", and
"network" provide immediate visual review and draw-free
plot data.fit_measures_table() gives a direct FACETS-style
fit-measure view for raters, criteria, or other facet elements. It
returns both R-friendly columns and a facets_table with
labels such as Infit MnSq, Outfit ZStd,
Fit Status, and Review Reason;
underfit, overfit, and mixed
subsets are included for immediate review.fit_measures_table() now returns
profile_summary_by_facet and
profile_summary_overall, reporting underfit, overfit,
mixed, and any-flag rates under multiple literature-based MnSq bands
from Linacre, Bond & Fox, and Wright & Linacre. The main table
still uses the active review band, while threshold_profiles
controls whether literature, active, all, or no profile summaries are
returned.fit_measures_table(ci_level = ...) now adds approximate
measure confidence intervals to both the R-friendly table and
facets_table.
plot(fit_measures, type = "measure_ci", ci_level = ..., preset = "monochrome")
draws an interval plot with the requested confidence level.facets_fit_df_guide() documents the engine-vs-FACETS-style
degrees-of-freedom distinction and the MnSq-to-ZSTD transformation
workflow. fit_measures_table(fit_df_method = "both") now
exposes primary df plus FACETS-style companion df/ZSTD columns, and adds
df_sensitivity, df_sensitive, and
df_sensitivity_summary so users can identify rows whose
|ZSTD| flag status or interpretation is convention-sensitive. The
df-sensitivity screen exposes explicit df_zstd_tolerance,
df_zstd_large_shift, and df_ratio_tolerance
settings so FACETS-style reviews can be reproduced under stricter or
more permissive rules.
plot(fit_measures, type = "df_sensitivity") visualizes the
largest engine-vs-FACETS-style ZSTD shifts.
facets_fit_review() now uses the same row-level
df-sensitivity engine and returns df_sensitivity,
df_sensitive, and df_sensitivity_summary; the
former internal_comparison component has been removed to
keep the public output vocabulary consistent. External FACETS ZSTD
tolerance is now named external_zstd_tolerance, separating
external-table comparison from engine-vs-FACETS-style df sensitivity.
The same plot(..., type = "df_sensitivity") route is
available for facets_fit_review() bundles.data_quality_report() now keeps the full fitted score
support in category_counts, adds
score_support_review, and surfaces caveats for
zero-frequency categories. Intermediate gaps such as a declared 1-5
scale with observed 1, 2, 4, 5 are flagged either as
retained zero-count categories (keep_original = TRUE) or as
original-label gaps hidden by internal recoding
(keep_original = FALSE).
plot(data_quality_report(...), type = "score_support")
highlights those categories, with preset = "monochrome"
available.data_quality_report() now adds
category_usage_by_facet and
category_usage_summary, covering every fitted facet level
crossed with the retained score support. This flags local zero and
sparse category use, such as a rater who never uses the middle category
even when the category appears elsewhere in the data.
plot(data_quality_report(...), type = "facet_category_usage")
provides a quick view of affected facet levels.plot(data_quality_report(...), type = "dashboard") now
combines row review, score-support category use, facet-level
category-use issues, and missing/invalid row counts in one base-R view.
With draw = FALSE, the returned plot data contains all four
panel tables for report handoff.data_quality_report() now includes
quality_flags, a prioritized QC table that summarizes row
exclusions, unknown design levels, score-support gaps, facet-level
category-use cautions, and restricted facet response patterns with
counts and next actions. summary(data_quality_report(...))
previews this table when any priority flag is present.data_quality_report() now adds
facet_response_patterns, which flags facet levels that use
only one score category, assign one category to at least the configured
dominant-category cutoff, or use only boundary categories. This catches
cases such as a rater assigning score 1 to all responses on a 1-5
scale.data_quality_report() now adds
quality_overview, a compact area-level status table for
rows, score support, facet-level category use, facet response patterns,
and design matching. plot(..., type = "quality_flags")
summarizes priority QC flags by area,
plot(..., type = "facet_response_patterns") shows dominant
local category use by facet level, and
plot(..., type = "score_map") shows original-to-internal
score mappings when labels have been recoded.mfrmr_output_guide("facets"),
mfrmr_output_guide("conquest"), and
mfrmr_output_guide("r") now give focused starting points
for users arriving from FACETS, ConQuest, or R-first visualization
workflows. New plot_data() extracts the full reusable
plot-data list, or one named component, from any
mfrm_plot_data object or mfrmr plot helper that supports
draw = FALSE. New plot_data_components() lists
each reusable plot-data component, its shape, role, accessor call, and
custom-graphics notes so users can discover plot_long,
annotation, settings, style, and review tables without reading list
internals.preset = "monochrome". Color remains the default
("standard"), while monochrome supports print-oriented
figures and color-independent review.GPCM scope consistently: direct data generation, parameter
recovery, fair averages, bias screening, summary-table bundles, and
appendix export are available within documented caveats, while scenario
planning/forecasting, FACETS-style score-side exports, APA writing, QC
pass/fail pipelines, linking synthesis, and fit-based export bundles
remain outside the validated route.plot_payloads only
where users need to access them.RSM / PCM reference route from the broader
ordered-response model surface. Stale “Rasch-only” and
“legacy-compatible” labels were narrowed where they described helpers
that now also serve bounded GPCM or direct summary-table
workflows.RSM,
PCM, or bounded GPCM, including report wording
templates and a warning that better GPCM fit is sensitivity
evidence rather than an automatic operational-scoring decision. A
documentation terminology regression test now guards against
reintroducing the stale Rasch-only phrasing removed in this pass.build_model_choice_review() bundles
compare_mfrm(), model-role guidance, downstream route
availability, report wording templates, the bounded-GPCM
support matrix, and an optional build_weighting_review()
run so users can review RSM / PCM /
bounded-GPCM candidates from the actual fitted
objects.review_mfrm_anchors(),
precision_review_report(),
review_conquest_overlap(),
facet_small_sample_review(), and
build_weighting_review() are now the only exported
review-name implementations. The former public *_audit*
function spellings, their S3 compatibility classes, and old public
component names such as orientation_audit,
nesting_audit, hierarchical_audit, and
shrinkage_audit have been removed or renamed rather than
shown as user-facing migration artifacts.compatibility_alias_table() now lists only retained
compatibility names that remain part of the public package surface, such
as mfrmRFacets, analyze_dif,
JMLE, and long-standing output column aliases. It no longer
advertises removed review-name migration artifacts.anchor_review labels in user-facing
source metadata, model-choice raw objects and summaries expose only
weighting_review_status / weighting_review,
and diagnostics summary-table bundles export
precision_review without the old duplicate table. Bias
reports now expose orientation_review; model-comparison
output uses nesting_review; and reproducibility manifests
use hierarchical_review and shrinkage_review.
DFF subgroup refit rows now record anchor-review notes in
LinkingReview.anchor_review() and precision_review() helpers
provide a stable route to package-native review components. They
intentionally read only canonical *_review fields.reference_case_review() is now the canonical package-native
report-completeness helper, and reporting-checklist / cheatsheet wording
now uses review labels for hierarchical and complete-case follow-up
items.audit as a user-facing
package concept. Data-quality row-status output uses
row_review; prediction provenance uses
row_review / population_review; bias,
model-comparison, and manifest components use *_review
names; and ordinary user-facing guidance uses review/check/traceability
terminology.mfrmr_output_guide() gives users a compact
purpose-to-helper map for choosing among *_table,
*_report, *_review, *_bundle,
export_*, and compatibility routes. The compatibility guide
now also states that old *_audit helper and component names
are not part of the 0.2.0 public surface.RSM, PCM, and bounded GPCM now
optimize step/threshold profiles with the correct sum-to-zero degrees of
freedom (steps - 1 per profile). Earlier pre-release
implementations centered the step vector after optimization but still
left the centered-away null direction in the optimizer, AIC/BIC
parameter count, and Hessian. The point-estimate scale is unchanged; the
likelihood parameter count and observed-information basis are now
aligned with the stated identification constraint.diagnose_mfrm() now reuses one
observed-information covariance for non-person facet SEs and exposes the
same covariance basis for step/threshold and bounded-GPCM
slope uncertainty in diagnostics$parameter_uncertainty.
Step rows get SE, normal CI_Lower /
CI_Upper, and covariance status metadata. GPCM slope rows
get log-slope SEs plus positive-scale delta-method SEs and log-normal
confidence limits. fit_mfrm(..., attach_diagnostics = TRUE)
attaches those structural SE columns to fit$steps and
fit$slopes when the MML Hessian is available.diagnose_mfrm()$measures now records
CI_Level = 0.95 and
CI_Method = "Normal approximation" alongside
CI_Lower / CI_Upper. The interval calculation
uses qnorm(0.975) rather than a rounded multiplier, while
row-level CIEligible, CIBasis, and
CIUse continue to distinguish primary reporting intervals
from review or screening approximations.compare_mfrm() now reports WeightedN,
ICSampleSize, and ICSampleSizeBasis in its
comparison table. This makes the BIC penalty basis explicit: ordinary
fits use row count, while weighted fits use the sum of weights already
used by the fitted model summary.analyze_residual_pca() now supports
parallel = TRUE for residual-permutation parallel analysis.
The null comparison permutes standardized residuals within residual
columns, preserving column distributions and missingness while breaking
residual association. PCA tables gain ParallelMean,
ParallelCutoff, ExcessOverParallelCutoff, and
ExceedsParallelCutoff, and parallel_status
records availability and successful permutation counts.
plot_residual_pca() adds parallel_scree and
parallel_excess views. This is reported as exploratory
follow-up evidence for dimensionality review, not as a standalone proof
of unidimensionality or multidimensionality.fair_average_table(fair_se = TRUE) now adds opt-in
structural delta-method SE and CI columns for bounded GPCM
fair averages when the MML observed-information covariance is available.
The original SE / Model S.E. /
Real S.E. columns keep their measure-SE meaning;
fair-average uncertainty is exposed in distinct columns such as
Fair(M) S.E., AdjustedAverageSE, and
AdjustedAverageCI_Lower /
AdjustedAverageCI_Upper. Person rows remain unavailable
because MML person EAP estimates are conditioned on rather than included
in the structural Hessian. summary(fair_average_table(...))
and its print method now surface whether fair-average SEs were
requested, how many rows are available, and the resulting status mix.
plot_fair_average(show_ci = TRUE) uses these columns
automatically for bounded-GPCM fit objects.expected_score_table() route now uses the same
response-probability bundle as diagnostics and category-count
calculations, so bounded GPCM expected scores respect the
fitted slope parameters instead of falling through to the PCM kernel.
Fair-average documentation now also states that non-slope-facet rows use
an identification-based reporting convention, not a FACETS score-side
equivalence claim.GPCM object cannot silently become a
PCM-style calculation in fair-average internals. The internal
iteration-state replay helper also now has an explicit GPCM
probability-kernel branch, avoiding a latent PCM fallback if that route
is later moved inside the supported GPCM boundary.RSM special case as common-threshold
PCM. Under identical common thresholds, RSM
and PCM must agree for category probabilities, unweighted
and weighted log-likelihoods, response-bundle diagnostics, generated
simulation data, and reconstructed simulation probabilities. The public
compare_mfrm(..., nested = TRUE) path now also has a
regression check that the reported LRT degrees of freedom equal the
identified RSM-to-PCM step-structure difference.compare_mfrm(..., nested = TRUE) now records
comparison_basis$lrt_status and
comparison_basis$lrt_reason. Non-finite log-likelihoods,
equal parameter counts, unsupported nesting, or negative
likelihood-ratio statistics no longer fail silently or imply a
model-choice conclusion; the LRT is withheld and the print/summary path
states why it was not reported.fit_mfrm(). Recovery summaries compare identified
log slopes without an additional mean-alignment step, so absolute
slope-scale bias is not hidden by the recovery table.GPCM has unit slopes. With the same simulation
specification, seed, and step-facet thresholds, the generated visible
data and reconstructed category-probability matrix must match
PCM to numerical tolerance. This guards the intended
mathematical reduction without implying that a freely estimated
GPCM fit should equal a PCM fit.GPCM reduction is now also tested at the
response-probability bundle layer. The bundle used by expected scores,
variance-based fit diagnostics, and information calculations must match
the PCM bundle for probabilities, expected category scores,
score variances, fourth central moments, and score information. A
companion sensitivity check verifies that non-unit slopes move the same
quantities away from the PCM values, so the GPCM path is
neither a hidden PCM fallback nor an unconstrained divergence.GPCM response bundle, RSM /
PCM / bounded-GPCM category-curve reports,
draw-free CCC/pathway plot data, and compute_information().
The checks also require facet-level information-contribution curves to
aggregate back to the total information curve. This guards user-visible
visualization and reporting tables against drifting away from the
probability kernels.fit_measures_table() preserves the
documented df/ZSTD formulas, confidence-interval formulas, active
fit-status labels, threshold-profile counts and rates, df-sensitivity
status taxonomy, and draw-free measure_ci /
df_sensitivity plot data. This pins the FACETS-style
reporting surface to the same row-level calculations users see in the
returned tables.data_quality_report() summary
counts are recomputable from returned detail tables, that
quality_flags and quality_overview summarize
the same QC evidence, and that draw-free quality_flags,
facet_category_usage, facet_response_patterns,
and score_map plot data preserve score-support gaps,
facet-level category-use issues, restricted rater response patterns, and
original-label gaps hidden by score recoding.estimate_bias()): the
conditional plug-in SE for the additive bias shift now uses the correct
GPCM information (_i a_i^2 (X_i)). The previous pre-release
implementation optimized the point estimate with the slope-aware GPCM
kernel but used the PCM information (_i (X_i)) for S.E. /
t / Prob.. The review label remains
"screening".diagnose_mfrm() now accepts
fit_df_method = "engine", "facets", or
"both". The default keeps the existing package-native df
convention (DF_Infit = sum(Var * Weight),
DF_Outfit = sum(Weight)). The FACETS path adds the
Wright-Masters/FACETS fourth-moment df approximation
(df = 2 / q^2) and caps FACETS-style ZSTD values at +/-9.
Use fit_df_method = "both" when comparing mfrmr fit flags
with FACETS output: it preserves the engine ZSTD columns and adds
DF_Infit_FACETS, DF_Outfit_FACETS,
InfitZSTD_FACETS, and OutfitZSTD_FACETS.facets_fit_review() separates engine-level
fit-standardization differences from optional external FACETS table
comparisons. The engine-vs-FACETS review compares engine and
FACETS-style df/ZSTD values row-by-row and flags cases where the df
convention changes the usual |ZSTD| >= 2 screen. When a
FACETS-like table is supplied, the external review matches rows by
Facet / Level (or person labels for
person-only tables) and classifies differences as same,
rounding, df_or_whexact_difference,
mnsq_or_measure_difference, or needs_review.
This makes FACETS comparisons reproducible without treating mfrmr’s
package-native df convention as an error.read_facets_fit_table() /
import_facets_fit_table() reads existing FACETS output into
the Facet / Level / Infit /
Outfit / ZSTD / df schema expected by
facets_fit_review(). It supports already harmonized
CSV/TSV-style tables, partial FACETS extracts with ZSTD and
TCount but no MnSq/df columns, and FACETS
score.N.txt files, including fixed-field score files using
the FACETS manual column positions, with an optional
facet_map for assigning score-file numbers to user-facing
facet names. facets_fit_review() now returns
external_table_quality so users can see duplicate
Facet x Level rows and whether MnSq, ZSTD, df,
and count columns were available in the supplied external table.plot_fair_average(show_ci = TRUE) no longer fabricates CIs
from measure-level SEs for bounded GPCM fits. It now uses
the opt-in structural fair-average SE columns when a fit object is
supplied and records an unavailable-CI note for precomputed fair-average
bundles that lack those columns.estimate_bias() now adds conditional profile-likelihood
columns for bounded GPCM rows: LR ChiSq,
LR d.f., LR Prob.,
Profile CI Lower, Profile CI Upper,
Profile CI Level, and Profile CI Status. These
compare the fitted additive bias shift with zero while holding theta,
steps, slopes, and other facet estimates fixed. They strengthen the GPCM
screening evidence without turning it into standalone confirmatory
fairness inference. summary(estimate_bias(...)) now
surfaces the profile-LR screen-positive count and carries these columns
through the top-row review table when they are available.category_curves_report() now carries per-curve
ScoreVariance, Slope, and
Information columns. For GPCM,
Information is computed as (a^2 (X )), matching the
Muraki/Samejima polytomous information identity; for RSM /
PCM, this reduces to the usual score variance.
plot(category_curves_report(fit), type = "information")
returns the corresponding curve-level information plot data.plot_bias_interaction(plot = "heatmap", draw = FALSE) now
returns heatmap_cells, heatmap_matrix,
flag/count matrices, interpretation guidance, and reference notes. The
display is documented as a FACETS Table 13-style screening follow-up,
not confirmatory evidence.evaluate_mfrm_recovery() adds a
dedicated parameter-recovery simulation route. It repeatedly simulates
from a known MFRM generating setup, refits the requested model, and
returns row-level truth/estimate comparisons plus summaries by parameter
type. Location-like parameters are mean-aligned within replication
before reporting recovery Bias, RMSE,
MAE, correlation, and 95% coverage where standard errors
are available; bounded-GPCM slopes are compared on the
identified log-slope scale after the generator and fitter have both
imposed the geometric-mean-one slope convention. The output also carries
ADEMP-style simulation-study metadata so recovery checks are separated
from broader design-evaluation claims.plot(evaluate_mfrm_recovery(...)) adds
review plots for recovery summaries, coverage, row-level error
distributions, truth-estimate scatter, and replication status.
draw = FALSE returns an mfrm_plot_data object
with reusable plot tables and notes.assess_mfrm_recovery() adds a
user-facing adequacy checklist for recovery simulations. It separates
run completion, convergence, uncertainty availability, coverage, Monte
Carlo precision, and optional practical RMSE/Bias thresholds into
ok / review / concern style
statuses with next-action text.
plot(assess_mfrm_recovery(...)) now provides checklist
status-count and parameter-metric review plots so users can see which
part of the assessment needs attention before reading the full tables.
The draw = FALSE plot data include
reading_order, guidance, and user-facing
handoff tables such as section_status so follow-up starts
with review/concern rows rather than raw row-level output.1:score_levels score support into fit_mfrm().
This keeps zero-count boundary categories in the fitted support during
recovery, design-evaluation, diagnostic-screening, and bias-screening
runs, and avoids repeated rating-range inference messages in
release-validation logs.build_mfrm_sim_spec() and simulate_mfrm_data()
now accept step-facet-specific thresholds as a named list or row-named
numeric matrix, in addition to the existing long StepFacet
/ StepIndex / Estimate table.evaluate_mfrm_design(progress = interactive()) now shows
the progress bar only in interactive sessions by default.
Non-interactive tests, Quarto rendering, and batch scripts stay quiet
unless users set progress = TRUE; users can also set
progress = FALSE for fully silent exploratory runs.inst/validation/recovery-validation.R provides an optional
long-run validation script for release review. It defines structured
review steps, core RSM / PCM /
bounded-GPCM recovery cases, an extended latent-regression
case, practical thresholds, and a summary writer that produces top-line
release-decision, case-level release-decision, review-step, case-plan,
case-summary, metric-summary, overall decision-table, domain
decision-table, run-note, RDS, and Markdown outputs without adding heavy
Monte Carlo runs to routine package tests. The release decision uses
recovery metrics, convergence, and Monte Carlo precision as the primary
evidence; the domain decision table separately reports uncertainty
status so missing JML coverage columns are not mistaken for recovery
failure. Printing the validation object or calling
summary(validation) now shows the release-level decision
and case-level statuses before the full tables.build_summary_table_bundle() now accepts
evaluate_mfrm_recovery() and
assess_mfrm_recovery() outputs directly, including
ADEMP-style methods metadata, replication status, checklist rows, metric
review rows, thresholds, notes, and appendix-preset roles.export_summary_appendix() now recognizes recovery
simulation and recovery assessment objects as direct inputs. The
workflow vignette shows the full sequence from simulation specification
to recovery plots, adequacy assessment, summary-table bundle, and
appendix export.inst/validation/release-evidence-map-0.2.0.md gives a
source-based review plan for 0.2.0. It links the release checks to
Andrich’s RSM, Masters’ PCM, Muraki’s
GPCM and information-function work, FACETS/Winsteps fit
conventions, and ADEMP-style simulation-study reporting, then separates
release-gate checks from post-release roadmap items. The companion
release-evidence-checklist-0.2.0.csv provides a structured
blocker / caveat / roadmap checklist for release review.
external-parameter-recovery-simulation-0.2.0.md summarizes
the separate common-data recovery and cross-engine agreement workflow
and its limits without bundling the generated simulation datasets; the
validation bundle also includes a sourceable helper for re-reading a
local external-output directory when that workflow is refreshed.inst/validation/release-readiness.R turns the evidence map
into a reproducible review object. It records eight review steps, parses
an R CMD check log, checks the 0.2.0 version contract,
verifies the CI workflow contract for warning failures and retained
check artifacts, scans public docs for disallowed removed-helper
wording, confirms evidence artifacts, and reports a top-line
ok / review / concern gate
summary without adding exported user-facing API.DESCRIPTION
now cites Muraki (1992) using the GPCM article DOI,
10.1177/014662169201600206. The Muraki (1993) Applied
Psychological Measurement reference in GPCM information help pages now
uses 10.1177/014662169301700403.R/api-shrinkage.R
references corrected from Rasch Measurement Transactions,
12(2), 638 to 632-633 (page 638 in the
same RMT issue is a different paper; verified at https://www.rasch.org/rmt/rmt122.htm).reporting_checklist(): the bare “Linacre (1989,
2004)” tag in R/api-reporting-checklist.R is now
Linacre (1989, 2002). The 2002 paper is “Optimizing
rating scale category effectiveness,” JAM, 3(1), 85-106 – the
canonical Linacre reference for rating-scale guidance. (No bibliographic
entry existed for “Linacre (2004)”.)(cf. Eckes, 2011; ...) caveat in dif_report()
now has a complete @references entry pointing to
Introduction to Many-Facet Rasch Measurement (1st ed., Peter
Lang). McNamara & Knoch (2012) is also fully cited.?mfrmr-package previously attributed the context-specific
bands (high-stakes / clinical / survey) to Linacre (2002). The actual
source is Wright & Linacre (1994), RMT
8(3), 370. The band assignments were also swapped: high-stakes MCQ is
0.8-1.2 (not 0.6-1.4), survey is 0.6-1.4, clinical
observation is 0.5-1.7. Corrected.q3_statistic()): previously
stated mfrmr’s Q3 uses standardized residuals as if matching Yen (1984).
Yen’s eq. 7 (p. 127) uses raw residuals; mfrmr’s
standardized-residual choice is now documented as a deliberate
departure. The |Q3| > 0.20 cutoff was attributed to Yen
but is from Chen & Thissen (1997), JEBS,
22(3), 265-289. Re-attributed.q3_statistic(): the central finding of Christensen
et al. is that no single critical value is appropriate
across designs and that a parametric bootstrap should be used.
Documentation now states this clearly; the fixed
relative_offset = 0.20 is described as a screening default
rather than as a re-implementation of Q3_*.?apply_empirical_bayes_shrinkage was dimensionally wrong:
previously written 2 B^2 (tau^2 + SE^2)^2 / (K - 3), which
is SE^4-units. The actual Morris (1983, eq. 4.1-4.2, p. 51) correction
is (2 / (K - r - 2)) * B^2 * delta^2. Corrected, with
re-derived magnitude examples (SE understated by ~73% at K=3, ~29% at
K=5, ~7% at K=15).compute_facet_icc() previously placed ICC = 0.9 in
Excellent (>= 0.9). Koo & Li (2016,
p. 161) write “values greater than 0.90 indicate
excellent reliability” – strict >. Code at
R/api-hierarchical-audit.R now uses > 0.9
for Excellent; ICC = 0.9 reads as Good.?mfrmr-package now notes that the default
mml_engine = "direct" optimises the marginal log-likelihood
by gradient methods (BFGS / L-BFGS-B), not by Bock & Aitkin’s
signature EM. The "em" and "hybrid" engines
follow the EM template but with a BFGS M-step (rather than B&A’s
probit IRLS), because the target is the polytomous Rasch family rather
than 2PL.mfrm_core.R and reporting.R now describe the
bands as “adapted from Linacre (1994)” rather than “follow Linacre
(1994)”. Only the 30-examinee floor is Linacre’s; the
< 10 sparse and < 50 standard watermarks
are mfrmr-specific screening choices.compute_person_fit_indices() now computes the Snijders
weight-projection correction for JML/fixed-effect person estimates,
conditional on the fitted non-person calibration. The implementation
uses the polytomous form
w_tilde_k = log(P_k) - c_n d log(P_k) / d theta, with
c_n = Cov(log P, score) / I(theta). MML/EAP person scores
keep lz_star = NA with
lz_star_status = "not_applicable_eap" because EAP does not
satisfy the ML/MAP/WLE estimating-equation setup.compute_person_fit_indices() now adds practical 5% / 1%
flag columns and compact ReportIndex,
ReportValue, ReviewStatus,
ReviewReason, and ReportCaveat columns.
ReportIndex uses lz_star only when the
Snijders correction was actually computed; otherwise it falls back to
uncorrected lz with the status caveat left visible.
plot_person_fit() now carries these person-fit indices in
its draw-free plot data, adds reusable plot_long and
flag_summary tables, and supports
fit_index = "loglik" plus
preset = "monochrome" for report-focused person-fit
displays.compute_person_fit_indices() now returns an
mfrm_person_fit_indices data-frame subclass.
summary(person_fit) gives overview counts,
ReviewStatus / ReportIndex /
lz_star_status summaries, top review rows, thresholds,
caveats, and a reporting map. The same summary is now accepted by
build_summary_table_bundle(), so person-fit review rows and
Snijders-availability caveats can move into appendix/report workflows
without custom table wrangling.|Q3| > 0.30:
documented as a community convention Marais cites, not as her own
recommendation; her actual recommendation is the relative-to-mean
comparison.No defaults change between 0.1.6 and 0.2.0. The 0.1.6 defaults
(quad_points = 31, diagnostic_mode = "both",
plot.mfrm_fit(type = "wright"),
keep_original = FALSE) are retained.
Note for users upgrading directly from CRAN 0.1.5 to 0.2.0 (skipping
intermediate 0.1.6 builds): three defaults were flipped in 0.1.6 and
remain on those values in 0.2.0 –
diagnose_mfrm(diagnostic_mode) went from
"legacy" to "both", plot(fit)
returns the Wright map alone instead of a three-plot overview (the
overview is still available via
plot(fit, type = "bundle")), and
fit_mfrm(quad_points) went from 15 to
31. See the “mfrmr 0.1.6” section below for the full
description and revert paths.
New GitHub Actions workflows added alongside the existing
pkgdown.yaml: R-CMD-check.yaml runs the matrix
on Ubuntu (release / devel / oldrel-1) plus macos-latest and
windows-latest (release), and test-coverage.yaml runs
covr with artifact upload (no external service
contacted).
plot_dif_heatmap() gains display controls for cell
labels (show_values, value_digits), absolute
flag thresholds (flag_threshold, flag_color),
and shared symmetric color limits (scale_limit) so several
heatmaps can be drawn on a comparable scale.
plot_dif_summary() gains optional normal-approximation
confidence intervals, effect-threshold guide lines, method-aware axis
labels, and interpretation-guide data that downstream code can render
alongside the figure.
print.mfrm_plot_data() is now defined, so the headline
draw = FALSE return value renders as a compact summary
(name, title, reusable data shapes, legend / reference-line counts)
instead of a raw list dump.
fair_average_table() and estimate_bias() no
longer hard-stop on GPCM fits. Both helpers now use the
slope-aware element-conditional GPCM construction:
fair_average_table(): for
slope-facet element rows, the fair-average uses that element’s own
discrimination a_{j*} and threshold structure:
FA_{p,j*} = sum_k k * P_GPCM(X = k | theta_p, a_{j*}, delta_{j*}).
For non-slope facets (Person, Rater, …), the fair-average uses the
geometric-mean-one slope by GPCM identification, so the construction is
continuous with the PCM Linacre fair-average and reduces to it exactly
when all slopes equal one (regression-tested at machine
precision).
estimate_bias(): the per-cell bias
parameter is the additive shift on the linear predictor that maximises
the per-cell GPCM log-likelihood. The dispatch routes the inner
nll and the per-iteration category_prob calls
through the GPCM kernel instead of the PCM kernel; SE / t / Prob columns
retain the screening-tier semantics documented in
?estimate_bias.
Both helpers gain method = "GPCM-slope-aware" and a
caveat field that names the slope convention. For fair
averages, the original SE columns remain measure-level SEs, while
fair_se = TRUE adds structural delta-method fair-average
SEs for non-person rows when the MML Hessian is available. For bias
values, the SE / t / Prob columns retain their conditional screening
interpretation. See ?fair_average_table,
?estimate_bias, and gpcm_capability_matrix()
for the full support contract.
build_apa_outputs(),
facets_output_contract_review(), and
facets_output_file_bundle(include = "score") remain blocked
under GPCM in 0.2.0; they require the same SE infrastructure to ship as
publication-quality outputs.
compute_person_fit_indices() now computes
lz from the model category probability of the observed
category directly (true Drasgow, Levine & Williams (1985) polytomous
form), via three new intermediate columns PrObserved,
ItemEntropy, and ItemVarLogP on
compute_obs_table(). The previous Gaussian-residual
approximation overstated Var[log P] by roughly a factor of
five on a 4-category fixture and pulled lz toward
zero.ECI4 column is removed from
compute_person_fit_indices(). The previous implementation
was the standardized chi-square
(sum StdSq - n) / sqrt(2 * n), which is the linear (Smith)
approximation to OutfitZSTD, not the Tatsuoka &
Tatsuoka (1983) extended-caution index. Users who want the equivalent
statistic should use OutfitZSTD directly.
lz_star now uses the Snijders
NA with an
explicit status.displacement_table()$summary now returns
NA_real_ for MaxAbsDisplacement and
MaxAbsDisplacementT when every flagged level has zero
information (so every Displacement is NA).
Previously the helper called max(..., na.rm = TRUE) on an
all-NA vector, which returned -Inf and emitted
a “no non-missing arguments to max; returning -Inf” warning. The guarded
version is regression-tested in
test-core-coverage-gaps.R.analyze_dff() and dif_interaction_table()
now reject invalid p_adjust, non-integer
min_obs, invalid focal groups, and all-missing
group columns up front, instead of failing later inside the contrast
computation. Missing or empty group rows are dropped with a
message().?analyze_dff, ?plot_dif_summary,
?mfrmr_linking_and_dff, and
?mfrmr_visual_diagnostics now distinguish residual-method
screening labels from refit-method ETS A/B/C classifications more
explicitly and route users to both plot_dif_heatmap() and
plot_dif_summary().
?compute_person_fit_indices now describes when
lz_star is computed and when it is intentionally left
NA: JML/fixed-effect person scores receive the Snijders
(2001) correction, whereas MML/EAP scores remain uncorrected because EAP
is outside the Snijders estimating-equation setup.
?mfrm_generalizability now discloses that the lme4
random-effects model is main-effects only
(Score ~ 1 + (1|Person) + (1|Facet) + ... + Residual, no
explicit (1|Person:Facet) interaction terms), which folds
two-way interaction variance into Residual and can bias G
downward. The companion mfrm_d_study() projects
G and Phi under planned facet counts, but
reports the residual-scaling assumption explicitly; users who need a
full p x r x i decomposition should treat these projections as planning
evidence, not as a substitute for separately estimated interaction
components.
?q3_statistic now discloses that, when the chosen
facet has multiple residual rows per (Person, Level) cell because of
additional facets in the design, the standardized residuals are
mean-aggregated to one value per cell before the Pearson correlation.
Yen’s (1984) original definition takes the correlation over per-(Person,
Item) residuals without aggregation, so the published
|Q3| > 0.20 threshold and the Christensen et al. (2017)
critical values were derived for the original formulation; the values
returned here should be treated as a screening summary rather than a
direct substitute for those thresholds.
?bias_pairwise_report now discloses that the
contrast SE uses the independence approximation
sqrt(SE_i^2 + SE_j^2). For same-facet bias values that
share a sum-to-zero identification the true
Cov(b_i, b_j) < 0, so the reported SE is an
over-estimate and the t-statistic / p-value are conservative (the true
significance is higher than reported). For across-facet contrasts the
covariance term is approximately zero and the approximation is
appropriate.
Two new vignettes ship in the Migration and Scope
section of the pkgdown article navigation:
vignette("mfrmr-facets-migration") walks Facets users
through the corresponding mfrmr workflow and numeric
contract checks, and vignette("mfrmr-gpcm-scope") documents
which downstream helpers the bounded GPCM route currently
supports versus restricts and what to use as a substitute when a helper
is restricted.
.Rbuildignore tightened the
inst/references/ source-package boundary. The two runtime /
user-facing files in that directory –
facets_column_contract.csv (read at runtime by
facets_output_contract_review()) and
FACETS_manual_mapping.md (the FACETS Table to
mfrmr helper mapping cited in the README) – are
preserved.
The cpp11 MML backend (src/mml_backend.cpp, RSM and PCM
only) is opt-in via options(mfrmr.use_cpp11_backend = TRUE)
for this release. It is validated against the pure-R reference at
tolerance = 1e-12 on a fixed regression fixture. The
default flip to ON is planned for a follow-up release after a cycle of
community testing.
Considered for 0.2.0 but not shipped in 0.2.0; carried over to a later release:
build_apa_outputs(),
facets_output_contract_review(), and
facets_output_file_bundle(include = "score").
(fair_average_table() and estimate_bias() are
unblocked above.)analyze_dif_classical()) covering Mantel-Haenszel, logistic
regression, and SIBTEST.These are scheduled for a follow-up release.
This release adds empirical-Bayes shrinkage for small-N facets, a hierarchical-structure and sample-adequacy review layer, integrated missing-code pre-processing, APA output adapters for Word / HTML, model-estimated two-way non-person facet interactions, confidence-interval propagation through the plot surface and the ICC reporting family, and expanded reproducibility manifests. Six bug fixes close issues that affected bias statistics, ZSTD sign, input validation, and graphical state hygiene.
Three default values change in this release. Scripts that explicitly pass the old value are unaffected; scripts that rely on the default should be reviewed.
diagnose_mfrm(diagnostic_mode = ...) default flips from
"legacy" to "both". Strict marginal screens
are produced automatically for RSM / PCM fits
without the caller having to request them. Pass
diagnostic_mode = "legacy" to restore the earlier
behaviour.plot(fit) default output is now the Wright map alone,
returned as an mfrm_plot_data object. The previous
three-plot overview (Wright + pathway + CCC) remains available via
plot(fit, type = "bundle"), which returns an
mfrm_plot_bundle with the same three slots.fit_mfrm(quad_points = ...) default increases from
15 to 31 so a default MML fit is stable enough
for direct manuscript reporting. Pass quad_points = 15 (or
7) to restore the earlier iteration speed for exploratory
scans.fit_mfrm() gains facet_interactions for
confirmatory two-way interactions between non-person facets in
RSM and PCM fits, for example
facet_interactions = "Rater:Criterion". These terms are
estimated simultaneously with the main MFRM parameters as fixed effects
under zero marginal-sum constraints, contributing
(A - 1) * (B - 1) free parameters for an A x B
interaction block.
New supporting pieces:
interaction_effect_table(fit) returns one row per
interaction cell, with estimates, weighted counts, sparse-cell flags,
and the identification note.summary(fit) reports a compact interaction overview
when interaction terms are present.compare_mfrm(..., nested = TRUE) now recognizes
same-family additive-vs- interaction comparisons as nested when all
other structural settings match and the smaller model’s interaction set
is a subset of the larger model’s set.The feature is intentionally narrow for the initial CRAN-facing
release: person-involving interactions, higher-order interactions, GPCM
interactions, and random-effect facet interactions are deferred.
Residual bias screening via estimate_bias() and
estimate_all_bias() remains separate from these
model-estimated fixed effects.
fit_mfrm(..., facet_shrinkage = "empirical_bayes")
applies James-Stein / empirical-Bayes shrinkage to each non-person
facet’s fixed-effect estimates. fit$facets$others gains
ShrunkEstimate, ShrunkSE, and
ShrinkageFactor columns, and
fit$shrinkage_report summarises the per-facet prior
variance, mean shrinkage, and effective degrees of freedom.
The estimator is the classical method-of-moments form (Efron & Morris, 1973):
tau_hat^2 = max(0, mean(delta_hat_j^2) - mean(SE_j^2)),
using the raw second moment under mfrmr’s sum-to-zero identification
(the facet mean is exactly 0 by construction, so no degree of freedom is
consumed).B_j = SE_j^2 / (tau_hat^2 + SE_j^2) (shrinkage
factor).delta_hat_j^EB = (1 - B_j) * delta_hat_j and
SE_j^EB = sqrt((1 - B_j) * SE_j^2) (posterior mean / SE;
the posterior SE treats tau_hat^2 as known, omitting the
Morris
tau_hat^2 uncertainty).Two post-hoc helpers make shrinkage available to existing fits:
apply_empirical_bayes_shrinkage(fit, facet_prior_sd = NULL, shrink_person = FALSE)
augments an existing mfrm_fit.shrinkage_report(fit) returns the per-facet summary
table.The "laplace" alias currently routes to the
empirical-Bayes path and is reserved for a future penalised-MML
implementation.
Integration: summary(fit) exposes
FacetShrinkage and FacetShrinkageTau2Mean;
build_apa_outputs() adds a Method-section sentence naming
the mode, mean tau_hat^2, and mean shrinkage with a Efron
& Morris (1973) citation; build_mfrm_manifest() gains a
shrinkage_audit table; reporting_checklist()
gains an “Empirical-Bayes shrinkage” item.
Five new exported functions describe the observed design, flag small-N facet levels, and quantify ICC / design effect. Estimation remains fixed-effects MFRM; these helpers are purely descriptive and do not alter the fit.
detect_facet_nesting(data, facets, person) classifies
every ordered pair of facets (plus Person, optionally) as Fully
nested, Near-perfectly nested, Partially nested,
or Crossed using the conditional-entropy index
1 - H(B|A)/H(B).facet_small_sample_review(fit) returns per-level
N / Estimate / SE / Infit / Outfit / SampleCategory for
every facet. SampleCategory is one of "sparse"
(< 10), "marginal" (< 30), "standard"
(< 50), "strong" (>= 50). Thresholds follow Linacre
(1994) and are configurable.compute_facet_icc(data, facets, score, person) fits
lme4::lmer(Score ~ 1 + (1|Person) + (1|Facet1) + ...) and
reports the variance-component share per facet. Person uses the Koo
& Li
compute_facet_design_effect(data, facets, icc_table)
computes the Kish (1965) Deff = 1 + (m - 1) * rho and
effective N per facet.analyze_hierarchical_structure(data, facets, ...)
bundles the four helpers above and (when igraph is
available) a bipartite connectivity summary over Person * facet-level
edges.Fit- and reporting-stack integration:
fit$summary carries FacetSampleSizeFlag,
FacetMinLevelN, and FacetSparseCount.reporting_checklist() gains two items: “Facet
sample-size adequacy” (auto-ready when the flag is
"standard" / "strong") and “Hierarchical
structure review” (ready when the user passes
hierarchical_structure = analyze_hierarchical_structure(...)).build_apa_outputs() adds a Method sentence naming the
sample-adequacy band and linking to
facet_small_sample_review().build_mfrm_manifest() gains a
hierarchical_audit table.recommend_mfrm_design()$caveats now points users at the
three post-fit audit functions.Optional dependencies igraph and lme4 move
to Suggests; when either is absent the relevant report is
omitted with a clear message().
fit_mfrm() now accepts
missing_codes = NULL | TRUE | "default" | <character vector>,
forwarded to prepare_mfrm_data(),
review_mfrm_anchors(), and
describe_mfrm_data(). When active, the standard FACETS /
SPSS / SAS sentinels ("99", "999",
"-1", "N", "NA",
"n/a", ".", "" by default, or any
caller- supplied set) are converted to NA on the
person, facets, and score columns
before any downstream processing. Replacement counts are recorded in
fit$prep$missing_recoding and surfaced through
build_mfrm_manifest()$missing_recoding. The default
(missing_codes = NULL) is strictly backward-compatible.
A standalone recode_missing_codes() helper is also
exported for users who prefer to recode before calling
fit_mfrm().
as_kable.apa_table() converts an apa_table
into a knitr::kable() object with the caption above and the
note below. When kableExtra is installed the note becomes a
proper table footnote; otherwise it is appended as Markdown.as_flextable.apa_table() produces a
flextable::flextable() with caption and note pre-wired,
suitable for officer / Word / PowerPoint exports.as_kable() and
as_flextable(), are exported so other mfrmr classes (or
third-party wrappers) can register compatible methods.build_apa_outputs(..., context = list(output_mode = "reflow"))
now returns the Method / Results paragraphs as single long lines per
sentence-joined paragraph, which is the format Word / Quarto / RMarkdown
prefer. The default "wrapped" keeps the 92-column layout
for console readability.kableExtra and flextable join
Suggests.
plot(fit, type = "shrinkage") renders a horizontal
forest-style dotplot of original and shrunk facet-level estimates, with
arrows indicating shrinkage direction, optional 95 % CI error bars
(show_ci = TRUE), and a reference line at zero. When
shrinkage is not applied the plot shows an unavailable-state message
inviting the user to re-fit with
facet_shrinkage = "empirical_bayes".plot.mfrm_facet_sample_review() draws a horizontal bar
chart of per-level observation counts coloured by Linacre band, with
dashed vertical lines at the thresholds.plot.mfrm_facet_nesting() renders the pairwise nesting
index as a heatmap with numeric cell labels.All three methods follow the existing
preset = c("standard", "publication", "compact") convention
and use base-R graphics.
plot_bias_interaction(show_ci = TRUE, ci_level = 0.95)
draws BiasSize +/- z * SE whiskers on the scatter and
ranked views.plot_displacement(show_ci = TRUE) draws
Displacement +/- z * DisplacementSE whiskers in the
lollipop view.plot_fair_average(show_ci = TRUE) draws fair-average CI
whiskers on the observed-score scale using a delta-method propagation
SE_fair = Var(X | Measure) * ModelSE from the logit
Measure error. Rows near a rating boundary (where the
implied score variance is effectively zero) are excluded from the
whiskers, drawn as open circles, and counted in the subtitle.compute_facet_icc(ci_method = "profile" | "boot")
returns ICC confidence intervals in new ICC_CI_Lower /
ICC_CI_Upper / ICC_CI_Level /
ICC_CI_Method columns, propagated through
analyze_hierarchical_structure() and drawn as whiskers on
plot.mfrm_hierarchical_structure(type = "icc"). The default
ci_method = "none" keeps the point-estimate-only
behaviour.Fourteen additions across the plot surface, all base-R / additive (default behaviours unchanged):
plot_threshold_ladder() (new) —
vertical ladder of Rasch-Andrich thresholds for RSM and PCM, with
disordered-step crossings highlighted in the preset’s fail
colour. The returned object includes per-step
Group / Step / Threshold / Disordered rows.plot(fit, type = "ccc_overlay") (new
branch on plot.mfrm_fit) — observed category proportions
binned by person measure overlaid on the model CCC curves, for an
at-a-glance model-data fit visual.plot_person_fit() (new) — FACETS Table
6 style per-person Infit / Outfit bubble with the standard 0.5-1.5
acceptance band (Linacre, 2002).plot_bias_interaction(plot = "heatmap")
(new mode) — diverging Rater x Criterion grid coloured by bias size,
with flagged cells outlined for emphasis.plot(fit, type = "wright", group = ..., group_data = ...)
(new option) — overlays per-group person-density curves on the Wright
map’s left density column, useful for DIF / DFF screening.plot_rater_severity_profile() (new) —
per-rater severity ranking with CI whiskers and optional
+/-0.5 (gentle) and +/-1.0 (strict) guidance
bands for rater-training feedback.plot_anchor_drift(type = "forest")
(new mode) — per-wave anchor-element CI forest with point estimate +
z * SE whiskers.plot.mfrm_equating_chain() (new S3
method) — type = "common_anchors" (default bar chart of
pairwise common-anchor counts) and type = "graph"
(bipartite Wave x anchor element graph via igraph).plot_apa_figure_one() (new) — 2x2
publication composite bundling Wright map, rater severity profile,
threshold ladder, and a one-panel summary block.plot_dif_summary() (new) — compact
effect-size summary for [analyze_dff()] / [analyze_dif()] with ETS A / B
/ C colour coding.plot_guttman_scalogram() (new) —
Person x facet-level observed-category matrix, ordered by person measure
and location measure, with unexpected cells highlighted.plot_residual_qq() (new) — normal Q-Q
plot of person-level standardized residuals for distributional misfit
diagnostics.plot_rater_trajectory() (new) —
per-rater severity trajectory across an ordered wave / session variable
with CI whiskers. Accepts a named list of fits.plot_rater_agreement_heatmap() (new) —
symmetric rater x rater agreement matrix colored by exact agreement
(default) or the Pearson-style Corr column from
interrater_agreement_table(). Quadratic-weighted kappa is
not currently computed by that helper and is therefore not exposed as a
metric option.igraph is already in Suggests; the
equating-graph view falls back to the bar chart when igraph
is not installed.
Direct regression tests for the 0.1.6 additions:
test-attach-diagnostics.R — 18 assertions covering the
attach_diagnostics = TRUE merge, type validation,
idempotence, and MML / JML agreement checks.test-icc-ci-method.R — 25 assertions covering
compute_facet_icc(ci_method = "profile" / "boot"),
bootstrap seed reproducibility, range validation, deprecated
icc_ci_method alias, and
plot.mfrm_hierarchical_structure(type = "icc")
integration.test-ci-api-consistency.R — 21 assertions covering the
lifecycle::deprecate_warn() path for
conf_level, show_ci / ci_level on
plot_fair_average / plot_displacement /
plot_bias_interaction, and CI column schema.test-messaging-and-guards.R — assertions covering the
quiet-by-default inferred rating-range message, the opt-in one-time
message, analyze_dff(method = "refit")
missing(diagnostics) guard, and missing_codes
integration.test-lme4-confint-helper.R — 17 assertions covering
.lme4_confint_components() across terse and verbose lme4
row-name conventions.test-plotting-extras.R +
test-plotting-screening.R — 78 assertions covering all 14
new plot helpers.row_max_fast() and the three
category_prob_* polytomous-response kernels are now in
R/core-category-probabilities.R instead of inline in
R/mfrm_core.R. Pure file-level reorganization; no behaviour
change. The remaining structural split of mfrm_core.R
(likelihood / optimizer / EM / gradients / prep / report tables) is
scheduled for a future release.
mfrm_misfit_thresholds() returns the lower / upper
Linacre acceptance band that mfrmr screens use when flagging
element-level Infit / Outfit MnSq misfit. Defaults are
c(lower = 0.5, upper = 1.5) and can be overridden globally
via R options:
options(mfrmr.misfit_lower = 0.7)options(mfrmr.misfit_upper = 1.3)Helpers that consume the band include
summary(diagnose_mfrm(...)) (misfit_flagged
block + key_warnings auto-flag),
build_misfit_casebook() (the new element_fit
source family), the bias / misfit narrative inside
build_apa_outputs(), and
facet_quality_dashboard() when
misfit_warn = NULL. Setting the options once at the top of
an analysis script therefore changes every downstream screen at
once.
Four new public helpers extend the diagnostic plot family:
plot_local_dependence_heatmap(fit) – N x N Q3-style
pairwise residual correlation heatmap between facet levels. Complements
plot_marginal_pairwise() by showing every pair on a shared
color scale rather than a top-N bar list.plot_reliability_snapshot(fit) – compact facet x
reliability / separation / strata bar overview built from
diagnostics$reliability. Useful as a single small figure
for “are persons / raters / criteria distinguishable?”.plot_residual_matrix(fit) – person x facet-level
standardized residual heatmap. Complements
plot_guttman_scalogram() by showing residual sign and
magnitude rather than the raw response code.plot_shrinkage_funnel(fit) – empirical-Bayes shrinkage
caterpillar / funnel for fits augmented via
apply_empirical_bayes_shrinkage().plot_bubble() gains a
view = c("measure", "infit_outfit") argument. The default
"measure" keeps the historical Measure (logit) x MnSq
bubble layout; view = "infit_outfit" switches to the
Winsteps Table 30 layout (Infit MnSq on x, Outfit MnSq on y, bubble size
defaults to N). Both views return the same
mfrm_plot_data contract.
plot_dif_heatmap(draw = FALSE) now returns an
mfrm_plot_data object whose data$matrix is the
metric matrix (was previously the bare matrix only).
plot_information(..., draw = FALSE) outputs now include
a series field listing which curves the legend describes
("Information", "SE", or both for
type = "both"), so downstream ggplot2 re-renderers can map
the right column without inspecting type manually.
summary(diagnose_mfrm(...)) now prints the
fixed-effect chi-square block (“are all elements
equal?”) directly from diag$facets_chisq
(Facet, Levels, MeanMeasure,
SD, FixedChiSq, FixedDF,
FixedProb, plus the random-effect counterparts when
present) and the inter-rater agreement summary (Exact /
Expected / Adjacent agreement, MeanAbsDiff, MeanCorr, RaterSeparation,
RaterReliability) instead of leaving them in the diagnostics object
only. The new summary(diag)$facets_chisq and
summary(diag)$interrater slots also expose the same tables
for programmatic use.summary(diagnose_mfrm(...))$key_warnings now names the
worst MnSq-misfit elements
(e.g. MnSq misfit: Person:P023 (Infit=1.70, Outfit=2.40; outside 0.5-1.5).)
and prints a dedicated MnSq misfit block showing every
flagged element. Threshold pair is exposed at
summary(diag)$misfit_thresholds and is steered by
mfrm_misfit_thresholds() (see above).summary(diagnose_mfrm(...)) now prints a
category usage block (one row per observed score with
Count, AvgMeasure, and a
Disordering flag when the average measure decreases across
adjacent categories). Exposed programmatically at
summary(diag)$category_usage.summary(fit_mfrm(...)) now prints a targeting
block (Person mean - Facet mean, plus
PersonSD / FacetSD / SpreadRatio)
for every non-person facet. Under the package’s sum-to-zero
identification this collapses to the person mean by construction; the
row labels make that explicit and the spread ratio surfaces whether
persons or facets dominate the test scale.summary(estimate_bias(...)) now reports
Bonferroni and Holm significant-cell
counts alongside the raw screen-positive count. Both are exposed in
summary(bias)$overview as
BonferroniSignificant and
HolmSignificant.print(fit) and print(summary(fit)) now
show an “Attached diagnostics” line when
fit_mfrm(..., attach_diagnostics = TRUE) has merged
per-element fit columns onto fit$facets. The
attach-diagnostics path now extends to the person-facet table, so
per-person Infit, Outfit,
InfitZSTD, OutfitZSTD, and
PtMeaCorr columns are visible in
summary(fit)$person_high and
summary(fit)$person_low.To improve navigability of the core estimation engine, four
self-contained sections moved out of R/mfrm_core.R into
focused files. All functions remain internal and the public API is
unchanged.
R/core-likelihood.R – polytomous Rasch likelihoods and
cumulative response-probability helpers.R/core-data-prep.R – data validation, indexing, and
small formatting utilities.R/core-anchor-review.R – anchor-table reading,
normalization, and connectivity / overlap audit.R/core-optimizer.R – optim() / EM dispatch and MML-EM
scaffolding.R/api-simulation.R similarly grew an
R/api-simulation-future-branch.R companion file holding the
future-branch design-schema layer. Public simulation entry points
(simulate_mfrm_data, evaluate_mfrm_design,
evaluate_mfrm_diagnostic_screening,
evaluate_mfrm_signal_detection) remain in
R/api-simulation.R.
R/api-plotting-extras2.R was renamed to
R/api-plotting-screening.R to drop the numerical suffix in
favour of a functional name; tests follow the same rename.
A new tests/testthat/helper-fixtures.R exposes
make_toy_fit() / make_toy_diagnostics() /
local_toy_fit() helpers so future tests can reuse the
standard example_core fit without retyping the
load_mfrmr_data() + fit_mfrm() +
diagnose_mfrm() chain.
export_mfrm_bundle() and
build_mfrm_replay_script() now write a self-contained
replay package:
replay.R includes every argument that
affected the original fit_mfrm() call. Earlier 0.1.x
scripts silently dropped missing_codes,
mml_engine, slope_facet,
anchor_policy, min_common_anchors,
min_obs_per_*, facet_shrinkage,
facet_prior_sd, shrink_person, and
attach_diagnostics, so fits that depended on those
arguments did not actually replay.fit_mfrm() now records its inputs in
fit$config$replay_inputs (post match.arg) so
the bundle generator has a single source of truth.utils::packageVersion("mfrmr") guard that warns when the
installed version differs from the recorded one.export_mfrm_bundle(..., data = ...) accepts the
original analysis data; when supplied, the data is written into the
bundle as <prefix>_replay_data.csv and the replay
script reads from that co-located file. The recorded input hash is now
computed against the user’s original data (not the package’s internal
prep$data, which carries synthesised columns), so users can
verify their CSV matches the recorded fingerprint.tests/testthat/test-replay-roundtrip.R actually
sources the generated replay script in a fresh environment and compares
the reproduced log-likelihood and person estimates to the original.diagnose_mfrm() on large designscalc_interrater_agreement() (the inter-rater agreement
helper that diagnose_mfrm() calls when Person
is part of facet_cols) previously used a
list() for the per-context probability lookup and
c(exp_vals, ...) accumulation inside a per-row loop. This
gave near-quadratic scaling: 6,400 observations took ~2 s, but 72,000
observations took ~141 s. The lookup is now an environment
(hash-backed for character keys) and exp_vals is
preallocated and filled by index, so the helper now scales linearly in
the number of observations. On the 72,000-observation benchmark in the
review, diagnose_mfrm() drops from ~141 s to ~15 s.
The make_union_find() helper used by the connectivity
audit was also rewritten with an iterative find_root (with
path compression) instead of the previous recursive form. Designs whose
union chain depth exceeded options(expressions) (default
5,000) no longer error out with “evaluation is too deeply nested”.
prepare_mfrm_data() now:
Person and facet
IDs and records the row count so ” P01 ” and “P01” do not silently
become two persons;warning()s when the input contains duplicate Person x
facet rows (which violate MFRM’s conditional-independence assumption)
but lets the fit continue rather than refusing it outright.fit_mfrm() now treats NaN /
Inf for maxit, reltol, and
quad_points as invalid input with a localised English
error, instead of falling through to R’s locale-dependent “missing value
where TRUE/FALSE needed” message.
The two-page landscape cheatsheet now ships in pre-rendered form at
system.file("cheatsheet", "mfrmr-cheatsheet.pdf", package = "mfrmr")
alongside the existing .Rmd source. Users without a working
LaTeX toolchain can open the PDF directly; users who want to customize
it can still knit the .Rmd with
rmarkdown::render(). The README and ?mfrmr
package help now point at both files.
The most-visited help pages now embed concrete interpretation
comments inside their @examples blocks. Each shipped
example shows what value ranges or patterns indicate “good”, what
threshold or rule of thumb applies, and what follow-up to run if the
value is off. Coverage in 0.1.6 includes:
?fit_mfrm (convergence, person SD, targeting
bands).?diagnose_mfrm (key_warnings, MnSq misfit lines,
facets_chisq, inter-rater agreement minus expected).?summary.mfrm_fit and
?summary.mfrm_diagnostics (overview, person distribution,
top_fit ZSTD bands, facets_chisq, targeting).?estimate_bias, ?analyze_dff,
?compute_facet_icc,
?apply_empirical_bayes_shrinkage (effect-size bands,
Penfield classification, Koo & Li 2016 reliability bands, shrinkage
factor interpretation).?build_apa_outputs, ?reporting_checklist,
?plot_qc_dashboard, ?plot.mfrm_fit
(manuscript-readiness signals, dashboard panel status, Wright / pathway
/ CCC interpretation).?plot_bubble, ?plot_dif_heatmap,
?plot_local_dependence_heatmap,
?plot_reliability_snapshot,
?plot_residual_matrix, ?plot_shrinkage_funnel,
?plot_guttman_scalogram, ?plot_residual_qq,
?plot_rater_trajectory,
?plot_rater_agreement_heatmap (cell / band thresholds,
reference-line interpretation).\donttest{}Several main entry points now expose a small fast-path block (a
JML fit on example_core plus a single
diagnostic / plot call) before the heavier \donttest{}
block. The fast path is below R CMD check’s example-time budget and
provides a regression net that runs every check, while the full
\donttest{} block continues to showcase the larger MML /
publication-route examples. A final CRAN-timing pass keeps the active
examples at lightweight maxit = 30 smoke fits so the
printed examples demonstrate converged objects without returning to the
original long-running example surface. Affected pages:
?fit_mfrm, ?diagnose_mfrm,
?plot_qc_dashboard, ?reporting_checklist,
?build_apa_outputs.
?mfrmr_visual_diagnostics adds a “Cross-reference to
FACETS / Winsteps tables” section that lists the closest mfrmr helper
for each canonical Rasch / MFRM table or figure family (Wright map,
pathway / probability curves, test information, misfit, bias /
interaction, DIF / DRF, inter-rater agreement, anchoring /
linking).?mfrmr_visual_diagnostics and the visual reporting
template now enumerate the 4 secondary plot helpers and the 4 screening
helpers added in 0.1.6.?diagnose_mfrm cites Wright & Masters (1982) at the
separation / strata / reliability section and reproduces the formulae (G
= TrueSD / RMSE, R = G^2 / (1 + G^2), H = (4G + 1) / 3) so the
reliability outputs are traceable to source.?fit_mfrm example block now flags the
quad_points = 7 opening fit as an exploratory speed
setting.?mfrmr package help now point at the
public cheatsheet
(system.file("cheatsheet", "mfrmr-cheatsheet.Rmd", package = "mfrmr")).|ZSTD|
(or |MnSq - 1| when ZSTD is unavailable) instead of the
generic |metric| label.build_misfit_casebook() now also draws element-level
Infit / Outfit MnSq misfit cases from diagnostics$fit (in
addition to marginal cells, pairwise screens, unexpected responses, and
displacement). The casebook therefore matches what its name
implies.q3_statistic(fit, diagnostics) returns the Yen (1984) Q3
index between every facet-level pair, with three published reporting
thresholds (Yen 0.20, Marais 0.30, Christensen et al. relative 0.20) and
a textual Interpretation column that names which flag(s)
each pair triggered. The helper reuses the standardized- residual pivot
that plot_local_dependence_heatmap() already draws, so the
table and the heatmap stay numerically consistent.
compute_person_fit_indices(diagnostics, fit) adds
person-level fit detail on top of the Infit / Outfit / ZSTD columns that
diagnose_mfrm() already exposes:
NA for MML/EAP scores
with an explanatory status.The reported lz statistic is asymptotically standard
normal under the conditional-independence assumption; |lz| > 1.96 /
2.58 are the 5% / 1% reporting flags.
mfrm_generalizability(fit) re-fits the rating data as a
crossed random-effects model
Score ~ 1 + (1 | Person) + (1 | Facet1) + ... via
lme4::lmer and returns the canonical G / Phi coefficients
plus per-source variance components. Useful when a reviewer asks for a
generalizability-theory complement to the Rasch-style separation /
reliability statistics that diagnose_mfrm() already
emits.
Three thin importers expose external fit objects via the same
mfrm_fit interface that the mfrmr plot and table helpers
consume:
import_mirt_fit(fit, model) accepts a
mirt::mirt() result.import_tam_fit(fit, model) accepts
TAM::tam.mml() / TAM::tam.jml().import_erm_fit(fit, model) accepts
eRm::PCM() / eRm::RM() /
eRm::RSM().The imported objects carry the mfrm_imported_fit class
and populate measurement-side slots (facets$person,
facets$others, steps, summary)
only. Bias / DIF / anchor / replay slots are explicitly not populated;
full bundle import is planned for a future release.
compute_facet_icc(boot = "boot") gains
ci_boot_parallel ("no" /
"multicore" / "snow") and
ci_boot_ncpus arguments that are forwarded to
lme4::bootMer(). The per-replicate cli
progress bar is suppressed under parallel execution because worker
processes hold their own copy of the progress state.
evaluate_mfrm_design() accepts a
parallel = c("no", "future") argument. When
"future" is requested and the future.apply
Suggests package is installed, the rep loop within each design row
honours whatever future::plan() is currently active;
cross-design-row parallelism is planned for a future release. Without
future.apply the call falls back to serial execution with
an explicit message.
fit_mfrm() accepts a
checkpoint = list(file = ..., every_iter = ...) argument.
When supplied to a mml_engine = "em" (or hybrid) fit, the
EM scaffolding writes its state to file every
every_iter outer iterations using saveRDS().
If the file exists when a subsequent call starts, the engine resumes
from the recorded iteration. The direct optim() engine
ignores the checkpoint; non-EM fits run unaffected.
A new tests/testthat/test-gpcm-verification.R exercises
every "supported" and "supported_with_caveat"
row of gpcm_capability_matrix() on a toy dataset and
asserts the documented helper returns the expected shape.
"blocked" and "deferred" rows have negative
tests that confirm the helper either refuses to run or returns an
explicit caveat. These tests make the GPCM scope a contract that future
commits cannot silently shrink.
fit_mfrm(attach_diagnostics = TRUE) runs
diagnose_mfrm() once after the fit and merges the per-level
SE, Infit, Outfit, and
PtMeaCorr columns onto fit$facets$others. This
makes the facet table look like a FACETS Table 7 summary without a
separate call. The default FALSE preserves the minimal
Facet / Level / Estimate layout
from 0.1.5.
build_mfrm_manifest() gains several new tables so replay
bundles carry everything a deterministic re-run needs:
environment now records RNGKind,
RNGSeedDigest, Locale, and a UTC ISO-8601
timestamp in addition to the existing package and platform fields.dependencies (new) records the installed version of
every Imports and Suggests dependency, with a
Role column.input_hash (new) hashes the input data, anchors, group
anchors, and score_map with SHA-256 (via
digest, now in Suggests) or an MD5-of-RDS
fallback. The hash is deterministic across sessions.session_info (new) unrolls
utils::sessionInfo() into a long data frame
(Scope / Package / Version).hierarchical_audit, missing_recoding, and
shrinkage_audit (new) surface the three new audit layers in
one place.digest is added to Suggests.
estimate_bias()
and estimate_all_bias() previously returned NA
for every cell’s S.E., t, Prob.,
Obs-Exp Average, Infit, and
Outfit, and Significant counts collapsed to
zero. Root cause was an nzchar(NA_character_) call (which
returns TRUE) in an internal predicate. Downstream helpers
such as bias_interaction_report() and
plot_bias_interaction() are now populated again.estimate_bias() silent failure on typo’d facet
names. A mis-spelled facet_a /
facet_b (e.g. "Raters" with trailing
s) previously returned an empty list() with no
warning. It now raises an informative error naming the available facets.
Missing diagnostics argument likewise raises an explicit
mfrmr error rather than falling through to R’s locale-dependent missing-
argument message.zstd_from_mnsq() was
numerically unstable for very small degrees of freedom and could return
large positive ZSTD when MnSq was close to zero, flipping
the sign relative to the companion Outfit ZSTD for the same element. A
df >= 1 guard returns NA in degenerate
cells.prepare_mfrm_data() now stops when any observed
Score falls outside the declared
[rating_min, rating_max] range. Previously negative
score_k values passed through m[cbind(i, 0)],
silently dropping those rows from the likelihood while
n_obs kept its original value.sanitize_noncenter_facet(),
sanitize_dummy_facets(), and
build_facet_signs() now emit a warning when supplied facet
names are not part of the fitted model. Previously typos such as
positive_facets = "rater" (lowercase) or
noncenter_facet = "Raters" could silently flip the sign
convention of facet measures.apply_plot_preset() and .draw_shrinkage_plot()
now restore the user’s par() on exit, per “Writing R
Extensions” 2.1. All plot methods that relied on
apply_plot_preset() inherit this automatically.analyze_dff()
adds a ContrastDirection column to the residual and refit
branches. The two methods use opposite sign conventions by design, so
the new column spells out which interpretation applies.compute_facet_icc() singular fit.
Total variance below sqrt(.Machine$double.eps) is now
reported as ICC = NA with
Interpretation = "Non-identifiable" instead of a falsely
meaningful value. The first lme4 convergence diagnostic
surfaces as a message() rather than being silently
suppressed.as.data.frame.mfrm_fit() now carries the new
Extreme column through to ggplot2 / CSV pipelines instead
of dropping it.as_kable(format = "pipe") output.
Previously silently returned HTML when kableExtra was
installed and the apa_table carried a non-empty
note. "pipe" now consistently returns the
Markdown table with an appended Note. line.review_mfrm_anchors() false positives.
Overlap-adequacy risk flags are skipped when no anchors or group anchors
were supplied, so single-wave analyses no longer emit “high severity”
warnings because OverlapLevels == 0 everywhere.sqrt(.Machine$double.eps) (~1.5e-8) to
1e-6, so integer codes like 1.0000001 that
round-trip through CSV floats are now accepted. Genuinely fractional
scores (1.5, 2.75) are still caught.rating_min / rating_max are inferred from the
observed scores, the provenance is now retained in fit summaries and
data-description output rather than emitted as a routine message. Users
who prefer the interactive reminder can set
options(mfrmr.show_inferred_rating_range = TRUE);
fit_mfrm() still limits that opt-in message to one per
fit.plot(fit, type = ...). Passing an unknown
type previously raised R’s locale-dependent
match.arg() error. It now raises an English mfrmr-style
error listing the valid choices.plot_dif_heatmap(draw = FALSE) return
contract. The helper documented an mfrm_plot_data
object but invisibly returned the bare matrix, breaking the
documented contract used by sibling plot_* helpers. It now
returns an mfrm_plot_data whose data slot
bundles matrix, pairs, metric,
and value_column. Code that relied on the old shape should
switch from dim(heat) to
dim(heat$data$matrix).plot_bias_interaction(show_ci = TRUE, ci_level = 0.95)
(scatter and ranked modes) now draws
BiasSize \u00b1 z \u00b7 SE whiskers using the per-cell SE
from [estimate_bias()].
plot_displacement( show_ci = TRUE) (lollipop mode) draws
Displacement \u00b1 z \u00b7 DisplacementSE whiskers from
the audit-table standard error. Both functions now populate
CI_Lower / CI_Upper / CI_Level
columns on the returned plot-data element so downstream pipelines can
reuse the bounds. plot_fair_average() CI support (now
implemented later in this release) uses a delta-method propagation
because the fair-average SE lives on the logit scale while the plot uses
the observed-score scale, which requires a delta-method
transformation.fit_mfrm() emits a one-time message() when
called with anchor_policy = "silent" while the anchor
review flags issues.prepare_mfrm_data() records whether
rating_min / rating_max were inferred from the
observed scores or supplied explicitly, and fit/data summaries surface
that provenance. The former informational message is opt-in through
options(mfrmr.show_inferred_rating_range = TRUE). Row
drops, ID trimming, and facets with only one observed level are recorded
in preparation_notes; routine preparation messages are
opt-in through
options(mfrmr.show_preparation_messages = TRUE)."low", "medium",
"high") now raise a targeted error up front instead of
surfacing as the opaque “No valid observations remain” message.detect_anchor_drift() and
build_equating_chain() thin-linking warnings now list
per-facet retained-vs-threshold counts
(e.g. "Rater (3/5)").bias_interaction_report()$summary carries a
FlagStatus column so empty ranked_table rows
are no longer ambiguous between “nothing flagged” and “nothing
computed”.rcond(mm) < 1e-8), catching numerically collinear
covariates rather than only exact rank deficiency.apply_empirical_bayes_shrinkage() docstring and R
comments now document the shrinkage-variance formula as
max(0, K^{-1} * sum(delta^2) - mean(SE^2)), matching the
implementation under sum-to-zero identification.?fit_mfrm “Input requirements” now states the MFRM
conditional independence assumption (Linacre, 1989) and points at
diagnose_mfrm(..., diagnostic_mode = "both") /
strict_pairwise_local_dependence as the exploratory
follow-up.?fit_mfrm example block now flags the
quad_points = 7 opening fit as an exploratory speed setting
and reminds readers that the package default
quad_points = 31 is the publication tier, so the example no
longer reads as a recommendation against the new default.?fit_mfrm now presents the recommended
quad_points tiers as a \tabular{} block
(7 fast scan, 15 default, 31+
publication) so readers do not have to re-extract the recommendation
from prose. The “adapted from Linacre (1994)” wording for the
sample-size bands and the wall-clock cost of
diagnostic_mode = "both" are also spelled out.?fit_mfrm missing_codes docstring is now
an itemised list of the three branches (NULL /
TRUE / custom vector) instead of dense prose.?run_mfrm_facets now notes that
method = "JML" is the default for legacy FACETS-style
output continuity and points users at
fit_mfrm(..., method = "MML") for new analysis
scripts.?apply_empirical_bayes_shrinkage now states that
EffectiveDF = Σ(1 − B_j) matches the “effective number of
parameters” from Efron & Morris (1973).?compute_facet_icc notes that Koo & Li (2016)
recommend applying the reliability bands to the 95% confidence interval
of the ICC, while the current implementation bands the point estimate
only.?analyze_facet_equivalence adds Kass & Raftery
(1995) as the reference for the BIC-based Bayes-factor approximation
BF_{01} ≈ exp((BIC_{H1} − BIC_{H0}) / 2).?mfrmr-package now cites Wilson &
Hilferty
calc_displacement_table() now cites the Winsteps user
guide for the combined |Displacement| > 0.5 logit and
|t| > 2 flagging rule.analyze_facet_equivalence() docstring frames the
equivalence_bound = 0.5 default as a starting point.print() S3 methods for 13 classes that previously
fell back to the default list printer (mfrm_apa_outputs,
mfrm_bias, mfrm_bundle,
mfrm_design_evaluation, mfrm_diagnostics,
mfrm_facet_dashboard,
mfrm_future_branch_active_branch,
mfrm_plausible_values,
mfrm_population_prediction,
mfrm_reporting_checklist,
mfrm_signal_detection,
mfrm_threshold_profiles,
mfrm_unit_prediction). Each delegates to the existing
summary() method.Reference citations corrected:
379-402 (was
379-421).plot_qc_dashboard() plots a signed ZSTD distribution
combining Infit and Outfit ZSTD, with reference lines at
-3 / -2 / 0 / 2 / 3. The previous absolute-value histogram
collapsed over-fit and under-fit tails.plot_residual_pca(..., plot_type = "scree") draws Rasch
secondary-dimension reference lines at
1.0 / 1.4 / 2.0 / 3.0, consistent with the Winsteps user
guide. The legend and returned reference_lines record the
new entries.content_checks entry uses a
case-insensitive regex so the check passes whether the APA contract uses
"Residual PCA" or the longer
"Exploratory residual PCA".fit$facets$person exposes PosteriorSD and
SE aliases alongside the legacy SD. MML fits
populate all three with the posterior SD under the Gauss-Hermite prior;
JML fits set SE = NA_real_ and note that per-person SEs
should be pulled from diagnose_mfrm()$measures.analyze_dff(method = "refit") subgroup fits return a
LinkingReview column that captures the anchor-review
messages emitted during the refit, replacing the previous hard-coded
anchor_policy = "silent" silence.detect_anchor_drift() returns
common_vs_reference and n_common_all_waves
alongside the existing pairwise common_elements table, for
3+ wave linking reviews.analyze_residual_pca(..., pca_max_factors = "auto")
caps the factor count at min(10, ncol - 1, nrow - 1) per
matrix; this value was previously silently coerced to
NA.describe_mfrm_data() returns two new components:
missing_rate_summary (per-column missing / non-missing
counts) and facet_crosstabs (long-format pairwise
observation-count tables, suitable for heatmap plotting).Author /
Maintainer fields auto-generated by CRAN;
Authors@R remains the single source of truth. The
Description: field is now three sentences (was 10 lines of
prose), improving CRAN web readability while retaining the two
rating-scale / partial-credit DOI references.inst/CITATION now tracks meta$Version and
meta$Title dynamically, so citation("mfrmr")
prints the current installed version rather than a hard-coded
string.6,380+ tests pass (up from 6,343 in 0.1.5), with 0 failures and 0 errors. New test files:
test-shrinkage.R (40 tests) covers the closed-form
math, edge cases (K < 3, tau^2 <= 0,
user-supplied prior), fit_mfrm integration, reporting and
manifest trails, and the three new plot methods.test-missing-codes-integration.R (17 tests) covers
fit_mfrm, describe_mfrm_data,
review_mfrm_anchors, and manifest paths.test-hierarchical-audit.R (10 tests) covers the five
new hierarchical-audit helpers and their integration points.Pre-existing test-harness errors unrelated to 0.1.5 behaviour have
also been cleaned up (S3 dispatch, GPCM scope wording, internal-helper
prefixing with mfrmr:::).
print(fit), summary(fit), and
summary(diagnose_mfrm(...)) so results start with
Status, Key warnings, and
Next actions.MML, review diagnostics with
diagnostic_mode = "both", then move to reporting
helpers.score_map, and clearer warnings
for retained zero-count categories.MML branch for
ordered RSM / PCM fits with person covariates,
including simulation and scoring support for the fitted population
model.GPCM support for the documented direct
route, including core summaries, diagnostics, plots, posterior scoring,
and information checks, while keeping unsupported downstream routes
explicit.RSM / PCM use, fixed-calibration scoring after
JML, and PCM information curves.plot_marginal_fit() and
plot_marginal_pairwise().reporting_checklist(),
build_summary_table_bundle(),
export_summary_appendix(), and
visual_reporting_template() for manuscript-oriented tables,
appendix artifacts, and figure-placement guidance.plot(fit, type = "ccc_surface", draw = FALSE) output for
advanced visualization while keeping 2D Wright/pathway/category plots as
the default reporting route.DESCRIPTION references to use the requested
authors (year) <doi:...> format.facet_quality_dashboard(), including the output class,
structure, and interpretation.\dontrun{} with \donttest{} for
executable examples so CRAN can exercise those examples during
checks.DESCRIPTION metadata to avoid CRAN incoming
spell-check notes on cited proper names.fit_mfrm() and
method selection (MML default, JML).estimate_bias().build_fixed_reports()).build_apa_outputs()).build_visual_summaries()) with configurable threshold
profiles.analyze_residual_pca(),
plot_residual_pca()).data/ and inst/extdata/.R CMD check.CONTRIBUTING.md, CODE_OF_CONDUCT.md,
and SECURITY.md.inst/CITATION,
CITATION.cff).