Skip to content

Instantly share code, notes, and snippets.

@almccon
Forked from bdon/gist:1288d93c45965942ed4373574a5abcc6
Last active December 18, 2016 06:12
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 almccon/6b4734a35a92a8674eaadaccb811d4b0 to your computer and use it in GitHub Desktop.
Save almccon/6b4734a35a92a8674eaadaccb811d4b0 to your computer and use it in GitHub Desktop.
stv_to_block.py

Simulation of a Block Voting election using STV ballots.

This code simulates a Block Voting election (which is what the OpenStreetMap US chapter uses to elect its board), using real-world Single Transferable Vote ballots (which is the system that the international OpenStreetMap Foundation uses).

STV uses ranked ballots, where voters rank as many candidates as they wish, in order of preference. Block Voting is not a ranked ballot, and voters choose up to n candidates, where n is the number of seats to be elected.

We can simulate a Block Voting election using STV ballots if we take the top n ranked candidates from each ballot and ignore their ordered ranking.

STV ballot files for the 2014 and 2015 OSMF elections can be found here and here.

Thanks to Brandon Liu.

Results:

2015:

"Yantisa Akhadi": 65 "Douglas Ssebaggala": 45 "Ilya Zverev": 145 "Guido Stein": 17 "Peter Barth": 148 "Joseph Reeves": 76 "Mikel Maron": 137 "Wille Marcel": 123 "Ryan Peterson": 56 "Martijn van Exel": 187 "Gonzalo Perez": 32

2014:

"Frederik Ramm": 138 "Marek Strassenburg-Kleciak": 62 "Peter Barth": 88 "Kathleen Danielson": 102 "Randy Meech": 66 "Steve Coast": 55 "Paul Normal": 89 "Ethan Nelson": 30

In both 2014 and 2015, Block Voting and STV would have elected the same candidates, according to this simulation.

import sys
filename = sys.argv[1]
# Takes a STV blt file.
f = open(filename,'r')
totals = {}
end_votes_reached = False
candidates = []
seats = None
for index, line in enumerate(f):
line = line.strip()
if index == 0:
s = line.split(" ")
seats = int(s[1])
print "{0} candidates running for {1} seats.".format(s[0],seats)
# seed the totals with 0 for each candidate.
# candidates are numbered starting at 1
for i in range(1,int(s[0])+1):
totals[str(i)] = 0
else:
if line[0] == '1' or line[0] == '(':
# it's a ballot
# 2014 ballot format has leading numbers in brackets. Like (218). Strip these.
if line[0] == '(':
idx = line.index(')')
idx = idx + 2
line = line[idx:]
print line
print line
line = line.split(" ")
# take the top 4 choices.
# ignore the leading 0 and trailing 1.
# Count the top "seats" choices as equal votes.
for vote in line[1:-1][0:seats]:
# if this ballot did not include any votes, skip it
if vote != '':
totals[vote] = totals[vote] + 1
elif line[0] == '0':
end_votes_reached = True
elif end_votes_reached:
candidates.append(line)
for key in totals:
print "{0}: {1}".format(candidates[int(key)-1],totals[key])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment