Add files via upload

This commit is contained in:
Peter Norvig authored and GitHub committed 2019-05-16 17:59:09 -07:00
1 parent b8dc1d771f
commit 11200e31dd
1 file changed
+157 -57
+157 -57
View File
@@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"<div align=\"right\"><i>Peter Norvig</i></div>\n",
"<div align=right><i>Peter Norvig</i></div>\n",
"\n",
"# Tracking Trump: Electoral Votes Edition\n",
"\n",
@@ -12,7 +12,7 @@
"\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 November 3rd, 2020. Things can change. Key economic, geopolitical, or legal events might happen.\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",
@@ -39,7 +39,7 @@
"\n",
"# The details for data science nerds\n",
"\n",
"First fetch the Tracking Trump web page and cache it locally, then define the code:"
"First fetch the Tracking Trump web page and cache it locally: "
]
},
{
@@ -53,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 230k 0 --:--:-- --:--:-- --:--:-- 231k\n"
"100 115k 0 115k 0 0 224k 0 --:--:-- --:--:-- --:--:-- 224k\n"
]
}
],
@@ -61,6 +61,13 @@
"! 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,
@@ -75,28 +82,28 @@
"from IPython.display import display, Markdown\n",
"from statistics import stdev\n",
"\n",
"EVs = {\n",
" 'Alaska': 3, 'Alabama': 9, 'Arkansas': 6,\n",
" 'Arizona': 11, 'California': 55, 'Colorado': 9,\n",
" 'Connecticut': 7, 'District of Columbia': 3, 'Delaware': 3,\n",
" 'Florida': 29, 'Georgia': 16, 'Hawaii': 4,\n",
" 'Iowa': 6, 'Idaho': 4, 'Illinois': 20,\n",
" 'Indiana': 11, 'Kansas': 6, 'Kentucky': 8,\n",
" 'Louisiana': 8, 'Massachusetts': 11, 'Maryland': 10,\n",
" 'Maine': 4, 'Michigan': 16, 'Minnesota': 10,\n",
" 'Missouri': 10, 'Mississippi': 6, 'Montana': 3,\n",
" 'North Carolina': 15, 'North Dakota': 3, 'Nebraska': 5,\n",
" 'New Hampshire': 4, 'New Jersey': 14, 'New Mexico': 5,\n",
" 'Nevada': 6, 'New York': 29, 'Ohio': 18,\n",
" 'Oklahoma': 7, 'Oregon': 7, 'Pennsylvania': 20,\n",
" 'Rhode Island': 4, 'South Carolina': 9, 'South Dakota': 3, \n",
" 'Tennessee': 11, 'Texas': 38, 'Utah': 6,\n",
" 'Virginia': 13, 'Vermont': 3, 'Washington': 12,\n",
" 'Wisconsin': 10, 'West Virginia': 5, 'Wyoming': 3}\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",
"# 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",
"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",
@@ -105,11 +112,12 @@
" '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, apps, diss')\n",
"State = namedtuple('State', 'name, ev, lean, 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",
"the partisan lean (.lean) and two dicts of {date: percent}:\n",
".apps (approvals) and .diss (disapprovals)'''\n",
"\n",
"def parse_page(filename='evs.html'):\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",
@@ -118,9 +126,9 @@
" text = re.findall(r'\\[\\[.*?\\]\\]', open(filename).read())[0]\n",
" table = ast.literal_eval(text)\n",
" dates = table[0][1::2]\n",
" states = [State(name, EVs[name], \n",
" dict(zip(dates, map(int, numbers[0::2]))),\n",
" dict(zip(dates, map(int, numbers[1::2]))))\n",
" states = [State(name, *data[name],\n",
" apps=dict(zip(dates, map(int, numbers[0::2]))),\n",
" diss=dict(zip(dates, map(int, numbers[1::2]))))\n",
" for (name, *numbers) in table[1:]]\n",
" return dates, states, dates[-1]\n",
"\n",
@@ -229,7 +237,7 @@
"source": [
"We see that:\n",
"- Trump is currently leading in states with only 180 electoral votes; \n",
"- The margin is 7%; \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."
]
},
@@ -239,7 +247,7 @@
"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 4% swing. Trump hasn't been above 270 since 4 months into his term, and even with the 4% swing, since 13 months in."
"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: Clinton was predicted by polls to win the popular vote by 6% but actually only won by 3%.) Trump hasn't been above 270 since 4 months into his term, and even with the 3% swing, since 6 months in."
]
},
{
@@ -249,29 +257,31 @@
"outputs": [
{
"data": {
"image/png": "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 truncated
"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 432x288 with 1 Axes>"
"<Figure size 648x432 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"def labels(xlab, ylab): plt.xlabel(xlab); plt.ylabel(ylab); plt.grid(True); plt.legend()\n",
" \n",
"def plot(states, dates, swing=4):\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, swing) - EV(states, date) for date in 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=5, label='EVs')\n",
" plt.plot(range(N), [270] * N, color='darkorange')\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",
"plot(states, dates)"
"plot1(states, dates)"
]
},
{
@@ -290,22 +300,20 @@
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAYoAAAEKCAYAAAAMzhLIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3Xd4VFX6wPHvSe8JAVJIIYHQQiAhhSAQqhQxFhZBrCDWFVZRFETW1VUUC5a1u4qA/hRUEASUJiZIh4QmJPQEEkJLICG9nt8fKUtJmSQzc2fC+TzPPJCZufe8h4R5c+95zzlCSomiKIqi1MVC6wAURVEU06YShaIoilIvlSgURVGUeqlEoSiKotRLJQpFURSlXipRKIqiKPVSiUJRFEWpl0oUiqIoSr1UolAURVHqZaV1APrQpk0bGRAQ0OTj8/PzcXR01F9AJkb1z/y19D6q/mkjMTExU0rZtqH3tYhEERAQQEJCQpOPj4+PZ9CgQfoLyMSo/pm/lt5H1T9tCCFO6vI+detJURRFqZdKFIqiKEq9VKJQFEVR6tUixigURWma0tJS0tPTKSoq0jQOV1dXkpOTNY3BkLTun52dHb6+vlhbWzfpeJUoFOUGlp6ejrOzMwEBAQghNIsjNzcXZ2dnzdo3NC37J6UkKyuL9PR0AgMDm3QOTW89CSG+FkKcF0IcuOI5dyHEeiHE0ao/W2kZo6K0ZEVFRbRu3VrTJKEYlhCC1q1bN+uqUesxigXAyGueewHYIKXsBGyo+lpRFANRSaLla+73WNNEIaX8E7h4zdN3AAur/r4QuNNQ7R+7eIqxP/yLtMJMQzWhKIpi9kxxjMJTSnmm6u9nAc/a3iSEeAx4DMDT05P4+PhGN7QtM41DRcs4e8YGb9s4rCxa5m9WeXl5Tfr3MRctvX9guD66urqSm5ur9/M2houLC2PHjmXevHkAlJWV0alTJyIjI/npp5/00saUKVOYMmUKXbt21cv5Gqu8vFzzf+eioqIm/wyZYqKoIaWUQghZx2v/Bf4LEBkZKZsy67FveQmL/m8u58rPsyG7NW+M7tEiL8NNdVaovrT0/oHh+picnKz5ILKjoyOHDh3CysoKe3t7Vq9eja+vL1ZWVjrHVlZWhpVV3R9nCxcurPM1YzCFwXo7Ozt69erVpGO1HqOozTkhhDdA1Z/nDdWQjaUNga4BtGt1nkU70/jvnycM1ZSiKPUYPnw4v/76KwCLFi3innvuqXlt586d3HTTTfTq1Yu+ffty+PBhABYsWMDtt9/OkCFDGDp0KBUVFTz55JN07dqVYcOGMWrUKJYsWQLAoEGDapb5cXJyYtasWYSGhtKnTx/OnTtn5N6aH1O8olgBTADerPrzF0M2FuQWxO783cT29ObNNYdo39qBkSHehmxSUUzSv1ceJCnjsl7PGdzOhZdv697g+8aMGcO7775LbGws+/fvZ9KkSWzatAmArl27smnTJqysrPj999958cUXWbp0KQC7d+9m//79uLu7s2TJElJTU0lKSuL8+fN069aNSZMmXddWfn4+ffr04fXXX2f69Ol8+eWX/POf/9Rrv1saTROFEGIRMAhoI4RIB16mMkH8KIR4GDgJjDNkDEGtglh/cj2vjevM6exCpv6wlx9c7Qn1czNks4qiXCEkJITU1FQWLVrEqFGjrnotJyeHCRMmcPToUYQQlJaW1rw2bNgw3N3dAdi8eTNjx47FwsICLy8vBg8eXGtbNjY2xMbGAhAREcH69esN1KuWQ9NEIaW8p46Xhhorhk5unZBIMvJP8uWDkYz+dAsPL0xg+eS++LZyMFYYiqI5XX7zN6Tbb7+d5557jvj4eLKysmqef+mllxg8eDDLli0jNTX1qrGapizdbW1tXTMWaWlpSVlZWbNjb+lMcYzCqDq6dQTgWPYx2jjZMn9iFMVl5Ty8IIHLRaUNHK0oir5MmjSJl19+mR49elz1fE5ODj4+PkDluERd+vXrx9KlS6moqODcuXMtvhLOmG74ROHn7IcVVhzLPgZAkIczn98fwfELeUz5fg9l5RUaR6goNwZfX1+eeuqp656fPn06M2fOpFevXvX+9j9mzBh8fX0JDg7m/vvvJzw8HFdXV0OGfOOQUpr9IyIiQjbHyO9GysfXP37Vc4t3npTtZ6ySL/68X1ZUVDTr/FqLi4vTOgSDaun9k9JwfUxKSjLIeRvr8uXLejlPbm6ulFLKzMxM2aFDB3nmzBm9nLe59NW/5qjtew0kSB0+Y02x6snovG28OXbp2FXP3R3lT0pmAZ9vPE5gG0ceiemgUXSKougqNjaW7OxsSkpKeOmll/Dy8tI6pBZBJQrA29qbXdm7uFxyGRcbl5rnp4/owqmL+bz+WzJ+7g6M6K5+6BTFlKlxCcO44ccooDJRAJzIvnrCnYWF4L1xYfT0dWPq4r38lZ6jRXiKoiiaUlcUVN56AjiafZQwj7CrXrOztuSrByO585MtTFq4i5dig3VeEyqwjSPdvF0afqOiKIoJU4kCaGXZCgcrh+vGKaq1dbZl/kNR3PXZVp5atKdR5x4T7suMW7rg4Wynj1AVRVGMTiUKwEJYEOQWVFMiW5vOns5smj6Es5d12/xDIvllbwZfbTrB2oNneWpoEBP7BmJjpe72KYpiXlSiqBLUKoj4tPh63+PqYI2rg+57znYd6cK4SD9mr0rijd8OsXhnGi/dFszgLh7NjFZRWg4hBFOmTOGjjz4CYO7cueTl5fHKK6/UeUx8fDw2Njb07dvXSFHe2NSvt1U6unbkYtFFsgqzGn5zIwS2cWTexCjmPxQFwEPzdzFpwS5SMvP12o6imCtbW1tWrlxJZqbuG4jFx8ezdetWvcahlvKom0oUVYJaBQFwPPu4Qc4/uIsHa6YO4MVRXdlxIovh72/kzdWHyCtWP5zKjc3KyoqJEyfy/vvvX/fahQsXGDNmDFFRUURFRbFlyxZSU1P5/PPPef/99wkLC6tZZbbaqFGjCAsLIywsDFdXVxYuXEh5eTnPP/88UVFR9OzZky+++AKoTDgxMTHcfvvtBAcHA/Dee+8REhJCSEgIH3zwgeH/AcyAuvVUpZNbJ6Cy8qm3d2+DtGFjZcFjAzpyZ5gPb605zOcbj/Pz7nRmjurKnWE+LXLTJMWMrH4Bzv6l33N69YBb3mzwbY8++ij9+vVj+vTpVz3/9NNP88wzz9C/f39OnTrFiBEjSE5O5oknnsDJyYnnnnvuunP99ttvACQmJvLQQw9x5513Mm/ePFxdXdm1axfFxcX069eP4cOHA5VLlR84cIDAwEASExOZP38+O3bsQEpJdHQ0AwcObPKGPy2FShRV2ti3wdXWtd4BbX3xcLHj3XGh3N/Hn1dWHOSZH/bxw640vpkUrQa7lRuSi4sLDz74IB9++CH29vY1z//+++8kJSXVfH358mXy8vIaPF9mZiYPPPAAP/74I66urqxbt479+/fXbGSUk5PD0aNHsbGxoXfv3gQGBgKVS5WPHj26ZlXav/3tb2zatEklCq0DMBVCiMrKpzpKZA2hl38rlj3Zj0/jjzF33RH2p2cTGeButPYV5So6/OZvSFOnTiU8PJyHHnqo5rmKigq2b9+OnZ3u5eXl5eWMHz+ef/3rX4SEhACVa9p99NFHjBgx4qr3xsfHN2mp8huN+vX1CkFuQRzPPk7lWlnGYWEhGBflB8DetGyjtasopsbd3Z1x48Yxb968mueGDx9eUw0FsHfvXgCcnZ3Jzc2t9TwvvPACPXv2ZPz48TXPjRgxgs8++6xm06MjR46Qn399QUlMTAzLly+noKCA/Px8li1bRkxMjF76Z85UorhCkFsQuaW5nCsw7h66Hs52+LjZs0clCuUGN23atKuqnz788EMSEhLo2bMnwcHBfP755wDcdtttLFu2rNbB7Llz57Ju3bqaAe0VK1bwyCOPEBwcTHh4OCEhITz++OO1VjmFh4czceJEevfuTXR0NI888sgNf9sJ1K2nqwS5VVY+Hcs+hpejcRcADPNzY59KFMoNKC8vr+bqwNPTk4KCgprX2rRpww8//HDdMZ07d2b//v21nq+uOwJvvPEGb7zxxlXPDRo06Kod8wCeffZZnn322cZ0ocVTVxRXqEkURhynqBbm50b6pUIy84qN3raiKEp9VKK4gpudG23t2xql8ulaoX5uAOw9pa4qFEUxLSpRXKOhNZ8MpYePK5YWQg1oK4piclSiuEZHt44czz5OhTTuXtn2NpZ08XRmX7pKFIqimBaVKK7RqVUnisqLOJ172uhth/m7sTctm4oK45XnKoqiNEQlimtUD2gfzT5q9LbDfN3ILSrjhFowUFEUE2KyiUIIkSqE+EsIsVcIkWCsdju6dQQMtzhgfcL8qwa01TiFcgM5e/YsEydOpGPHjkRERDBq1CiOHDmi1zYMsdpsbR555JGrlhyp9t133zFlyhSdzxMfH48QgpUrV9Y8Fxsb2+Ce4AsWLCAjI0PndnRlsomiymApZZiUMtJYDTpaO+Lj5KPJFUXHtk442Vqp+RTKDUNKyejRo4mJieH48eMkJiYyZ84czp3T76TX+hKFPpcX/+qrr2pWoW0uX19fXn/99UYdc6MmCk10dOuoSeWTpYWgp6+ruqJQbhhxcXFYW1vz8MMP1zwXGhpKTEwMUkqef/55QkJC6NGjR83Eu/j4eGJjY2veP2XKFBYsWABAQEAAL7/8MuHh4fTo0YNDhw7Vuiz5xIkTeeKJJ4iOjmb69Ol06tSJCxcuAJXrSwUFBdV8Xe2nn36qmYj3n//8hw4dOgBw4sQJ+vXrB1RO4EtIqLwBMn/+fDp37kzv3r3Zvn17zXlqWzq9NqGhobi6urJ+/frrXktMTGTgwIFEREQwYsQIzpw5w5IlS0hISOC+++4jLCyMwsJC3b8RDTDlmdkSWCeEkMAXUsr/XvmiEOIx4DGonM3Z0CVZffLy8q463uaLine truncated
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAnkAAAGTCAYAAABUPgTYAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJzs3XlYVGX7B/DvmZUdZFcBSUQFF9wVA5c0LDPN3FNzySwzW36ZWZl7Wqn1Vpaatr4ub2i4laa55r4i5gIigqCyb8M+yzm/P8iBM8MywMycmfH+XBfX5TlzltvDwNw8y/0wBQUFHAghhBBCiE0RCR0AIYQQQggxPkryCCGEEEJsECV5hBBCCCE2iJI8QgghhBAbREkeIYQQQogNoiSPEEIIIcQGUZJHCCGEEGKDKMkzscTERKFDeGTRsxcOPXvh0LMXBj134dCzrx0leYQQQgghNoiSPEIIIYQQG0RJHiGEEEKIDaIkjxBCCCHEBkmEDoAQQggxN7VajZKSEqNdz87ODoWFhUa7HjGcrT97R0dHSCSNS9coySOEEPJIUavVKCoqgpubGxiGMco15XI57OzsjHIt0jC2/Ow5jkNBQQGcnZ0blehRdy0hhJBHSklJiVETPEJMhWEYuLm5NbrVmZI8QgghjxxK8Ii1aMp7lZI8QgghhBAbREkeIYQQQogNsqok79SpUxg/fjxCQkLg5uaGLVu28F7nOA4rV65E+/bt4evri2eeeQY3b94UKFpCCCGEEOFYVZJXUlKC0NBQfPLJJ7C3t9d7/csvv8Q333yDTz/9FEeOHIGXlxdGjhyJoqIiAaKtwnGcoPcnhBBCyKPHqpK8qKgoLFy4ECNGjIBIxA+d4zisW7cOb731FkaMGIHQ0FCsW7cOxcXF2LFjh0ARA4dyxBi5Pwtlakr0CCGENN6sWbPg5uaGVatW8fafOHECbm5uyM3NbdC1xo0bZ+wQiYWxmTp5d+/eRWZmJp544gntPnt7e/Tt2xfnzp3DtGnTajwvMTHR6LGIlOXwOn8Y2XfvY1jmfYzVVGCc6nN8GqKEhCZ0mZUpvr/EMPTshUPPvm52dnaQy+VGv255ebnRr1mdRqOBnZ0dvvzyS0yYMAGenp4AAKVSqb2/oTFoNBpoNBqTx6xLqVRCJpMZ/brm/n+Ym0KhQFZWlt7+4ODgOs+zmSQvMzMTAODl5cXb7+XlhfT09FrPq+8BNYpaDfvVb6MFq9buupZZhg2ePviiL9VmMpfExETTfH9JvejZC4eeff0KCwtrLJ7rusnNvHHMKGjQ8WKxGJGRkbh//z6++uorfPbZZwCgTZrs7Oy0/6/4+HgsXLgQp0+fhp2dHfr3748VK1bAx8cHK1euRHR0NADA19cXALB3715ERkby7nf37l2EhYXpxeHv749//vmn3vsAlS2GeXl5CA8Px3fffQelUonbt2+joKAA8+fPx/79+1FRUYHevXvjk08+QUhISIOeCVCZ4NlqMeSHXFxc4O/v3+DzrKq71hC6CRTHceZPqiQSaFq04u3qVJyGn26V4rM4YccHEkIIsV4ikQiLFy/Gjz/+iOTk5BqPycjIwNChQxESEoLDhw9j165dKC4uxoQJE8CyLObMmYORI0diwIABSEhIQEJCAnr37q13HT8/P+3rCQkJuHTpEvz9/REREWHQfR46deoUrl+/jh07dmD37t0AKpO/S5cuYevWrTh8+DDs7e0xevRolJWVmeCpPbpspiXv4V8NWVlZ8PPz0+7PycnRa90zByYgCLiXpN3uVJKK481CsTK2CM0dxHixraPZYyKEEGL9oqKi0Lt3byxbtgw//PCD3uvff/89OnbsiCVLlmj3bdiwAYGBgYiNjUX37t21XdYPPztrIhaLta+zLIs33ngDPj4++OKLLwy+D1C57NjatWu1XeRJSUnYv38//vjjDzz++OPa8zp16oTt27fjxRdfbOITIg/ZTEteq1at4OPjg6NHj2r3lZeX48yZMzX+hWJqrH9r3nan4jTtv98+XYA/0+ivFUIIIY2zdOlS7Nq1C7GxsXqvxcXF4fTp02jZsqX2q0OHDgBQa+tffRYtWoTr169jy5Yt2q5RQ+8TEhLCGwOZkJAAkUiEXr16afe5uroiNDQU8fHxjYqP1MyqWvKKi4tx584dAJV/Vdy7dw9Xr15Fs2bN4O/vj1mzZmHNmjUIDg5GmzZtsHr1ajg6OmL06NFmj1U3yQsrTdX+W8MB047mY89TYvT0Nv4AVEIIIQ3X0DFy1Zl7XFi3bt0wfPhwLFq0CO+++y7vNZZlERUVheXLl+ud15iera1bt+LHH3/E/v37eS1/ht7H0ZHfc1VXWTEas25cVpXkxcbG4tlnn9Vur1y5EitXrsSECROwbt06vPnmmygrK8O7776LgoICdO/eHTExMXB2djZ7rKx/EG+7S9l9SMBC/W/jaZmGw7hDuTjwjCeCXaVmj48QQoh1W7hwIXr37o3Dhw/z9oeFhWHnzp3w9/eHVFrz54tMJoNGo6n3HufOncM777yDTZs2oVOnTg2+T03at28PlmVx/vx5bXetQqHAjRs38MILLxh8HVI/q+qujYyMREFBgd7XunXrAFT+BfD+++8jISEBmZmZ2LdvH0JDQwWJlXN1B+fsqt2WqCqwoR2/izavgsXzB3ORUVr/DxohhBBSXevWrTF16lSsX7+et3/GjBlQKBSYNm0aLl68iJSUFBw7dgxvvvmmdnGAgIAA3Lx5E4mJicjNzYVKpdK7fmZmJiZNmoSXXnoJPXr0QGZmJjIzM5GTk2PwfWoSFBSEoUOH4u2338bp06dx/fp1zJw5E87OzhgzZowRnxCxqiTPqjAMNDqteWOl6Zgbxm9VTCvWYPRfuVAoWRBCCCENMW/ePEgk/E655s2b48CBAxCJRBg1ahT69OmDuXPnQiaTacfGTZkyBW3btsXAgQMRFBSEs2fP6l371q1byM7Oxtq1a9GuXTvt18CBAw2+T22+/fZbdOvWDRMmTMCgQYNQVlaGHTt21LiaFWk8pqCggJZiMBHZlrWQHaxabUM5YgoqRk7FnFMF2JxYyju2f3M5tj/pAZmYxiMYC9ULEw49e+HQs69fYWEhXF1d6z+wAR6FWm2W6lF49o19z1JLngnpTr4QpSWBYRh80dcNUX78v3KOp1fgtZP5YGmdW0IIIYQYASV5JqSf5FXODJaKGPw4wB3dPfkDVXfcKcPCCwqzxUcIIYQQ20VJngmxLQLBVZsOLsp+AJRVdtM6SkX49UkPBLmIeeesvV6MtddoVQxCCCGENA0leaYkt0OFO7+auOh+VYFITzsxfovyhLc9/9uw4IICO+7wx+wRQgghhDQEJXkmVubdkrf9sMv2oUBnCbY/6QEnCX/CxawT+Tj+oNzk8RFCCCHENlGSZ2Jl3n68bVFakt4xYR4y/PcJd1TP81QsMOlIHq7mKk0dIiGEEEJsECV5Jlauk+SJdVryHhrY0g7fRjbj7StScRjzVy7uFqlNFh8hhBBCbBMleSam1117LwmopUzK2CAHLO3hwtuXWcZi9F+5yC2nVTEIIYQQYjhK8kxM6eYBzs5Bu82UloDJy6r1+DkdnfBqKH8x58RCNcYfykWpmlbFIIQQQohhJPUfQpqEEYH1aw3x7WvaXaK0O9B4+NR8OMNgRS9XZJay2JlStdbthWwVxv6Vi3CfupeKMSgkBujkLsXT/naQiGiFDUIIIYY7ceIEnn32WSQlJcHDw0PocEgdKMkzA9a/hiSvS3itx4sYBuv7NUNOuQYnMqomXpzMUOJkhvEmYrR3k2BpD1c86ScHw1CyRwghlmzWrFnYtm0bJk+ejK+//pr32sKFC/HVV19hyJAh+PXXX00aR+/evZGQkAB3d3eT3oc0HXXXmoHGP4i3XdMMW11yMYPNgzzQoZnp8vD4AjXGHsrFyIO5uJanMtl9CCGEGIefnx927tyJkpIS7T61Wo1ff/0Vfn5+dZxZP6XSsEYEmUwGHx8fahywAtSSZwas/2O8bd1aebVxlYmwI8oTQ/7IRmqx6SZeHHtQgcjdWZgU7IAPu7nA10Fc/0mEEGJj3H68b9b7FUxrWf9BOjp06ID09HTs3LkTkyZNAgAcOHAAcrkcffv2RX5+PgDg8uXLWLZsGeLi4qBSqdChQwcsXboUvXr10l7Lzc0Nq1atwvHjx3HkyBFMnz4dy5cvx4EDB/Dhhx8iLS0N3bp1w4wZM/DSSy8hLi4OrVq10uuu3bJlC+bNm4etW7di/vz5uHv3Lrp164a1a9ciMDDQKM+KNA4leWbA+umsYZuRCigrAFn94+uaO4hx7FkvbL9ThgKlcSZeJBWqsf1OGarP8eUA/DexFDHJZXijkxNe7+AERyk19BJCiKWZPHkytmzZok3yNm/ejIkTJyIlJUV7TFFREcaNG4dPPvkEDMNg48aNGDNmDC5fvswbR/fpp59i4cKFWL58OQAgLS0NkydPxowZMzBt2jTcuHEDH374Yb0xVVRU4PPPP8fatWshl8sxa9Ys/N///R9iYmKM+58nDUJJnjk4OIH19IEoJxMAwLAsROmpYFsFG3S6u50Yr4Q6GTWk2R2V+OiCAn+nV/D2l6g5rIwtwk8JJVjQzQXjgxwgpskZhBBiMcaMGYOPPvoISUlJcHJywuHDh/HZZ59hxYoV2mP69+/PO+ezzz7Dnj17cOjQIYwbN067f+TIkXjxxRe120uWLEFgYCA+/vhjMAyD4OBg3L59G8uWLaszJrVajdWrVyM4uPJzbc6cOZg9ezZYloVIRA0GQqEnbyasn+64PMO6bE0lzEOG3UM88L/B7mjrqp/rp5eymH2yAAP2ZuP4g4oarkAIIUQIbm5uGDZsGDZv3oxt27YhIiIC/v7+vGOys7Px1ltvoXv37ggICICfnx+ys7Nx79493nFdu3blbd+6dQtdu3bljbfr0aNHvTHJ5XJLine truncated
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
"<Figure size 648x432 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"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), [0] * N, label='Net zero')\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",
@@ -330,8 +338,8 @@
{
"data": {
"text/markdown": [
"| Month|EVs|Margin|Country|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",
@@ -370,9 +378,10 @@
}
],
"source": [
"def header(head): return head + '\\n' + '-'.join('|' * head.count('|'))\n",
"\n",
"def monthly(states, dates=reversed(dates)):\n",
" yield '| Month|EVs|Margin|Country|Undecided|'\n",
" yield '|--------|---|------|-------|---------|'\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",
@@ -404,7 +413,7 @@
"data": {
"text/markdown": [
"|State|Net|Move|EV|Total|+|-|?|𝝈|\n",
"|-----|---|----|--|-----|-|-|-|-|\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",
@@ -468,8 +477,7 @@
"source": [
"def by_state(states, d=now):\n",
" total = 0\n",
" yield '|State|Net|Move|EV|Total|+|-|?|𝝈|'\n",
" yield '|-----|---|----|--|-----|-|-|-|-|'\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",
@@ -478,6 +486,98 @@
"\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": {
@@ -496,7 +596,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.2"
"version": "3.7.0"
}
},
"nbformat": 4,