From 8f1b44e816506e99bc1278e83b40f5b4b6391e58 Mon Sep 17 00:00:00 2001
From: Chandrasekhar Ramakrishnan <cramakri@ethz.ch>
Date: Fri, 20 Mar 2020 18:47:14 +0000
Subject: [PATCH] feat: display number of tests per 100k pop in US states

---
 .../examples/covidtracking-example.ipynb      | 312 ++++++++++++++----
 1 file changed, 255 insertions(+), 57 deletions(-)

diff --git a/notebooks/examples/covidtracking-example.ipynb b/notebooks/examples/covidtracking-example.ipynb
index 74c3ce4d..a2d141e1 100644
--- a/notebooks/examples/covidtracking-example.ipynb
+++ b/notebooks/examples/covidtracking-example.ipynb
@@ -2,25 +2,12 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 60,
+   "execution_count": 1,
    "metadata": {},
-   "outputs": [
-    {
-     "ename": "ModuleNotFoundError",
-     "evalue": "No module named 'matplotlib'",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-60-ffb398b1b277>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpandas\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0maltair\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0malt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mmatplotlib\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
-      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'matplotlib'"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "import pandas as pd\n",
-    "import altair as alt\n",
-    "import matplotlib"
+    "import altair as alt"
    ]
   },
   {
@@ -189,7 +176,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 222,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -199,7 +186,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 223,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -309,7 +296,7 @@
        "4  2020-03-19T20:00:00Z  "
       ]
      },
