Created
November 20, 2017 04:23
-
-
Save kespindler/a532ed990a616162da42a9b6bb1f18c2 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
""" | |
ipython3 -i run.py | |
""" | |
import numpy as np | |
N_candidates = 5 | |
def general_votes(candidate_R, candidate_D): | |
"""If candidate_R and candidate_D were the winners of the | |
R and D primaries, respectively, this function creates a matrix | |
to send votes from the original preferred candidate to the nearest | |
candidate on the spectrum. | |
:param candidate_R: | |
:param candidate_D: | |
:return: | |
""" | |
assert candidate_R < candidate_D, '%s, %s' % (candidate_R, candidate_D) | |
r = np.zeros((N_candidates, N_candidates)) | |
midp = (candidate_R + candidate_D) // 2 | |
r[0:midp, candidate_R] = 1 | |
r[midp:N_candidates, candidate_D] = 1 | |
if midp * 2 == candidate_R + candidate_D: | |
r[midp, candidate_R] = 0.5 | |
r[midp, candidate_D] = 0.5 | |
return r | |
def election_results(population, turnout, voting_blocs): | |
""" | |
Let B be number of voting blocs. Let C be the number of candidates. | |
:param population: percent of the population represented by each voting bloc. 1xB array which sums to 1. | |
:param turnout: percent of each voting bloc that turns out to vote. 1xB array with all entries in [0, 1]. | |
:param voting_blocs: The likelihood that a person in a voting bloc votes for a candidate. BxC matrix, where each row sums to 1. | |
:return: results of an election on a per-bloc basis, a BxC matrix. result[i, j] is result for bloc i and candidate j. | |
""" | |
assert population.sum() == 1 | |
assert (0 <= turnout <= 1).all() | |
assert (voting_blocs.sum(1) == 1).all() | |
voters = population * turnout | |
votes = voting_blocs.T * voters | |
return votes | |
def vote_distribution_normal(num_candidates, preferred_candidate, std_dev, samples=1000): | |
"""Create a voting bloc using a statistical distribution instead of static definition. | |
:param num_candidates: How many candidates are available. | |
:param preferred_candidate: Most preferred candidate for bloc. (Can be fractional). | |
:param std_dev: How diffused is the preference. A typical value might range from .5 (highly specific) to 2 (fairly diffuse) | |
:param samples: Samples from normal. This controls the variance in the ultimate distribution. | |
:return: | |
""" | |
bins = np.arange(-0.5, num_candidates + 0.5) | |
distribution = np.random.normal(preferred_candidate, std_dev, samples) | |
histogram, bins = np.histogram(distribution, bins, normed=True) | |
return histogram | |
""" | |
There are assumed to be 5 candidates in this race, ranging from a hard-line Republican (0) | |
to a centrist (2), to a hard-line Democrat (4). | |
We divide voters into 3 buckets of each Republicans (R) and Democrats (D): hard, moderate, and leaning, | |
for a total of 6 blocs of voters. | |
`voting_blocs` is then a 6x5 matrix, where each of 6 voting blocs have a percent | |
chance to vote for each of 5 candidates. Each 1x5 row in the matrix should sum to 1. | |
""" | |
hard_R = [.9, .1, 0, 0, 0] | |
mode_R = [.3, .5, .2, 0, 0] | |
lean_R = [0, .55, .45, .0, 0] | |
hard_D = list(reversed(hard_R)) | |
mode_D = list(reversed(mode_R)) | |
lean_D = list(reversed(lean_R)) | |
voting_blocs = np.array([hard_R, mode_R, lean_R, lean_D, mode_D, hard_D]) | |
""" | |
`population` represents what percent of the population falls into each of the | |
6 voting blocs. It is a 1x6 matrix which sums to 1. | |
`primary_turnout` is the turnout of each bloc, 1x6 matrix with each value in [0, 1]. | |
""" | |
population = np.array([.05, .25, .23, .16, .27, .04]) | |
primary_turnout = np.array([.90, .13, .20, .20, .13, .90]) | |
""" | |
Now comes the fun part. By playing with the ratio of R voters | |
who vote in the D primary (tallies_D), we can flip the election | |
from going to the hardline Republican to the centrist Republican. | |
Vary `R_turnout_for_D_primary` to change the outcome. Flip occurs between 0.7 and 0.8. | |
""" | |
primary_votes = election_results(population, primary_turnout, voting_blocs) | |
blocs_R = primary_votes[:, :3] | |
blocs_D = primary_votes[:, 3:] | |
tallies_R = blocs_R.sum(1) | |
victor_R = tallies_R.argmax() | |
tallies_D = blocs_D.sum(1) | |
R_turnout_for_D_primary = 0.8 | |
R_votes_in_D_primary = tallies_R.copy() * R_turnout_for_D_primary | |
R_votes_in_D_primary[:N_candidates//2] = 0 | |
tallies_D = blocs_R.sum(1) * R_turnout_for_D_primary + tallies_D # open primary | |
victor_D = tallies_D.argmax() | |
# General | |
rebalancing = general_votes(victor_R, victor_D) | |
votes = population @ voting_blocs @ rebalancing | |
victor_overall = votes.argmax() | |
print(tallies_R, victor_R) | |
print(tallies_D, victor_D) | |
print(votes, victor_overall) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment