|
|
|
@ -34,180 +34,6 @@ type Poll struct {
|
|
|
|
|
|
|
|
|
|
/* Globals */
|
|
|
|
|
var r = rand.New(rand.NewPCG(uint64(100), uint64(2224)))
|
|
|
|
|
var dev = false
|
|
|
|
|
|
|
|
|
|
/* Load data from csvs */
|
|
|
|
|
func readStates() ([]State, error) {
|
|
|
|
|
var states map[string]State = make(map[string]State)
|
|
|
|
|
|
|
|
|
|
/* Electoral college votes for the 2024 election*/
|
|
|
|
|
votes_file, err := os.Open("data/num-electors/electoral-college-votes.csv")
|
|
|
|
|
// votes_file, err := os.Open("data/electoral-college-votes-2010-census.csv")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the votes file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer votes_file.Close()
|
|
|
|
|
|
|
|
|
|
votes_reader := csv.NewReader(votes_file)
|
|
|
|
|
if _, err := votes_reader.Read(); err != nil { // Skip header
|
|
|
|
|
return nil, fmt.Errorf("error reading votes header: %v", err)
|
|
|
|
|
}
|
|
|
|
|
for {
|
|
|
|
|
csv_record, err := votes_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
votes, err := strconv.Atoi(csv_record[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue // Error in converting votes, skip this record
|
|
|
|
|
}
|
|
|
|
|
state := csv_record[0]
|
|
|
|
|
if _, exists := states[state]; !exists {
|
|
|
|
|
states[state] = State{Name: state, Votes: votes, PresidentialElectoralHistory: make(map[string]string)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Election results */
|
|
|
|
|
var years = []string{"2000", "2004", "2008", "2012", "2016", "2020"}
|
|
|
|
|
for _, year := range years {
|
|
|
|
|
electoral_history_filename := fmt.Sprintf("data/electoral-history/%s.csv", year)
|
|
|
|
|
electoral_history_file, err := os.Open(electoral_history_filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the electoral_history file for %s: %v", year, err)
|
|
|
|
|
}
|
|
|
|
|
electoral_history_reader := csv.NewReader(electoral_history_file)
|
|
|
|
|
if _, err := electoral_history_reader.Read(); err != nil { // Skip header
|
|
|
|
|
return nil, fmt.Errorf("error reading electoral_history header for %s: %v", year, err)
|
|
|
|
|
}
|
|
|
|
|
for {
|
|
|
|
|
record, err := electoral_history_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
state, party := record[0], record[1]
|
|
|
|
|
data, exists := states[state]
|
|
|
|
|
if !exists {
|
|
|
|
|
continue // State not found in votes map, skip
|
|
|
|
|
}
|
|
|
|
|
// Update the party winning in the specific year
|
|
|
|
|
data.PresidentialElectoralHistory[year] = party
|
|
|
|
|
states[state] = data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
electoral_history_file.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read polls */
|
|
|
|
|
polls_file, err := os.Open("data/polls/president_polls_state.csv") // Make sure to update this path
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the polls file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer polls_file.Close()
|
|
|
|
|
|
|
|
|
|
// Using a temporary map to group poll results by state and poll ID
|
|
|
|
|
state_polls_map := make(map[string]map[string]Poll)
|
|
|
|
|
|
|
|
|
|
polls_reader := csv.NewReader(polls_file)
|
|
|
|
|
_, err = polls_reader.Read() // Skip the header
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error reading polls header: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
record, err := polls_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll_id := record[0]
|
|
|
|
|
state_name := record[12]
|
|
|
|
|
end_date := record[14]
|
|
|
|
|
partisan := record[32]
|
|
|
|
|
candidate_name := record[44]
|
|
|
|
|
|
|
|
|
|
date_layout := "1/2/06"
|
|
|
|
|
parsed_date, err := time.Parse(date_layout, end_date)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Error parsing date: ", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sample_size, err := strconv.Atoi(record[22])
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue // If error, skip this record
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
percentage, err := strconv.ParseFloat(record[47], 64) // percentage is in the 42nd column
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Error parsing percentage")
|
|
|
|
|
continue // If error, skip this record
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, exists := state_polls_map[state_name]; !exists {
|
|
|
|
|
state_polls_map[state_name] = make(map[string]Poll)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll, exists := state_polls_map[state_name][poll_id]
|
|
|
|
|
if !exists {
|
|
|
|
|
poll = Poll{
|
|
|
|
|
PollId: poll_id,
|
|
|
|
|
SampleSize: sample_size,
|
|
|
|
|
PollResults: make(map[string]float64),
|
|
|
|
|
Date: parsed_date,
|
|
|
|
|
Partisan: partisan,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
poll.PollResults[candidate_name] = percentage
|
|
|
|
|
state_polls_map[state_name][poll_id] = poll
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the aggregated poll data to the respective states
|
|
|
|
|
for state_name, polls := range state_polls_map {
|
|
|
|
|
|
|
|
|
|
// Filter polls by recency and by having both Biden and Trump
|
|
|
|
|
var recent_polls []Poll
|
|
|
|
|
for _, poll := range polls {
|
|
|
|
|
if poll.Date.After(time.Now().AddDate(0, 0, -30)) {
|
|
|
|
|
recent_polls = append(recent_polls, poll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var recent_biden_trump_polls []Poll
|
|
|
|
|
for _, recent_poll := range recent_polls {
|
|
|
|
|
has_biden := false
|
|
|
|
|
has_trump := false
|
|
|
|
|
for candidate_name, _ := range recent_poll.PollResults {
|
|
|
|
|
if candidate_name == "Biden" {
|
|
|
|
|
has_biden = true
|
|
|
|
|
} else if candidate_name == "Trump" {
|
|
|
|
|
has_trump = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if has_biden && has_trump {
|
|
|
|
|
recent_biden_trump_polls = append(recent_biden_trump_polls, recent_poll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if state, exists := states[state_name]; exists {
|
|
|
|
|
state.Polls = recent_biden_trump_polls
|
|
|
|
|
states[state_name] = state // Not redundant
|
|
|
|
|
} else {
|
|
|
|
|
// fmt.Printf("Encountered new state: %s\n", state_name)
|
|
|
|
|
/*
|
|
|
|
|
states[state_name] = State{
|
|
|
|
|
Name: state_name,
|
|
|
|
|
Polls: polls_slice,
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert statesData map to a slice for returning
|
|
|
|
|
var states_slice []State
|
|
|
|
|
for _, state := range states {
|
|
|
|
|
states_slice = append(states_slice, state)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return states_slice, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sampling helper functions */
|
|
|
|
|
func getNormalCDF(x float64, mean float64, std float64) float64 {
|
|
|
|
@ -225,7 +51,6 @@ func getChanceCandidateWinsFromPollShare(candidate_p float64, poll_sample_size f
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getChanceRepublicanWinFromPoll(poll Poll, pretty_print bool) float64 {
|
|
|
|
|
|
|
|
|
|
biden_percentage, biden_exists := poll.PollResults["Biden"]
|
|
|
|
|
trump_percentage, trump_exists := poll.PollResults["Trump"]
|
|
|
|
|
if !biden_exists || !trump_exists {
|
|
|
|
@ -249,11 +74,9 @@ func getChanceRepublicanWinFromPoll(poll Poll, pretty_print bool) float64 {
|
|
|
|
|
fmt.Printf("\n\t\tPoll says chance of R win: %f", p_republican_win)
|
|
|
|
|
}
|
|
|
|
|
return p_republican_win
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getChanceRepublicanWinFromPollPlusUncertainty(poll Poll, state State, pretty_print bool) float64 {
|
|
|
|
|
|
|
|
|
|
// Uncertainty from the state
|
|
|
|
|
n_republican_win := 0
|
|
|
|
|
for _, party := range state.PresidentialElectoralHistory {
|
|
|
|
@ -297,7 +120,6 @@ func getChanceRepublicanWinFromPollPlusUncertainty(poll Poll, state State, prett
|
|
|
|
|
fmt.Printf("\n\t\tN republican wins: %d", n_republican_win)
|
|
|
|
|
fmt.Printf("\n\t\t=> Reducing additional uncertainty")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std_error := std_error_poll_mean + std_additional_uncertainty
|
|
|
|
@ -310,7 +132,6 @@ func getChanceRepublicanWinFromPollPlusUncertainty(poll Poll, state State, prett
|
|
|
|
|
fmt.Printf("\n\t\tPoll plus uncertainty says chance of R win: %f", p_republican_win)
|
|
|
|
|
}
|
|
|
|
|
return p_republican_win
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print state by state data */
|
|
|
|
@ -357,9 +178,7 @@ func printStates(states []State) {
|
|
|
|
|
_ = getChanceRepublicanWinFromPoll(aggregate_poll, true)
|
|
|
|
|
_ = getChanceRepublicanWinFromPollPlusUncertainty(aggregate_poll, state, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sample state by state */
|
|
|
|
@ -445,7 +264,6 @@ func sampleFromState(state State) VotesForEachParty {
|
|
|
|
|
|
|
|
|
|
/* Simulate election */
|
|
|
|
|
func simulateElection(states []State) int {
|
|
|
|
|
|
|
|
|
|
republican_seats := 0
|
|
|
|
|
for _, state := range states {
|
|
|
|
|
election_sample := sampleFromState(state)
|
|
|
|
@ -464,7 +282,6 @@ func barString(n int) string {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func printElectoralCollegeHistogram(samples []int) {
|
|
|
|
|
|
|
|
|
|
histogram := [538]int{}
|
|
|
|
|
for _, sample := range samples {
|
|
|
|
|
histogram[sample]++
|
|
|
|
@ -490,7 +307,179 @@ func printElectoralCollegeHistogram(samples []int) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Load data from csvs */
|
|
|
|
|
// Glue code
|
|
|
|
|
func readStates() ([]State, error) {
|
|
|
|
|
var states map[string]State = make(map[string]State)
|
|
|
|
|
|
|
|
|
|
/* Electoral college votes for the 2024 election*/
|
|
|
|
|
votes_file, err := os.Open("data/num-electors/electoral-college-votes.csv")
|
|
|
|
|
// votes_file, err := os.Open("data/electoral-college-votes-2010-census.csv")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the votes file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer votes_file.Close()
|
|
|
|
|
|
|
|
|
|
votes_reader := csv.NewReader(votes_file)
|
|
|
|
|
if _, err := votes_reader.Read(); err != nil { // Skip header
|
|
|
|
|
return nil, fmt.Errorf("error reading votes header: %v", err)
|
|
|
|
|
}
|
|
|
|
|
for {
|
|
|
|
|
csv_record, err := votes_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
votes, err := strconv.Atoi(csv_record[1])
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue // Error in converting votes, skip this record
|
|
|
|
|
}
|
|
|
|
|
state := csv_record[0]
|
|
|
|
|
if _, exists := states[state]; !exists {
|
|
|
|
|
states[state] = State{Name: state, Votes: votes, PresidentialElectoralHistory: make(map[string]string)}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Election results */
|
|
|
|
|
var years = []string{"2000", "2004", "2008", "2012", "2016", "2020"}
|
|
|
|
|
for _, year := range years {
|
|
|
|
|
electoral_history_filename := fmt.Sprintf("data/electoral-history/%s.csv", year)
|
|
|
|
|
electoral_history_file, err := os.Open(electoral_history_filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the electoral_history file for %s: %v", year, err)
|
|
|
|
|
}
|
|
|
|
|
electoral_history_reader := csv.NewReader(electoral_history_file)
|
|
|
|
|
if _, err := electoral_history_reader.Read(); err != nil { // Skip header
|
|
|
|
|
return nil, fmt.Errorf("error reading electoral_history header for %s: %v", year, err)
|
|
|
|
|
}
|
|
|
|
|
for {
|
|
|
|
|
record, err := electoral_history_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
state, party := record[0], record[1]
|
|
|
|
|
data, exists := states[state]
|
|
|
|
|
if !exists {
|
|
|
|
|
continue // State not found in votes map, skip
|
|
|
|
|
}
|
|
|
|
|
// Update the party winning in the specific year
|
|
|
|
|
data.PresidentialElectoralHistory[year] = party
|
|
|
|
|
states[state] = data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
electoral_history_file.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read polls */
|
|
|
|
|
polls_file, err := os.Open("data/polls/president_polls_state.csv") // Make sure to update this path
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error opening the polls file: %v", err)
|
|
|
|
|
}
|
|
|
|
|
defer polls_file.Close()
|
|
|
|
|
|
|
|
|
|
// Using a temporary map to group poll results by state and poll ID
|
|
|
|
|
state_polls_map := make(map[string]map[string]Poll)
|
|
|
|
|
|
|
|
|
|
polls_reader := csv.NewReader(polls_file)
|
|
|
|
|
_, err = polls_reader.Read() // Skip the header
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error reading polls header: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
record, err := polls_reader.Read()
|
|
|
|
|
if err != nil {
|
|
|
|
|
break // EOF or an error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll_id := record[0]
|
|
|
|
|
state_name := record[12]
|
|
|
|
|
end_date := record[14]
|
|
|
|
|
partisan := record[32]
|
|
|
|
|
candidate_name := record[44]
|
|
|
|
|
|
|
|
|
|
date_layout := "1/2/06"
|
|
|
|
|
parsed_date, err := time.Parse(date_layout, end_date)
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println("Error parsing date: ", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sample_size, err := strconv.Atoi(record[22])
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue // If error, skip this record
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
percentage, err := strconv.ParseFloat(record[47], 64) // percentage is in the 42nd column
|
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Printf("Error parsing percentage")
|
|
|
|
|
continue // If error, skip this record
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, exists := state_polls_map[state_name]; !exists {
|
|
|
|
|
state_polls_map[state_name] = make(map[string]Poll)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
poll, exists := state_polls_map[state_name][poll_id]
|
|
|
|
|
if !exists {
|
|
|
|
|
poll = Poll{
|
|
|
|
|
PollId: poll_id,
|
|
|
|
|
SampleSize: sample_size,
|
|
|
|
|
PollResults: make(map[string]float64),
|
|
|
|
|
Date: parsed_date,
|
|
|
|
|
Partisan: partisan,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
poll.PollResults[candidate_name] = percentage
|
|
|
|
|
state_polls_map[state_name][poll_id] = poll
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add the aggregated poll data to the respective states
|
|
|
|
|
for state_name, polls := range state_polls_map {
|
|
|
|
|
|
|
|
|
|
// Filter polls by recency and by having both Biden and Trump
|
|
|
|
|
var recent_polls []Poll
|
|
|
|
|
for _, poll := range polls {
|
|
|
|
|
if poll.Date.After(time.Now().AddDate(0, 0, -30)) {
|
|
|
|
|
recent_polls = append(recent_polls, poll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var recent_biden_trump_polls []Poll
|
|
|
|
|
for _, recent_poll := range recent_polls {
|
|
|
|
|
has_biden := false
|
|
|
|
|
has_trump := false
|
|
|
|
|
for candidate_name, _ := range recent_poll.PollResults {
|
|
|
|
|
if candidate_name == "Biden" {
|
|
|
|
|
has_biden = true
|
|
|
|
|
} else if candidate_name == "Trump" {
|
|
|
|
|
has_trump = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if has_biden && has_trump {
|
|
|
|
|
recent_biden_trump_polls = append(recent_biden_trump_polls, recent_poll)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if state, exists := states[state_name]; exists {
|
|
|
|
|
state.Polls = recent_biden_trump_polls
|
|
|
|
|
states[state_name] = state // Not redundant
|
|
|
|
|
} else {
|
|
|
|
|
// fmt.Printf("Encountered new state: %s\n", state_name)
|
|
|
|
|
/*
|
|
|
|
|
states[state_name] = State{
|
|
|
|
|
Name: state_name,
|
|
|
|
|
Polls: polls_slice,
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert statesData map to a slice for returning
|
|
|
|
|
var states_slice []State
|
|
|
|
|
for _, state := range states {
|
|
|
|
|
states_slice = append(states_slice, state)
|
|
|
|
|
}
|
|
|
|
|
return states_slice, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
@ -518,5 +507,4 @@ func main() {
|
|
|
|
|
|
|
|
|
|
p_republicans = p_republicans / float64(n_sims)
|
|
|
|
|
fmt.Printf("\n%% republicans: %f\n", p_republicans)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|