-     "execution_count": 223,
+     "execution_count": 5,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -327,57 +314,32 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 257,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [],
    "source": [
     "# compute daily differences\n",
     "tdf = data_df.sort_values(['state', 'date'], ascending=[True, False]).set_index(['state', 'date'])\n",
     "diffs_df = tdf[['positive', 'negative', 'death']].groupby(level='state').diff(periods=-1).dropna(how='all')\n",
-    "tdf_diff=tdf.join(diffs_df, rsuffix='_diff').reset_index()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 276,
-   "metadata": {},
-   "outputs": [],
-   "source": [
+    "tdf_diff=tdf.join(diffs_df, rsuffix='_diff').reset_index()\n",
+    "\n",
     "# \"Normalizing\" the totals\n",
     "tdf_diff['total_10'] = tdf_diff['total']/10."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 278,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:7: SettingWithCopyWarning: \n",
-      "A value is trying to be set on a copy of a slice from a DataFrame.\n",
-      "Try using .loc[row_indexer,col_indexer] = value instead\n",
-      "\n",
-      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
-      "  import sys\n",
-      "/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:9: SettingWithCopyWarning: \n",
-      "A value is trying to be set on a copy of a slice from a DataFrame.\n",
-      "Try using .loc[row_indexer,col_indexer] = value instead\n",
-      "\n",
-      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
-      "  if __name__ == '__main__':\n"
-     ]
-    },
     {
      "data": {
       "text/html": [
        "\n",
-       "<div id=\"altair-viz-a2f753a0d34f48da9107b4032abab8e2\"></div>\n",
+       "<div id=\"altair-viz-65df72664b9f476eaf839e817659dd24\"></div>\n",
        "<script type=\"text/javascript\">\n",
        "  (function(spec, embedOpt){\n",
-       "    const outputDiv = document.getElementById(\"altair-viz-a2f753a0d34f48da9107b4032abab8e2\");\n",
+       "    const outputDiv = document.getElementById(\"altair-viz-65df72664b9f476eaf839e817659dd24\");\n",
        "    const paths = {\n",
        "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
        "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
@@ -425,7 +387,7 @@
        "alt.HConcatChart(...)"
       ]
      },
-     "execution_count": 278,
+     "execution_count": 11,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -435,11 +397,10 @@
     "\n",
     "charts=[]\n",
     "for state in ['WA', 'CA', 'NY']: \n",
-    "    state_df = tdf_diff[tdf_diff['state'] == state]\n",
-    "\n",
-    "    state_df['daily_positive'] = state_df['positive'][::-1].diff()\n",
+    "    state_df = tdf_diff[tdf_diff['state'] == state].copy()\n",
     "\n",
-    "    state_df['total_10'] = state_df['total']/10.\n",
+    "    state_df.loc[:,'daily_positive'] = state_df['positive'][::-1].diff()\n",
+    "    state_df.loc[:,'total_10'] = state_df['total']/10.\n",
     "\n",
     "    base = alt.Chart(state_df, title=state).encode(alt.X('date', axis=alt.Axis(title='Date'))).properties(width=250, height=150)\n",
     "    dailies = base.mark_bar(size=10).encode(alt.Y('daily_positive', axis=alt.Axis(title='Daily positive')))\n",
@@ -454,12 +415,249 @@
     "alt.hconcat(*charts)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Counts per 100k"
+   ]
+  },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 55,
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "pop_df = pd.read_csv('../../data/geodata/us_pop_fung_2019.csv').set_index('ST')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Most recent test date 2020-03-19 00:00:00\n",
+      "56 states/territories have data on this date.\n"
+     ]
+    }
+   ],
+   "source": [
+    "most_recent_test_date = data_df['date'].max()\n",
+    "most_recent_df = data_df[data_df['date'] == most_recent_test_date].set_index('state')\n",
+    "print(\"Most recent test date\", most_recent_test_date)\n",
+    "print(len(most_recent_df), \"states/territories have data on this date.\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 57,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "most_recent_df['total/100k'] = (most_recent_df['total'] / pop_df['Population']) * 100000\n",
+    "most_recent_df = most_recent_df.reset_index()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 58,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div>\n",
+       "<style scoped>\n",
+       "    .dataframe tbody tr th:only-of-type {\n",
+       "        vertical-align: middle;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe tbody tr th {\n",
+       "        vertical-align: top;\n",
+       "    }\n",
+       "\n",
+       "    .dataframe thead th {\n",
+       "        text-align: right;\n",
+       "    }\n",
+       "</style>\n",
+       "<table border=\"1\" class=\"dataframe\">\n",
+       "  <thead>\n",
+       "    <tr style=\"text-align: right;\">\n",
+       "      <th></th>\n",
+       "      <th>state</th>\n",
+       "      <th>date</th>\n",
+       "      <th>positive</th>\n",
+       "      <th>negative</th>\n",
+       "      <th>pending</th>\n",
+       "      <th>death</th>\n",
+       "      <th>total</th>\n",
+       "      <th>dateChecked</th>\n",
+       "      <th>total/100k</th>\n",
+       "    </tr>\n",
+       "  </thead>\n",
+       "  <tbody>\n",
+       "    <tr>\n",
+       "      <th>0</th>\n",
+       "      <td>AK</td>\n",
+       "      <td>2020-03-19</td>\n",
+       "      <td>6</td>\n",
+       "      <td>400.0</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>406</td>\n",
+       "      <td>2020-03-19T20:00:00Z</td>\n",
+       "      <td>55.498978</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>1</th>\n",
+       "      <td>AL</td>\n",
+       "      <td>2020-03-19</td>\n",
+       "      <td>68</td>\n",
+       "      <td>28.0</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>96</td>\n",
+       "      <td>2020-03-19T20:00:00Z</td>\n",
+       "      <td>1.957911</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>2</th>\n",
+       "      <td>AR</td>\n",
+       "      <td>2020-03-19</td>\n",
+       "      <td>46</td>\n",
+       "      <td>310.0</td>\n",
+       "      <td>113.0</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>469</td>\n",
+       "      <td>2020-03-19T20:00:00Z</td>\n",
+       "      <td>15.540994</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>3</th>\n",
+       "      <td>AS</td>\n",
+       "      <td>2020-03-19</td>\n",
+       "      <td>0</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>NaN</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>0</td>\n",
+       "      <td>2020-03-19T20:00:00Z</td>\n",
+       "      <td>NaN</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>4</th>\n",
+       "      <td>AZ</td>\n",
+       "      <td>2020-03-19</td>\n",
+       "      <td>44</td>\n",
+       "      <td>175.0</td>\n",
+       "      <td>130.0</td>\n",
+       "      <td>0.0</td>\n",
+       "      <td>349</td>\n",
+       "      <td>2020-03-19T20:00:00Z</td>\n",
+       "      <td>4.794801</td>\n",
+       "    </tr>\n",
+       "  </tbody>\n",
+       "</table>\n",
+       "</div>"
+      ],
+      "text/plain": [
+       "  state       date  positive  negative  pending  death  total  \\\n",
+       "0    AK 2020-03-19         6     400.0      NaN    NaN    406   \n",
+       "1    AL 2020-03-19        68      28.0      NaN    0.0     96   \n",
+       "2    AR 2020-03-19        46     310.0    113.0    NaN    469   \n",
+       "3    AS 2020-03-19         0       NaN      NaN    0.0      0   \n",
+       "4    AZ 2020-03-19        44     175.0    130.0    0.0    349   \n",
+       "\n",
+       "            dateChecked  total/100k  \n",
+       "0  2020-03-19T20:00:00Z   55.498978  \n",
+       "1  2020-03-19T20:00:00Z    1.957911  \n",
+       "2  2020-03-19T20:00:00Z   15.540994  \n",
+       "3  2020-03-19T20:00:00Z         NaN  \n",
+       "4  2020-03-19T20:00:00Z    4.794801  "
+      ]
+     },
+     "execution_count": 58,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "most_recent_df.head()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 60,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "\n",
+       "<div id=\"altair-viz-e7f6c101686c406b93be99359dbeb66b\"></div>\n",
+       "<script type=\"text/javascript\">\n",
+       "  (function(spec, embedOpt){\n",
+       "    const outputDiv = document.getElementById(\"altair-viz-e7f6c101686c406b93be99359dbeb66b\");\n",
+       "    const paths = {\n",
+       "      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n",
+       "      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n",
+       "      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.0.2?noext\",\n",
+       "      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n",
+       "    };\n",
+       "\n",
+       "    function loadScript(lib) {\n",
+       "      return new Promise(function(resolve, reject) {\n",
+       "        var s = document.createElement('script');\n",
+       "        s.src = paths[lib];\n",
+       "        s.async = true;\n",
+       "        s.onload = () => resolve(paths[lib]);\n",
+       "        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n",
+       "        document.getElementsByTagName(\"head\")[0].appendChild(s);\n",
+       "      });\n",
+       "    }\n",
+       "\n",
+       "    function showError(err) {\n",
+       "      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n",
+       "      throw err;\n",
+       "    }\n",
+       "\n",
+       "    function displayChart(vegaEmbed) {\n",
+       "      vegaEmbed(outputDiv, spec, embedOpt)\n",
+       "        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n",
+       "    }\n",
+       "\n",
+       "    if(typeof define === \"function\" && define.amd) {\n",
+       "      requirejs.config({paths});\n",
+       "      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n",
+       "    } else if (typeof vegaEmbed === \"function\") {\n",
+       "      displayChart(vegaEmbed);\n",
+       "    } else {\n",
+       "      loadScript(\"vega\")\n",
+       "        .then(() => loadScript(\"vega-lite\"))\n",
+       "        .then(() => loadScript(\"vega-embed\"))\n",
+       "        .catch(showError)\n",
+       "        .then(() => displayChart(vegaEmbed));\n",
+       "    }\n",
+       "  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300}}, \"data\": {\"name\": \"data-105f25aa4bb22f20e7892e19480d209c\"}, \"mark\": \"bar\", \"encoding\": {\"x\": {\"type\": \"nominal\", \"field\": \"state\", \"sort\": \"y\"}, \"y\": {\"type\": \"quantitative\", \"field\": \"total/100k\"}}, \"title\": \"Tests per 100k\", \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.0.2.json\", \"datasets\": {\"data-105f25aa4bb22f20e7892e19480d209c\": [{\"state\": \"AL\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 68, \"negative\": 28.0, \"pending\": null, \"death\": 0.0, \"total\": 96, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 1.9579110312990435}, {\"state\": \"OH\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 119, \"negative\": 140.0, \"pending\": null, \"death\": null, \"total\": 259, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 2.2157394495726788}, {\"state\": \"MD\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 107, \"negative\": 94.0, \"pending\": null, \"death\": 1.0, \"total\": 201, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 3.3246880417091216}, {\"state\": \"IA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 38, \"negative\": 83.0, \"pending\": null, \"death\": null, \"total\": 121, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 3.83509716107725}, {\"state\": \"AZ\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 44, \"negative\": 175.0, \"pending\": 130.0, \"death\": 0.0, \"total\": 349, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 4.794801061780531}, {\"state\": \"MO\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 24, \"negative\": 308.0, \"pending\": null, \"death\": 1.0, \"total\": 332, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 5.409432094356138}, {\"state\": \"IN\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 56, \"negative\": 324.0, \"pending\": null, \"death\": 2.0, \"total\": 380, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 5.644498492993172}, {\"state\": \"DE\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 30, \"negative\": 36.0, \"pending\": null, \"death\": 0.0, \"total\": 66, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 6.777822963264199}, {\"state\": \"TN\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 154, \"negative\": 349.0, \"pending\": null, \"death\": null, \"total\": 503, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 7.361147250165151}, {\"state\": \"TX\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 143, \"negative\": 2212.0, \"pending\": null, \"death\": 3.0, \"total\": 2355, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 8.121843236975625}, {\"state\": \"WV\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 2, \"negative\": 143.0, \"pending\": 3.0, \"death\": 0.0, \"total\": 148, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 8.281355702692615}, {\"state\": \"HI\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 16, \"negative\": 93.0, \"pending\": 9.0, \"death\": null, \"total\": 118, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 8.334086697102563}, {\"state\": \"KY\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 35, \"negative\": 454.0, \"pending\": null, \"death\": 1.0, \"total\": 489, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 10.945295235349588}, {\"state\": \"NJ\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 742, \"negative\": 210.0, \"pending\": 74.0, \"death\": 9.0, \"total\": 1026, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 11.551205277076937}, {\"state\": \"SC\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 60, \"negative\": 583.0, \"pending\": null, \"death\": 1.0, \"total\": 643, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 12.488555394609216}, {\"state\": \"FL\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 390, \"negative\": 1533.0, \"pending\": 1019.0, \"death\": 8.0, \"total\": 2942, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 13.697904951531905}, {\"state\": \"NE\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 27, \"negative\": 240.0, \"pending\": null, \"death\": 0.0, \"total\": 267, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 13.802672445523383}, {\"state\": \"PA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 185, \"negative\": 1608.0, \"pending\": null, \"death\": 1.0, \"total\": 1793, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 14.005636155444282}, {\"state\": \"KS\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 34, \"negative\": 417.0, \"pending\": null, \"death\": 1.0, \"total\": 451, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 15.480651931099771}, {\"state\": \"AR\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 46, \"negative\": 310.0, \"pending\": 113.0, \"death\": null, \"total\": 469, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 15.540993927745976}, {\"state\": \"GA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 287, \"negative\": 1544.0, \"pending\": null, \"death\": 10.0, \"total\": 1831, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 17.245239263802524}, {\"state\": \"LA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 347, \"negative\": 458.0, \"pending\": null, \"death\": 8.0, \"total\": 805, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 17.31631902811783}, {\"state\": \"OK\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 44, \"negative\": 466.0, \"pending\": 250.0, \"death\": 1.0, \"total\": 760, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 19.206610308743734}, {\"state\": \"CT\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 96, \"negative\": 604.0, \"pending\": null, \"death\": 1.0, \"total\": 700, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 19.633763004212565}, {\"state\": \"MS\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 50, \"negative\": 552.0, \"pending\": null, \"death\": null, \"total\": 602, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 20.227481890187622}, {\"state\": \"VA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 94, \"negative\": 1829.0, \"pending\": null, \"death\": 2.0, \"total\": 1923, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 22.52938573506778}, {\"state\": \"NC\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 97, \"negative\": 2408.0, \"pending\": null, \"death\": 0.0, \"total\": 2505, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 23.88424806666308}, {\"state\": \"MI\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 336, \"negative\": 2113.0, \"pending\": null, \"death\": 3.0, \"total\": 2449, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 24.522229566319012}, {\"state\": \"CA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 924, \"negative\": 8787.0, \"pending\": null, \"death\": 18.0, \"total\": 9711, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 24.577204881638778}, {\"state\": \"IL\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 426, \"negative\": 2725.0, \"pending\": null, \"death\": 4.0, \"total\": 3151, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 24.86619721032991}, {\"state\": \"ID\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 11, \"negative\": 457.0, \"pending\": null, \"death\": null, \"total\": 468, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 26.1151241723933}, {\"state\": \"DC\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 39, \"negative\": 153.0, \"pending\": 11.0, \"death\": 0.0, \"total\": 203, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 28.76376728837023}, {\"state\": \"WI\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 155, \"negative\": 2192.0, \"pending\": null, \"death\": null, \"total\": 2347, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 40.309602478963264}, {\"state\": \"CO\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 216, \"negative\": 2112.0, \"pending\": null, \"death\": 2.0, \"total\": 2328, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 40.42553782635634}, {\"state\": \"OR\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 88, \"negative\": 1329.0, \"pending\": 437.0, \"death\": 3.0, \"total\": 1854, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 43.95722160959775}, {\"state\": \"MA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 328, \"negative\": 2804.0, \"pending\": null, \"death\": null, \"total\": 3132, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 45.067971047713776}, {\"state\": \"WY\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 18, \"negative\": 271.0, \"pending\": null, \"death\": null, \"total\": 289, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 49.934428665472154}, {\"state\": \"UT\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 78, \"negative\": 1526.0, \"pending\": null, \"death\": 0.0, \"total\": 1604, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 50.03184695495075}, {\"state\": \"MN\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 89, \"negative\": 2949.0, \"pending\": null, \"death\": null, \"total\": 3038, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 53.86876306822856}, {\"state\": \"AK\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 6, \"negative\": 400.0, \"pending\": null, \"death\": null, \"total\": 406, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 55.49897818999514}, {\"state\": \"NV\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 95, \"negative\": 1626.0, \"pending\": null, \"death\": 1.0, \"total\": 1721, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 55.873793405269076}, {\"state\": \"NH\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 39, \"negative\": 621.0, \"pending\": 231.0, \"death\": null, \"total\": 891, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 65.52863071638016}, {\"state\": \"ND\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 15, \"negative\": 493.0, \"pending\": null, \"death\": 0.0, \"total\": 508, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 66.66124278602003}, {\"state\": \"MT\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 12, \"negative\": 761.0, \"pending\": 0.0, \"death\": null, \"total\": 773, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 72.3255905342363}, {\"state\": \"RI\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 33, \"negative\": 540.0, \"pending\": 334.0, \"death\": null, \"total\": 907, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 85.61765064033885}, {\"state\": \"VT\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 22, \"negative\": 645.0, \"pending\": null, \"death\": null, \"total\": 667, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 106.89290997116937}, {\"state\": \"SD\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 11, \"negative\": 551.0, \"pending\": 385.0, \"death\": 1.0, \"total\": 947, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 107.04689603564763}, {\"state\": \"NY\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 4152, \"negative\": 18132.0, \"pending\": null, \"death\": 12.0, \"total\": 22284, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 114.5497217707339}, {\"state\": \"NM\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 35, \"negative\": 2762.0, \"pending\": null, \"death\": null, \"total\": 2797, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 133.39189795639035}, {\"state\": \"ME\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 53, \"negative\": 2004.0, \"pending\": null, \"death\": null, \"total\": 2057, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 153.02645713622553}, {\"state\": \"WA\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 1187, \"negative\": 15918.0, \"pending\": null, \"death\": 66.0, \"total\": 17105, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": 224.62561194228203}, {\"state\": \"AS\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 0, \"negative\": null, \"pending\": null, \"death\": 0.0, \"total\": 0, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": null}, {\"state\": \"GU\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 12, \"negative\": 69.0, \"pending\": null, \"death\": null, \"total\": 81, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": null}, {\"state\": \"MP\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 0, \"negative\": null, \"pending\": null, \"death\": 0.0, \"total\": 0, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": null}, {\"state\": \"PR\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 5, \"negative\": 56.0, \"pending\": 29.0, \"death\": null, \"total\": 90, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": null}, {\"state\": \"VI\", \"date\": \"2020-03-19T00:00:00\", \"positive\": 3, \"negative\": null, \"pending\": null, \"death\": 0.0, \"total\": 3, \"dateChecked\": \"2020-03-19T20:00:00Z\", \"total/100k\": null}]}}, {\"mode\": \"vega-lite\"});\n",
+       "</script>"
+      ],
+      "text/plain": [
+       "alt.Chart(...)"
+      ]
+     },
+     "execution_count": 60,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "chart = alt.Chart(most_recent_df.sort_values('total/100k'), title=\"Tests per 100k\")\n",
+    "chart.mark_bar().encode(alt.X('state', sort='y'), alt.Y('total/100k'))"
+   ]
   }
  ],
  "metadata": {
-- 
GitLab