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 0The 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] FALSEGiven a ranking, find_approval() enumerates the
approvals compatible with it, and pa_universe() builds the
whole universe of preference-approvals on \(n\) alternatives:
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.000DIVA (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.125The 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.2168259The 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 0diva_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")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: