Title: | Proportional Apportionment |
---|---|
Description: | Calculate seat apportionment for legislative bodies with various methods. The algorithms include divisor or highest averages methods (e.g. Jefferson, Webster or Adams), largest remainder methods and biproportional apportionment. Gaffke, N. & Pukelsheim, F. (2008) <doi:10.1016/j.mathsocsci.2008.01.004> Oelbermann, K. F. (2016) <doi:10.1016/j.mathsocsci.2016.02.003>. |
Authors: | Flavio Poletti [aut, cre, cph] |
Maintainer: | Flavio Poletti <[email protected]> |
License: | GPL (>= 3) |
Version: | 1.5.0 |
Built: | 2024-11-03 06:22:27 UTC |
Source: | https://github.com/polettif/proporz |
Method to proportionally allocate seats among parties (or lists) and districts (or entities, regions), thus bi-proportional.
biproporz( votes_matrix, district_seats, quorum, use_list_votes = TRUE, method = "round" )
biproporz( votes_matrix, district_seats, quorum, use_list_votes = TRUE, method = "round" )
votes_matrix |
Vote count matrix with votes by party in rows and votes by district in columns |
district_seats |
Vector defining the number of seats per district. Must be the same
length as |
quorum |
Optional list of functions which take the votes_matrix and return a logical
vector that denotes for each list/party whether they reached the quorum (i.e. are
eligible for seats). The easiest way to do this is via |
use_list_votes |
By default ( |
method |
Defines which method is used to assign seats. The following methods are recommended:
It is also possible to use any divisor method name listed in |
Each party nominates a candidate list for every district. The voters vote for the parties of their district. The seat allocation is calculated in two steps:
In the so called upper apportionment
the number of seats for each party (over all districts) is determined.
Normally, the number of seats for each region are defined before the
election and are independent of the vote counts.
In the so called lower apportionment
the seats are distributed to the regional party list respecting the
results from the upper apportionment.
Parties failing to reach quorums cannot get seats. This function does not handle seat assignment to candidates.
Matrix with the same dimension as votes_matrix
containing the number of seats
with the row and column divisors stored in attributes (hidden from print, see
get_divisors()
).
The iterative process in the lower apportionment is only guaranteed to terminate with the default Sainte-Laguë/Webster method.
Gaffke, Norbert; Pukelsheim, Friedrich (2008): Divisor methods for proportional representation systems: An optimization approach to vector and matrix apportionment problems. Mathematical Social Sciences, 56 (2), 166-184.
pukelsheim()
for biproportional apportionment with data.frames
as inputs.
votes_matrix = uri2020$votes_matrix district_seats = uri2020$seats_vector biproporz(votes_matrix, district_seats) # apply quorum (high values for illustrative purposes) biproporz(votes_matrix, district_seats, quorum_all(any_district = 0.1, total = 0.25))
votes_matrix = uri2020$votes_matrix district_seats = uri2020$seats_vector biproporz(votes_matrix, district_seats) # apply quorum (high values for illustrative purposes) biproporz(votes_matrix, district_seats, quorum_all(any_district = 0.1, total = 0.25))
Round x
up to ceiling(x)
if x-floor(x) >= threshold
,
otherwise round down to floor(x)
.
ceil_at(x, threshold)
ceil_at(x, threshold)
x |
numeric vector or matrix >= 0 ( |
threshold |
threshold in [0,1] or "harmonic"/"geometric" to use harmonic or geometric mean thresholds |
the rounded vector or matrix
ceil_at(c(0.5, 1.5, 2.49, 2.5, 2.51), 0.5) # compare to round(c(0.5, 1.5, 2.49, 2.5, 2.51)) ceil_at(c(1.45, 2.45, 3.45), 0) # like floor() ceil_at(c(1.45, 2.45, 3.45, 0.2), "geometric")
ceil_at(c(0.5, 1.5, 2.49, 2.5, 2.51), 0.5) # compare to round(c(0.5, 1.5, 2.49, 2.5, 2.51)) ceil_at(c(1.45, 2.45, 3.45), 0) # like floor() ceil_at(c(1.45, 2.45, 3.45, 0.2), "geometric")
Functions to directly apply divisor apportionment methods instead
of calling proporz()
with a method parameter.
divisor_round(votes, n_seats, quorum = 0) divisor_floor(votes, n_seats, quorum = 0) divisor_harmonic(votes, n_seats, quorum = 0) divisor_geometric(votes, n_seats, quorum = 0) divisor_ceiling(votes, n_seats, quorum = 0)
divisor_round(votes, n_seats, quorum = 0) divisor_floor(votes, n_seats, quorum = 0) divisor_harmonic(votes, n_seats, quorum = 0) divisor_geometric(votes, n_seats, quorum = 0) divisor_ceiling(votes, n_seats, quorum = 0)
votes |
numeric vector with number of votes for each party |
n_seats |
total number of seats |
quorum |
Vote threshold a party must reach. Used as quota of total votes within a district if less than 1 otherwise as number of votes. |
Divisor methods are known under different names:
d'hondt, jefferson, hagenbach-bischoff: use divisor_floor()
sainte-lague, webster: use divisor_round()
adams: use divisor_ceiling()
dean: use divisor_harmonic()
huntington-hill, hill-huntington: use divisor_geometric()
All divisor functions call highest_averages_method()
with a different sequence of
divisors.
The number of seats per party as a vector
votes = c("Party A" = 690, "Party B" = 400, "Party C" = 250, "Party D" = 120) divisor_round(votes, 10) divisor_floor(votes, 10) divisor_ceiling(votes, 10) divisor_ceiling(votes, 5) divisor_geometric(votes, 10, quorum = 0.05) divisor_harmonic(votes, 10)
votes = c("Party A" = 690, "Party B" = 400, "Party C" = 250, "Party D" = 120) divisor_round(votes, 10) divisor_floor(votes, 10) divisor_ceiling(votes, 10) divisor_ceiling(votes, 5) divisor_geometric(votes, 10, quorum = 0.05) divisor_harmonic(votes, 10)
Example data from the 2019 Finnish parliamentary elections. The data has been cleaned up and only contains information relevant for this package.
finland2019
finland2019
List containing two data.frames:
votes_df
containing the number of votes for each party and district.
229 rows, 3 columns (party_name
, district_name
, votes
)
district_seats_df
with the number of seats per district.
12 rows, 2 columns (district_name
, seats
)
https://tulospalvelu.vaalit.fi/EKV-2019/en/ladattavat_tiedostot.html
finland2019$district_seats_df head(finland2019$votes_df)
finland2019$district_seats_df head(finland2019$votes_df)
Show the district and party divisors used to assign seats.
This method provides easier access to divisors stored in
attributes(...)$divisors
get_divisors(biproporz_result)
get_divisors(biproporz_result)
biproporz_result |
a matrix created by |
The district and party divisors in a list, each as a vector
seats_matrix = biproporz(uri2020$votes_matrix, uri2020$seats_vector) get_divisors(seats_matrix)
seats_matrix = biproporz(uri2020$votes_matrix, uri2020$seats_vector) get_divisors(seats_matrix)
Allocate seats proportionally for divisor methods
.
highest_averages_method(votes, n_seats, divisors)
highest_averages_method(votes, n_seats, divisors)
votes |
numeric vector with number of votes for each party |
n_seats |
total number of seats |
divisors |
sequence of divisors (length equal to the number of seats). If it is a single number (e.g. 0.5), a sequence is generated starting with it. |
The highest averages method requires the number of votes for each party to be divided successively by a series of divisors. This produces a table of quotients, or averages, with a row for each divisor and a column for each party. The nth seat is allocated to the party whose column contains the nth largest entry in this table, up to the total number of seats available. (Wikipedia)
The number of seats per party as a vector
highest_averages_method(c(5200, 1700, 3100), 15, 0.5) highest_averages_method(votes = c(50, 0, 30), n_seats = 3, divisors = c(0, 1.3333, 2.4))
highest_averages_method(c(5200, 1700, 3100), 15, 0.5) highest_averages_method(votes = c(50, 0, 30), n_seats = 3, divisors = c(0, 1.3333, 2.4))
Allocate seats based on the largest fractional remainder. The largest remainder method is also known as: Hamilton, Hare-Niemeyer or Vinton method.
largest_remainder_method(votes, n_seats, quorum = 0)
largest_remainder_method(votes, n_seats, quorum = 0)
votes |
numeric vector with number of votes for each party |
n_seats |
total number of seats |
quorum |
Vote threshold a party must reach. Used as quota of total votes within a district if less than 1 otherwise as number of votes. |
The numbers of votes for each party is divided by a quota representing the number of votes required for a seat. Then, each party receives the rounded down quota value as seats. The remaining seats are given to the party with the largest remainder until all seats have been distributed.
The number of seats per party as a vector
Only the quota total votes / total seats
(which is used by the aforementioned
methods) is implemented.
votes = c(47000, 16000, 15800, 12000, 6100, 3100) largest_remainder_method(votes, 10)
votes = c(47000, 16000, 15800, 12000, 6100, 3100) largest_remainder_method(votes, 10)
Iterate and change column and row divisors such that the row and column sums of the seats matrix satisfies the constraints given by the upper apportionment.
lower_apportionment(votes_matrix, seats_cols, seats_rows, method = "round")
lower_apportionment(votes_matrix, seats_cols, seats_rows, method = "round")
votes_matrix |
matrix with votes by party in rows and votes by district in columns. |
seats_cols |
number of seats per column (districts/regions), predetermined or
calculated with |
seats_rows |
number of seats per row (parties/lists), calculated with
|
method |
Apportion method that defines how seats are assigned. The following methods are supported:
|
The result is obtained by an iterative process ('Alternate Scaling Algorithm', see Reference). Initially, for each district a divisor is chosen using the highest averages method for the votes allocated to each regional party list in this region. For each party a party divisor is initialized with 1.
Effectively, the objective of the iterative process is to modify the regional divisors and party divisors so that the number of seats in each regional party list equals the number of their votes divided by both the regional and the party divisors.
The following two correction steps are executed until this objective is satisfied:
modify the party divisors such that the apportionment within each party is correct with the chosen rounding method,
modify the regional divisors such that the apportionment within the region is correct with the chosen rounding method.
A seat matrix with district (columns) and party (rows) divisors stored in attributes.
Oelbermann, K. F. (2016): Alternate scaling algorithm for biproportional divisor methods. Mathematical Social Sciences, 80, 25-32.
biproporz()
, upper_apportionment()
votes_matrix = matrix(c(123,912,312,45,714,255,815,414,215), nrow = 3) district_seats = c(7,5,8) party_seats = c(5,11,4) lower_apportionment(votes_matrix, district_seats, party_seats) # using "winner take one" vm = matrix(c(200,100,10,11), 2, dimnames = list(c("Party A", "Party B"), c("I", "II"))) district_seats = setNames(c(2,1), colnames(vm)) ua = upper_apportionment(vm, district_seats) lower_apportionment(vm, ua$district, ua$party, method = "wto") # compare to standard method lower_apportionment(vm, ua$district, ua$party, method = "round")
votes_matrix = matrix(c(123,912,312,45,714,255,815,414,215), nrow = 3) district_seats = c(7,5,8) party_seats = c(5,11,4) lower_apportionment(votes_matrix, district_seats, party_seats) # using "winner take one" vm = matrix(c(200,100,10,11), 2, dimnames = list(c("Party A", "Party B"), c("I", "II"))) district_seats = setNames(c(2,1), colnames(vm)) ua = upper_apportionment(vm, district_seats) lower_apportionment(vm, ua$district, ua$party, method = "wto") # compare to standard method lower_apportionment(vm, ua$district, ua$party, method = "round")
Create a matrix in 'wide' format from a data.frame with 3 columns with
pivot_to_matrix
or create a data.frame in long format from a matrix with
pivot_to_df.
pivot_to_matrix(df_long) pivot_to_df(matrix_wide, value_colname = "values")
pivot_to_matrix(df_long) pivot_to_df(matrix_wide, value_colname = "values")
df_long |
data.frame in long format with exactly 3 columns |
matrix_wide |
matrix in wide format |
value_colname |
name for the new value column in the resulting data.frame |
These pivot functions are used to prepare data for biproporz()
in
pukelsheim()
. They are not supposed to cover general use cases or provide
customization. They mainly exist because reshape is hard to handle and the
package should have no dependencies.
A data.frame with 3 columns or a matrix. Note that the results are sorted by the first and second column (data.frame) or row/column names (matrix).
# From data.frame to matrix df = data.frame(party = c("A", "A", "A", "B", "B", "B"), region = c("III", "II", "I", "I", "II", "III"), seats = c(5L, 3L, 1L, 2L, 4L, 6L)) pivot_to_matrix(df) # from matrix to data.frame mtrx = matrix(1:6, nrow = 2) pivot_to_df(mtrx) # from matrix to data.frame using dimnames dimnames(mtrx) <- list(party = c("A", "B"), region = c("I", "II", "III")) pivot_to_df(mtrx, "seats") # Note that pivot results are sorted pivot_to_df(pivot_to_matrix(df)) == df[order(df[[1]], df[[2]]),]
# From data.frame to matrix df = data.frame(party = c("A", "A", "A", "B", "B", "B"), region = c("III", "II", "I", "I", "II", "III"), seats = c(5L, 3L, 1L, 2L, 4L, 6L)) pivot_to_matrix(df) # from matrix to data.frame mtrx = matrix(1:6, nrow = 2) pivot_to_df(mtrx) # from matrix to data.frame using dimnames dimnames(mtrx) <- list(party = c("A", "B"), region = c("I", "II", "III")) pivot_to_df(mtrx, "seats") # Note that pivot results are sorted pivot_to_df(pivot_to_matrix(df)) == df[order(df[[1]], df[[2]]),]
Calculate seat apportionment for legislative bodies.
proporz(votes, n_seats, method, quorum = 0)
proporz(votes, n_seats, method, quorum = 0)
votes |
numeric vector with number of votes for each party |
n_seats |
total number of seats |
method |
Apportionment method to use, as character. Not case sensitive. See details. |
quorum |
Vote threshold a party must reach. Used as quota of total votes within a district if less than 1 otherwise as number of votes. |
The following methods are available:
d'hondt, jefferson, hagenbach-bischoff, floor: use divisor_floor()
sainte-lague, webster, round: use divisor_round()
adams, ceiling: use divisor_ceiling()
dean, harmonic: use divisor_harmonic()
huntington-hill, hill-huntington, geometric: use divisor_geometric()
hare-niemeyer, hamilton, vinton, largest_remainder_method: use largest_remainder_method()
The number of seats per party as a vector
Seats can also be apportioned among regions instead of parties. The
parameter votes
is then normally used with census data (e.g.
population counts).
votes = c("Party A" = 651, "Party B" = 349, "Party C" = 50) proporz(votes, 10, "sainte-lague") proporz(votes, 10, "hill-huntington") proporz(votes, 10, "hill-huntington", quorum = 0.05) proporz(votes, 10, "jefferson", quorum = 70)
votes = c("Party A" = 651, "Party B" = 349, "Party C" = 50) proporz(votes, 10, "sainte-lague") proporz(votes, 10, "hill-huntington") proporz(votes, 10, "hill-huntington", quorum = 0.05) proporz(votes, 10, "jefferson", quorum = 70)
Method to proportionally allocate seats among parties/lists and districts/regions/entities ('Doppelter Pukelsheim').
pukelsheim( votes_df, district_seats_df, quorum, new_seats_col = "seats", use_list_votes = TRUE, winner_take_one = FALSE )
pukelsheim( votes_df, district_seats_df, quorum, new_seats_col = "seats", use_list_votes = TRUE, winner_take_one = FALSE )
votes_df |
data.frame (long format) with 3 columns (actual colnames can differ):
|
district_seats_df |
data.frame with 2 columns (actual colnames can differ):
|
quorum |
Optional list of functions which take the votes_matrix and return a logical
vector that denotes for each list/party whether they reached the quorum (i.e. are
eligible for seats). The easiest way to do this is via |
new_seats_col |
name of the new column |
use_list_votes |
By default ( |
winner_take_one |
Set to |
Each party nominates a candidate list for every district. The voters vote for the parties of their district. The seat allocation is calculated in two steps:
In the so called upper apportionment
the number of seats for each party (over all districts) is determined.
In the so called lower apportionment
the seats are distributed to the regional party list respecting the results
from the upper apportionment.
Parties failing to reach quorums cannot get seats. This function does not handle seat assignment to candidates.
If you want to use other apportion methods than Sainte-Laguë use biproporz()
.
A data.frame like votes_df
with a new column denoting the number seats per
party and district. Party and district divisors stored in attributes in attributes
(hidden from print, see get_divisors()
).
This function calls biproporz()
after preparing the input data.
# Zug 2018 votes_df = unique(zug2018[c("list_id", "entity_id", "list_votes")]) district_seats_df = unique(zug2018[c("entity_id", "election_mandates")]) seats_df = pukelsheim(votes_df, district_seats_df, quorum_any(any_district = 0.05, total = 0.03), winner_take_one = TRUE) head(seats_df) # Finland 2019 finland19_result = pukelsheim(finland2019$votes_df, finland2019$district_seats_df, new_seats_col = "mandates", use_list_votes = FALSE) tail(finland19_result[order(finland19_result$mandates),])
# Zug 2018 votes_df = unique(zug2018[c("list_id", "entity_id", "list_votes")]) district_seats_df = unique(zug2018[c("entity_id", "election_mandates")]) seats_df = pukelsheim(votes_df, district_seats_df, quorum_any(any_district = 0.05, total = 0.03), winner_take_one = TRUE) head(seats_df) # Finland 2019 finland19_result = pukelsheim(finland2019$votes_df, finland2019$district_seats_df, new_seats_col = "mandates", use_list_votes = FALSE) tail(finland19_result[order(finland19_result$mandates),])
quorum_any()
and quorum_all()
are used for the quorum
parameter in
biproporz()
or pukelsheim()
and help describe how quorums should be
applied previous to seat distributions.
quorum_all(any_district, total) quorum_any(any_district, total)
quorum_all(any_district, total) quorum_any(any_district, total)
any_district |
Vote threshold a party must reach in at least one
district. Used as share of total votes within a district if less than 1
otherwise as number of votes. Must be greater than 0. Uses
|
total |
Vote threshold a party must reach for all votes cast. Used as
share of total votes if less than 1, otherwise as number of votes. Must be
greater than 0. Uses |
There's a difference in how the functions work. With quroum_any
,
at least one quorum must be reached. With quorum_all
all
(i.e. both) quorums must be reached. If you only use one parameter,
quorum_any()
and quorum_all()
are identical.
a function which, when called with function(votes_matrix)
, returns
a boolean vector with length equal to the number of lists/parties
(votes_matrix
rows). The vector shows whether a party has reached any/all
quorums.
votes_matrix = matrix(c(502, 55, 80, 10, 104, 55, 0, 1), ncol = 2) dimnames(votes_matrix) <- list(c("A", "B", "C", "D"), c("Z1", "Z2")) seats = c(Z1 = 50, Z2 = 20) # use as parameter in biproporz or pukelsheim (general use case) biproporz(votes_matrix, seats, quorum = quorum_any(any_district = 0.1, total = 100)) biproporz(votes_matrix, seats, quorum = quorum_all(any_district = 0.1, total = 100)) biproporz(votes_matrix, seats, quorum = quorum_any(any_district = 0.1)) biproporz(votes_matrix, seats, quorum = quorum_any(total = 100)) biproporz(votes_matrix, seats, quorum = quorum_any(total = 0.5)) # the quorum parameter also accepts vectors (e.g. calculated elsewhere) biproporz(votes_matrix, seats, quorum = c(FALSE, TRUE, TRUE, TRUE))
votes_matrix = matrix(c(502, 55, 80, 10, 104, 55, 0, 1), ncol = 2) dimnames(votes_matrix) <- list(c("A", "B", "C", "D"), c("Z1", "Z2")) seats = c(Z1 = 50, Z2 = 20) # use as parameter in biproporz or pukelsheim (general use case) biproporz(votes_matrix, seats, quorum = quorum_any(any_district = 0.1, total = 100)) biproporz(votes_matrix, seats, quorum = quorum_all(any_district = 0.1, total = 100)) biproporz(votes_matrix, seats, quorum = quorum_any(any_district = 0.1)) biproporz(votes_matrix, seats, quorum = quorum_any(total = 100)) biproporz(votes_matrix, seats, quorum = quorum_any(total = 0.5)) # the quorum parameter also accepts vectors (e.g. calculated elsewhere) biproporz(votes_matrix, seats, quorum = c(FALSE, TRUE, TRUE, TRUE))
Base implementation, used by quorum_any()
and quorum_all()
.
reached_quorum_any_district(votes_matrix, quorum_districts)
reached_quorum_any_district(votes_matrix, quorum_districts)
votes_matrix |
votes matrix |
quorum_districts |
Vote threshold a party must reach in at least one district. Used as quota of total votes within a district if less than 1 otherwise as number of votes. Must be greater than 0. |
boolean vector with length equal to the number of lists/parties
(votes_matrix
rows) whether they reached the quorum or not
(vm = matrix(c(239, 10, 308, 398, 20, 925), nrow = 3)) reached_quorum_any_district(vm, 25)
(vm = matrix(c(239, 10, 308, 398, 20, 925), nrow = 3)) reached_quorum_any_district(vm, 25)
Base implementation, used by quorum_any()
and quorum_all()
.
reached_quorum_total(votes_matrix, quorum_total)
reached_quorum_total(votes_matrix, quorum_total)
votes_matrix |
votes matrix |
quorum_total |
Vote threshold a party must reach for all votes cast. Used as quota of total votes if less than 1, otherwise as number of votes. Must be greater than 0. |
boolean vector with length equal to the number of lists/parties
(votes_matrix
rows) whether they reached the quorum or not
(vm = matrix(c(239, 10, 308, 398, 20, 925), nrow = 3)) reached_quorum_total(vm, 35)
(vm = matrix(c(239, 10, 308, 398, 20, 925), nrow = 3)) reached_quorum_total(vm, 35)
Use biproportional apportionment interactively in a shiny app
run_app(votes_matrix = NULL, district_seats = NULL)
run_app(votes_matrix = NULL, district_seats = NULL)
votes_matrix |
optional votes_matrix to load upon start |
district_seats |
optional district_seats to load upon start |
Calling the function starts the shiny app
if(interactive()){ # You need to have the packages 'shiny' and 'shinyMatrix' installed to run the app run_app() # It's possible to load a matrix with the app run_app(uri2020$votes_matrix, uri2020$seats_vector) }
if(interactive()){ # You need to have the packages 'shiny' and 'shinyMatrix' installed to run the app run_app() # It's possible to load a matrix with the app run_app(uri2020$votes_matrix, uri2020$seats_vector) }
In the upper apportionment, the seats for each party are computed with a highest averages method. This determines how many of all seats each party deserves due to the total of all their votes (that is the sum of the votes for all regional lists of that party). Analogical, the same highest averages method is used to determine how many of all seats each region deserves.
upper_apportionment( votes_matrix, district_seats, use_list_votes = TRUE, method = "round" )
upper_apportionment( votes_matrix, district_seats, use_list_votes = TRUE, method = "round" )
votes_matrix |
Vote count matrix with votes by party in rows and votes by district in columns |
district_seats |
Vector defining the number of seats per district. Must be the same
length as |
use_list_votes |
By default ( |
method |
Apportion method that defines how seats are assigned, see |
A named list with district seats (for votes_matrix
columns) and party seats
(for rows).
The results from the upper apportionment are final results for the number of the seats of one party (and analogically for the number of the seats of one region) within the whole voting area, the lower apportionment will only determine where (which regions) the party seats are allocated. Thus, after the upper apportionment is done, the final strength of a party/region within the parliament is definite.
biproporz()
, lower_apportionment()
votes_matrix = matrix(c(123,912,312,45,714,255,815,414,215), nrow = 3) district_seats = c(7,5,8) upper_apportionment(votes_matrix, district_seats)
votes_matrix = matrix(c(123,912,312,45,714,255,815,414,215), nrow = 3) district_seats = c(7,5,8) upper_apportionment(votes_matrix, district_seats)
Example election data from the 2020 election for the cantonal council of Uri (Landrat) in Switzerland. The data has been extracted from the report "Landratswahlen 2020: Statistische Auswertung".
uri2020
uri2020
List containing:
votes_matrix
the number of votes for each party and district
(4 rows, 4 columns)
seats_vector
with the number of seats per district
(length 4)
https://www.ur.ch/abstimmungen/termine/9322
Weigh list votes by dividing the votes matrix entries by the number
of seats per district. This method is used in upper_apportionment()
if
use_list_votes
is TRUE
(default). The weighted votes are not rounded.
weight_list_votes(votes_matrix, seats_district)
weight_list_votes(votes_matrix, seats_district)
votes_matrix |
votes matrix |
seats_district |
seats per district, vector with same length
as |
the weighted votes_matrix
weight_list_votes(uri2020$votes_matrix, uri2020$seats_vector)
weight_list_votes(uri2020$votes_matrix, uri2020$seats_vector)
Example election data from the 2018 election for the cantonal council of Zug (Kantonsrat) in Switzerland.
zug2018
zug2018
An object of class data.frame
with 267 rows and 49 columns.
Kanton Zug (01.07.2022, 10:27:58). Kantonsratswahl 2018 (CSV). https://wab.zug.ch/elections/kantonsratswahl-2018/data-csv