Add files via upload
This commit is contained in:
1 parent
1919d7f8f5
commit
5f483bb6c4
1 file changed
+604
@@ -0,0 +1,604 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<div align=right><i>Peter Norvig</i></div>\n",
|
||||
"\n",
|
||||
"# Tracking Trump: Electoral Votes Edition\n",
|
||||
"\n",
|
||||
"[538](https://projects.fivethirtyeight.com/trump-approval-ratings/) shows presidential approval ratings (currently about 42% (±4) approval and 52% (±4) disapproval). But do approval ratings predict election results? Surely there is a correlation—popular presidents are more likely to be re-elected. But there are three big caveats:\n",
|
||||
"\n",
|
||||
"1. These are approval polls, not votes. We don't know who will be on the ballot and what their approval levels will be, we don't know if there is systematic bias in the polling data, and we don't know how many people will vote for a candidate they disapprove of or against a candidate they approve of.\n",
|
||||
"\n",
|
||||
"2. This is today, not election day 2020. Things can change. Key economic, geopolitical, or legal events might happen.\n",
|
||||
"\n",
|
||||
"3. These are popular votes, not electoral votes. \n",
|
||||
"\n",
|
||||
"We can't be conclusive about the first two points, but this notebook can take the state-by-state, month-by-month approval data from Morning Consult's\n",
|
||||
"[Tracking Trump](https://morningconsult.com/tracking-trump/) web page and compute electoral votes, under the assumption that Trump wins the electoral votes of states he has positive net approval (and wins half the electoral votes for states where approval exactly equals disapproval).\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# TL;DR for policy wonks\n",
|
||||
"\n",
|
||||
"As of 1 April 2019, Trump would expect **180 electoral votes** under these assumptions (recall that you need **270** to win). He's been below 270 every month for the last two years.\n",
|
||||
"I have five ways of understanding the fluidity of the situation:\n",
|
||||
"\n",
|
||||
"- **Undecided**: If many voters are undecided, the net approval could change a lot. So I track the number of states for which at least 5% of voters are undecided. At the inauguration in 2017, all 51 states (including DC) had at least 5% undecided; now there are no such states. Overall 4% of voters are undecided. Most people have made up their mind.\n",
|
||||
"\n",
|
||||
"- **Variance**: How much are voters changing their minds from month to month in each state? I track the standard deviation, 𝝈, of the net approval for each state over the last 12 months.\n",
|
||||
"\n",
|
||||
"- **Movement**: What's the most a state's net approval could be expected to move, due to random fluctuations (that is, assuming there is no big event that changes people's minds)? I define the maximum expected **movement** of a state as 1/5 of the undecided voters (i.e. assume the undecided voters broke 60/40 one way or the other) plus 2 standard deviations in the net approval. If all the states had maximum expected movement towards Trump he would take **259** electoral votes, and if the states all swung the other way, he would take **79** electoral votes.\n",
|
||||
"\n",
|
||||
"- **Swing state**: I define a swing state as one whose maximum expected movement is greater than the absolute value of the net approval. There are 15 such states now.\n",
|
||||
"\n",
|
||||
"- **Margin**: Suppose a future event swings voters in one direction or another uniformly, across the board in all states. How much of a swing would be necessary to change the election outcome? We call that the **margin**. Today **Trump's margin is 7%:** if he got 7% more votes in all states he would be over 270 electoral votes. (This could come, for example, by convincing undecided voters to break for him at a 2% to 1% ratio, and then convincing 3% of disapproving voters to switch to approving.)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# The details for data science nerds\n",
|
||||
"\n",
|
||||
"First fetch the Tracking Trump web page and cache it locally: "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
|
||||
" Dload Upload Total Spent Left Speed\n",
|
||||
"100 115k 0 115k 0 0 210k 0 --:--:-- --:--:-- --:--:-- 210k\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"! curl -o evs.html https://morningconsult.com/tracking-trump/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now define the code. Note that `data` contains the [electoral votes by state](https://www.britannica.com/topic/United-States-Electoral-College-Votes-by-State-1787124) and the [partisan lean by state](https://github.com/fivethirtyeight/data/tree/master/partisan-lean) (how much more Republican (plus) or Democratic (minus) leaning the state is compared to the country as a whole, across multiple recent elections), and `net_usa` has the [country-wide net presidential approval](https://projects.fivethirtyeight.com/trump-approval-ratings/)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%matplotlib inline\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import re\n",
|
||||
"import ast\n",
|
||||
"from collections import namedtuple\n",
|
||||
"from IPython.display import display, Markdown\n",
|
||||
"from statistics import stdev\n",
|
||||
"\n",
|
||||
"data = { # From https://github.com/fivethirtyeight/data/tree/master/partisan+lean \n",
|
||||
" # a dict of {\"state name\": (electoral_votes, partisan_lean)}\n",
|
||||
" \"Alabama\": (9, +27), \"Alaska\": (3, +15), \"Arizona\": (11, +9), \n",
|
||||
" \"Arkansas\": (6, +24), \"California\": (55, -24), \"Colorado\": (9, -1), \n",
|
||||
" \"Connecticut\": (7, -11), \"Delaware\": (3, -14), \"District of Columbia\": (3, -43),\n",
|
||||
" \"Florida\": (29, +5), \"Georgia\": (16, +12), \"Hawaii\": (4, -36), \n",
|
||||
" \"Idaho\": (4, +35), \"Illinois\": (20, -13), \"Indiana\": (11, +18), \n",
|
||||
" \"Iowa\": (6, +6), \"Kansas\": (6, +23), \"Kentucky\": (8, +23), \n",
|
||||
" \"Louisiana\": (8, +17), \"Maine\": (4, -5), \"Maryland\": (10, -23), \n",
|
||||
" \"Massachusetts\": (11, -29), \"Michigan\": (16, -1), \"Minnesota\": (10, -2), \n",
|
||||
" \"Mississippi\": (6, +15), \"Missouri\": (10, +19), \"Montana\": (3, +18), \n",
|
||||
" \"Nebraska\": (5, +24), \"Nevada\": (6, +1), \"New Hampshire\": (4, +2), \n",
|
||||
" \"New Jersey\": (14, -13), \"New Mexico\": (5, -7), \"New York\": (29, -22), \n",
|
||||
" \"North Carolina\": (15, +5), \"North Dakota\": (3, +33), \"Ohio\": (18, +7), \n",
|
||||
" \"Oklahoma\": (7, +34), \"Oregon\": (7, -9), \"Pennsylvania\": (20, +1), \n",
|
||||
" \"Rhode Island\": (4, -26), \"South Carolina\": (9, +17), \"South Dakota\": (3, +31), \n",
|
||||
" \"Tennessee\": (11, +28), \"Texas\": (38, +17), \"Utah\": (6, +31), \n",
|
||||
" \"Vermont\": (3, -24), \"Virginia\": (13, 0), \"Washington\": (12, -12), \n",
|
||||
" \"West Virginia\": (5, +30), \"Wisconsin\": (10, +1), \"Wyoming\": (3, +47)}\n",
|
||||
"\n",
|
||||
"net_usa = { # From https://projects.fivethirtyeight.com/trump-approval-ratings/\n",
|
||||
" '1-Jan-17': +10, # a dict of {date: country-wide-net-approval}\n",
|
||||
" '1-Feb-17': 0, '1-Mar-17': -6, '1-Apr-17': -13, '1-May-17': -11,\n",
|
||||
" '1-Jun-17': -16, '1-Jul-17': -15, '1-Aug-17': -19, '1-Sep-17': -20,\n",
|
||||
" '1-Oct-17': -17, '1-Nov-17': -19, '1-Dec-17': -18, '1-Jan-18': -18,\n",
|
||||
" '1-Feb-18': -15, '1-Mar-18': -14, '1-Apr-18': -13, '1-May-18': -12,\n",
|
||||
" '1-Jun-18': -11, '1-Jul-18': -10, '1-Aug-18': -12, '1-Sep-18': -14,\n",
|
||||
" '1-Oct-18': -11, '1-Nov-18': -11, '1-Dec-18': -10, '1-Jan-19': -12,\n",
|
||||
" '1-Feb-19': -16, '1-Mar-19': -11, '1-Apr-19': -11, '1-May-19': -12}\n",
|
||||
"\n",
|
||||
"State = namedtuple('State', 'name, ev, lean, apps, diss')\n",
|
||||
"State.__doc__ = '''A State has a name, the number of electoral votes (.ev),\n",
|
||||
"the partisan lean (.lean)and two dicts of {date: percent}:\n",
|
||||
".apps (approvals) and .diss (disapprovals)'''\n",
|
||||
"\n",
|
||||
"def parse_page(filename='evs.html', data=data):\n",
|
||||
" \"Read data from the file and return (list of dates, list of `State`s, last date).\"\n",
|
||||
" # File format: Date headers, then [state, approval, disapproval ...]\n",
|
||||
" # [[\"Demographic\",\"1-Jan-17\",\"\",\"1-Feb-17\",\"\", ... \"1-Apr-19\",\"\"],\n",
|
||||
" # [\"Alabama\",\"62\",\"26\",\"65\",\"29\", ... \"61\",\"35\"], ... ] =>\n",
|
||||
" # State(\"Alabama\", 9, apps={\"1-Jan-17\": 62, ...}, diss={\"1-Jan-17\": 26, ...}), ...\n",
|
||||
" text = re.findall(r'\\[\\[.*?\\]\\]', open(filename).read())[0]\n",
|
||||
" table = ast.literal_eval(text)\n",
|
||||
" dates = table[0][1::2]\n",
|
||||
" states = [State(name, *data[name],\n",
|
||||
" dict(zip(dates, map(int, numbers[0::2]))),\n",
|
||||
" dict(zip(dates, map(int, numbers[1::2]))))\n",
|
||||
" for (name, *numbers) in table[1:]]\n",
|
||||
" return dates, states, dates[-1]\n",
|
||||
"\n",
|
||||
"dates, states, now = parse_page()\n",
|
||||
"\n",
|
||||
"assert len(states) == 51 and sum(s.ev for s in states) == 538\n",
|
||||
"\n",
|
||||
"def EV(states, date=now, swing=0):\n",
|
||||
" \"Total electoral votes with net positive approval (plus half the votes for net zero).\"\n",
|
||||
" return sum(s.ev * (1/2 if net(s, date) + swing == 0 else int(net(s, date) + swing > 0))\n",
|
||||
" for s in states)\n",
|
||||
"\n",
|
||||
"def margin(states, date=now):\n",
|
||||
" \"What's the least swing that would lead to a majority?\"\n",
|
||||
" return next(swing for swing in range(-50, 50) if EV(states, date, swing) >= 270)\n",
|
||||
"\n",
|
||||
"def net(state, date=now): return state.apps[date] - state.diss[date]\n",
|
||||
"def undecided(state, date=now): return 100 - state.apps[date] - state.diss[date]\n",
|
||||
"def movement(state, date=now): return undecided(state, date) / 5 + 2 * 𝝈(state)\n",
|
||||
"def 𝝈(state, recent=dates[-12:]): return stdev(net(state, d) for d in recent)\n",
|
||||
"def is_swing(state): return abs(net(state)) < movement(state)\n",
|
||||
"\n",
|
||||
"def md(lines): display(Markdown('\\n'.join(lines)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Current expected electoral votes, with various swings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"180"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"EV(states)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"7"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"margin(states)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{0: 180,\n",
|
||||
" 1: 180,\n",
|
||||
" 2: 202.0,\n",
|
||||
" 3: 224,\n",
|
||||
" 4: 233.0,\n",
|
||||
" 5: 242,\n",
|
||||
" 6: 251.5,\n",
|
||||
" 7: 271.0,\n",
|
||||
" 8: 289.5,\n",
|
||||
" 9: 298}"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"{swing: EV(states, now, swing)\n",
|
||||
" for swing in range(10)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We see that:\n",
|
||||
"- Trump is currently leading in states with only 180 electoral votes; \n",
|
||||
"- The margin is 7% (if he got 7% more popular in every state, he would win a narrow 271 vote victory).\n",
|
||||
"- Swings from 0 to 9% produce electoral vote totals from 180 ro 298."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Electoral votes by month\n",
|
||||
"\n",
|
||||
"The following plot shows, for each month in office, the expected number of electoral votes (based on net approval) with error bars indicating a 3% swing in either direction (Why 3%? That was the [average error](https://fivethirtyeight.com/features/the-polls-are-all-right/) in national presidential polls in 2016.) Trump hasn't been above 270 since 4 months into his term, and even with the 3% swing, since 6 months in."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAGVCAYAAABzbLxIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJzs3XdUVNfaBvDnMDB0AZFiARRBVMQuBEkUG8aLXRNNvDHBTkyuJRpDYrn6GY1dc2PUqNeYeBMLEjVqbFGsCBprLEhsKCodZOgzc74/iINDHXCGoTy/tVjLs09754jyss/e7xbS0tJEEBEREVGNZ6DvAIiIiIhIO5jYEREREdUSTOyIiIiIagkmdkRERES1BBM7IiIiolqCiR0RERFRLcHEjoiIiKiWYGJHREREVEsYlrYjMjKyUhf08fGpdDBEREREVHlCaStP2NjYQBAEjS8kiiIEQUBKSorWgqupYmJi4O7uru8w6iQ+e/3hs9cfPnv94HPXHz770pXaY7dnz56qjIOIiIiIXlGpiV337t2rMg4iIiIiekWcPEFERERUS5TaY1eSvLw8HDx4EFeuXEF6ejqUSqXafkEQsHr1aq0GSERERESa0Tixe/r0KQYNGoSYmBhYWlpCJpOhXr16eP78OURRhI2NDczMzHQZKxEREdVQcrkcmZmZWrmWiYkJ0tPTtXKt6sjc3ByGhhXqe1PR+Kx58+bh2bNn2L9/P1q2bAk3Nzds3boVXbp0wTfffIP//e9/2Lt3b6WCICIiotpLLpcjIyMD1tbWFaq4URpjY2OYmJhoIbLqRxRFpKWlwdLSslLJncZj7I4fP44xY8bAz89P7S/FzMwMn376KXx8fPD5559XOAAiIiKq3TIzM7WW1NV2giDA2tq60r2bGid2MpkMrq6uAAoyZQDIyMhQ7ff19cXZs2crFQQRERHVbkzqNPcqz0rjxM7R0RFPnjwBUPDu18bGBjdu3FDtj4uLg0QiqXQgRERERPRqNH556+vrixMnTuCzzz4DAPTv3x9ff/01pFIplEol1q9fj969e+ssUCIiIiIqm8Y9dh9++CH+8Y9/ICcnBwAwf/58dOrUCQsWLMDChQvh5eWFr776qtKBrFixAtbW1pg5c6aqLTg4GNbW1mpfRZPH3NxczJw5E66urmjUqBFGjhyJuLi4SsdBREREVFNp3GPn5eUFLy8v1baNjQ327duHlJQUGBgYwNrautJBXLhwAVu3boWnp2exff7+/tiwYYNqWyqVqu0PCQnBwYMHsXnzZtjY2OCLL77AiBEjcPLkSb4aJiIiojpF4x67R48eldhev379V0rq0tPTMX78ePznP/8p8TrGxsZwcHBQfdnY2Kid++OPP2LBggXo0aMH2rdvjw0bNuDGjRsIDw+vdExEREREK1euRI8ePeDk5ITmzZtjxIgRuHnzptoxRd8svviaMWOG6hhRFLF48WK0bNkSjo6OCAwMxK1bt3QSs8Y9dm3btoWPjw+GDh2KwYMHw97eXisBTJ06FYMGDUL37t2xdOnSYvsjIiLg5uYGKysr+Pn5Yc6cObCzswMAXLlyBfn5+ejZs6fq+CZNmsDDwwORkZHo1auXVmIsSUREBCIjIyt8no+PD3x9fXUQEREREWnTmTNnMHbsWHTs2BGiKGLRokUYPHgwIiMjVR1N0dHRaudcvnwZI0eOxODBg1Vta9aswdq1a7F27Vq4u7tj6dKlGDJkCC5cuABLS0utxiykpaWJmhy4atUqhIWF4c8//4REIkHXrl0xfPhwDBgwQK0XrSK2bt2K//73vzh69CikUikCAwPRunVrLFu2DACwe/dumJqawsXFBbGxsVi4cCGUSiXCw8NhbGyMXbt2YdKkSUhKSlKbGjxgwAA0b9681OXNYmJiKhVveQ4cOKC2HRgYqJP7EBER1SQmJiaqTpmXOWxzrNI44v/57JXOz8zMhLu7O77//nsEBASUeMwnn3yC8+fPq0rAiaKIdu3aYcyYMZg6dSoAIDs7G23atMG8efMwevToEq+TmJiomtfwMnd39zJj1LjHbtq0aZg2bRr++usvhIaGYs+ePZgyZQpmzJgBf39/DB06FIGBgRpnnjExMViwYAF+++23YuPmXhg2bJjqz56enmjfvj28vLxw+PBhDBw4sNRri6JYZg2Y8h7Kq1BAwJ9GTrhj2BBT3NxYt6eKxcTE6PTvl0rHZ68/fPb6weeuufT09GqxUsSrxpCWlgalUgk7O7sSryWTybB3717MmjVLtf/BgwdISEhAQECAqs3ExAR+fn64dOkSJkyYUOK96tWrBycnpwrHqPEYuxfc3Nzw2Wef4fz58zh16hQmT56MmJgYfPjhh/Dw8ND4OlFRUUhOToavry9sbW1ha2uLs2fPYtOmTbC1tUVubm6xcxo2bIhGjRrh3r17AAB7e3soFAokJyerHZeUlFTibwa6Fil1wxrLf+AXMx/ckDrjWFzxz0BEREQ102effQYvLy94e3uXuD80NBS5ubl45513VG3x8fEAUCwvsbOzQ0JCgtZjrNwKs3/z8vKCRCKBgYEBNm7cCJlMpvG5gYGB6NChg1rb5MmT0bx5c0yfPr3EXrzk5GQ8ffoUDg4OAID27dvDyMgIJ06cwFtvvQWgoFBydHQ0fHx8XuGTVc5DSQM8NzBTba++noE+TfT/GwoRERG9ms8//xznz5/HoUOHSq26sXXrVgQGBqJBgwbF9hV9g1fe28XKqlRid+/ePezevRthYWGIjo6GoaEhevToofbqtDwvZo28zMzMDDY2NmjdujVkMhm++uorDBw4EA4ODoiNjcWCBQtgZ2eH/v37AwCsrKzw3nvvYe7cubCzs1OVO/H09IS/v39lPtoreT33Nm5KC7tNzz7Lw8XEPHS2K/lVMxERUV2WPi6tUufl5ORU6avdkJAQhIWF4ddff0XTpk1LPObatWu4fPky5s6dq9b+ojMqISEBTZo0UbXr6u2ixondo0eP8Msvv2D37t24fv06BEGAn58fJk2ahEGDBr1SyZOSSCQS3Lx5E9u3b0d6ejocHBzwxhtvYMuWLWrj+BYtWgSJRIKgoCDk5OSgW7duWL9+vV5q2DVSpqGZPB73DR1UbauvZWBbL9sqj4WIiIhe3axZsxAWFob9+/ejRYsWpR63detWODs7F+tYcnFxgYODA06cOIGOHTsCKEhMIyIisGDBAq3HW6FyJwDQpUsXLFq0CEOGDFFlodry8qxSU1NThIWFlXuOiYkJli1bpppJq2+v595WS+wOxOYgJj0f7lZGeoyKiIiIKmrGjBnYsWMHtm3bBmtra9V4OXNzc1hYWKiOy8rKwq5du/Cvf/2r2OtVQRAQHByMFStWwN3dHW5ubli+fDnMzc0xfPhwrcescWI3b948DB06FM7OzloPojZxlSfAUZGKZ5KCEjAigK+vy/Cf1ytXEoaIiIj0Y9OmTQCAQYMGqbXPmjULISEhqu2wsDBkZmZi1KhRJV5nypQpyM7OxsyZM5GWloZOnTohLCxM6zXsgArUsaPyvaibd93ICbvNXlO1Sw2Aq285oqEZlzjTNZYf0B8+e/3hs9cPPnfNpaenw8rKSmvXq+oxdvpQ2WdWoXInMpkMS5YsQa9eveDu7g53d3f06tULS5cuRUZGRoVvXlu1zn8Ma2XhDOE8JbDuhuYzhomIiIgqQ+PELj4+Ht26dcNXX32F1NRUdOrUCR07dkRaWhoWL14Mf39/ndRjqYkkENE1945a25boTKTnKfUUEREREdUFGid28+fPx5MnT7B161ZcunQJ27dvx44dO/DHH3/ghx9+QFxcHObPn6/LWGuUDnn30cCk8PFm5IvYcjtTjxERERFRbadxYnf06FFMmDChxKW8BgwYgPHjx+PIkSNaDa4mM4ISE1uZq7WtuylDjpxDGomIiEg3NE7sMjIyylyzzNnZmePsihjXygLmhoXTnuOzldhxN0uPEREREVFtpnFi5+rqiv3790MUi/c4iaKI/fv3w9XVVavB1XQ2xgZ438NMre3rPzOgULLXjoiIiLRP48RuwoQJOHXqFIYNG4bff/8dDx8+xMOHD3Hs2DEMHz4cp0+fxqRJk3QZa430YWsLvNRph7vPFdgfm6O/gIiIiKjW0rhA8QcffIDExEQsX74c4eHhavuMjIzw+eefY/To0dqOr8ZrYmGI4a6m2H43W9W25noGBrqY6GTxXyIiotogIiICkZGRFT7Px8cHvr6+OoioZtA4sQOAmTNnIigoCMePH0dsbCyAgrF1PXv2RIMGDXQSYG0wxctSLbG7lJSP08/y0K2hMQB+8xIRERXl6+tb7Gfci4UAXpg6dWpVhlQjaJTY5eTkYO3atejUqRP8/f3x9ttv6zquWqWVjRH6Opng8KPCV7BrrmeoEjttfvMySSQiIqq7NErsTExMsGzZMixdulTX8dRaU70s1BK73+NycT0lH171jbR6H/6GQ0REpB3BwcH4+eefi7W3bdsWcXFxmDRpEmbOnFls/6ZNmzBnzhzcvn1bq0upaULjyROenp548OCBDkOp3XwdjOFjL1Vr+/o6y8MQERFVZ/7+/oiOjlb72rt3L95++23873//K7FayLZt2zBw4MAqT+qACoyxmzt3LoKCguDn54devXrpMqZaa4qXBd79PUW1HXY/G7M7yuFiWaGhjkRERLWC9Za4ip1g9Zba5r8reH5aUOOK3Q+AsbExHBwcirWPHj0a69atw+nTp9GtWzdV+/Xr13HlyhV8+eWXAID09HTMnDkTx48fR0ZGBhwdHTFx4kR8+OGHFY5FExpnFOvWrYONjQ3eeustODs7o2nTpjAxMVE7RhCEErssqcCbTiZoaW2I22lyAIBCBL65IcOy16z1HBkRERFVRKtWrdC5c2ds27ZNLbH78ccf0bx5c/j5+QEAFi5ciJs3b2LHjh1o0KABYmNjkZycrLOLine truncated
|
||||
"text/plain": [
|
||||
"<Figure size 648x432 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def labels(xlab, ylab): plt.xlabel(xlab); plt.ylabel(ylab); plt.grid(True); plt.legend()\n",
|
||||
" \n",
|
||||
"plt.rcParams[\"figure.figsize\"] = [9, 6]\n",
|
||||
"plt.style.use('fivethirtyeight')\n",
|
||||
" \n",
|
||||
"def plot1(states, dates, swing=3):\n",
|
||||
" N = len(dates)\n",
|
||||
" err = [[EV(states, date) - EV(states, date, -swing) for date in dates],\n",
|
||||
" [EV(states, date, swing) - EV(states, date) for date in dates]]\n",
|
||||
" plt.errorbar(range(N), [EV(states, date) for date in dates],\n",
|
||||
" yerr=err, ecolor='grey', capsize=7, label='EVs')\n",
|
||||
" plt.plot(range(N), [270] * N, color='darkorange', label=\"270\")\n",
|
||||
" labels('Months into term', 'Electoral Votes with Net Positive Approval')\n",
|
||||
" \n",
|
||||
"plot1(states, dates)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Margin and country-wide net approval by month\n",
|
||||
"\n",
|
||||
"The next plot gives the swing margin needed to reach 270 for each month, along with the country-wide net approval."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 648x432 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def plot2(states, dates):\n",
|
||||
" N = len(dates)\n",
|
||||
" plt.plot(range(N), [0] * N, label='Net zero', color='darkorange')\n",
|
||||
" plt.plot(range(N), [-margin(states, date) for date in dates], label='Margin')\n",
|
||||
" plt.plot(range(N), [net_usa[date] for date in dates], label='Country-wide Net')\n",
|
||||
" labels('Months into term', 'Net popularity')\n",
|
||||
" \n",
|
||||
"plot2(states, dates)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Month-by-month summary\n",
|
||||
"\n",
|
||||
"For each month, we show the expected electoral vote total (**EVs**), the swing margin needed to get to 270 (**Margin**), the overall (popular vote) net approval across the whole country (**Country**), and then the total percentage of undecided voters and in parentheses the number of states with at least 5% undecided.\n",
|
||||
"Note that the country-wide vote is not all that correlated with the state-by-state margin: recently the state-by-state margin has held at 7% while the country-wide net approval has ranged from -10% to -16%, and when the state-by-state margin jumped to 11%, the country-wide measure stayed right in the middle at 12%."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"|Month|EVs|Margin|Country|Undecided|\n",
|
||||
"|-|-|-|-|-|\n",
|
||||
"|Apr 2019|180|7%|-11%|4% (0)|\n",
|
||||
"|Mar 2019|193|7%|-11%|4% (2)|\n",
|
||||
"|Feb 2019|170|7%|-16%|4% (0)|\n",
|
||||
"|Jan 2019|126|11%|-12%|4% (0)|\n",
|
||||
"|Dec 2018|164|7%|-10%|5% (3)|\n",
|
||||
"|Nov 2018|233|5%|-11%|4% (1)|\n",
|
||||
"|Oct 2018|247|6%|-11%|4% (3)|\n",
|
||||
"|Sep 2018|203|8%|-14%|4% (1)|\n",
|
||||
"|Aug 2018|224|6%|-12%|4% (0)|\n",
|
||||
"|Jul 2018|225|6%|-10%|4% (1)|\n",
|
||||
"|Jun 2018|226|5%|-11%|4% (0)|\n",
|
||||
"|May 2018|232|5%|-12%|4% (0)|\n",
|
||||
"|Apr 2018|209|7%|-13%|4% (0)|\n",
|
||||
"|Mar 2018|196|9%|-14%|4% (0)|\n",
|
||||
"|Feb 2018|247|4%|-15%|4% (2)|\n",
|
||||
"|Jan 2018|201|4%|-18%|5% (4)|\n",
|
||||
"|Dec 2017|189|8%|-18%|5% (8)|\n",
|
||||
"|Nov 2017|174|8%|-19%|5% (7)|\n",
|
||||
"|Oct 2017|209|8%|-17%|5% (7)|\n",
|
||||
"|Sep 2017|201|7%|-20%|5% (8)|\n",
|
||||
"|Aug 2017|163|10%|-19%|7% (33)|\n",
|
||||
"|Jul 2017|196|3%|-15%|5% (4)|\n",
|
||||
"|Jun 2017|248|2%|-16%|5% (15)|\n",
|
||||
"|May 2017|269|1%|-11%|5% (4)|\n",
|
||||
"|Apr 2017|365|-7%|-13%|4% (4)|\n",
|
||||
"|Mar 2017|374|-8%|-6%|5% (14)|\n",
|
||||
"|Feb 2017|402|-8%|0%|6% (48)|\n",
|
||||
"|Jan 2017|448|-10%|10%|11% (51)|"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def header(head): return head + '\\n' + '-'.join('|' * head.count('|'))\n",
|
||||
"\n",
|
||||
"def monthly(states, dates=reversed(dates)):\n",
|
||||
" yield header('|Month|EVs|Margin|Country|Undecided|')\n",
|
||||
" for date in dates:\n",
|
||||
" month = date.replace('1-', '').replace('-', ' 20')\n",
|
||||
" yield (f'|{month}|{int(EV(states, date))}|{margin(states, date)}%|{net_usa[date]}%'\n",
|
||||
" f'|{sum(s.ev * undecided(s, date) for s in states) / 538:.0f}% '\n",
|
||||
" f'({sum(undecided(s, date) > 5 for s in states)})|')\n",
|
||||
" \n",
|
||||
"md(monthly(states))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# State-by-state summary\n",
|
||||
"\n",
|
||||
"Below is each state sorted by net approval, with the state's maximum expected movement, and electoral vote allotment, followed by the cumulative running total of electoral votes and the percentages of approval, dissaproval, and undecided, and finally the standard deviation of the net approval over the last 12 months. By going down the **Total** column, you can see what it takes to win. \n",
|
||||
"\n",
|
||||
"The **bold state names** are the **swing states**, which I define as states in which the absolute value of net approval is less than two standard deviations of the net approval over time, plus a fifth of the undecided voters. The idea is that if we are just dealing with random sampling variation, you could expect future approval to be within two standard deviations 95% of the time, and if the undecideds split 60/40, then a candidate could get a net fifth of them. So it would be very unusual for the non-bold states to flip, unless some events change perception of the candidates.\n",
|
||||
"\n",
|
||||
"This analysis says that if we consider all and only the bold swing states to be in play, then the total electoral votes for Trump could be anywhere in the range of 79 (if he lost them all) to 248 + 11 = 259 (if he won them all). It would take winning all the swing states plus a three-standard deviation swing in Virgina for Trump to reach 272.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"|State|Net|Move|EV|Total|+|-|?|𝝈|\n",
|
||||
"|-|-|-|-|-|-|-|-|-|\n",
|
||||
"|Wyoming|+28%|8%|3|3|62%|34%|4%|3.5%|\n",
|
||||
"|Alabama|+26%|8%|9|12|61%|35%|4%|3.4%|\n",
|
||||
"|Louisiana|+20%|8%|8|20|58%|38%|4%|3.7%|\n",
|
||||
"|Mississippi|+20%|8%|6|26|58%|38%|4%|3.8%|\n",
|
||||
"|West Virginia|+20%|8%|5|31|58%|38%|4%|3.6%|\n",
|
||||
"|Tennessee|+18%|7%|11|42|57%|39%|4%|3.1%|\n",
|
||||
"|Idaho|+17%|4%|4|46|57%|40%|3%|1.8%|\n",
|
||||
"|Kentucky|+16%|3%|8|54|56%|40%|4%|1.1%|\n",
|
||||
"|Oklahoma|+11%|7%|7|61|54%|43%|3%|3.4%|\n",
|
||||
"|Arkansas|+10%|6%|6|67|53%|43%|4%|2.8%|\n",
|
||||
"|South Carolina|+10%|5%|9|76|53%|43%|4%|2.2%|\n",
|
||||
"|South Dakota|+10%|9%|3|79|53%|43%|4%|4.3%|\n",
|
||||
"|**North Dakota**|**+6%**|**6%**|3|82|51%|45%|4%|2.8%|\n",
|
||||
"|**Utah**|**+5%**|**8%**|6|88|51%|46%|3%|3.6%|\n",
|
||||
"|**Indiana**|**+4%**|**5%**|11|99|50%|46%|4%|2.0%|\n",
|
||||
"|**Missouri**|**+4%**|**7%**|10|109|50%|46%|4%|3.0%|\n",
|
||||
"|**Nebraska**|**+4%**|**6%**|5|114|50%|46%|4%|2.7%|\n",
|
||||
"|**Texas**|**+4%**|**6%**|38|152|50%|46%|4%|2.6%|\n",
|
||||
"|**Georgia**|**+3%**|**7%**|16|168|49%|46%|5%|3.2%|\n",
|
||||
"|**Montana**|**+3%**|**7%**|3|171|50%|47%|3%|3.4%|\n",
|
||||
"|**Kansas**|**+2%**|**7%**|6|177|49%|47%|4%|2.9%|\n",
|
||||
"|**Alaska**|**+1%**|**11%**|3|180|48%|47%|5%|5.1%|\n",
|
||||
"|**Florida**|**-2%**|**7%**|29|209|47%|49%|4%|3.3%|\n",
|
||||
"|**North Carolina**|**-2%**|**5%**|15|224|47%|49%|4%|2.2%|\n",
|
||||
"|**Ohio**|**-4%**|**6%**|18|242|46%|50%|4%|2.4%|\n",
|
||||
"|**Nevada**|**-6%**|**7%**|6|248|45%|51%|4%|3.1%|\n",
|
||||
"|Virginia|-6%|4%|13|261|45%|51%|4%|1.8%|\n",
|
||||
"|Pennsylvania|-7%|4%|20|281|45%|52%|3%|1.6%|\n",
|
||||
"|**Arizona**|**-8%**|**8%**|11|292|44%|52%|4%|3.7%|\n",
|
||||
"|Iowa|-8%|6%|6|298|44%|52%|4%|2.6%|\n",
|
||||
"|Michigan|-10%|5%|16|314|43%|53%|4%|2.2%|\n",
|
||||
"|New Mexico|-12%|7%|5|319|42%|54%|4%|2.9%|\n",
|
||||
"|Colorado|-13%|5%|9|328|42%|55%|3%|2.4%|\n",
|
||||
"|Minnesota|-13%|5%|10|338|42%|55%|3%|2.2%|\n",
|
||||
"|Wisconsin|-13%|5%|10|348|42%|55%|3%|2.4%|\n",
|
||||
"|Delaware|-15%|5%|3|351|41%|56%|3%|2.0%|\n",
|
||||
"|Maine|-15%|9%|4|355|41%|56%|3%|4.0%|\n",
|
||||
"|New Jersey|-17%|5%|14|369|40%|57%|3%|2.4%|\n",
|
||||
"|New Hampshire|-19%|8%|4|373|39%|58%|3%|3.6%|\n",
|
||||
"|Illinois|-22%|3%|20|393|37%|59%|4%|1.2%|\n",
|
||||
"|Oregon|-22%|5%|7|400|37%|59%|4%|2.0%|\n",
|
||||
"|Rhode Island|-22%|6%|4|404|37%|59%|4%|2.8%|\n",
|
||||
"|Connecticut|-23%|8%|7|411|37%|60%|3%|3.8%|\n",
|
||||
"|New York|-24%|4%|29|440|36%|60%|4%|1.8%|\n",
|
||||
"|Washington|-26%|5%|12|452|35%|61%|4%|2.1%|\n",
|
||||
"|Massachusetts|-28%|5%|11|463|34%|62%|4%|2.2%|\n",
|
||||
"|California|-29%|7%|55|518|34%|63%|3%|3.1%|\n",
|
||||
"|Maryland|-30%|8%|10|528|33%|63%|4%|3.5%|\n",
|
||||
"|Hawaii|-34%|9%|4|532|31%|65%|4%|4.2%|\n",
|
||||
"|Vermont|-37%|10%|3|535|30%|67%|3%|4.8%|\n",
|
||||
"|District of Columbia|-60%|7%|3|538|18%|78%|4%|3.1%|"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def by_state(states, d=now):\n",
|
||||
" total = 0\n",
|
||||
" yield header('|State|Net|Move|EV|Total|+|-|?|𝝈|')\n",
|
||||
" for s in sorted(states, key=net, reverse=True):\n",
|
||||
" total += s.ev\n",
|
||||
" b = '**' if is_swing(s) else ''\n",
|
||||
" yield (f'|{b}{s.name}{b}|{b}{net(s):+d}%{b}|{b}{movement(s):.0f}%{b}|{s.ev}|{total}'\n",
|
||||
" f'|{s.apps[d]}%|{s.diss[d]}%|{undecided(s, now)}%|{𝝈(s):3.1f}%|')\n",
|
||||
"\n",
|
||||
"md(by_state(states))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Popularity Above Replacement President\n",
|
||||
"\n",
|
||||
"Fivethirtyeight is a combination sports/politics site, and it has a lot of statistics about soorts players and how much better they are than the average replacement player. Given that, they [decided](https://fivethirtyeight.com/features/the-states-where-trump-is-more-and-less-popular-than-he-should-be/) to rate the president's approval versus each state's overall approval of his party, which is a way of rating the president's performance versus an average replacement candidate from the same party. I'll duplicate that work and keep it up to date.\n",
|
||||
"\n",
|
||||
"There are only five states where Trump is exceeding a replacement Republican (i.e., has a positive PARP): two deep-red, deep South states, Mississippi and Lousiana, and three deep-blue coastal states, Rhode Island, Hawaii, and Massachussetts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"|State|PARP|Lean|Net|EV|\n",
|
||||
"|-|-|-|-|-|\n",
|
||||
"|Mississippi|+5|+15|+20|6|\n",
|
||||
"|Rhode Island|+4|-26|-22|4|\n",
|
||||
"|Louisiana|+3|+17|+20|8|\n",
|
||||
"|Hawaii|+2|-36|-34|4|\n",
|
||||
"|Massachusetts|+1|-29|-28|11|\n",
|
||||
"|Alabama|-1|+27|+26|9|\n",
|
||||
"|Delaware|-1|-14|-15|3|\n",
|
||||
"|New York|-2|-22|-24|29|\n",
|
||||
"|New Jersey|-4|-13|-17|14|\n",
|
||||
"|California|-5|-24|-29|55|\n",
|
||||
"|New Mexico|-5|-7|-12|5|\n",
|
||||
"|Virginia|-6|+0|-6|13|\n",
|
||||
"|Florida|-7|+5|-2|29|\n",
|
||||
"|Kentucky|-7|+23|+16|8|\n",
|
||||
"|Maryland|-7|-23|-30|10|\n",
|
||||
"|Nevada|-7|+1|-6|6|\n",
|
||||
"|North Carolina|-7|+5|-2|15|\n",
|
||||
"|South Carolina|-7|+17|+10|9|\n",
|
||||
"|Pennsylvania|-8|+1|-7|20|\n",
|
||||
"|Georgia|-9|+12|+3|16|\n",
|
||||
"|Illinois|-9|-13|-22|20|\n",
|
||||
"|Michigan|-9|-1|-10|16|\n",
|
||||
"|Maine|-10|-5|-15|4|\n",
|
||||
"|Tennessee|-10|+28|+18|11|\n",
|
||||
"|West Virginia|-10|+30|+20|5|\n",
|
||||
"|Minnesota|-11|-2|-13|10|\n",
|
||||
"|Ohio|-11|+7|-4|18|\n",
|
||||
"|Colorado|-12|-1|-13|9|\n",
|
||||
"|Connecticut|-12|-11|-23|7|\n",
|
||||
"|Oregon|-13|-9|-22|7|\n",
|
||||
"|Texas|-13|+17|+4|38|\n",
|
||||
"|Vermont|-13|-24|-37|3|\n",
|
||||
"|Alaska|-14|+15|+1|3|\n",
|
||||
"|Arkansas|-14|+24|+10|6|\n",
|
||||
"|Indiana|-14|+18|+4|11|\n",
|
||||
"|Iowa|-14|+6|-8|6|\n",
|
||||
"|Washington|-14|-12|-26|12|\n",
|
||||
"|Wisconsin|-14|+1|-13|10|\n",
|
||||
"|Missouri|-15|+19|+4|10|\n",
|
||||
"|Montana|-15|+18|+3|3|\n",
|
||||
"|Arizona|-17|+9|-8|11|\n",
|
||||
"|District of Columbia|-17|-43|-60|3|\n",
|
||||
"|Idaho|-18|+35|+17|4|\n",
|
||||
"|Wyoming|-19|+47|+28|3|\n",
|
||||
"|Nebraska|-20|+24|+4|5|\n",
|
||||
"|Kansas|-21|+23|+2|6|\n",
|
||||
"|New Hampshire|-21|+2|-19|4|\n",
|
||||
"|South Dakota|-21|+31|+10|3|\n",
|
||||
"|Oklahoma|-23|+34|+11|7|\n",
|
||||
"|Utah|-26|+31|+5|6|\n",
|
||||
"|North Dakota|-27|+33|+6|3|"
|
||||
],
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Markdown object>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def parp(state): return net(state) - state.lean \n",
|
||||
"\n",
|
||||
"def by_parp(states, d=now):\n",
|
||||
" yield header('|State|PARP|Lean|Net|EV|')\n",
|
||||
" for s in sorted(states, key=parp, reverse=True):\n",
|
||||
" yield (f'|{s.name}|{parp(s):+d}|{s.lean:+d}|{net(s):+d}|{s.ev}|')\n",
|
||||
"\n",
|
||||
"md(by_parp(states))"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.0"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
Reference in new issue
Block a user