In [2]:
!pip install -U plotly
!pip install dash
!pip install dash-html-components
!pip install dash-core-components
!pip install dash-table
!pip install dash_bootstrap_components
!pip install pycountry
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import os
import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
import imageio
import json
import pycountry
from google.colab import drive, files
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from getpass import getpass
drive.mount("/content/drive")
%cd "/content/drive/My Drive/Coronavirus"
!rm -rf coronavirus-data
!git config --global user.email "johan.d.s.vonk@gmail.com"
!git config --global user.name "Johan Vonk"
!git clone https://github.com/jvonk/coronavirus-data.git
%cd coronavirus-data


Requirement already up-to-date: plotly in /usr/local/lib/python3.6/dist-packages (4.6.0)
Requirement already satisfied, skipping upgrade: retrying>=1.3.3 in /usr/local/lib/python3.6/dist-packages (from plotly) (1.3.3)
Requirement already satisfied, skipping upgrade: six in /usr/local/lib/python3.6/dist-packages (from plotly) (1.12.0)
Requirement already satisfied: dash in /usr/local/lib/python3.6/dist-packages (1.11.0)
Requirement already satisfied: dash-core-components==1.9.1 in /usr/local/lib/python3.6/dist-packages (from dash) (1.9.1)
Requirement already satisfied: dash-renderer==1.4.0 in /usr/local/lib/python3.6/dist-packages (from dash) (1.4.0)
Requirement already satisfied: Flask>=1.0.2 in /usr/local/lib/python3.6/dist-packages (from dash) (1.1.2)
Requirement already satisfied: flask-compress in /usr/local/lib/python3.6/dist-packages (from dash) (1.4.0)
Requirement already satisfied: dash-table==4.6.2 in /usr/local/lib/python3.6/dist-packages (from dash) (4.6.2)
Requirement already satisfied: dash-html-components==1.0.3 in /usr/local/lib/python3.6/dist-packages (from dash) (1.0.3)
Requirement already satisfied: plotly in /usr/local/lib/python3.6/dist-packages (from dash) (4.6.0)
Requirement already satisfied: future in /usr/local/lib/python3.6/dist-packages (from dash) (0.16.0)
Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash) (1.1.0)
Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash) (7.1.1)
Requirement already satisfied: Jinja2>=2.10.1 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash) (2.11.1)
Requirement already satisfied: Werkzeug>=0.15 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash) (1.0.1)
Requirement already satisfied: retrying>=1.3.3 in /usr/local/lib/python3.6/dist-packages (from plotly->dash) (1.3.3)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from plotly->dash) (1.12.0)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/dist-packages (from Jinja2>=2.10.1->Flask>=1.0.2->dash) (1.1.1)
Requirement already satisfied: dash-html-components in /usr/local/lib/python3.6/dist-packages (1.0.3)
Requirement already satisfied: dash-core-components in /usr/local/lib/python3.6/dist-packages (1.9.1)
Requirement already satisfied: dash-table in /usr/local/lib/python3.6/dist-packages (4.6.2)
Requirement already satisfied: dash_bootstrap_components in /usr/local/lib/python3.6/dist-packages (0.9.2)
Requirement already satisfied: dash>=1.9.0 in /usr/local/lib/python3.6/dist-packages (from dash_bootstrap_components) (1.11.0)
Requirement already satisfied: dash-renderer==1.4.0 in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (1.4.0)
Requirement already satisfied: flask-compress in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (1.4.0)
Requirement already satisfied: dash-table==4.6.2 in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (4.6.2)
Requirement already satisfied: dash-core-components==1.9.1 in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (1.9.1)
Requirement already satisfied: plotly in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (4.6.0)
Requirement already satisfied: dash-html-components==1.0.3 in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (1.0.3)
Requirement already satisfied: future in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (0.16.0)
Requirement already satisfied: Flask>=1.0.2 in /usr/local/lib/python3.6/dist-packages (from dash>=1.9.0->dash_bootstrap_components) (1.1.2)
Requirement already satisfied: retrying>=1.3.3 in /usr/local/lib/python3.6/dist-packages (from plotly->dash>=1.9.0->dash_bootstrap_components) (1.3.3)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from plotly->dash>=1.9.0->dash_bootstrap_components) (1.12.0)
Requirement already satisfied: Jinja2>=2.10.1 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash>=1.9.0->dash_bootstrap_components) (2.11.1)
Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash>=1.9.0->dash_bootstrap_components) (7.1.1)
Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash>=1.9.0->dash_bootstrap_components) (1.1.0)
Requirement already satisfied: Werkzeug>=0.15 in /usr/local/lib/python3.6/dist-packages (from Flask>=1.0.2->dash>=1.9.0->dash_bootstrap_components) (1.0.1)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/dist-packages (from Jinja2>=2.10.1->Flask>=1.0.2->dash>=1.9.0->dash_bootstrap_components) (1.1.1)
Requirement already satisfied: pycountry in /usr/local/lib/python3.6/dist-packages (19.8.18)
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
/content/drive/My Drive/Coronavirus
Cloning into 'coronavirus-data'...
remote: Enumerating objects: 153, done.
remote: Counting objects: 100% (153/153), done.
remote: Compressing objects: 100% (76/76), done.
remote: Total 153 (delta 69), reused 122 (delta 50), pack-reused 0
Receiving objects: 100% (153/153), 18.36 MiB | 13.98 MiB/s, done.
Resolving deltas: 100% (69/69), done.
/content/drive/My Drive/Coronavirus/coronavirus-data

In [101]:
INPUT_PATH = "/content/drive/My Drive/Coronavirus/coronavirus-data/input"
INPUT_URL = "https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data"

LOOKUP_CSV_PATH = os.path.join(INPUT_PATH, 'lookup.csv')
CONFIRMED_CSV_PATH = os.path.join(INPUT_PATH, 'confirmed.csv')
DEATHS_CSV_PATH = os.path.join(INPUT_PATH, 'deaths.csv')
RECOVERED_CSV_PATH = os.path.join(INPUT_PATH, 'recovered.csv')

!wget -nv -cO "$LOOKUP_CSV_PATH" $INPUT_URL/UID_ISO_FIPS_LookUp_Table.csv
!wget -nv -cO "$CONFIRMED_CSV_PATH" $INPUT_URL/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv
!wget -nv -cO "$DEATHS_CSV_PATH" $INPUT_URL/csse_covid_19_time_series/time_series_covid19_deaths_global.csv
!wget -nv -cO "$RECOVERED_CSV_PATH" $INPUT_URL/csse_covid_19_time_series/time_series_covid19_recovered_global.csv

df_lookup = pd.read_csv(LOOKUP_CSV_PATH);

def transform_and_standardize(df, var_name):
    df = df.drop(columns=['Lat', 'Long']).merge(
        df_lookup.rename(columns={'Country_Region': 'Country/Region', 'Province_State': 'Province/State'})[['Country/Region', 'Province/State', 'iso3','Population']],
        how='outer',
        on=['Country/Region', 'Province/State']
    ).dropna(subset=["iso3"])
    df = df.groupby(['iso3','Country/Region']).sum().reset_index()
    df = df.melt(id_vars=[df.columns[0],df.columns[1],df.columns[-1]], 
        value_vars=df.columns[2:-1], 
        var_name='date', 
        value_name=var_name
    ).dropna()
    df['date']=pd.to_datetime(df['date'])
    return df.sort_values(by=['iso3', 'date'])

df_confirmed = transform_and_standardize(pd.read_csv(CONFIRMED_CSV_PATH), 'confirmed')
df_deaths = transform_and_standardize(pd.read_csv(DEATHS_CSV_PATH), 'deaths')
df_recovered = transform_and_standardize(pd.read_csv(RECOVERED_CSV_PATH), 'recovered')
df = df_confirmed.merge(df_deaths,how='outer',on=['date', 'iso3', 'Population','Country/Region']).merge(df_recovered,how='outer',on=['date', 'iso3', 'Population','Country/Region'])
for col in ['confirmed', 'deaths', 'recovered']:
    df[f'{col}_rate'] = df[col]/df_merged['Population']
df


Out[101]:
iso3 Country/Region Population date confirmed deaths recovered confirmed_rate deaths_rate recovered_rate
0 ABW Netherlands 106766.0 2020-01-22 0.0 0.0 0.0 0.000000e+00 0.000000e+00 0.0
1 ABW Netherlands 106766.0 2020-01-23 0.0 0.0 0.0 0.000000e+00 0.000000e+00 0.0
2 ABW Netherlands 106766.0 2020-01-24 0.0 0.0 0.0 0.000000e+00 0.000000e+00 0.0
3 ABW Netherlands 106766.0 2020-01-25 0.0 0.0 0.0 0.000000e+00 0.000000e+00 0.0
4 ABW Netherlands 106766.0 2020-01-26 0.0 0.0 0.0 0.000000e+00 0.000000e+00 0.0
... ... ... ... ... ... ... ... ... ... ...
17840 ZWE Zimbabwe 14862927.0 2020-04-09 11.0 3.0 0.0 7.400965e-07 2.018445e-07 0.0
17841 ZWE Zimbabwe 14862927.0 2020-04-10 13.0 3.0 0.0 8.746595e-07 2.018445e-07 0.0
17842 ZWE Zimbabwe 14862927.0 2020-04-11 14.0 3.0 0.0 9.419410e-07 2.018445e-07 0.0
17843 ZWE Zimbabwe 14862927.0 2020-04-12 14.0 3.0 0.0 9.419410e-07 2.018445e-07 0.0
17844 ZWE Zimbabwe 14862927.0 2020-04-13 17.0 3.0 0.0 1.143785e-06 2.018445e-07 0.0

17845 rows × 10 columns


In [106]:
!wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
!unzip ngrok-stable-linux-amd64.zip
get_ipython().system_raw('./ngrok http 8050 &')
! curl -s http://localhost:4040/api/tunnels | python3 -c "import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])"


--2020-04-14 18:59:20--  https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
Resolving bin.equinox.io (bin.equinox.io)... 52.3.157.51, 34.192.123.246, 52.207.93.234, ...
Connecting to bin.equinox.io (bin.equinox.io)|52.3.157.51|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13773305 (13M) [application/octet-stream]
Saving to: ‘ngrok-stable-linux-amd64.zip.1’

ngrok-stable-linux- 100%[===================>]  13.13M  34.7MB/s    in 0.4s    

2020-04-14 18:59:20 (34.7 MB/s) - ‘ngrok-stable-linux-amd64.zip.1’ saved [13773305/13773305]

Archive:  ngrok-stable-linux-amd64.zip
replace ngrok? [y]es, [n]o, [A]ll, [N]one, [r]ename: n
https://39554d3c.ngrok.io

In [112]:
import time
from flask import request

unixTimeMillis = lambda dt: int(time.mktime(dt.timetuple()))

