See also:
Last active
December 27, 2020 18:57
-
-
Save endolith/fa1d19767e5c2e9d4bd15e391ba79f91 to your computer and use it in GitHub Desktop.
Election simulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
Created on Sat Dec 17 11:33:35 2016 | |
""" | |
from __future__ import division, print_function | |
from numpy.random import multivariate_normal | |
import matplotlib.pyplot as plt | |
from scipy.spatial.distance import cdist | |
import numpy as np | |
# Polarization between the two main factions | |
pol = np.random.randn(1)[0] | |
voters = np.concatenate((multivariate_normal(mean=(-pol, -pol), | |
cov=((1, 0), (0, 1)), size=500), | |
multivariate_normal(mean=(+pol, +pol), | |
cov=((1, 0), (0, 1)), size=500) | |
)) | |
# Candidates distributed similarly to voters | |
candidates = multivariate_normal(mean=(0, 0), | |
cov=np.cov(voters, rowvar=False), | |
size=9 | |
) | |
plt.figure() | |
plt.scatter(voters[:, 0], voters[:, 1], color='b', marker='.', alpha=0.2, | |
label='voters') | |
plt.scatter(candidates[:, 0], candidates[:, 1], color='r', marker='o', | |
alpha=0.7, label='candidates') | |
centroid = np.mean(voters, axis=0) | |
plt.plot(centroid[0], centroid[1], color='k', marker='+', markersize=10, | |
ls='none', label='centroid') | |
dists = cdist(voters, candidates) | |
def plurality(dists): | |
""" | |
Honest plurality voting | |
""" | |
# Voters choose the nearest candidate | |
ballots = np.argmin(dists, axis=1) | |
# Winner is the candidate with the most votes | |
winner = np.argmax(np.bincount(ballots)) | |
return ballots, winner | |
def approval_nearest(dists, n=3): | |
""" | |
Honest approval voting of the nearest `n` candidates | |
""" | |
# Voters choose the n nearest candidates | |
ballots = np.argsort(dists, axis=1)[:, :n] | |
# Winner is the candidate with the most votes | |
winner = np.argmax(np.bincount(ballots.flat)) | |
return ballots, winner | |
def approval_half(dists): | |
""" | |
Honest approval voting of the nearest 50% of candidates | |
""" | |
n_cands = dists.shape[1] | |
return approval_nearest(dists, n=int(0.5 * n_cands)) | |
def score_normed(dists, levels=None): | |
""" | |
Honest score voting, normalized to min and max for worst and best, | |
optionally quantized to number of `levels`. All candidates are scored, so | |
total vs average is irrelevant. | |
""" | |
# Normalize from 0 to 1 for nearest to farthest | |
normed = dists - np.amin(dists, axis=1)[:, np.newaxis] | |
normed /= np.amax(normed, axis=1)[:, np.newaxis] | |
# Farthest candidates get the lowest scores | |
normed = 1 - normed | |
# Optionally quantize to discrete scale | |
if levels is not None: | |
normed = np.around(normed * levels) | |
# Total scores for each candidate | |
scores = np.sum(normed, axis=0) | |
# Winner is the candidate with the highest total/average score | |
winner = np.argmax(scores) | |
return ballots, winner | |
def next_color(): | |
ax = plt.gca() | |
while True: | |
color = next(ax._get_lines.prop_cycler)['color'] | |
if color not in {'r', }: # , 'b'}: | |
return color | |
def next_size(): | |
s = 6 | |
while True: | |
yield s | |
s += 2 | |
sizes = next_size() | |
for func in plurality, approval_nearest, approval_half, score_normed: | |
name = func.__name__.replace('_', ' ') | |
ballots, winner = func(dists) | |
winner_loc = candidates[winner] | |
plt.plot(winner_loc[0], winner_loc[1], marker='o', markersize=sizes.next(), | |
ls='none', markeredgewidth=1, markeredgecolor=next_color(), | |
color='none', # don't increment color cycle for invisible face | |
markerfacecolor='none', label=name) | |
plt.legend(loc='lower right', numpoints=1, fontsize='small') | |
plt.axis('square') | |
plt.grid(True) | |
plt.tight_layout() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
More: