This vignette is a compact map of the main base-R diagnostics in
mfrmr. It is organized around four practical questions:
All examples use packaged data and
preset = "publication" so the same code is suitable for
manuscript-oriented graphics.
If you are selecting figures for a report, use
reporting_checklist() before or alongside this vignette.
Its "Visual Displays" rows now mirror the public plotting
family shown here.
library(mfrmr)
toy <- load_mfrmr_data("example_core")
fit <- fit_mfrm(
toy,
person = "Person",
facets = c("Rater", "Criterion"),
score = "Score",
method = "JML",
model = "RSM",
maxit = 20
)
diag <- diagnose_mfrm(fit, residual_pca = "none")
checklist <- reporting_checklist(fit, diagnostics = diag)
subset(
checklist$checklist,
Section == "Visual Displays",
c("Item", "Available", "NextAction")
)Use the Wright map first when you want one shared logit view of persons, facet levels, and step thresholds.
Interpretation:
Next, use the pathway map when you want to see how expected scores progress across theta.
Interpretation:
Unexpected-response screening is useful for case-level review.
plot_unexpected(
fit,
diagnostics = diag,
abs_z_min = 1.5,
prob_max = 0.4,
plot_type = "scatter",
preset = "publication"
)Interpretation:
Displacement focuses on level movement rather than individual responses.
plot_displacement(
fit,
diagnostics = diag,
anchored_only = FALSE,
plot_type = "lollipop",
preset = "publication"
)Interpretation:
When you need the package’s latent-integrated follow-up path, switch
to MML and request diagnostic_mode = "both" so
the legacy and strict branches stay visible side by side. The chunk
below uses compact quadrature for optional local execution; final
reporting should be refit with the package default or a higher
quadrature setting.
fit_strict <- fit_mfrm(
toy,
person = "Person",
facets = c("Rater", "Criterion"),
score = "Score",
method = "MML",
model = "RSM",
quad_points = 7,
maxit = 40
)
diag_strict <- diagnose_mfrm(
fit_strict,
residual_pca = "none",
diagnostic_mode = "both"
)
strict_checklist <- reporting_checklist(fit_strict, diagnostics = diag_strict)
subset(
strict_checklist$checklist,
Section == "Visual Displays" &
Item %in% c("QC / facet dashboard", "Strict marginal visuals"),
c("Item", "Available", "NextAction")
)
plot_marginal_fit(
diag_strict,
top_n = 12,
preset = "publication"
)Interpretation:
plot_marginal_pairwise(diag_strict, preset = "publication").When the design may be incomplete or spread across subsets, inspect the coverage matrix before interpreting cross-subset contrasts.
sc <- subset_connectivity_report(fit, diagnostics = diag)
plot(sc, type = "design_matrix", preset = "publication")Interpretation:
If you are working across administrations, follow up with anchor-drift plots:
Residual PCA is a follow-up layer after the main fit screen.
diag_pca <- diagnose_mfrm(fit, residual_pca = "both", pca_max_factors = 4)
pca <- analyze_residual_pca(diag_pca, mode = "both")
plot_residual_pca(pca, mode = "overall", plot_type = "scree", preset = "publication")Interpretation:
For interaction screening, use the packaged bias example.
bias_df <- load_mfrmr_data("example_bias")
fit_bias <- fit_mfrm(
bias_df,
person = "Person",
facets = c("Rater", "Criterion"),
score = "Score",
method = "MML",
model = "RSM",
quad_points = 7
)
diag_bias <- diagnose_mfrm(fit_bias, residual_pca = "none")
bias <- estimate_bias(fit_bias, diag_bias, facet_a = "Rater", facet_b = "Criterion")
plot_bias_interaction(
bias,
plot = "facet_profile",
preset = "publication"
)Interpretation:
The package ships a second-wave visual layer for teaching and diagnostic follow-up. These helpers are not default reporting figures; use them after the main screens above.
plot_guttman_scalogram(fit, diagnostics) renders a
person x facet-level response matrix with an unexpected-response
overlay, for teaching-oriented scalogram intuition and local
triage.plot_residual_qq(fit, diagnostics) plots a Normal Q-Q
of person-level standardized residual aggregates as exploratory
follow-up on residual tail behavior.plot_rater_trajectory(list(T1 = fit_a, T2 = fit_b))
tracks rater severity across named waves. The helper does not perform
linking; supply waves that have already been placed on a common anchored
scale (see vignette("mfrmr-linking-and-dff")) before
interpreting movement as rater drift.plot_rater_agreement_heatmap(fit, diagnostics) renders
a compact pairwise rater x rater agreement matrix; pass
metric = "correlation" to colour by the Pearson-style
Corr column instead of exact agreement.response_time_review(data, person, facets, time)
summarizes response-time metadata by person, facet, and score category.
Pair it with plot_response_time_review() for distribution
and grouped timing plots. This is a descriptive QC layer, not a joint
speed-accuracy model.plot_shrinkage_funnel(fit_eb, show_ci = TRUE) draws raw
and empirical-Bayes shrunken facet estimates on the same row, with
optional confidence whiskers for both estimates. Use this only after
apply_empirical_bayes_shrinkage() or
fit_mfrm(..., facet_shrinkage = "empirical_bayes").If your rating-event data include response times, review them separately from the MFRM likelihood. Rapid and slow response-time flags are descriptive quality-control prompts; they do not change measures and should not be treated as proof of disengagement, cheating, or speededness.
toy_rt <- toy
toy_rt$ResponseTime <- 12 + (seq_len(nrow(toy_rt)) %% 7) +
as.numeric(toy_rt$Score)
toy_rt$ResponseTime[1] <- 2
toy_rt$ResponseTime[2] <- 38
rt <- response_time_review(
toy_rt,
person = "Person",
facets = c("Rater", "Criterion"),
score = "Score",
time = "ResponseTime",
rapid_quantile = 0.10,
slow_quantile = 0.90
)
summary(rt)
plot_response_time_review(rt, type = "distribution", preset = "publication")
plot_response_time_review(rt, type = "person", preset = "publication")Interpretation:
When a non-person facet has few levels or sparse observations, a large raw severity estimate can be a noisy estimate rather than a stable facet signal. The shrinkage funnel shows how far empirical-Bayes pooling moved each level toward the facet mean and whether the uncertainty remains wide after pooling.
fit_eb <- apply_empirical_bayes_shrinkage(fit)
shrink <- plot_shrinkage_funnel(
fit_eb,
show_ci = TRUE,
ci_level = 0.95,
preset = "publication",
draw = FALSE
)
head(shrink$data$table[, c(
"Facet", "Level", "RawEstimate", "RawCI_Lower", "RawCI_Upper",
"ShrunkEstimate", "ShrunkCI_Lower", "ShrunkCI_Upper",
"ShrinkageFactor"
)])
plot_shrinkage_funnel(
fit_eb,
show_ci = TRUE,
ci_level = 0.95,
preset = "publication"
)Interpretation:
For a compact visual workflow:
reporting_checklist() when you want the package to
route which figures are already supported.plot_qc_dashboard() for one-page triage.plot_unexpected(), plot_displacement(),
plot_marginal_fit(), and
plot_interrater_agreement() for local follow-up.plot(fit, type = "wright") and
plot(fit, type = "pathway") for targeting and scale
interpretation.plot_residual_pca(),
plot_bias_interaction(), and
plot_information() for deeper structural review.response_time_review() and
plot_response_time_review() when response-time metadata are
available.plot_shrinkage_funnel(show_ci = TRUE) when
empirical-Bayes shrinkage was applied.plot_guttman_scalogram(),
plot_residual_qq(), plot_rater_trajectory(),
and plot_rater_agreement_heatmap() as the teaching / drift
/ agreement-heatmap follow-up layer.