app = dash.Dash("Coronavirus Dashboard", external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css'])

app.layout = html.Div([
    dcc.Graph(id='indicator-graphic'),
    dcc.Slider(
        id='date-slider',
        min=unixTimeMillis(df['date'].min()),
        max=unixTimeMillis(df['date'].max()),
        value=[unixTimeMillis(df['date'].min()),unixTimeMillis(df['date'].min())],
        marks={unixTimeMillis(date):str(date.strftime('%Y-%m-%d')) for date in df['date']},
        step=None
    )
])

def shutdown():
    func = request.environ.get('werkzeug.server.shutdown'),
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
        func()

@app.callback(
    Output('indicator-graphic', 'figure'),
    [Input('url', 'pathname'), Input('date-slider', 'value')])

def update_graph(pathname, date_value):
    if pathname =='/shutdown':
        shutdown()
    dff = df[df['date'] == date_value]
    return {
        'data': [
            go.Choropleth(
                locations=dff['iso3'],
                z=dff['confirmed_rate'],
                text=dff['Country/Region'],
                autocolorscale=False,
                colorscale="YlGnBu",
                marker={'line': {'color': 'rgb(180,180,180)','width': 0.5}},
                colorbar={"thickness": 10,"len": 0.3,"x": 0.9,"y": 0.7}
            )
        ],
        'layout': go.Layout(height=800,geo={'showframe': False,'showcoastlines': False})
    }


if __name__ == '__main__':
    app.run_server(debug=True)


Running on http://127.0.0.1:8050/
Running on http://127.0.0.1:8050/
Running on http://127.0.0.1:8050/
Running on http://127.0.0.1:8050/
Running on http://127.0.0.1:8050/
Debugger PIN: 198-221-310
Debugger PIN: 198-221-310
Debugger PIN: 198-221-310
Debugger PIN: 198-221-310
Debugger PIN: 198-221-310
 * Serving Flask app "Coronavirus Dashboard" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
An exception has occurred, use %tb to see the full traceback.

SystemExit: 1
/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py:2890: UserWarning:

To exit: use 'exit', 'quit', or Ctrl-D.


In [111]:
scatter_confirmed = px.scatter_geo(df, title="Cases as a Scatter", locations="iso_alpha", color_discrete_sequence=["#cb181d"], size="Confirmed", animation_frame="Days_since_start", hover_name="Country/Region")
scatter_death = px.scatter_geo(df, title="Deaths as a Scatter", locations="iso_alpha", color_discrete_sequence=["#cb181d"], size="Deaths", animation_frame="Days_since_start", hover_name="Country/Region")
choropleth_confirmed = px.choropleth(df, title="Cases as a Choropleth", locations="iso_alpha", color="Rate", animation_frame="Days_since_start", hover_name="Country/Region")
choropleth_death = px.choropleth(df, title="Deaths as a Choropleth", locations="iso_alpha", color="Death Rate", animation_frame="Days_since_start", hover_name="Country/Region")


---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-111-dbe3d03d69e4> in <module>()
----> 1 scatter_confirmed = px.scatter_geo(df, title="Cases as a Scatter", locations="iso_alpha", color_discrete_sequence=["#cb181d"], size="Confirmed", animation_frame="Days_since_start", hover_name="Country/Region")
      2 scatter_death = px.scatter_geo(df, title="Deaths as a Scatter", locations="iso_alpha", color_discrete_sequence=["#cb181d"], size="Deaths", animation_frame="Days_since_start", hover_name="Country/Region")
      3 choropleth_confirmed = px.choropleth(df, title="Cases as a Choropleth", locations="iso_alpha", color="Rate", animation_frame="Days_since_start", hover_name="Country/Region")
      4 choropleth_death = px.choropleth(df, title="Deaths as a Choropleth", locations="iso_alpha", color="Death Rate", animation_frame="Days_since_start", hover_name="Country/Region")

/usr/local/lib/python3.6/dist-packages/plotly/express/_chart_types.py in scatter_geo(data_frame, lat, lon, locations, locationmode, color, text, hover_name, hover_data, custom_data, size, animation_frame, animation_group, category_orders, labels, color_discrete_sequence, color_discrete_map, color_continuous_scale, range_color, color_continuous_midpoint, opacity, size_max, projection, scope, center, title, template, width, height)
    925         args=locals(),
    926         constructor=go.Scattergeo,
--> 927         trace_patch=dict(locationmode=locationmode),
    928     )
    929 

/usr/local/lib/python3.6/dist-packages/plotly/express/_core.py in make_figure(args, constructor, trace_patch, layout_patch)
   1366 
   1367     args, trace_specs, grouped_mappings, sizeref, show_colorbar = infer_config(
-> 1368         args, constructor, trace_patch
   1369     )
   1370     grouper = [x.grouper or one_group for x in grouped_mappings] or [one_group]

/usr/local/lib/python3.6/dist-packages/plotly/express/_core.py in infer_config(args, constructor, trace_patch)
   1209             all_attrables += [group_attr]
   1210 
-> 1211     args = build_dataframe(args, all_attrables, array_attrables)
   1212     if constructor in [go.Treemap, go.Sunburst] and args["path"] is not None:
   1213         args = process_dataframe_hierarchy(args)

/usr/local/lib/python3.6/dist-packages/plotly/express/_core.py in build_dataframe(args, attrables, array_attrables)
    973                             "\n To use the index, pass it in directly as `df.index`."
    974                         )
--> 975                     raise ValueError(err_msg)
    976                 if length and len(df_input[argument]) != length:
    977                     raise ValueError(

ValueError: Value of 'size' is not the name of a column in 'data_frame'. Expected one of ['iso3', 'Country/Region', 'Population', 'date', 'confirmed', 'deaths', 'recovered', 'confirmed_rate', 'deaths_rate', 'recovered_rate'] but received: Confirmed

In [0]:
%cd "/content/drive/My Drive/Coronavirus/coronavirus-data"
fig.write_html("docs/index.html",include_plotlyjs='cdn',include_mathjax='cdn',default_width="100%",default_height="100%")
PASSWORD = getpass()
!git add --all
!git commit -a
!git config remote.origin.url 'https://jvonk:{PASSWORD}@github.com/jvonk/coronavirus-data.git'
!git push
%cd data