Add files via upload
This commit is contained in:
1 parent
9afa00f309
commit
133a4af69a
1 file changed
+155
-110
+155
-110
@@ -16,26 +16,30 @@
|
||||
"\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 \n",
|
||||
"[Morning Consult](https://morningconsult.com/tracking-trump/) and compute electoral votes, under the assumption that Trump wins the electoral votes of states he has positive net approval, and wins half the votes for states with zero net approval (i.e. approval exactly equals disapproval).\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 three ways of understanding the fluidity of the situation:\n",
|
||||
"I have five ways of understanding the fluidity of the situation:\n",
|
||||
"\n",
|
||||
"- **Undecided**: if many voters are undecided, the numbers could change. 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. **Most people have made up their mind.**\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 what would happen in each state if the undecided voters broke 60/40 for Trump, and the other voters swung in his favor by an amount equal to two standard deviations of their month-by-month change. The answer is that he would take **259** electoral votes (and if the states all swung the other way, he would take 79 electoral votes).\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",
|
||||
"- **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 results? We call that the **margin**. Today **Trump's margin is a 7%:** he would need 7% more votes in all states to win. (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",
|
||||
"- **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 web page and cache it locally, then define the code:"
|
||||
"First fetch the Tracking Trump web page and cache it locally, then define the code:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -49,7 +53,7 @@
|
||||
"text": [
|
||||
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
|
||||
" Dload Upload Total Spent Left Speed\n",
|
||||
"100 113k 0 113k 0 0 243k 0 --:--:-- --:--:-- --:--:-- 243k\n"
|
||||
"100 113k 0 113k 0 0 230k 0 --:--:-- --:--:-- --:--:-- 231k\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -90,8 +94,8 @@
|
||||
" 'Virginia': 13, 'Vermont': 3, 'Washington': 12,\n",
|
||||
" 'Wisconsin': 10, 'West Virginia': 5, 'Wyoming': 3}\n",
|
||||
"\n",
|
||||
"# net_usa: From https://projects.fivethirtyeight.com/trump-approval-ratings/\n",
|
||||
"# a dict of {date: country-wide net approval}\n",
|
||||
"# From https://projects.fivethirtyeight.com/trump-approval-ratings/\n",
|
||||
"# a dict of {date: country-wide net approval}\n",
|
||||
"net_usa = {'1-Jan-17': +10, \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",
|
||||
@@ -99,17 +103,18 @@
|
||||
" '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}\n",
|
||||
" '1-Feb-19': -16, '1-Mar-19': -11, '1-Apr-19': -11, '1-May-19': -12}\n",
|
||||
"\n",
|
||||
"State = namedtuple('State', 'name, ev, apps, diss')\n",
|
||||
"State.__doc__ = '''A State has a name, the number of electoral votes (.ev),\n",
|
||||
"and two dicts of {date: percent}, .apps (approvals) and .diss (disapprovals)'''\n",
|
||||
"\n",
|
||||
"def parse_page(filename='evs.html'):\n",
|
||||
" \"Read data from the file and return (list of dates, list of `State`s).\"\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",
|
||||
" # [\"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",
|
||||
@@ -117,10 +122,9 @@
|
||||
" 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\n",
|
||||
" return dates, states, dates[-1]\n",
|
||||
"\n",
|
||||
"dates, states = parse_page()\n",
|
||||
"now = dates[-1]\n",
|
||||
"dates, states, now = parse_page()\n",
|
||||
"\n",
|
||||
"assert len(states) == 51 and sum(s.ev for s in states) == 538\n",
|
||||
"\n",
|
||||
@@ -133,8 +137,12 @@
|
||||
" \"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 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)))"
|
||||
]
|
||||
},
|
||||
@@ -149,6 +157,46 @@
|
||||
"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": {
|
||||
@@ -165,7 +213,7 @@
|
||||
" 9: 298}"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -179,7 +227,10 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The first number says that Trump is currently leading in states with only 180 electoral votes, and we see that the margin is 7%, because that leads to 271 electoral votes, and any smaller swing is below 270."
|
||||
"We see that:\n",
|
||||
"- Trump is currently leading in states with only 180 electoral votes; \n",
|
||||
"- The margin is 7%; \n",
|
||||
"- Swings from 0 to 9% produce electoral vote totals from 180 ro 298."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -193,12 +244,12 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEKCAYAAAAIO8L1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzt3Xd4VGX2wPHvSYeQAKFEeiJVOhJARaXY0bWtYldExd3VVXSL5eeuuq6ru7qudV1RELBhV+ygEhTpvYNAQu+QQBIgJDm/P+6NxpByJ8lkSs7neebJzK3nnYF55+2iqhhjjDGlRQQ6AGOMMcHJMghjjDFlsgzCGGNMmSyDMMYYUybLIIwxxpTJMghjjDFlsgzCGGNMmSyDMMYYUybLIIwxxpQpKtABVEfTpk01JSWlSufm5uYSHx9fswEFmXBPY7inD8I/jZa+wFiwYMEeVW1W2XEhnUGkpKQwf/78Kp2bnp7O4MGDazagIBPuaQz39EH4p9HSFxgistHLcVbFZIwxpkyWQRhjjCmTZRDGGGPKZBmEMcaYMpXbSC0izwHlLhahqnf4JSJjjDFBoaJeTFXrHmSMMSYslJtBqOqE2gzEGGNMcKl0HISINAPuAboCccXbVXWoH+MyxhgTYF4Gyr0BvA2cD/wGuAHY7c+galp6ejrTp08/ZnvpbYMGDQrKQS3GGBMIXjKIJqo6VkTuVNXpwHQRmef1BiISidOesVVVLxCR8cAgINs9ZISqLhYRAZ4BhgF57vaFviSmPIMHD/7FF//48ePJyspi9OjRNXF5Y4wJS14yiKPu3+0icj6wDUjy4R53AquAxBLb/qSq75U67jygo/sYALzo/jXGGBMAXsZB/F1EGgJ/AP4IvALc5eXiItIap2rqFQ+HXwRMVMdsoJGItPByH2OMMTXPSwlijqpm41QJDfHx+k8DfwYSSm1/VET+CnwD3KuqR4BWwOYSx2xxt20veaKIjAJGASQnJ5Oenu5jSLBrXzar8howbdo0nJqt8JSTk1Ol9ydUhHv6IPzTaOkLbl4yiB9EJBOnofoDVd3v5cIicgGwS1UXiMjgErvuA3YAMcAYnB5Sf/MasKqOcc8jLS1Nq9Ko/NGS7UzLSeTa47pyxgnJPp8fKoJ1JsmaEu7pg/BPo6UvuFVaxaSqnYAHgG7AAhH5VESu9XDtgcCFbuYyCRgqIq+r6na3GukI8CrQ3z1+K9CmxPmt3W01rkfiERpG5vP01z+iWu5gcWOMqdM8zcWkqnNV9W6cL/N9QKWD6FT1PlVtraopwJXAt6p6bXG7gttr6WJguXvKZOB6cZwEZKvq9rKuXV2RAv3j97NsazbT1uzyxy2MMSbkeRkolwhcgvMl3x74kJ9/9VfFG+7gOwEW44ytAPgcp4vrOpxurjdW4x6V6lLvAKujUnjm6x8Z0rl5ldsiyhtjUZqNsTDGhBovbRBLgI+Av6nqrKrcRFXTgXT3eZkjsNWp67mtKtevikiB24d04J73l5G+ZjdDujSv0nXKGmMBMGLEiOoHaYwxAeQlgzheVVVEGohIA1XN8XtUteTSE1vz3LfrePqbHxncudlPpQgrFRhjjLcMopuIvIYzOE5EZDdwg6our+S8oFHeF/4//v4I7Qqa8sP+FEY9+DTXDO3zU4nASgXGmLrOSwYxBrhbVacBuF1WxwCn+DGuGlX6Cx9+7n6WX1DEkCfT2Z3Qn0GDQiZJxhjjd156McUXZw7wU3tCvN8iqmUxURHcPrQDizdnMX1tSM1BaIwxfuUlg9ggIn8RkRT38QCwwd+B1aZfn9iaVo3q8cw3Ni7CGGOKeckgRgLNgA+A94Gm7rawERMVwe+GtGfRpiy+/3FPoMMxxpigUGEG4U7V/X+qeoeqnqiqfVV1tNfpNkLJ5X3b0LJhHE9/vdZKEcYYQyUZhKoWAqfWUiwB5ZQiOrBwUxYz1lkpwhhjvFQxLRKRySJynYhcWvzwe2QBcHlaa7cUYW0RxhjjJYOIA/YCQ4FfuY8L/BlUoMRGRfLbIR1YsHE/P6zbG+hwjDEmoCodB6Gqfp0TKdgMT2vNf6et45lv1jKwQ5MaXS/CRmgbY0KJl8n6jsdZK/okQIFZwGhVzfBzbAERGxXJ7wa35y8fr2Dm+r0M7NC0xq5tI7SNMaHESxXTm8A7QAugJfAuzvoOYWt4vzYclxjHM9YWYYypw7xkEPVV9TVVLXAfr+O0S4St2KhIfjekPXMz9zFrvbVFGGPqJi8ZxBcicq87irqdiPwZ+FxEkkQkyd8BBsrwtDYkJ8by9Dc/YoUIY0xd5CWDGA7cCkzDWdPhtziLBy0A5ld2sohEisgiEfnUfZ0qInNEZJ2IvC0iMe72WPf1Ond/SpVSVEPioiP57aD2zM3YR2ZedCBDMcaYgPCyJnVqBY/jPdzjTmBVidf/BP6jqh2A/cBN7vabgP3u9v+4xwXUlf3b0jwhlvQ99QMdijHG1LpKMwgRiRORu0XkAxF5X0RGi4inNggRaQ2cD7zivhac8RTvuYdMwFmXGuAifl7r+j3gDKnJPqZVEBcdyW8Ht2fjoRjW51opwhhTt3ipYpoIdAOeA553n7/m8fpPA38GitzXTYAsVS1wX28BWrnPWwGbAdz92e7xAXVV/7YkRRfwwbZEtmUdCnQ4xhhTa7wsGNRdVbuWeD1NRFZWdpKIXADsUtUF7iJDNUJERgGjAJKTk0lPT6/SdXJycjyfe0HDLUza24ar/pvO/QPiiI0sv2CTlZUF4OnavhxbFb6kMRSFe/og/NNo6QtuXjKIhSJykqrOBhCRAXhonAYGAheKyDCcbrGJOAPuGolIlFtKaA1sdY/fCrQBtohIFNAQZ4qPX1DVMTgr2pGWlqZVHXFcvKKcF5mZmfw6LodJWxvy6a5GPHtl73JHWGdmZgJ4urYvx1aFL2kMReGePgj/NFr6gpuXKqa+wEwRyRSRTJyR1P1EZJmILC3vJFW9T1Vbq2oKTq+nb1X1GpzeUJe5h90AfOw+n+y+xt3/rQbRKLXODfL50zmd+WTJNv6bvj7Q4RhjjN95KUGcW8P3vAeYJCJ/BxYBY93tY4HXRGQdsA8nUwkqvx3UntXbD/LklDV0Tk7gzK7JgQ7JGGP8xstkfRuLn4tIPHAJcJWqnu/1Ju461unu8w1A/zKOOQxc7vWagSAi/PPXPcnYk8votxfz4e9OoWNyQqDDMsYYv/DSzTVGRC4RkXeB7cAZwP/8HlmQqhcTyZjr+xIXHcnNE+eTlZcf6JCMMcYvys0gRORsEXkVyAB+jdPddZ+q3qiqn9RWgMGoRcN6vHTdiWzPOsztby6ioLCo8pOMMSbEVFSC+BI4HjhVVa91MwX7JnT1bZfE3y/uzox1e3j081WVn2CMMSGmojaIE3Eair8WkQ04U3xH1kpUIWJ4vzas2nGAV3/I5IQWiQxPaxPokIwxpsaUm0Go6mJgMXCviJwCXAVEi8gXwIfueIQ67/+GncCPO3N44MPltG8WH+hwjDGmxngZB4GqzlTV3+MMbPsPzupyBoiKjOD5q/vQolEct762kOyjnt5SY4wJej59m6lqkapOUdWR/gooFDWqH8Mr16dx+Gghk7Ymkm8tNcaYMGA/d2tIx+QEnr6iN9sPRzF+UyM+XLSFw0cLAx2WMcZUmWUQNejMrslc0uIghwojuOvtJfR/9Gse/Hg5q7YfCHRoxhjjMy9TbSAipwIdVfVVEWkGNFDVDP+GFpp6NTxCj8QjdDntAibN28xbczczYdZGerVuyJX92/KrXi1pEOvpbTfGmICq9JtKRB4E0oDOwKtANPA6zmytpgwRAqd0aMopHZqyPzefDxZtZdLcTdz3wTIe+XQlv+rZkiv7t0EVArskkjHGlM/LT9lLgD7AQgBV3SYiNgGRR43jY7jp1FRGDkxh4aYs3p63iclLtvH2/M00j23MaU3yAh2iMcaUyUsGka+qKiIKP03YF9bS09OZPn36MdsffvjhX7weNGiQ57neRYS+7RrTt11j/nJBVz5Zsp3/fLaI97cl8Ne8ozSsb0uaGmOCi5cM4h0ReQlnoZ9bgJHAy/4NK7AGDx7s10U+EuKiuXpAW9Yu+J7xmxoxf+M+zjjBpg43xgSXSnsxqeqTwHvA+zjtEH9V1ef8HVhd0CruKBEoczP3BToUY4w5hpdG6ruBt1V1ai3EU6dER0CregXMy7AMwhgTfLyMg0gApojI9yJyu4h4qgsRkTgRmSsiS0RkhYg87G4fLyIZIrLYffR2t4uIPCsi60RkqYicWPVkhY629Y6ydEs2h/JtUJ0xJrh4qWJ6WFW7AbcBLYDpIvK1h2sfAYaqai+gN3CuiBTP4fQnVe3tPha7284DOrqPUcCLPqYlJLWrf5SCImXR5v2BDsUYY37Bl5HUu4AdwF6geWUHqyPHfRntPrSCUy4CJrrnzcZpFG/hQ3whqU29o4jAvAzLIIwxwcXLkqO/E5F04BugCXCLqvb0cnERiRSRxTiZy1RVnePuetStRvqPiMS621oBm0ucvsXdFtbqRSqdkxOYZw3Vxpgg46WbaxtgdImqIM9UtRDoLSKNgA9FpDtwH05JJAYYA9wD/M3rNUVkFE4VFMnJyaSnp/saFgA5OTlVPrciWVlZAJ6uXXxsq5hDzMg4yDffTiMyouaGVvsrjcEi3NMH4Z9GS19wKzeDEJFEVT0APOG+Tiq5X1U9/+RV1SwRmQac63abBTjirnn9R/f1VpzMqFhrd1vpa43ByVhIS0vTqo5XSE9P98tYh8zMTABP1y4+9uI+PfjmrUU07diHXm0a1Vgs/kpjsAj39EH4p9HSF9wqqmJ60/27AJjv/l1Q4nWFRKSZW3JAROoBZwGri9sVRESAi4Hl7imTgevd3kwnAdmqut33JIWe/qlO3mvVTMaYYFLRkqMXuH9Tq3jtFsAEEYnEyYjeUdVPReRbd0ZYwVnS9Dfu8Z8Line truncated
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 432x288 with 1 Axes>"
|
||||
]
|
||||
@@ -210,15 +261,15 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def plot(states, dates):\n",
|
||||
"def labels(xlab, ylab): plt.xlabel(xlab); plt.ylabel(ylab); plt.grid(True); plt.legend()\n",
|
||||
" \n",
|
||||
"def plot(states, dates, swing=4):\n",
|
||||
" N = len(dates)\n",
|
||||
" err = [EV(states, date, swing=4) - 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=5)\n",
|
||||
" err = [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=5, label='EVs')\n",
|
||||
" plt.plot(range(N), [270] * N, color='darkorange')\n",
|
||||
" plt.xlabel('Months into term')\n",
|
||||
" plt.ylabel('Electoral Votes with Net Positive Approval')\n",
|
||||
" plt.grid(True)\n",
|
||||
" labels('Months into term', 'Electoral Votes with Net Positive Approval')\n",
|
||||
" \n",
|
||||
"plot(states, dates)"
|
||||
]
|
||||
@@ -227,19 +278,19 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Margin and popular net approval by month\n",
|
||||
"# 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": 5,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 432x288 with 1 Axes>"
|
||||
]
|
||||
@@ -255,11 +306,8 @@
|
||||
" N = len(dates)\n",
|
||||
" plt.plot(range(N), [-margin(states, date) for date in dates], label='Margin')\n",
|
||||
" plt.plot(range(N), [0] * N, label='Net zero')\n",
|
||||
" plt.plot(range(N), [net_usa[date] for date in dates], label='Popular')\n",
|
||||
" plt.xlabel('Months into term')\n",
|
||||
" plt.ylabel('Net popularity')\n",
|
||||
" plt.legend()\n",
|
||||
" plt.grid(True)\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)"
|
||||
]
|
||||
@@ -270,20 +318,20 @@
|
||||
"source": [
|
||||
"# Month-by-month summary\n",
|
||||
"\n",
|
||||
"For each month, we show the expected electoral vote total (**EV**), the swing margin needed to get to 270 (**Margin**), the overall (popular vote) net approval across the whole country (**Pop**), and then the total percentage of undecided voters and in parentheses the number of states with at least 5% undecided.\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": 6,
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"| Month| EV|Margin|Pop|Undecided|\n",
|
||||
"|--------|---|------|---|---------|\n",
|
||||
"| 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",
|
||||
@@ -323,13 +371,13 @@
|
||||
],
|
||||
"source": [
|
||||
"def monthly(states, dates=reversed(dates)):\n",
|
||||
" yield '| Month| EV|Margin|Pop|Undecided|'\n",
|
||||
" yield '|--------|---|------|---|---------|'\n",
|
||||
" yield '| Month|EVs|Margin|Country|Undecided|'\n",
|
||||
" yield '|--------|---|------|-------|---------|'\n",
|
||||
" for date in dates:\n",
|
||||
" us_un = sum(s.ev * undecided(s, date) for s in states) / 538\n",
|
||||
" undec = sum(undecided(s, date) > 5 for s in states)\n",
|
||||
" month = date.replace('1-', '').replace('-', ' 20')\n",
|
||||
" yield f'|{month}|{int(EV(states, date))}|{margin(states, date)}%|{net_usa[date]}%|{us_un:.0f}% ({undec})|'\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))"
|
||||
]
|
||||
@@ -338,9 +386,9 @@
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# State-by-state net approval\n",
|
||||
"# State-by-state summary\n",
|
||||
"\n",
|
||||
"Below is each state sorted by net approval, with the state's electoral vote allotment, and the cumulative running total of electoral votes, followed by the percentages of approval, dissaproval, and undecided, and then the standard deviation of the net approval over the last 12 months (bolded if it is over **5%**). By going down the **Total** column, you can see what it takes to win. \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",
|
||||
@@ -349,65 +397,65 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/markdown": [
|
||||
"|State|Net|EV|Total|+|-|?|𝝈|\n",
|
||||
"|-----|---|--|-----|-|-|-|-|\n",
|
||||
"|Wyoming|+28|3|3|62%|34%|4%|3.5\n",
|
||||
"|Alabama|+26|9|12|61%|35%|4%|3.4\n",
|
||||
"|Louisiana|+20|8|20|58%|38%|4%|3.7\n",
|
||||
"|Mississippi|+20|6|26|58%|38%|4%|3.8\n",
|
||||
"|West Virginia|+20|5|31|58%|38%|4%|3.6\n",
|
||||
"|Tennessee|+18|11|42|57%|39%|4%|3.1\n",
|
||||
"|Idaho|+17|4|46|57%|40%|3%|1.8\n",
|
||||
"|Kentucky|+16|8|54|56%|40%|4%|1.1\n",
|
||||
"|Oklahoma|+11|7|61|54%|43%|3%|3.4\n",
|
||||
"|Arkansas|+10|6|67|53%|43%|4%|2.8\n",
|
||||
"|South Carolina|+10|9|76|53%|43%|4%|2.2\n",
|
||||
"|South Dakota|+10|3|79|53%|43%|4%|4.3\n",
|
||||
"|**North Dakota**|+6|3|82|51%|45%|4%|2.8\n",
|
||||
"|**Utah**|+5|6|88|51%|46%|3%|3.6\n",
|
||||
"|**Indiana**|+4|11|99|50%|46%|4%|2.0\n",
|
||||
"|**Missouri**|+4|10|109|50%|46%|4%|3.0\n",
|
||||
"|**Nebraska**|+4|5|114|50%|46%|4%|2.7\n",
|
||||
"|**Texas**|+4|38|152|50%|46%|4%|2.6\n",
|
||||
"|**Georgia**|+3|16|168|49%|46%|5%|3.2\n",
|
||||
"|**Montana**|+3|3|171|50%|47%|3%|3.4\n",
|
||||
"|**Kansas**|+2|6|177|49%|47%|4%|2.9\n",
|
||||
"|**Alaska**|+1|3|180|48%|47%|5%|5.1\n",
|
||||
"|**Florida**|-2|29|209|47%|49%|4%|3.3\n",
|
||||
"|**North Carolina**|-2|15|224|47%|49%|4%|2.2\n",
|
||||
"|**Ohio**|-4|18|242|46%|50%|4%|2.4\n",
|
||||
"|**Nevada**|-6|6|248|45%|51%|4%|3.1\n",
|
||||
"|Virginia|-6|13|261|45%|51%|4%|1.8\n",
|
||||
"|Pennsylvania|-7|20|281|45%|52%|3%|1.6\n",
|
||||
"|**Arizona**|-8|11|292|44%|52%|4%|3.7\n",
|
||||
"|Iowa|-8|6|298|44%|52%|4%|2.6\n",
|
||||
"|Michigan|-10|16|314|43%|53%|4%|2.2\n",
|
||||
"|New Mexico|-12|5|319|42%|54%|4%|2.9\n",
|
||||
"|Colorado|-13|9|328|42%|55%|3%|2.4\n",
|
||||
"|Minnesota|-13|10|338|42%|55%|3%|2.2\n",
|
||||
"|Wisconsin|-13|10|348|42%|55%|3%|2.4\n",
|
||||
"|Delaware|-15|3|351|41%|56%|3%|2.0\n",
|
||||
"|Maine|-15|4|355|41%|56%|3%|4.0\n",
|
||||
"|New Jersey|-17|14|369|40%|57%|3%|2.4\n",
|
||||
"|New Hampshire|-19|4|373|39%|58%|3%|3.6\n",
|
||||
"|Illinois|-22|20|393|37%|59%|4%|1.2\n",
|
||||
"|Oregon|-22|7|400|37%|59%|4%|2.0\n",
|
||||
"|Rhode Island|-22|4|404|37%|59%|4%|2.8\n",
|
||||
"|Connecticut|-23|7|411|37%|60%|3%|3.8\n",
|
||||
"|New York|-24|29|440|36%|60%|4%|1.8\n",
|
||||
"|Washington|-26|12|452|35%|61%|4%|2.1\n",
|
||||
"|Massachusetts|-28|11|463|34%|62%|4%|2.2\n",
|
||||
"|California|-29|55|518|34%|63%|3%|3.1\n",
|
||||
"|Maryland|-30|10|528|33%|63%|4%|3.5\n",
|
||||
"|Hawaii|-34|4|532|31%|65%|4%|4.2\n",
|
||||
"|Vermont|-37|3|535|30%|67%|3%|4.8\n",
|
||||
"|District of Columbia|-60|3|538|18%|78%|4%|3.1"
|
||||
"|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>"
|
||||
@@ -420,16 +468,13 @@
|
||||
"source": [
|
||||
"def by_state(states, d=now):\n",
|
||||
" total = 0\n",
|
||||
" yield '|State|Net|EV|Total|+|-|?|𝝈|'\n",
|
||||
" yield '|-----|---|--|-----|-|-|-|-|'\n",
|
||||
" yield '|State|Net|Move|EV|Total|+|-|?|𝝈|'\n",
|
||||
" yield '|-----|---|----|--|-----|-|-|-|-|'\n",
|
||||
" for s in sorted(states, key=net, reverse=True):\n",
|
||||
" total += s.ev\n",
|
||||
" std = stdev(net(s, d) for d in dates[-12:])\n",
|
||||
" und = f'{undecided(s, now)}%'\n",
|
||||
" b = '**' if swing(s, std) else ''\n",
|
||||
" yield f'|{b}{s.name}{b}|{net(s):+d}|{s.ev}|{total}|{s.apps[d]}%|{s.diss[d]}%|{und}|{std:3.1f}'\n",
|
||||
" \n",
|
||||
"def swing(s, std): return abs(net(s)) < 2 * std + undecided(s, now) / 5\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))"
|
||||
]
|
||||
|
||||
Reference in new issue
Block a user