Created
October 17, 2015 17:56
-
-
Save phs/242b5e6cbce05f9d0f2d to your computer and use it in GitHub Desktop.
Simple Monte Carlo to predict who will control US Congress after the 2016 elections
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
#! /usr/bin/env ruby | |
class CongressCarlo | |
TRIALS = 100_000 | |
STAR_SCALE = 500 | |
attr_reader :name, :dems_not_running, :reps_not_running | |
def estimate(*ratings) | |
ratings.inject(0) { |t, e| t + e } / ratings.size # arithmetic | |
# ratings.inject(1.0) { |t, e| t * e } ** (1.0 / ratings.size) # geometric | |
end | |
def safe_d; 0.9; end | |
def likely_d; 0.8; end | |
def lean_d; 0.7; end | |
def tilt_d; 0.6; end | |
def tossup; 0.5; end | |
def tilt_r; 1.0 - tilt_d; end | |
def lean_r; 1.0 - lean_d; end | |
def likely_r; 1.0 - likely_d; end | |
def safe_r; 1.0 - safe_d; end | |
def odds_of_dem_win_per_race | |
raise 'override me' | |
end | |
def run | |
# Run the helper method onces | |
races = odds_of_dem_win_per_race | |
trials_by_dem_win = [0] * races.size | |
TRIALS.times do | |
dem_wins = 0 | |
races.each do |odds_of_dem_win| | |
if rand() < odds_of_dem_win | |
dem_wins += 1 | |
end | |
end | |
trials_by_dem_win[dem_wins] += 1 | |
end | |
puts "Odds of Democratic seats in the #{name}:" | |
puts | |
majority = (0.5 * (dems_not_running + races.size + reps_not_running)).ceil | |
trials_with_rep_super_majority = 0 | |
trials_with_dem_majority = 0 | |
trials_with_dem_super_majority = 0 | |
percentiles = [] | |
trials_seen = 0 | |
trials_by_dem_win.each_with_index do |trials, dem_wins| | |
elected_dems = dems_not_running + dem_wins | |
trials_seen += trials | |
lower_percentile = percentiles.size | |
upper_percentile = ((100.0 * trials_seen) / TRIALS).ceil | |
while percentiles.size < upper_percentile | |
percentiles << elected_dems | |
end | |
trials_with_rep_super_majority += trials if elected_dems < majority | |
trials_with_dem_majority += trials if elected_dems >= majority | |
trials_with_dem_super_majority += trials if elected_dems > majority | |
stars = '*' * (STAR_SCALE * (1.0 * trials) / TRIALS).round | |
next if stars == '' | |
m = elected_dems == majority ? 'M' : ' ' | |
puts "#{m} #{elected_dems} (#{lower_percentile.to_s.rjust(3)}): #{stars}" | |
end | |
odds_of_rep_super_majority = (1.0 * trials_with_rep_super_majority) / TRIALS | |
odds_of_dem_majority = (1.0 * trials_with_dem_majority) / TRIALS | |
odds_of_dem_super_majority = (1.0 * trials_with_dem_super_majority) / TRIALS | |
puts | |
puts "Democratic seats at 90% confidence: #{percentiles[10]}" | |
puts "Democratic seats at 75% confidence: #{percentiles[25]}" | |
puts "Democratic seats at 50% confidence: #{percentiles[50]}" | |
puts | |
puts "Republican super majority: #{(100 * odds_of_rep_super_majority).round(1)}%" | |
puts "Democratic majority: #{(100 * odds_of_dem_majority).round(1)}%" | |
puts "Democratic super majority: #{(100 * odds_of_dem_super_majority).round(1)}%" | |
end | |
end | |
# https://en.wikipedia.org/wiki/United_States_Senate_elections,_2016 | |
class Senate < CongressCarlo | |
def initialize | |
@name = 'Senate' | |
@dems_not_running = 34 + 2 # 2 independents caucus with democrats | |
@reps_not_running = 30 | |
end | |
def odds_of_dem_win_per_race | |
[estimate(safe_d, safe_d, safe_d, safe_d)] * 7 + | |
[ | |
estimate(likely_r, safe_r, safe_r, safe_r), # Alaska | |
estimate(likely_r, lean_r, lean_r, lean_r), # Arizona | |
estimate(likely_d, safe_d, safe_d, safe_d), # California | |
estimate(lean_d, lean_d, lean_d, lean_d), # Colorado | |
estimate(tossup, tossup, tossup, tossup), # Florida | |
estimate(likely_r, safe_r, likely_r, safe_r), # Georgia | |
estimate(tossup, lean_d, tilt_d, tossup), # Illinois | |
estimate(likely_r, likely_r, likely_r, likely_r), # Indiana | |
estimate(safe_r, likely_r, safe_r, safe_r), # Kentucky | |
estimate(safe_r, likely_r, safe_r, safe_r), # Louisiana | |
estimate(likely_r, likely_r, safe_r, likely_r), # Missouri | |
estimate(tossup, tossup, tossup, tossup), # Nevada | |
estimate(tossup, tossup, tilt_r, tossup), # New Hampshire | |
estimate(lean_r, likely_r, lean_r, likely_r), # North Carolina | |
estimate(lean_r, lean_r, lean_r, lean_r), # Ohio | |
estimate(lean_d, lean_d, tilt_d, lean_d), # Pennsylvania | |
estimate(tossup, lean_d, tossup, tossup), # Wisconsin | |
] + | |
[estimate(safe_r, safe_r, safe_r, safe_r)] * 10 | |
end | |
end | |
# https://en.wikipedia.org/wiki/United_States_House_of_Representatives_elections,_2016 | |
class House < CongressCarlo | |
def initialize | |
@name = 'House' | |
@dems_not_running = 0 | |
@reps_not_running = 0 | |
end | |
def odds_of_dem_win_per_race | |
[estimate(safe_d, safe_d, safe_d, safe_d)] * 166 + | |
[ | |
estimate(safe_r, safe_r, likely_r), # AK AL | |
estimate(tossup, tossup, tossup), # AZ 1 | |
estimate(lean_r, lean_r, tossup), # AZ 2 | |
estimate(likely_d, safe_d, likely_d), # AZ 9 | |
estimate(lean_d, likely_d, lean_d), # CA 7 | |
estimate(lean_r, safe_r, lean_r), # CA 10 | |
estimate(likely_d, safe_d, likely_d), # CA 16 | |
estimate(lean_r, likely_r, lean_r), # CA 21 | |
estimate(likely_d, safe_d, lean_d), # CA 24 | |
estimate(lean_r, lean_r, likely_r), # CA 25 | |
estimate(safe_d, safe_d, likely_d), # CA 26 | |
estimate(likely_d, safe_d, likely_d), # CA 31 | |
estimate(likely_d, safe_d, likely_d), # CA 36 | |
estimate(likely_d, safe_d, lean_d), # CA 52 | |
estimate(tossup, lean_r, tossup), # CO 6 | |
estimate(likely_d, safe_d, safe_d), # CT 5 | |
estimate(lean_r, likely_d, lean_d), # FL 2 | |
estimate(likely_r, safe_r, safe_r), # FL 7 | |
estimate(lean_d, safe_r, safe_r), # FL 10 | |
estimate(lean_d, likely_r, tossup), # FL 13 | |
estimate(tossup, tossup, tossup), # FL 18 | |
estimate(tossup, tilt_r, tossup), # FL 26 | |
estimate(tossup, tossup, tossup), # IL 10 | |
estimate(likely_r, likely_r, likely_r), # IL 12 | |
estimate(likely_r, safe_r, likely_r), # IL 13 | |
estimate(safe_r, safe_r, safe_r), # IN 2 | |
estimate(tossup, tilt_d, lean_d), # IA 1 | |
estimate(likely_d, safe_d, likely_d), # IA 2 | |
estimate(lean_r, tossup, lean_r), # IA 3 | |
estimate(tossup, tossup, tossup), # ME 2 | |
estimate(likely_d, safe_d, likely_d), # MD 6 | |
estimate(lean_r, lean_r, tossup), # MI 1 | |
estimate(lean_r, tilt_r, lean_r), # MI 7 | |
estimate(likely_r, safe_r, likely_r), # MI 8 | |
estimate(safe_d, safe_d, likely_d), # MN 1 | |
estimate(tossup, tossup, tossup), # MN 2 | |
estimate(likely_d, safe_d, likely_d), # MN 7 | |
estimate(lean_d, likely_d, lean_d), # MN 8 | |
estimate(tossup, tossup, tossup), # NE 2 | |
estimate(tossup, tossup, lean_r), # NV 3 | |
estimate(lean_d, tilt_d, lean_d), # NV 4 | |
estimate(tossup, tossup, tossup), # NH 1 | |
estimate(safe_d, safe_d, likely_d), # NH 2 | |
estimate(likely_r, safe_r, likely_r), # NJ 3 | |
estimate(lean_r, likely_r, likely_r), # NJ 5 | |
estimate(likely_r, safe_r, likely_r), # NM 2 | |
estimate(lean_r, tilt_r, tossup), # NY 1 | |
estimate(safe_d, safe_d, safe_d), # NY 4 | |
estimate(likely_r, safe_r, likely_r), # NY 11 | |
estimate(likely_d, safe_d, likely_d), # NY 18 | |
estimate(tossup, tossup, tossup), # NY 19 | |
estimate(likely_r, safe_r, likely_r), # NY 21 | |
estimate(likely_r, safe_r, likely_r), # NY 23 | |
estimate(lean_r, tossup, tossup), # NY 24 | |
estimate(likely_d, safe_d, likely_d), # NY 25 | |
estimate(likely_r, safe_r, safe_r), # NC 9 | |
estimate(safe_r, safe_r, likely_r), # OH 14 | |
estimate(likely_r, likely_r, likely_r), # PA 6 | |
estimate(tossup, tilt_r, tossup), # PA 8 | |
estimate(tossup, tossup, tossup), # TX 23 | |
estimate(likely_r, safe_r, likely_r), # UT 4 | |
estimate(safe_r, safe_r, likely_r), # VA 2 | |
estimate(lean_r, safe_r, likely_r), # VA 4 | |
estimate(likely_r, lean_r, likely_r), # VA 10 | |
estimate(likely_r, safe_r, safe_r) # WV 2 | |
] + | |
[estimate(safe_r, safe_r, safe_r, safe_r)] * 204 | |
end | |
end | |
Senate.new.run | |
puts | |
House.new.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment