| Title: | Voting Methods for Ranked, Rated and Approval Ballots |
|---|---|
| Description: | Implements a range of voting methods and electoral systems for determining election winners, including the D21 method with and without minus votes (Janecek, <https://www.ih21.org/en/d21-janecek-method>), first-past-the-post, two-round runoff, instant runoff, the Borda count, approval voting, majority judgement and the Condorcet method. The functions accept several ballot formats - ranking, cardinal utilities, approvals and scores - with automatic detection of the input type, configurable tie-breaking and tidy summaries of the results. |
| Authors: | Ivan Iakimov [aut, cre] |
| Maintainer: | Ivan Iakimov <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-07-22 10:49:40 UTC |
| Source: | https://github.com/cran/electsys21 |
Runs an election under Approval Voting, where each voter may approve any
number of candidates and the candidate with the most approvals wins. When
the input is not already a binary approval matrix, rule converts the
rankings or utility scores into approvals. Approvals are then summed across
voters and ties for the top approval count are resolved according to ties.
approval( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, ties = c("random", "lexicographic", "all"), return_approvals = FALSE )approval( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, ties = c("random", "lexicographic", "all"), return_approvals = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
rule |
The approval rule used to convert non-approval ballots (ranks or
utilities) into approvals; ignored when the input is already an approval
matrix. May be a preset string – |
threshold |
Single numeric value (or |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_approvals |
Logical; if |
An object of class "approval_result", a list with elements
summary (a data frame of candidates, approval counts, percentages and
ranks), winners (the winning candidate name(s)), n_voters, n_valid,
n_candidates and method (a list recording the inferred type, rule,
ties and threshold). When return_approvals = TRUE, the derived binary
approval matrix is also returned as $approvals. Print the object or call
summary() on it for a formatted results table.
# Direct approval ballots ballots <- gen_approvals(n_voters = 50, n_candidates = 4, seed = 1) approval(ballots) # Utility ballots converted to approvals via a rule scores <- gen_utilities(n_voters = 50, n_candidates = 4, seed = 1) approval(scores, type = "utility", rule = "mean")# Direct approval ballots ballots <- gen_approvals(n_voters = 50, n_candidates = 4, seed = 1) approval(ballots) # Utility ballots converted to approvals via a rule scores <- gen_utilities(n_voters = 50, n_candidates = 4, seed = 1) approval(scores, type = "utility", rule = "mean")
Runs an election using the Borda Count. Each voter's ballot is turned into
a ranking and each candidate earns points according to their position: with
n candidates, a candidate ranked first receives n - 1 points, the second
n - 2, down to 0 for the last. Points are summed across all voters and
the candidate with the highest total wins. Utility and approval ballots are
converted to rankings internally before scoring.
borda( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE, return_scores = FALSE )borda( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE, return_scores = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_ranks |
Logical. If |
return_scores |
Logical. If |
An object of class "borda_result", a list with elements:
summary (a data frame of candidates ordered by total score, with columns
candidate, score, percentage and rank), winners (a character
vector of the winning candidate name(s), or character(0) if no voter
provided a valid preference), n_voters, n_valid, n_candidates, and
method (a list recording the inferred type and the ties rule).
Optionally includes $ranks (when return_ranks = TRUE) and $scores
(when return_scores = TRUE). Print the object or call summary() on it
for a formatted results table.
fptp(), condorcet(), approval()
ballots <- gen_ranks(n_voters = 50, n_candidates = 4, seed = 1) borda(ballots) # Resolve ties alphabetically and keep the derived ranking matrix borda(ballots, ties = "lexicographic", return_ranks = TRUE)ballots <- gen_ranks(n_voters = 50, n_candidates = 4, seed = 1) borda(ballots) # Resolve ties alphabetically and keep the derived ranking matrix borda(ballots, ties = "lexicographic", return_ranks = TRUE)
Determines the winner by comparing every candidate head-to-head against
every other candidate. In each pairwise contest the candidate preferred by a
strict majority of voters wins; the Condorcet winner is the candidate who
beats all others. Such a candidate need not exist, in which case winners is
empty. A Copeland-style score (wins minus losses) is always computed so the
full field is ordered even when no Condorcet winner exists.
condorcet( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_pairwise = FALSE )condorcet( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_pairwise = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_pairwise |
Logical. If |
An object of class "condorcet_result", a list with elements
summary (a data frame of candidates ordered by Copeland score, with
columns candidate, wins, losses, ties, rank and status),
winners (name(s) of the Condorcet winner, or character(0) if none),
losers (name(s) of the Condorcet loser, or character(0) if none),
n_voters, n_valid, n_candidates, and method (a list recording the
inferred type and the ties rule). When return_pairwise = TRUE the
elements pairwise and pairwise_margins are also included. Print the
object or call summary() on it for a formatted results table.
ballots <- gen_ranks(n_voters = 30, n_candidates = 4, seed = 1) condorcet(ballots) condorcet(ballots, return_pairwise = TRUE)ballots <- gen_ranks(n_voters = 30, n_candidates = 4, seed = 1) condorcet(ballots) condorcet(ballots, return_pairwise = TRUE)
Runs the D21 method. Each voter is granted a fixed number of plus votes
(1, 2, or 3, depending on the number of candidates) and uses them to support
their most preferred candidates. When the input is not already an approval
matrix, each voter's eligible candidates are first selected from rankings or
utility scores using a configurable rule, then capped to the allowed number
of plus votes, keeping the highest-scored candidates. The candidate with the
most plus votes across all voters wins.
d21( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, ties = c("random", "lexicographic", "all"), overflow = c("random", "lexicographic"), return_approvals = FALSE )d21( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, ties = c("random", "lexicographic", "all"), overflow = c("random", "lexicographic"), return_approvals = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
rule |
The approval rule used to convert non-approval ballots (ranks or
utilities) into eligibility for plus votes; ignored when the input is
already an approval matrix. May be a preset string – |
threshold |
Single numeric value (or |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
overflow |
Character string giving the tie-breaking rule applied per
voter when more candidates are eligible than the plus-vote budget allows
(and at the |
return_approvals |
Logical; if |
An object of class "d21_result": a list with elements summary
(a data frame of candidates, plus-vote counts, percentages and ranks),
winners (name(s) of the winning candidate(s)), n_voters, n_valid,
n_candidates, and method (a list recording type, rule, ties,
threshold, plus_votes and overflow). If return_approvals = TRUE,
the element approvals (the derived approval matrix) is also included.
Print the object or call summary() on it for a formatted results table.
d21_minus(), approval(), fptp()
u <- gen_utilities(n_voters = 40, n_candidates = 5, seed = 1) d21(u, type = "utility") r <- gen_ranks(n_voters = 40, n_candidates = 5, seed = 1) d21(r, type = "rank", rule = "topk", threshold = 2)u <- gen_utilities(n_voters = 40, n_candidates = 5, seed = 1) d21(u, type = "utility") r <- gen_ranks(n_voters = 40, n_candidates = 5, seed = 1) d21(r, type = "rank", rule = "topk", threshold = 2)
Runs the single-winner D21 method with minus votes. Each voter receives a
fixed number of plus votes (1, 2, or 3, depending on the number of
candidates) to support their most preferred candidates, and voters who use at
least two plus votes may additionally cast one minus vote against the
candidate they most oppose. Eligibility for plus votes is derived from ranks
or utility scores via a configurable rule and then capped to the allowed
number; the minus vote targets the lowest-scored candidate the voter did not
plus. The candidate with the highest net total wins.
d21_minus( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, minus_prob = 1, ties = c("random", "lexicographic", "all"), overflow = c("random", "lexicographic"), return_approvals = FALSE )d21_minus( x, type = c("auto", "rank", "utility", "approval"), rule = c("mean", "median", "topk", "topp", "quantile", "above0", "nonzero"), threshold = NULL, minus_prob = 1, ties = c("random", "lexicographic", "all"), overflow = c("random", "lexicographic"), return_approvals = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
rule |
The approval rule used to convert non-approval ballots (ranks or
utilities) into eligibility for plus votes; ignored when the input is
already an approval matrix. May be a preset string – |
threshold |
Single numeric value (or |
minus_prob |
Numeric in |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
overflow |
Character string giving the tie-breaking rule used at the
plus-vote cap, within the |
return_approvals |
Logical. If |
An object of class "d21_minus_result": a list with elements
summary (a data frame of per-candidate plus, minus and total counts with
percentages and ranks), winners, n_voters, n_valid, n_candidates
and method (a list recording type, rule, ties, threshold,
plus_votes, minus_votes, minus_prob and overflow). When
return_approvals = TRUE, the elements plus_matrix, minus_matrix and
total_matrix are also included. Print the object or call summary() on
it for a formatted results table.
# Utility ballots with 5 candidates (2 plus votes, 1 minus vote) u <- gen_utilities(n_voters = 50, n_candidates = 5, seed = 1) d21_minus(u) # Ranking ballots, eligible = explicitly ranked candidates r <- gen_ranks(n_voters = 50, n_candidates = 6, seed = 1) d21_minus(r, rule = "above0", ties = "lexicographic") # Suppress the minus phase (equivalent to plain D21) d21_minus(u, minus_prob = 0)# Utility ballots with 5 candidates (2 plus votes, 1 minus vote) u <- gen_utilities(n_voters = 50, n_candidates = 5, seed = 1) d21_minus(u) # Ranking ballots, eligible = explicitly ranked candidates r <- gen_ranks(n_voters = 50, n_candidates = 6, seed = 1) d21_minus(r, rule = "above0", ties = "lexicographic") # Suppress the minus phase (equivalent to plain D21) d21_minus(u, minus_prob = 0)
Runs a First-Past-The-Post election. Each voter contributes a single
first-preference vote, and the candidate receiving the most first-preference
votes wins. The function accepts ranking, utility (score) or approval
ballots, reduces each ballot to its single top choice, tallies the
first-preference votes, and resolves any tie for the lead according to
ties.
fptp( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE )fptp( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_ranks |
Logical. If |
An object of class "fptp_result", a list with elements:
summary: a data frame of candidates ordered by votes, with columns
candidate, vote, percentage (share of valid votes) and rank.
winners: a character vector of the winning candidate name(s).
n_voters: the total number of voters (rows of x).
n_candidates: the total number of candidates (columns of x).
method: a list recording the inferred type and the ties rule used.
ranks: optionally, the derived ranking matrix, present only when
return_ranks = TRUE.
Print the object or call summary() on it for a formatted results table.
ballots <- gen_ranks(n_voters = 20, n_candidates = 4, seed = 1) fptp(ballots) fptp(ballots, ties = "lexicographic", return_ranks = TRUE)ballots <- gen_ranks(n_voters = 20, n_candidates = 4, seed = 1) fptp(ballots) fptp(ballots, ties = "lexicographic", return_ranks = TRUE)
Simulates an approval-ballot matrix for testing and demonstration. Each
candidate is independently approved by each voter with probability
p_approve, and voters may abstain entirely.
gen_approvals( n_voters, n_candidates, p_approve = 0.5, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )gen_approvals( n_voters, n_candidates, p_approve = 0.5, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )
n_voters |
Single positive integer. Number of voters (matrix rows). |
n_candidates |
Single positive integer. Number of candidates (matrix columns). |
p_approve |
Single number in |
p_abstain |
Single number in |
candidate_names |
|
voter_names |
|
seed |
|
A logical matrix with n_voters rows and n_candidates columns:
TRUE marks an approved candidate, FALSE a non-approved one, and an
abstaining voter's row is all NA. Suitable as the x argument of the
voting functions.
gen_approvals(n_voters = 5, n_candidates = 3, seed = 1) # Stricter voters approve fewer candidates gen_approvals(n_voters = 5, n_candidates = 3, p_approve = 0.25, seed = 1)gen_approvals(n_voters = 5, n_candidates = 3, seed = 1) # Stricter voters approve fewer candidates gen_approvals(n_voters = 5, n_candidates = 3, p_approve = 0.25, seed = 1)
Simulates a ranking-ballot matrix for testing and demonstration. Latent
utilities are drawn uniformly at random for every voter-candidate pair and
converted into strict ranks (rank 1 = most preferred). Optionally truncates
each ballot to its top n_ranked candidates and lets voters abstain.
gen_ranks( n_voters, n_candidates, n_ranked = NULL, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )gen_ranks( n_voters, n_candidates, n_ranked = NULL, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )
n_voters |
Single positive integer. Number of voters (matrix rows). |
n_candidates |
Single positive integer. Number of candidates (matrix columns). |
n_ranked |
|
p_abstain |
Single number in |
candidate_names |
|
voter_names |
|
seed |
|
A numeric matrix with n_voters rows and n_candidates columns
holding ranks (1 = most preferred); unranked or abstained entries are
NA. Suitable as the x argument of the voting functions.
gen_utilities(), gen_approvals()
gen_ranks(n_voters = 5, n_candidates = 3, seed = 1) # Truncated ballots: each voter ranks only their top 2 candidates gen_ranks(n_voters = 5, n_candidates = 4, n_ranked = 2, seed = 1)gen_ranks(n_voters = 5, n_candidates = 3, seed = 1) # Truncated ballots: each voter ranks only their top 2 candidates gen_ranks(n_voters = 5, n_candidates = 4, n_ranked = 2, seed = 1)
Simulates a cardinal utility-ballot matrix for testing and demonstration. Utilities are drawn uniformly at random over a configurable scale (higher = more preferred), and voters may abstain on some or all candidates.
gen_utilities( n_voters, n_candidates, scale = NULL, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )gen_utilities( n_voters, n_candidates, scale = NULL, p_abstain = 0, candidate_names = NULL, voter_names = NULL, seed = NULL )
n_voters |
Single positive integer. Number of voters (matrix rows). |
n_candidates |
Single positive integer. Number of candidates (matrix columns). |
scale |
|
p_abstain |
Single number in |
candidate_names |
|
voter_names |
|
seed |
|
A numeric matrix with n_voters rows and n_candidates columns of
utilities (higher = more preferred); abstained entries are NA. Suitable
as the x argument of the voting functions.
gen_utilities(n_voters = 5, n_candidates = 3, seed = 1) # Utilities on a 0-10 scale with occasional abstentions gen_utilities(n_voters = 5, n_candidates = 3, scale = c(0, 10), p_abstain = 0.1, seed = 1)gen_utilities(n_voters = 5, n_candidates = 3, seed = 1) # Utilities on a 0-10 scale with occasional abstentions gen_utilities(n_voters = 5, n_candidates = 3, scale = c(0, 10), p_abstain = 0.1, seed = 1)
Runs an Instant Runoff Voting. Each voter ranks the candidates, and in each
round the candidate with the fewest first-preference votes among the active
candidates is eliminated and their ballots transfer to the next-ranked active
candidate. The process repeats until a candidate's share of the active
(non-exhausted) votes strictly exceeds the majority threshold or only
one candidate remains.
irv( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), elim_ties = c("random", "lexicographic", "all"), majority = 0.5, return_history = FALSE )irv( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), elim_ties = c("random", "lexicographic", "all"), majority = 0.5, return_history = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
elim_ties |
Character string giving the tie-breaking rule applied at the
elimination step when two or more active candidates share the lowest vote
count in a round. One of |
majority |
Single numeric value in |
return_history |
Logical. If |
An object of class "irv_result", a list with the elements:
summary: a data frame with columns candidate, vote, percentage,
rank and eliminated_in_round, where vote and percentage reflect
each candidate's score at their last appearance.
winners: character vector of the winning candidate name(s).
n_voters: total number of voters.
n_valid: number of voters with at least one valid preference.
n_candidates: total number of candidates.
rounds: a list with one entry per round (active set, vote counts,
percentages, totals, exhausted ballots and candidate(s) eliminated).
counts: candidate-by-round matrix of integer vote counts.
percentages: candidate-by-round matrix of active-vote percentages.
method: a list recording type, ties, elim_ties and majority.
history: present only when return_history = TRUE (see that argument).
Print the object or call summary() on it for a formatted results table.
ballots <- gen_ranks(n_voters = 40, n_candidates = 4, seed = 1) result <- irv(ballots) result # Deterministic tie-breaking and round-by-round history irv(ballots, ties = "lexicographic", elim_ties = "lexicographic", return_history = TRUE)ballots <- gen_ranks(n_voters = 40, n_candidates = 4, seed = 1) result <- irv(ballots) result # Deterministic tie-breaking and round-by-round history irv(ballots, ties = "lexicographic", elim_ties = "lexicographic", return_history = TRUE)
Runs the Majority Judgment voting. Each voter assigns every candidate an
absolute grade on a common ordinal scale (1 = worst, K = best), and for
each candidate the median grade across voters is computed (using the lower
of the two middle grades when the count is even). The candidate with the
highest median wins. Ties on the median are resolved either by the
majority-gauge proportions or by the iterative "majority sequence" rule, which
repeatedly removes the median grade until the candidates' sequences diverge.
majority_judgement( x, type = c("auto", "rank", "utility", "approval", "score"), num_categories = NULL, median_tie_break = c("gauge", "iterative"), ties = c("random", "lexicographic", "all"), return_grades = FALSE )majority_judgement( x, type = c("auto", "rank", "utility", "approval", "score"), num_categories = NULL, median_tie_break = c("gauge", "iterative"), ties = c("random", "lexicographic", "all"), return_grades = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
num_categories |
Integer |
median_tie_break |
Character string giving the rule used to separate
candidates that share the same median grade. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_grades |
Logical. If |
An object of class "majority_judgement_result": a list with
elements summary (a data frame with columns candidate, median,
p_above, p_below and rank), winners, n_voters, n_valid,
n_candidates, grade_distribution and method (itself a list of type,
num_categories, median_tie_break and ties), plus grades when
return_grades = TRUE. Print the object or call summary() on it for a
formatted results table.
borda(), approval(), condorcet()
utilities <- gen_utilities(n_voters = 30, n_candidates = 4, seed = 1) majority_judgement(utilities) majority_judgement(utilities, num_categories = 5, median_tie_break = "iterative")utilities <- gen_utilities(n_voters = 30, n_candidates = 4, seed = 1) majority_judgement(utilities) majority_judgement(utilities, num_categories = 5, median_tie_break = "iterative")
Runs a Two-Round System election. In the first round each voter's top preference is counted; if a candidate wins more than 50% of the valid votes they are elected outright. Otherwise the top two candidates advance to a second round, where each voter's preference between the two finalists is counted and the finalist with the most votes wins. The majority threshold is strictly greater than 50%.
tworound( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE )tworound( x, type = c("auto", "rank", "utility", "approval"), ties = c("random", "lexicographic", "all"), return_ranks = FALSE )
x |
A matrix of ballots with one row per voter and one column per
candidate. Accepts a ranking matrix (rank |
type |
Character string giving the ballot type. One of |
ties |
Character string giving the tie-breaking rule applied when
several candidates are tied for a winning position. One of |
return_ranks |
Logical. If |
An object of class "tworound_result", a list with elements:
round1 (a list with the first-round summary data frame and n_valid
vote count), round2 (a list with the second-round summary and
n_valid, or NULL when the election is decided in round 1),
finalists (the candidate name(s) advancing to round 2, or
character(0)), winners (the winning candidate name(s)), n_voters,
n_candidates, and method (a list recording type, ties, and
tworound, a logical flag for whether a second round was held).
If return_ranks = TRUE, the derived ranking matrix is also attached as
$ranks. Print the object or call summary() on it for a formatted
results table.
ranks <- gen_ranks(n_voters = 30, n_candidates = 4, seed = 1) tworound(ranks) utilities <- gen_utilities(n_voters = 30, n_candidates = 4, seed = 1) tworound(utilities, type = "utility", ties = "lexicographic")ranks <- gen_ranks(n_voters = 30, n_candidates = 4, seed = 1) tworound(ranks) utilities <- gen_utilities(n_voters = 30, n_candidates = 4, seed = 1) tworound(utilities, type = "utility", ties = "lexicographic")