Skip to content

Instantly share code, notes, and snippets.

@phs
Created October 17, 2015 17:56
Show Gist options
  • Save phs/242b5e6cbce05f9d0f2d to your computer and use it in GitHub Desktop.
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
#! /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