Skip to content

Instantly share code, notes, and snippets.

@stefanw
Created September 13, 2013 02:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stefanw/6546325 to your computer and use it in GitHub Desktop.
Save stefanw/6546325 to your computer and use it in GitHub Desktop.
BTW 2009 Seat Calculator. Do not use if your democracy relies on it.
# -*- coding: utf-8 -*-
from collections import defaultdict, Counter
year = 2009
default_mandates = 598
mandates = default_mandates
sperrklausel = 0.05
laender = ['berlin']#, 'bayern']
laender_data = {}
for land in laender:
land_module = __import__(land)
laender_data.update(land_module.get_data(year))
direct_vote = {}
seats = defaultdict(int)
zweitstimmen = Counter()
landzweitstimmen = defaultdict(Counter)
def get_direct_candidate(district, e):
l = [(k, v) for k, v in e['votes'].items()]
l.sort(key=lambda x: x[1]['count'], reverse=True)
if len(l) > 1 and l[0][1]['count'] == l[1][1]['count']:
# § 5
raise Exception('Draw for direct vote at %s' % district)
if l[0][1].get('partyless', False):
# § 6 (1) Satz 2
raise Exception('Independent candidate won at %s' % district)
return l[0]
for land in laender_data:
landesliste = {}
for district, votedict in laender_data[land].items():
party, d = get_direct_candidate(district, votedict['E'])
direct_vote[district] = (party, d)
seats[party] += 1
zweitvotes = dict([(k, v['count']) for k, v in votedict['Z']['votes'].items()])
zweitstimmen.update(zweitvotes)
landzweitstimmen[land].update(zweitvotes)
totalz = float(sum(zweitstimmen.values()))
ignored = set()
for party in zweitstimmen:
if zweitstimmen[party] / totalz < sperrklausel and seats[party] < 3:
ignored.add(party)
# § 1 (1) Satz 3
decrease_mandates = 0
for party, d in direct_vote.values():
if party in ignored:
decrease_mandates += 1
mandates -= decrease_mandates
print "New Number of Mandates:", mandates
old_landzweitstimmen = dict(landzweitstimmen)
old_zweitstimmen = dict(zweitstimmen)
old_totalz = totalz
for land in laender_data:
for party in ignored:
if party in landzweitstimmen[land]:
del landzweitstimmen[land][party]
for party in ignored:
if party in zweitstimmen:
del zweitstimmen[party]
def saint_lague(zweitstimmen, mandates, ensure_seat_majority=True):
def distribute_seats(landeslisten, divisor):
seats = {}
ambiguous = []
for party in landeslisten:
val = landeslisten[party] / float(divisor)
floatval = val - int(val)
if floatval > 0.5:
seats[party] = int(val) + 1
elif floatval < 0.5:
seats[party] = int(val)
else:
seats[party] = int(val)
ambiguous.append(party)
return seats, ambiguous
total = float(sum(zweitstimmen.values()))
divisor = total / mandates
additional = defaultdict(int)
additional_needed = False
while True:
while True:
print "Distributing Seats"
seats, ambig = distribute_seats(zweitstimmen, divisor)
for p in additional:
seats[p] += additional[p]
sumseats = sum(seats.values())
print "New Seats with Divisor %s: %d" % (divisor, sumseats)
if sumseats == mandates and len(ambig) < 2:
break
diff = mandates - sumseats
if diff == len(ambig):
# difference is equal to the number of
# unrounded (possibly + 1) seats, all get a seat
for p in ambig:
seats[p] += 1
assert sum(seats.values()) == mandates
break
if diff > 0 and len(ambig) > diff:
raise Exception('Ambiguous seats, need random decision')
if diff < 0:
if int(divisor) != divisor:
divisor = int(divisor)
divisor += 1
else:
if int(divisor) != divisor:
divisor = int(divisor)
else:
divisor -= 1
if not ensure_seat_majority:
break
# §6 (3)
if additional_needed:
break
for party in zweitstimmen:
val = zweitstimmen[party]
if val * 2 > totalz and seats[party] * 2 < mandates:
additional[party] += 1
additional_needed = True
if not additional_needed:
break
return seats
print seats
print saint_lague(zweitstimmen, mandates)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment