Build a screened linking chain across ordered calibrations
Source:R/api-advanced.R
build_equating_chain.RdLinks a series of calibration waves by computing mean offsets between adjacent pairs of fits. Common linking elements (e.g., raters or items that appear in consecutive administrations) are used to estimate the scale shift. Cumulative offsets place all waves on a common metric anchored to the first wave. The procedure is intended as a practical screened linking aid, not as a full general-purpose equating framework.
Arguments
- fits
Named list of
mfrm_fitobjects in chain order.- anchor_facets
Character vector of facets to use as linking elements.
- include_person
Include person estimates in linking.
- drift_threshold
Threshold for flagging large residuals in links.
- x
An
mfrm_equating_chainobject.- ...
Ignored.
- object
An
mfrm_equating_chainobject (forsummary).
Value
Object of class mfrm_equating_chain with components:
- links
Tibble of link-level statistics (offset, SD, etc.).
- cumulative
Tibble of cumulative offsets per wave.
- element_detail
Tibble of element-level linking details.
- common_by_facet
Tibble of retained common-element counts by facet.
- config
List of analysis configuration.
Details
The screened linking chain uses a screened link-offset method. For each pair of adjacent waves \((A, B)\), the function:
Identifies common linking elements (facet levels present in both fits).
Computes per-element differences: $$d_e = \hat{\delta}_{e,B} - \hat{\delta}_{e,A}$$
Computes a preliminary link offset using the inverse-variance weighted mean of these differences when standard errors are available (otherwise an unweighted mean).
Screens out elements whose residual from that preliminary offset exceeds
drift_threshold, then recomputes the final offset on the retained set.Records
Offset_SD(standard deviation of retained residuals) andMax_Residual(maximum absolute deviation from the mean) as indicators of link quality.Flags links with fewer than 5 retained common elements in any linking facet as having thin support.
Cumulative offsets are computed by chaining link offsets from Wave 1 forward, placing all waves onto the metric of the first wave.
Elements whose per-link residual exceeds drift_threshold are flagged
in $element_detail$Flag. A high Offset_SD, many flagged elements, or a
thin retained anchor set signals an unstable link that may compromise the
resulting scale placement.
Which function should I use?
Use
anchor_to_baseline()for a single new wave anchored to a known baseline.Use
detect_anchor_drift()when you want direct comparison against one reference wave.Use
build_equating_chain()when no single wave should dominate and you want ordered, adjacent links across the series.
Interpreting output
$links: one row per adjacent pair withFrom,To,N_Common,N_Retained,Offset_Prelim,Offset,Offset_SD, andMax_Residual. SmallOffset_SDrelative to the offset indicates a consistent shift across elements.LinkSupportAdequate = FALSEmeans at least one linking facet retained fewer than 5 common elements after screening.$cumulative: one row per wave with its cumulative offset from Wave 1. Wave 1 always has offset 0.$element_detail: per-element linking statistics (estimate in each wave, difference, residual from mean offset, and flag status). Flagged elements may indicate DIF or rater re-training effects.$common_by_facet: retained common-element counts by linking facet for each adjacent link.$config: records wave names and analysis parameters.Read
linksbeforecumulative: weak adjacent links can make later cumulative offsets less trustworthy.
Typical workflow
Fit each administration wave separately:
fit_a <- fit_mfrm(...).Combine into an ordered named list:
fits <- list(Spring23 = fit_s, Fall23 = fit_f, Spring24 = fit_s2).Call
chain <- build_equating_chain(fits).Review
summary(chain)for link quality.Visualize with
plot_anchor_drift(chain, type = "chain").For problematic links, investigate flagged elements in
chain$element_detailand consider removing them from the anchor set.
Examples
toy <- load_mfrmr_data("example_core")
people <- unique(toy$Person)
d1 <- toy[toy$Person %in% people[1:12], , drop = FALSE]
d2 <- toy[toy$Person %in% people[13:24], , drop = FALSE]
fit1 <- fit_mfrm(d1, "Person", c("Rater", "Criterion"), "Score",
method = "JML", maxit = 10)
#> Warning: Optimizer did not fully converge (code = 1). Consider increasing maxit (current: 10) or relaxing reltol (current: 1e-06).
fit2 <- fit_mfrm(d2, "Person", c("Rater", "Criterion"), "Score",
method = "JML", maxit = 10)
#> Warning: Optimizer did not fully converge (code = 1). Consider increasing maxit (current: 10) or relaxing reltol (current: 1e-06).
chain <- build_equating_chain(list(Form1 = fit1, Form2 = fit2))
#> Warning: Thin linking support between 'Form1' and 'Form2': fewer than 5 retained common elements in Criterion, Rater.
summary(chain)
#> --- Screened Linking Chain ---
#> Method: screened_common_element_alignment | Intended use: screened_linking_aid
#> Links: 1 | Waves: Form1 -> Form2
#>
#> Link details:
#> Link From To N_Common N_Retained Min_Common_Per_Facet
#> 1 Form1 Form2 8 7 4
#> Min_Retained_Per_Facet Offset_Prelim Offset Offset_SD Max_Residual
#> 3 0.00203 -0.0705 0.176 0.58
#> LinkSupportAdequate Offset_Method
#> FALSE inverse_variance
#>
#> Retained common elements by facet:
#> Link From To Facet N_Common N_Retained GuidelineMinCommon
#> 1 Form1 Form2 Criterion 4 4 5
#> 1 Form1 Form2 Rater 4 3 5
#> LinkSupportAdequate
#> FALSE
#> FALSE
#>
#> Cumulative offsets:
#> Wave Cumulative_Offset
#> Form1 0.0000
#> Form2 -0.0705
#>
#> Flagged linking elements: 1
chain$cumulative
#> # A tibble: 2 × 2
#> Wave Cumulative_Offset
#> <chr> <dbl>
#> 1 Form1 0
#> 2 Form2 -0.0705