--- title: "Rasch analysis of comparative judgements" author: "Joshua A. McGrane" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Rasch analysis of comparative judgements} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4.2) options(digits = 4) source("precomputed.R") ``` ```{r library} library(rasch) ``` ## Fit the Bradley--Terry--Luce model Paired-comparison data record two objects and an observed preference. In the Bradley--Terry--Luce model (Bradley and Terry 1952; Luce 1959), the log odds of choosing object A over object B are their location difference: $$ P(A\succ B)=\frac{\exp(\beta_A)} {\exp(\beta_A)+\exp(\beta_B)}. $$ This is the conditional form of the dichotomous Rasch model (Rasch 1960; Andrich 1978). The comparison graph must connect all objects; otherwise their relative locations are not identified. For ordered comparisons, `btl` fits the adjacent-category extension $$ \log\frac{P(Y=r)}{P(Y=r-1)}=\beta_A-\beta_B-\tau_r, $$ with thresholds symmetric under reversal of presentation order (Tutz 1986). ```{r fit} d <- simulate_btl( n_objects = 8, n_judges = 48, reps_per_pair = 84, erratic_judges = 2 / 48, dependence = list(exposure = 0.7, carry_over = 0), seed = 2 ) truth <- attr(d, "truth") judge_number <- as.integer(sub("^J", "", d$judge)) d$panel <- factor(ifelse(judge_number %% 2L, "panel A", "panel B")) d$experience <- factor(ifelse(judge_number <= 24, "experienced", "novice")) fit <- btl(d, object_a = "object_a", object_b = "object_b", winner = "winner", judge = "judge", order = "order") fit fit$objects ``` Panel and experience are crossed judge factors with no planted object difference. Two judges answer at random, an exposure effect is added in judgment order, and carry-over remains zero. Residual dimensionality is examined separately below because combining a second attribute with this history model would make the carry-over estimate conditional on a deliberately misspecified common scale. Judge residuals describe agreement with the common object scale; they are not person measures. Object fit, judge fit, targeting, and comparison information address different parts of the design and should be considered together. ```{r judges} judge_order <- order(abs(fit$judges$fit_resid), decreasing = TRUE) head(fit$judges[judge_order, ], 6) erratic_fit <- fit$judges[match(truth$erratic, fit$judges$judge), ] erratic_judge <- erratic_fit$judge[ which.max(abs(erratic_fit$fit_resid)) ] judge_surprise(fit, erratic_judge) btl_information(fit) ``` ## Bootstrap goodness of fit The pairwise chi-square and the object and judge residuals use approximate references. `fit_bootstrap()` generates outcomes on the observed comparison design and refits the model. It reports the whole-model probability and joint adjusted probabilities for pairs, objects and judges. Ordered thresholds and history effects are retained; history-dependent outcomes are generated in sequence. Report the usable, non-converged, and other-failure counts. A maxT adjustment is unavailable for the complete family if one testable member lacks a usable joint null. Each statistic is adjusted under the fitted global null. The adjustment does not guarantee familywise error among fitting pairs, objects or judges when another member departs from the model. With exposure and carry-over fitted jointly at 30 judges, a 1,000-dataset dichotomous study gave 4.8% total error and 3.9%, 4.8% and 5.5% adjusted familywise error for pairs, objects and judges. A 200-dataset four-category study gave 3.0%, 3.5%, 6.0% and 6.0%. All 238,800 refits were usable. ```{r btl-fit-bootstrap, eval = FALSE} boot <- fit_bootstrap(fit, B = 999, seed = 2026) boot$total head(boot$pairs[order(boot$pairs$chisq_p_boot_adj), c( "object_a", "object_b", "chisq", "chisq_p_boot_adj" )]) head(boot$objects[order(boot$objects$fit_resid_p_boot_adj), c( "object", "fit_resid", "fit_resid_p_boot_adj" )]) head(boot$judges[order(boot$judges$fit_resid_p_boot_adj), c( "judge", "fit_resid", "fit_resid_p_boot_adj" )]) ``` ## Check transitivity and residual structure Circular triads (Kendall and Babington Smith 1940) identify local contradictions in the observed ordering. Residual dimensionality asks whether comparisons contain a structured second attribute after the primary scale is fitted. When a judgment-order column is supplied, `fit$dependence` reports the exposure and carry-over effects; adding `position = TRUE` reports the position effect alongside them. Use `p_adj`, which applies Holm's correction across the declared effects even when one probability is withheld; `p` is retained as the raw probability. ```{r structure} tr <- btl_transitivity(fit) tr dim_data <- simulate_btl( n_objects = 8, n_judges = 48, reps_per_pair = 84, second_attribute = list(rho = 0.3), seed = 47 ) dim_fit <- btl(dim_data, object_a = "object_a", object_b = "object_b", winner = "winner", judge = "judge") ``` The dimensionality reference is precomputed in the distributed vignette. The following call reproduces it. ```{r dimensions, eval = recompute} dimensions <- btl_dimensionality(dim_fit, reps = 20, seed = 2026, independent_comparisons = TRUE) dimensions ``` ```{r dimensions-precomputed, echo = FALSE} if (!recompute) { dimensions <- vignette_result("paired-comparisons")$dimensions dimensions } ``` ```{r plot-transitivity, fig.alt = "Per-judge consistency of the paired comparisons."} plot_btl_transitivity(tr) ``` ```{r plot-scree, fig.alt = "Residual bimension strengths against the simulated noise reference."} plot_btl_scree(dimensions) ``` The decomposition compares observed and fitted expected points within each object pair. For ordered responses this includes the category thresholds; fitted position and history effects also enter the expectation. The simulated comparisons in this example are independent, so the call explicitly requests that reference. For judge-clustered data, the default shows only the residual decomposition: clustered repetition can look like dimensional structure against an independent-comparison reference. The shaded area runs from the simulated mean to the finite-simulation 5% upper limit. Twenty replicates keep this example quick; a final sensitivity analysis needs enough replicates to stabilise the reference distribution. An ordered analysis needs one row per comparison: a replication count does not retain the sequence needed to construct exposure and carry-over histories, so `btl()` refuses that combination. ## Examine DIF across judge groups `btl_dif` tests whether object locations differ across nominated judge factors. The omnibus analysis uses judges as the independent units. A significant term is resolved into factor-specific object locations and pairwise logit differences. HC3 covariance allows the precision of judge means to vary with their comparison workloads. Omnibus and pairwise inference require at least eight judges and eight effective judges in every relevant factor cell; estimates remain descriptive below that boundary. The tables report both counts. Panel and experience are null in this example, so an adjusted flag would be a false positive rather than a planted result. ```{r btl-dif-example} bd <- btl_dif(fit, d[c("panel", "experience")]) bd_order <- order(bd$summary$p_uniform_adj) head(bd$summary[bd_order, c( "object", "term", "F_uniform", "p_uniform_adj", "eta2_uniform", "min_judges", "min_effective_judges" )], 6) ``` An optional fitted-outcome bootstrap repeats the comparison fit and the complete object-by-term family. It is a sensitivity analysis beside the primary HC3 residual analysis. Its familywise probabilities refer to the fitted global invariant null. ```{r btl-dif-bootstrap, eval = FALSE} bd_boot <- dif_bootstrap(fit, bd, B = 999, seed = 2026) bd_boot$summary ``` ## Equate panels through common objects `btl_equate` aligns two calibrations that share at least two objects. Two common objects identify a descriptive origin shift; at least three with usable covariance information are needed for object-drift tests. For two fitted calibrations, drift inference is withheld until independent judges and comparisons are stated explicitly. The shift is precision-weighted when at least two common objects have usable variances. If they do not, the function uses the unweighted mean location difference, labels it in `shift_method`, and keeps the link descriptive. An exact common anchor fixes the shift even when other common objects have unavailable SEs. ```{r equating-example, eval = FALSE} eq <- btl_equate(current_panel, reference_panel, independent = TRUE) eq$table eq$equated # reference panel on the current panel's origin ``` The SEs in `eq$equated` include uncertainty in the shift and its covariance with the reference locations. They are withheld when that joint uncertainty is unavailable. The table retains its covariance and finite-sample degrees of freedom as attributes; it is not independent of either input calibration. A bank table may be used in place of `reference_panel`. Marginal object standard errors are not enough for drift tests because they omit the covariance created by the bank's fitted origin. Attach the joint matrix as `attr(bank, "cov_location")`, ordered like the bank rows or named by object; otherwise the alignment is descriptive. A bank treated as fixed may instead carry zero standard errors. Dependent panels require a joint or paired bootstrap outside this function. ## Linked frames for paired comparisons `btl_efrm` combines the comparative judgement model with Humphry's extended frame of reference structure (Humphry and Andrich 2008). Judges belong to panels, and objects belong to linked sets. For object $k$ in set $s$, $$ v_k=\alpha_s\beta_k+\kappa_s. $$ A same-set comparison in panel $g$ has logit $\phi_g(\beta_A-\beta_B)$; a cross-set comparison has logit $\phi_g(v_A-v_B)$. As in Humphry and Andrich (2008, eq. 15), $\phi_g$ and $\alpha_s$ are unit ratios: the common reference unit over the frame's own unit. The panel units $\phi_g$ have geometric mean one. For the linked item sets, the first set fixes $\alpha_1=1$ and $\kappa_1=0$, defining the unit and origin of the common scale; the remaining set transformations are estimated relative to it. A value above one denotes a finer unit and steeper comparisons. Cross-set comparisons identify the set units and origins. The cross-set likelihood holds the within-set locations and panel units fixed. It estimates the set transformations directly from the comparison outcomes and does not use the finite-grid person-distribution link in `rasch_efrm()`. ```{r efrm} de <- simulate_btl_efrm( n_objects_per_set = 5, n_sets = 2, n_judges_per_panel = 6, n_panels = 2, reps_within = 15, reps_cross = 15, set_units = c(1, 1.3), set_origins = c(0, 0.6), seed = 9 ) ef <- btl_efrm( de, "object_a", "object_b", winner = "winner", judge = "judge", panels = "panel", object_sets = attr(de, "truth")$object_sets, se_method = "conditional" ) ef$phi_table ef$alpha_table ef$kappa_table ``` The default judge bootstrap resamples judges within panels and refits both stages. A set's panel-ratio fit must pass an exact likelihood-curvature check before entering the reconciliation. Failed sets are omitted from that reconciliation; estimation stops if the remaining sets cannot link all panels. Cross-set outcomes are checked for complete and quasi-complete separation; neither supports a finite link. Reaching the iteration limit is not convergence. Saved frame fits without a current likelihood-check record require refitting before reopening in the app; their source files remain unchanged. The parametric bootstrap (`se_method = "bootstrap"`) draws independent outcomes from the fitted model. The conditional option used above reports estimates and conditional standard errors but withholds probabilities because it does not propagate stage-one uncertainty into the set link. With either bootstrap, omnibus probabilities are Holm-adjusted across the three unit families. Individual panel-unit, set-unit and set-origin contrasts form one separate Holm-adjusted follow-up family. Judge-bootstrap tests require at least six judges and 5.5 effective judges in every panel, and eight of each on a set link. The pooled pairwise chi-square is retained as a descriptive fit summary, but its row-based probability is not reported because judges contribute repeated comparisons. Judge resamples are distributed over four workers by default, or fewer where the system imposes a lower limit. Set `seed` to reproduce the resamples. The parametric bootstrap remains serial because its refits are inexpensive. In the application, frame estimation runs in the background and may be cancelled. With six judges per panel and 20 repetitions per pair, a 1,000-fit null study gave raw marginal rejection of 3.3 per cent for panel units, 6.7 per cent for set units and 6.0 per cent for set origins. Holm familywise rejection was 3.9 per cent across the omnibus decisions and 3.0 per cent across their follow-ups; set-unit coverage was 0.900. This design lies in the caution band reported by the fit. With 12 judges per panel and the default 200 resamples, raw set-unit rejection was 4.6 per cent, the empirical-to-reported SE ratio was 0.992 and coverage was 0.934 over 500 null fits. With more within- and cross-set information, finite-sample attenuation of the set-unit estimate declined: log-unit bias was -0.041 at 20 repetitions per pair, -0.016 at 50 and -0.006 at 100. Reported decisions use Holm adjustment across the three omnibus tests; the individual unit contrasts form a separate Holm-adjusted family. ## The same analysis in the application `rasch::run_app()` fits comparative judgement designs alongside the other models, and the sequence above maps onto its panels. Choosing **Comparative Judgement** as the model changes what the **Data** panel asks for: instead of item columns it wants the two object columns and the observed preference, with the judge column and any judge factors beside them. A judgment-order column is assigned here too, which is what makes the exposure and carry-over terms available later. ```{r app-cj-data, echo = FALSE, out.width = "100%", fig.alt = "The Data panel with the comparative judgement roles assigned: the two object columns, the observed preference, and the judge column."} knitr::include_graphics("figures/app-cj-data.png") ``` **Summary** reports what `btl()` prints: the design counts, the pairwise chi-square, the object separation index, and the within-judge exposure and carry-over effects when a judgment-order column was assigned --- with inference withheld on designs too small to support it, stated as a note rather than silently. A design whose comparison graph does not connect is refused for the same reason it is refused in the code --- the relative locations are not identified. The fit-bootstrap button runs the fitted-design bootstrap in the background and adds adjusted probabilities to the pair, object and judge tables. It can be cancelled without losing the fitted model. ```{r app-cj-summary, echo = FALSE, out.width = "100%", fig.alt = "The Summary panel for a comparative judgement fit: the design counts, object separation and pairwise fit tiles, and the test-of-fit table."} knitr::include_graphics("figures/app-cj-summary.png") ``` **Items** becomes the object panel. The caterpillar plot orders the objects by location with their confidence intervals, which is the graphical form of `fit$objects`; the panels beside it hold the symmetric thresholds, the threshold components and the category probability curves for an ordered design, and the pairwise fit that `btl_transitivity()` summarises. ```{r app-cj-items, echo = FALSE, out.width = "100%", fig.alt = "The object panel, showing the caterpillar plot of object locations with confidence intervals."} knitr::include_graphics("figures/app-cj-items.png") ``` **Persons** becomes the judge panel: judge fit, the surprise index that `judge_surprise()` returns, and the consistency of each judge's comparisons. These describe agreement with the common object scale, and are not person measures --- the same caution the code section makes. ```{r app-cj-judges, echo = FALSE, out.width = "100%", fig.alt = "The judge panel, showing judge fit and consistency against the common object scale."} knitr::include_graphics("figures/app-cj-judges.png") ``` Under **Invariance**, the DIF panel runs `btl_dif()` over the nominated judge factors. It reports the omnibus test with judges as the independent units, the factor-specific object locations and their pairwise differences in logits, and the raw and effective judge counts in the least-supported cells --- so a design below the eight-judge boundary shows why its inference is withheld rather than simply returning nothing. ```{r app-cj-dif, echo = FALSE, out.width = "100%", fig.alt = "The DIF panel for a comparative judgement fit, showing the omnibus test across judge groups with the judge counts per level."} knitr::include_graphics("figures/app-cj-dif.png") ``` Equating and extended frames sit in the same menu. Frame estimation runs in the background and can be cancelled, because the judge bootstrap is the expensive part of the analysis. As in the Rasch panels, every table and figure carries an **R code** disclosure holding the call that produced it. ## A worked analysis on real data The [party blocs case study](https://drjoshmcgrane.github.io/rasch/articles/articles/party-blocs-crisis.html) fits the comparative judgement frame model to real paired comparisons between political parties, with judge panels and ideological blocs as frames. Its script ships with the package under `casestudies`. ## References Andrich, D. (1978). Relationships between the Thurstone and Rasch approaches to item scaling. *Applied Psychological Measurement*, 2, 451--462. Bradley, R. A., and Terry, M. E. (1952). Rank analysis of incomplete block designs: I. The method of paired comparisons. *Biometrika*, 39, 324--345. Humphry, S. M., and Andrich, D. (2008). Understanding the unit in the Rasch model. *Journal of Applied Measurement*, 9(3), 249--264. Kendall, M. G., and Babington Smith, B. (1940). On the method of paired comparisons. *Biometrika*, 31(3/4), 324--345. Tutz, G. (1986). Bradley-Terry-Luce models with an ordered response. *Journal of Mathematical Psychology*, 30(3), 306--316. Luce, R. D. (1959). *Individual Choice Behavior: A Theoretical Analysis*. Wiley. Rasch, G. (1960). *Probabilistic Models for Some Intelligence and Attainment Tests*. Copenhagen: Danish Institute for Educational Research. (Expanded edition, 1980, Chicago: University of Chicago Press.)