kopia lustrzana https://github.com/martinlackner/apportionment
rodzic
0a1a742b72
commit
03673bf679
|
@ -15,7 +15,7 @@ The following apportionment methods are implemented:
|
||||||
- Adams
|
- Adams
|
||||||
* the quota method [1]
|
* the quota method [1]
|
||||||
|
|
||||||
This module supports 3.7+.
|
This module supports Python 3.7+.
|
||||||
|
|
||||||
## How-to
|
## How-to
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import apportionment.methods as app
|
||||||
|
|
||||||
|
"""
|
||||||
|
Dominik's remark:
|
||||||
|
It is actually not without loss of generality to focus just on ties that still appear in the end.
|
||||||
|
Here is an example: votes = [720, 720, 120, 120], house size h = 8. Then the quota method selects
|
||||||
|
exactly the following: 3 seats go to each of the big parties, and then choose 1 big party and 1
|
||||||
|
small party and give those a seat each. This last structure can't be captured by ties just at the
|
||||||
|
end. (In contrast, for divisor methods, the ties are always of the form "assign necessary seats
|
||||||
|
(say there are t of them), and then choose an arbitrary subset of size h - t from a specified
|
||||||
|
set S of parties".)
|
||||||
|
"""
|
||||||
|
|
||||||
|
votes = [720, 720, 120, 120]
|
||||||
|
seats = 8
|
||||||
|
|
||||||
|
print("votes: ", votes)
|
||||||
|
print(seats, "seats")
|
||||||
|
|
||||||
|
result = app.compute("quota", votes, seats, verbose=True)
|
|
@ -1,4 +1,6 @@
|
||||||
# Apportionment methods
|
"""
|
||||||
|
Apportionment methods
|
||||||
|
"""
|
||||||
|
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
import math
|
import math
|
||||||
|
@ -29,13 +31,15 @@ def compute(
|
||||||
parties=string.ascii_letters,
|
parties=string.ascii_letters,
|
||||||
threshold=None,
|
threshold=None,
|
||||||
tiesallowed=True,
|
tiesallowed=True,
|
||||||
verbose=True
|
verbose=True,
|
||||||
):
|
):
|
||||||
filtered_votes = apply_threshold(votes, threshold)
|
filtered_votes = apply_threshold(votes, threshold)
|
||||||
if method == "quota":
|
if method == "quota":
|
||||||
return quota(filtered_votes, seats, fractions, parties, tiesallowed, verbose)
|
return quota(filtered_votes, seats, fractions, parties, tiesallowed, verbose)
|
||||||
elif method in ["lrm", "hamilton", "largest_remainder"]:
|
elif method in ["lrm", "hamilton", "largest_remainder"]:
|
||||||
return largest_remainder(filtered_votes, seats, fractions, parties, tiesallowed, verbose)
|
return largest_remainder(
|
||||||
|
filtered_votes, seats, fractions, parties, tiesallowed, verbose
|
||||||
|
)
|
||||||
elif method in [
|
elif method in [
|
||||||
"dhondt",
|
"dhondt",
|
||||||
"jefferson",
|
"jefferson",
|
||||||
|
@ -52,7 +56,9 @@ def compute(
|
||||||
"majorfractions",
|
"majorfractions",
|
||||||
"greatestdivisors",
|
"greatestdivisors",
|
||||||
]:
|
]:
|
||||||
return divisor(filtered_votes, seats, method, fractions, parties, tiesallowed, verbose)
|
return divisor(
|
||||||
|
filtered_votes, seats, method, fractions, parties, tiesallowed, verbose
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("apportionment method " + method + " not known")
|
raise NotImplementedError("apportionment method " + method + " not known")
|
||||||
|
|
||||||
|
@ -117,7 +123,12 @@ def within_quota(votes, representatives, parties=string.ascii_letters, verbose=T
|
||||||
|
|
||||||
# Largest remainder method (Hamilton method)
|
# Largest remainder method (Hamilton method)
|
||||||
def largest_remainder(
|
def largest_remainder(
|
||||||
votes, seats, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True
|
votes,
|
||||||
|
seats,
|
||||||
|
fractions=False,
|
||||||
|
parties=string.ascii_letters,
|
||||||
|
tiesallowed=True,
|
||||||
|
verbose=True,
|
||||||
):
|
):
|
||||||
# votes = np.array(votes)
|
# votes = np.array(votes)
|
||||||
if verbose:
|
if verbose:
|
||||||
|
@ -125,7 +136,9 @@ def largest_remainder(
|
||||||
if fractions:
|
if fractions:
|
||||||
q = Fraction(int(sum(votes)), seats)
|
q = Fraction(int(sum(votes)), seats)
|
||||||
quotas = [Fraction(int(p), q) for p in votes]
|
quotas = [Fraction(int(p), q) for p in votes]
|
||||||
representatives = np.array([int(qu.numerator // qu.denominator) for qu in quotas])
|
representatives = np.array(
|
||||||
|
[int(qu.numerator // qu.denominator) for qu in quotas]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
votes = np.array(votes)
|
votes = np.array(votes)
|
||||||
quotas = (votes * seats) / np.sum(votes)
|
quotas = (votes * seats) / np.sum(votes)
|
||||||
|
@ -167,7 +180,13 @@ def largest_remainder(
|
||||||
|
|
||||||
# Divisor methods
|
# Divisor methods
|
||||||
def divisor(
|
def divisor(
|
||||||
votes, seats, method, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True
|
votes,
|
||||||
|
seats,
|
||||||
|
method,
|
||||||
|
fractions=False,
|
||||||
|
parties=string.ascii_letters,
|
||||||
|
tiesallowed=True,
|
||||||
|
verbose=True,
|
||||||
):
|
):
|
||||||
votes = np.array(votes)
|
votes = np.array(votes)
|
||||||
representatives = np.zeros(len(votes), dtype=int)
|
representatives = np.zeros(len(votes), dtype=int)
|
||||||
|
@ -214,18 +233,25 @@ def divisor(
|
||||||
else:
|
else:
|
||||||
representatives = np.array([1 if p > 0 else 0 for p in votes])
|
representatives = np.array([1 if p > 0 else 0 for p in votes])
|
||||||
if fractions:
|
if fractions:
|
||||||
divisors = np.array([
|
divisors = np.array(
|
||||||
Fraction(2 * (i + 1) * (i + 2), 2 * (i + 1) + 1) for i in range(seats)
|
[
|
||||||
])
|
Fraction(2 * (i + 1) * (i + 2), 2 * (i + 1) + 1)
|
||||||
|
for i in range(seats)
|
||||||
|
]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
divisors = np.arange(seats)
|
divisors = np.arange(seats)
|
||||||
divisors = (2 * (divisors + 1) * (divisors + 2)) / (2 * (divisors + 1) + 1)
|
divisors = (2 * (divisors + 1) * (divisors + 2)) / (
|
||||||
|
2 * (divisors + 1) + 1
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("divisor method " + method + " not known")
|
raise NotImplementedError("divisor method " + method + " not known")
|
||||||
# assigning representatives
|
# assigning representatives
|
||||||
if seats > np.sum(representatives):
|
if seats > np.sum(representatives):
|
||||||
if fractions and method not in ["huntington", "hill", "modified_saintelague"]:
|
if fractions and method not in ["huntington", "hill", "modified_saintelague"]:
|
||||||
weights = np.array([[Fraction(int(p), d) for d in divisors.tolist()] for p in votes])
|
weights = np.array(
|
||||||
|
[[Fraction(int(p), d) for d in divisors.tolist()] for p in votes]
|
||||||
|
)
|
||||||
flatweights = sorted([w for l in weights for w in l])
|
flatweights = sorted([w for l in weights for w in l])
|
||||||
else:
|
else:
|
||||||
weights = np.array([p / divisors for p in votes])
|
weights = np.array([p / divisors for p in votes])
|
||||||
|
@ -296,7 +322,14 @@ def __divzero_fewerseatsthanparties(votes, seats, parties, tiesallowed, verbose)
|
||||||
return representatives
|
return representatives
|
||||||
|
|
||||||
|
|
||||||
def quota(votes, seats, fractions=False, parties=string.ascii_letters, tiesallowed=True, verbose=True):
|
def quota(
|
||||||
|
votes,
|
||||||
|
seats,
|
||||||
|
fractions=False,
|
||||||
|
parties=string.ascii_letters,
|
||||||
|
tiesallowed=True,
|
||||||
|
verbose=True,
|
||||||
|
):
|
||||||
"""The quota method
|
"""The quota method
|
||||||
see Balinski, M. L., & Young, H. P. (1975).
|
see Balinski, M. L., & Young, H. P. (1975).
|
||||||
The quota method of apportionment.
|
The quota method of apportionment.
|
||||||
|
@ -316,8 +349,10 @@ def quota(votes, seats, fractions=False, parties=string.ascii_letters, tiesallow
|
||||||
|
|
||||||
while np.sum(representatives) < seats:
|
while np.sum(representatives) < seats:
|
||||||
if fractions:
|
if fractions:
|
||||||
quotas = [Fraction(int(votes[i]), int(representatives[i]) + 1)
|
quotas = [
|
||||||
for i in range(len(votes))]
|
Fraction(int(votes[i]), int(representatives[i]) + 1)
|
||||||
|
for i in range(len(votes))
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
quotas = votes / (representatives + 1)
|
quotas = votes / (representatives + 1)
|
||||||
# check if upper quota is violated
|
# check if upper quota is violated
|
||||||
|
|
Ładowanie…
Reference in New Issue