Predict the Electoral College: The Paths to Victory
​Seth J. Chandler
Disputes currently rage in the United States about which candidate, Joe Biden or Donald Trump, should receive the electoral votes of various states in which the popular vote outcome was close and disputed. Each candidate has a number of paths to victory by receiving the electoral votes in some subset of these states. This essay develops an interactive tool whereby the user can input their probabilities for each candidate winning each state and computes the overall probability that the particular candidate will receive enough votes in the electoral college to serve as President beginning on January 20, 2021

Scenarios

I start with electoral vote data. Here are the states that, based on my reading of the news (others can differ) appear to be in play with both close election results and allegations of election improprieties or miscounts.
Create an Association from state to electoral votes:
unclearElectoralVotes=AssociationThread[{"Georgia","Pennsylvania","Michigan","Arizona","Nevada"},{20,16,16,11,6}]
Out[]=
Georgia20,Pennsylvania16,Michigan16,Arizona11,Nevada6
Let' s form a list of all possible outcomes. Since, ultimately, one slate of electors must be acknowledged by Congress as representing the state, I exclude the possibility of ties.
Use Tuples to form all
5
2
possible scenarios:
In[]:=
unclear=Keys[unclearElectoralVotes];
(scenarios=Map[AssociationThread[unclear,#]&,Tuples[{"Biden","Trump"},Length[unclear]]])//
[◼]
Terse
[10]
Out[]//Short=
{GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaBiden,GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaTrump,GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaTrump,NevadaBiden,26,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaBiden,NevadaTrump,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaBiden,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaTrump}

State - by - state outcome distributions

I now create a probability distribution of outcomes for each state. Notice that I do not yet need to assign values to the probabilities; I keep them symbolic:
Create synbolic categorical distributions for each state:
In[]:=
dist[state_]:=CategoricalDistribution[{"Biden","Trump"},{p[state,"Biden"],1-p[state,"Biden"]}]
dists=AssociationMap[dist,unclear]
Out[]=
GeorgiaCategoricalDistribution
Input type: Scalar
Categories:
Biden
Trump
,PennsylvaniaCategoricalDistribution
Input type: Scalar
Categories:
Biden
Trump
,MichiganCategoricalDistribution
Input type: Scalar
Categories:
Biden
Trump
,ArizonaCategoricalDistribution
Input type: Scalar
Categories:
Biden
Trump
,NevadaCategoricalDistribution
Input type: Scalar
Categories:
Biden
Trump


Assign probabilities to scenarios

I now want to assign a probability to each outcome. For example, if the probability that Trump would win Michigan is very low, any scenario that contains such an assumption will itself have a low probability. The scenario probabilities remain symbolic.
Use KeyValueMap and the PDF function to compute the symbolic probability of each scenario:
(scenarioProbabilities=AssociationMap[scenarioTimes@@KeyValueMap[{k,v}PDF[dists[k],v],scenario],scenarios])//
[◼]
Terse
[20]
Out[]//Short=
GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaBidenp[Arizona,Biden]p[Georgia,Biden]p[Michigan,Biden]p[Nevada,Biden]p[Pennsylvania,Biden],GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaTrumpp[Arizona,Biden]p[Georgia,Biden]p[Michigan,Biden](1-p[Nevada,Biden])p[Pennsylvania,Biden],GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaTrump,NevadaBiden(1-p[Arizona,Biden])p[Georgia,Biden]p[Michigan,Biden]p[Nevada,Biden]p[Pennsylvania,Biden],27,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaBiden(1-p[Arizona,Biden])(1-p[Georgia,Biden])(1-p[Michigan,Biden])p[Nevada,Biden](1-p[Pennsylvania,Biden]),GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaTrump(1-p[Arizona,Biden])(1-p[Georgia,Biden])(1-p[Michigan,Biden])(1-p[Nevada,Biden])(1-p[Pennsylvania,Biden])

Assign electoral votes to scenarios

To determine who wins, I need to assign electoral votes to each scenario.
For each scenario, total the electoral votes the candidate would receive :
(scenarioVotes=AssociationMap[scenarioGroupBy[Reverse/@Normal[scenario],FirstLast,(Lookup[unclearElectoralVotes,#,Missing[]]&)/*Total],scenarios])//
[◼]
Terse
[12]
Out[]//Short=
GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaBidenBiden69,GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaBiden,NevadaTrumpBiden63,Trump6,GeorgiaBiden,PennsylvaniaBiden,MichiganBiden,ArizonaTrump,NevadaBidenBiden58,Trump11,26,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaBiden,NevadaTrumpTrump58,Biden11,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaBidenTrump63,Biden6,GeorgiaTrump,PennsylvaniaTrump,MichiganTrump,ArizonaTrump,NevadaTrumpTrump69

Develop a symbolic expression for a victory criteria

I now want to merge our probability information with our vote information and compute the aggregate probability of each candidate winning. I'll do this symbolically at first and later plug in estimated values.
Use GroupBy and Merge to get the aggregate probability of Biden receiving the requisite number of votes :
In[]:=
victoryFunction[scenariovotes_,v_:33]:=If[Lookup[scenariovotes,"Biden",0]≥v,"Biden","Trump"]
results=GroupBy[Values@Merge[{scenarioProbabilities,scenarioVotes},{#[[1]],victoryFunction[#[[2]],33]}&],LastFirst,Total];
In[]:=
Short[results]
Out[]//Short=
Biden1,Trump1+12+1

An Interactive Tool

I can now construct an interactive tool that lets the user input the probabilities for each state and computes the aggregate probability of each candidate receiving enough votes to win the presidency. So as not to take sides in any of the disputes, I initialize the Biden win probability in each of the contested states to 0.5. Obviously, users and partisans can change those probabilities as they wish:
Create a simple Manipulate to make the results concrete :
Manipulate[Map[Evaluate,results/.{p["Georgia","Biden"]bidenga,p["Pennsylvania","Biden"]bidenpa,p["Michigan","Biden"]bidenmi,p["Arizona","Biden"]bidenaz,p["Nevada","Biden"]bidennv}]//Dataset,​​{{bidenga,0.5,"Biden win probability in Georgia"},0,1,0.01,Appearance"Labeled"},​​{{bidenpa,0.5,"Biden win probability in Pennsylvania"},0,1,0.01,Appearance"Labeled"},​​{{bidenmi,0.5,"Biden win probability in Michigan"},0,1,0.01,Appearance"Labeled"},​​{{bidenaz,0.5,"Biden win probability in Arizona"},0,1,0.01,Appearance"Labeled"},​​{{bidennv,0.5,"Biden win probability in Nevada"},0,1,0.01,Appearance"Labeled"},SaveDefinitionsTrue​​]​​
Out[]=
​
Biden win probability in Georgia
0.5
Biden win probability in Pennsylvania
0.5
Biden win probability in Michigan
0.5
Biden win probability in Arizona
0.5
Biden win probability in Nevada
0.5
Biden
0.5625
Trump
0.4375

Generalizing the process

The material above considers a specific example drawn from the 2020 election as it appears in mid November, 2020. But we can construct a more general approach.
Write a function that combines the steps taken above :
In[]:=
v[electoralVotes_Association,categoricalDists_Association,victoryfunction_]:=​​Module[{scenarios,scenarioProbabilities,scenarioVotes,results},​​scenarios=Map[AssociationThread[unclear,#]&,Tuples[Information[categoricalDists[Keys[electoralVotes][[1]]],"Categories"],Length[unclear]]];​​scenarioVotes=AssociationMap[scenarioGroupBy[Reverse/@Normal[scenario],FirstLast,(Lookup[electoralVotes,#,Missing[]]&)/*Total],scenarios];​​scenarioProbabilities=AssociationMap[scenarioTimes@@KeyValueMap[{k,v}PDF[categoricalDists[k],v],scenario],scenarios];​​results=GroupBy[Values@Merge[{scenarioProbabilities,scenarioVotes},{#[[1]],victoryfunction[#[[2]]]}&],LastFirst,Total]​​]
We can check to see if it replicates the results from above :
See if it works on the pre - existing example :
Now let' s extend it to a six state scenario in which I add the hypothetical "West" candidate to the equation. Let's create the new distributions
Create probability distributions by state with three candidates :
Tweak the victory function to accomodate the third candidate :
Run the v function :
We can make our state - by - state distributions concrete and see the results.
Create new distributions:

Building a Website

We can also create a simple website that enables users to input their own probability estimates. It's an amazingly simple process:
Develop a FormPage with controls, a function and some options:
Run the form to make sure it works:

Keywords

◼
  • Political science
  • ◼
  • Electoral college
  • ◼
  • FormPage
  • ◼
  • Combinatorics