Introduction to TheOrdinals: consensus for preference-approvals

Alessandro Albano, Maurizio Romano

Preference-approvals

A preference-approval is a pair \((\pi, A)\) where \(\pi\) is a (weak) ranking of \(n\) alternatives and \(A\) is the subset of approved alternatives. The two components must be consistent: approved alternatives are ranked above unapproved ones, and tied alternatives share the same approval status (Definition 1 of Albano and Romano, 2026).

Throughout the package a set of \(m\) preference-approvals is stored as a numeric matrix with \(2n\) columns: the first \(n\) columns hold the ranking (positions, with ties allowed) and the last \(n\) columns hold the approval indicators (1 approved, 0 not approved). For example, four voters over four alternatives:

x <- rbind(
  c(1, 2, 3, 4, 1, 1, 0, 0),
  c(2, 1, 3, 4, 1, 0, 0, 0),
  c(1, 2, 4, 3, 1, 1, 0, 0),
  c(1, 3, 2, 4, 1, 1, 1, 0)
)
x
#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
#> [1,]    1    2    3    4    1    1    0    0
#> [2,]    2    1    3    4    1    0    0    0
#> [3,]    1    2    4    3    1    1    0    0
#> [4,]    1    3    2    4    1    1    1    0

The consistency of a single preference-approval can be checked with is_consistent():

is_consistent(c(1, 2, 3, 4), c(1, 1, 0, 0))  # admissible
#> [1] TRUE
is_consistent(c(1, 2, 3, 4), c(0, 1, 0, 0))  # not admissible
#> [1] FALSE

Given a ranking, find_approval() enumerates the approvals compatible with it, and pa_universe() builds the whole universe of preference-approvals on \(n\) alternatives:

find_approval(c(1, 2, 2, 3))
#>   V1 V2 V3 V4 V5 V6 V7 V8
#> 1  1  2  2  3  0  0  0  0
#> 2  1  2  2  3  1  0  0  0
#> 3  1  2  2  3  1  1  1  0
#> 4  1  2  2  3  1  1  1  1
dim(pa_universe(3))
#> [1] 44  6

Distance between preference-approvals

The disagreement between two preference-approvals is measured by the family of distances \(d_\lambda\) of Erdamar et al. (2014), a convex combination of the normalised Kemeny distance on the ranking component and the normalised Hamming distance on the approval component:

\[ d_\lambda\big((\pi_1, A_1), (\pi_2, A_2)\big) = \lambda \, d_R + (1 - \lambda)\, d_A, \qquad \lambda \in [0, 1]. \]

The function pref_dist() returns the matrix of pairwise distances:

round(pref_dist(x), 3)              # lambda = 0.5
#>       1     2     3     4
#> 1 0.000 0.208 0.083 0.208
#> 2 0.208 0.000 0.292 0.417
#> 3 0.083 0.292 0.000 0.292
#> 4 0.208 0.417 0.292 0.000
round(pref_dist(x, lambda = 0.8), 3)
#>       1     2     3     4
#> 1 0.000 0.183 0.133 0.183
#> 2 0.183 0.000 0.317 0.367
#> 3 0.133 0.317 0.000 0.317
#> 4 0.183 0.367 0.317 0.000

The DIVA consensus

DIVA (Divide and Conquer for Preference-Approvals) returns the preference-approval that minimises the average distance \(d_\lambda\) to the set of voters. The result is always admissible.

res <- diva(x, algorithm = "quick")
res
#> DIVA consensus preference-approval
#>   algorithm: quick | lambda: 0.5 | search: FALSE
#>   average distance (D_lambda): 0.125
#>   number of (tied) solutions: 1
#>   ranking component | approval component:
#>   X1 X2 X3 X4 X5 X6 X7 X8
#> 1  1  2  3  4  1  1  0  0
res$d_lambda
#> [1] 0.125

French Presidential Election (2002)

The dataset french_election_2002 contains the admissible preference-approvals on the 15 candidates of the first round of the 2002 French presidential election.

data(french_election_2002)
dim(french_election_2002)
#> [1] 314  30
fc <- diva(french_election_2002, algorithm = "quick")
fc$d_lambda
#> [1] 0.2168259

The consensus ranks Lionel Jospin first; the approved candidates are those whose consensus rank is among the top positions:

n <- 15
cons <- fc$consensus[1, ]
data.frame(
  candidate = colnames(french_election_2002)[1:n],
  rank      = as.numeric(cons[1:n]),
  approved  = as.numeric(cons[(n + 1):(2 * n)])
)
#>      candidate rank approved
#> 1       Bayrou    2        0
#> 2   Besancenot    3        0
#> 3       Boutin    3        0
#> 4    Cheminade    3        0
#> 5  Chevenement    2        0
#> 6       Chirac    2        0
#> 7          Hue    3        0
#> 8       Jospin    1        1
#> 9    Laguiller    3        0
#> 10     Lalonde    3        0
#> 11      Lepage    3        0
#> 12      Le Pen    3        0
#> 13     Madelin    3        0
#> 14      Mamere    2        0
#> 15     Maigret    3        0

Sensitivity to the weighting parameter

diva_sensitivity() reports the achieved average distance over a grid of \(\lambda\), showing how the relative weight of the ranking and the approval components affects the consensus:

s <- diva_sensitivity(french_election_2002)
plot(s$lambda, s$d_lambda, type = "b", pch = 19,
     xlab = expression(lambda), ylab = expression(D[lambda]),
     main = "DIVA consensus distance vs lambda")

DIVA consensus distance as a function of lambda

Formula 1 World Championship (1950)

The dataset formula1_1950 reads the classification of each of the 7 Grands Prix of the 1950 season as a ranking of 81 drivers, with the top five finishers approved. With 81 alternatives the consensus search is heavier, so the call below is shown but not executed in this vignette:

data(formula1_1950)
diva(formula1_1950, algorithm = "quick")

References