
Build a category curve export bundle (preferred alias)
Source:R/api-reports.R
category_curves_report.RdBuild a category curve export bundle (preferred alias)
Usage
category_curves_report(
fit,
theta_range = c(-6, 6),
theta_points = 241,
digits = 4,
include_fixed = FALSE,
fixed_max_rows = 400,
newdata = NULL
)Arguments
- fit
Output from
fit_mfrm().- theta_range
Theta/logit range for curve coordinates.
- theta_points
Number of points on the theta grid.
- digits
Rounding digits for numeric graph output.
- include_fixed
If
TRUE, include a legacy-compatible fixed-width text block.- fixed_max_rows
Maximum rows shown in fixed-width graph tables.
- newdata
Optional data frame with every non-Person facet, one row per rating context, using fitted level labels and no
Thetacolumn. For native GPCM MML, evaluates the full fitted predictor, including one or two slope families.Thetais reserved for the ability grid. Unknown/missing levels are refused; unobserved combinations of known levels are allowed and labelled. NULL retains reference profiles.
Details
Preferred high-level API for category-probability curve exports.
model_context records the fitted step/slope owners. curve_profiles maps
each CurveGroup to the owner levels, including separate-owner GPCM.
By default, curves are conditional reference profiles with additive locations
fixed at zero; see the retained CurveBasis and PredictorOffset columns.
Explicit newdata enables full-context one- and two-family GPCM MML curves.
This helper does not compute calibration SEs or confidence intervals.
plot(out) and its selected curve views use base graphics. Automatic
as_ggplot() conversion of this bundle is unavailable because curve
families have distinct targets and scales; extract probabilities and retain
its curve-family and group columns for custom figures. For fitted CCC
graphics, plot(fit, type = "ccc") has a dedicated conversion;
review its reference-profile settings before comparing curves.
Returns tidy curve coordinates and summary metadata for quick
plotting/report integration without calling low-level helpers directly.
The expected-score table also carries the per-curve score variance and
information function. For GPCM, the information column follows the
Muraki/Samejima identity \(a^2 \mathrm{Var}(X \mid \theta)\);
for RSM / PCM, this reduces to the usual score variance because
discrimination is fixed at one. The category_information table decomposes
that total into category-level contributions,
\(a^2 P_k(\theta)(k - E[X \mid \theta])^2\), whose sum equals the
reported information at the same theta value. The
cumulative_probabilities table follows the FACETS / Winsteps graph
convention of accumulating modeled probabilities across ordered categories
(P(X <= k) by default, with P(X >= k) also returned for flipped curves).
cumulative_boundaries reports approximate theta values where
P(X <= k) = .5, with BoundaryStatus and CrossingCount to avoid
over-interpreting boundaries outside the requested theta range or with
multiple crossings.
Without newdata, these are reference-profile curves. Estimated step parameters and, for
GPCM, each step-facet group's estimated slope are retained, while additive
facet main effects and fitted interactions are fixed at zero. The
CurveBasis and PredictorOffset columns, and the corresponding entries in
settings, make that conditioning explicit. Thus the curves do not
represent an arbitrary observed Person-by-facet cell.
Explicit rating contexts
With newdata, curve_profiles maps each curve to InputRow in the saved
newdata. It retains the step owner/level, effective discrimination (the
product for two families), additive PredictorOffset, and ObservedContext.
The predictor is Theta + PredictorOffset; facet locations and interactions
are retained. No Person posterior or population integration is involved.
Category and expected scores retain the internal scoring scale;
CategoryLabel and boundary label columns retain the original score labels.
Plot series use these original labels without changing the scoring rule.
cumulative_boundaries solves P(X <= k) = .5, equivalently
P(X >= k+1) = .5, directly within theta_range. It is independent of
theta_points and retains full numerical precision irrespective of digits.
Binary boundaries use the analytic adjacent intersection. Other boundaries
use bracketed root finding on a stable difference of unnormalized lower and
upper category masses, avoiding subtraction of rounded probabilities.
The root tolerance is 1e-10 in native ability units: the mass difference
must change sign within that distance on either side of the reported root.
The probability residual must also be at most 1e-8. These are numerical
checks, not statistical
uncertainty bounds. Out-of-range roots remain NA with outside_theta_range;
failed numerical checks remain NA with numerical_failure and a reason.
AndrichThreshold is the centered step offset; AdjacentThreshold is the
ability where the adjacent probabilities are equal, including the context
offset. Neither is generally the cumulative ThurstonianThreshold.
Positive discriminations order cumulative thresholds even when an intermediate
category is never most probable; ordering alone does not establish category
adequacy. SE, CI_Lower, and CI_Upper are NA and CIEligible is FALSE.
Finite calculations are retained even when the source fit needs review.
fit_readiness, settings$source_converged and the summary/plot notes retain
that distinction. Two-family values remain provisional. Unobserved contexts
are predictions under the fitted model, not an identification guarantee.
Save the complete result with saveRDS(); summary(), plot() and
plot_data() replay it without fitting, diagnostics or covariance calculation.
Interpreting output
Use this report to inspect:
where each category has highest probability across theta
where cumulative category probabilities cross .5
whether adjacent categories cross in expected order
whether probability bands look compressed (often sparse categories)
Recommended read order:
summary(out)for compact diagnostics.out$probabilities,out$expected_ogive, andout$category_informationfor custom graphics.plot(out)for a default visual check, orplot(out, type = "cumulative")to inspect cumulative probabilities.plot(out, type = "information")to inspect curve-level information. Useplot(out, type = "category_information")when category-level contributions are needed.
References
Category response curves follow Andrich's rating-scale formulation,
Masters' partial-credit model, and Muraki's generalized partial-credit
model. The Information column for GPCM uses Muraki's
item-information result obtained from Samejima's general polytomous
information formula.
Andrich, D. (1978). A rating formulation for ordered response categories. Psychometrika, 43(4), 561-573.
Masters, G. N. (1982). A Rasch model for partial credit scoring. Psychometrika, 47(2), 149-174.
Muraki, E. (1992). A generalized partial credit model: Application of an EM algorithm. Applied Psychological Measurement, 16(2), 159-176. doi:10.1177/014662169201600206
Muraki, E. (1993). Information functions of the generalized partial credit model. Applied Psychological Measurement, 17(4), 351-363. doi:10.1177/014662169301700403
Typical workflow
Fit model with
fit_mfrm().Run
category_curves_report()with suitabletheta_points.Use
summary()andplot(); export tables for manuscripts/dashboard use.plot(out)gives a four-panel overview. Usepreset = "monochrome"for grayscale/line-type output andboundary_status = "none"when cumulative.5boundary lines should be suppressed.plot(out, type = "category_probability")andplot(out, type = "conditional_probability")are explicit aliases for the same category-probability curves astype = "ccc". The historical conditional alias is ordinary P(k), not the FACETS adjacent-conditional curve P(k)/(P(k-1)+P(k)); prefer the unambiguous"ccc"name. Useplot_data(out, component = "plot_long")when rebuilding the curves with ggplot2, plotly, or another R graphics system.
Examples
# Saved synthetic JML/RSM analysis of example_core.
# For your own data, fit a model first; see ?fit_mfrm.
example_fit <- readRDS(system.file("examples", "core-jml.rds", package = "mfrmr"))
fit <- example_fit$fit
out <- category_curves_report(fit, theta_points = 101)
summary(out)
#> mfrmr Category Curves Summary
#> Class: mfrm_category_curves
#> Components: 10
#>
#> Curve grid summary
#> Metric Value
#> curve_groups 1.000
#> theta_points 101.000
#> categories 4.000
#> legacy_graph_rows 101.000
#> expected_ogive_rows 101.000
#> probability_rows 404.000
#> probability_columns 4.000
#> cumulative_probability_rows 808.000
#> cumulative_boundary_rows 3.000
#> category_information_rows 404.000
#> boundary_rows_needing_review 0.000
#> boundary_rows_outside_theta_range 0.000
#> boundary_rows_with_multiple_crossings 0.000
#> max_total_information 0.661
#> max_category_information 0.283
#>
#> Boundary / curve rows: cumulative_boundaries
#> CurveGroup BoundaryOrder LowerOrEqualCategory AboveCategory ThresholdCategory
#> Common 1 1 2 2
#> Common 2 2 3 3
#> Common 3 3 4 4
#> CumulativeDirection TargetProbability ThurstonianThreshold InThetaRange
#> at_or_below 0.5 -1.540 TRUE
#> at_or_below 0.5 -0.034 TRUE
#> at_or_below 0.5 1.572 TRUE
#> CrossingCount BoundaryStatus BoundaryLabel
#> 1 in_range P(X <= 1) = 0.5
#> 1 in_range P(X <= 2) = 0.5
#> 1 in_range P(X <= 3) = 0.5
#>
#> Settings
#> Setting
#> theta_range
#> theta_points
#> digits
#> curve_basis
#> predictor_offset
#> curve_basis_description
#> include_fixed
#> fixed_max_rows
#> scales
#> Value
#> -6, 6
#> 101
#> 4
#> zero_additive_facet_profile
#> 0
#> Estimated step and, for GPCM, slope parameters are retained; additive facet main effects and fitted interactions are fixed at zero.
#> FALSE
#> 400
#> <table 1x2>
#>
#> Notes
#> - Category-curve bundle with probabilities, cumulative probabilities, total
#> information, and category-specific information.
#> - Category-specific information contributions sum to total information at the
#> same curve and theta point.
#> - Cumulative .5 boundary rows are in range with a single crossing where
#> reported.
head(out$probabilities[, c("CurveGroup", "Theta", "Category", "Probability")])
#> CurveGroup Theta Category Probability
#> 1 Common -6.00 1 0.9907
#> 2 Common -5.88 1 0.9896
#> 3 Common -5.76 1 0.9882
#> 4 Common -5.64 1 0.9868
#> 5 Common -5.52 1 0.9851
#> 6 Common -5.40 1 0.9832
p_overview <- plot(out, draw = FALSE)
p_overview$data$plot
#> NULL
p_cum <- plot(out, type = "cumulative", draw = FALSE)
head(p_cum$data$cumulative_boundaries)
#> NULL
p_info <- plot(out, type = "category_information", draw = FALSE)
head(p_info$data$category_information)
#> NULL
curve_long <- plot_data(out, component = "plot_long")
head(curve_long[, c("PlotType", "Theta", "Series", "Value")])
#> PlotType Theta Series Value
#> 1 ogive -6.00 Common 1.0093
#> 2 ogive -5.88 Common 1.0105
#> 3 ogive -5.76 Common 1.0118
#> 4 ogive -5.64 Common 1.0133
#> 5 ogive -5.52 Common 1.0150
#> 6 ogive -5.40 Common 1.0169
if (FALSE) { # \dontrun{
# Which cumulative boundaries apply to a specified rater and criterion?
ratings <- load_mfrmr_data("example_core")
gpcm_fit <- fit_mfrm(ratings, "Person", c("Rater", "Criterion"), "Score",
model = "GPCM", method = "MML", step_facet = "Criterion",
slope_facet = "Rater", rating_min = 1, rating_max = 4,
category_policy = "preserve", quad_points = 61)
contexts <- unique(ratings[c("Rater", "Criterion")])[1:2, ]
curves <- category_curves_report(gpcm_fit, newdata = contexts)
curves$cumulative_boundaries[, c("CurveGroup", "UpperCategoryLabel",
"ThurstonianThreshold", "AdjacentThreshold", "BoundaryStatus")]
summary(curves) # Inspect source readiness and interval limitations too.
plot(curves, type = "cumulative")
} # }