<jupyter_start><jupyter_text>COVID-19 India Matplotlib Overview> Tracking coronavirus total cases, deaths and new cases in India- author: <NAME>- image: images/covid-overview.png- permalink: /covid-overview-linde-matplotlib-exp/- hide: false- badges: false<jupyter_code>#hide

## Import essential packages below

import pandas as pd
import numpy as np
import requests
import json
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib as mpl
import seaborn as sns
from IPython.core.display import display,HTML
%matplotlib inline
#hide

# This is a helper dictionary we will use later to map between state abbrevations and names

url = 'https://raw.githubusercontent.com/4722794/dashboards/master/abb_dict.json'
r = requests.get(url)
abb_dict = r.json()
#hide

#This helper function will be handy later to select only the columns which are days
#valid_i is a valid_index because sometimes raw data from api can have last column empty

def get_dates(df):
    dt_cols = df.columns[~df.columns.isin(['stateABB','states'])]
    valid_i = -1
    for i in range(-1,-len(dt_cols),-1):
        if not df[dt_cols[i]].fillna(0).eq(0).all():
            valid_i = i
            break
    return valid_i, dt_cols
#hide

# This giant function takes raw data from api and wrangles it into a nice dataframe for further processing
def get_frame(name):
#    pdb.set_trace()
    url = f'http://api.covid19india.org/states_daily_csv/{name}.csv'
    df = pd.read_csv(url)
    df.set_index('date',inplace = True)
    df = df.T
    df = df.iloc[:-1,:]
    df.fillna(0,inplace=True)
    df = df[df.columns].astype(int)
    df = df.rename_axis('stateABB').reset_index()
    df['states'] = df['stateABB'].map(abb_dict)
    df = df[[df.columns[-1]] + list(df.columns[:-1])]
    df =df.iloc[1:,:]
    df = df[df.states.notna()]
    df.sort_values(by = [df.columns[-1]], ascending = False, inplace = True)
    df.drop(['stateABB'],axis = 1)
    df = df.groupby('states').sum().cumsum(axis=1)
    df.reset_index(inplace = True)
    return df
#hide
# We use above helper fucnction to get us timeseries dataframe for confirmed cases and deceased cases
dft_cases = get_frame('confirmed')
dft_deaths = get_frame('deceased')
#hide
# helper function get_dates used to find valid columns and index
#We also define two important column entries, today and yesterday
valid_i, dt_cols = get_dates(dft_cases)
dt_today = dt_cols[valid_i]
dt_yday = dt_cols[valid_i -1]
#hide
# Using groupby on timeseries dataframe to get latest cases, and past-cases (pcases)
#Here past cases are a day earlier, but this can be changed
dfc_cases = dft_cases.groupby('states')[dt_today].sum()
dfc_deaths = dft_deaths.groupby('states')[dt_today].sum()
dfp_cases = dft_cases.groupby('states')[dt_yday].sum()
dfp_deaths = dft_deaths.groupby('states')[dt_yday].sum()
#hide
# Here we build a nice table that gives us a view across states
#TIP: Do a df_table.head() to see what's happening
df_table = (pd.DataFrame(dict(Cases = dfc_cases, Deaths = dfc_deaths, PCases = dfp_cases, PDeaths = dfp_deaths))
            .sort_values(by = ['Cases','Deaths'], ascending = [False, False])
            .reset_index())
for c in 'Cases, Deaths'.split(', '):
    df_table[f'{c} (+)'] = (df_table[c] - df_table[f'P{c}']).clip(0)
    #Clip ça veut dire, les chiffres negatif sont interdit
df_table['Fatality Rate'] = (100* df_table['Deaths']/ df_table['Cases']).round(2)
#hide
#Building the summary tab
# Here we build a summary dictionary, with key data from the table
#Tip: print the dictionary
metrics = [df_table.columns[index] for index in [1,2,5,6]]
s_mah = df_table[df_table.states == 'Maharashtra'][metrics].sum().add_prefix('MH ').astype(int)
s_del = df_table[df_table.states =='Delhi'][metrics].sum().add_prefix('DEL ').astype(int)
s_guj = df_table[df_table.states == 'Gujarat'][metrics].sum().add_prefix('GUJ ').astype(int)
summary = {'updated': pd.to_datetime(dt_today).date(), 'since': pd.to_datetime(dt_yday).date()}
summary = {**summary, **df_table[metrics].sum().astype(int), **s_mah, **s_del, **s_guj}
#hide
#Data on new cases
#Making new dataframe of cases per day (Note, dataframe from helper function gave the cumulative each day)
dft_ct_new_cases = dft_cases.groupby('states')[dt_cols].sum().diff(axis=1).fillna(0).astype(int)
dft_ct_new_cases.sort_values(by = [dft_ct_new_cases.columns[-1]], ascending = False,inplace = True)
#hide_input
# A basic HTML layout to display India summary
overview = '''
<!-- #######  YAY,HTML!! #########-->
<h1 style="color: #5e9ca0; text-align: center;">India</h1>
<p style="text-align: center;">Last update: <strong>{update}</strong></p>
<p style="text-align: center;">Confirmed cases:</p>
<p style="text-align: center;font-size:24px;">{cases} (<span style="color: #ff0000;">+{new}</span>)</p>
<p style="text-align: center;">Confirmed deaths:</p>
<p style="text-align: center;font-size:24px;">{deaths} (<span style="color: #ff0000;">+{dnew}</span>)</p>
'''

update = summary['updated'].strftime('%B %d %Y')
cases = summary['Cases']
new = summary['Cases (+)']
deaths = summary['Deaths']
dnew = summary['Deaths (+)']

html = HTML(overview.format(update=update, cases=cases,new=new,deaths=deaths,dnew=dnew))

display(html)
#hide_input
#3 BIG THINGS ARE HAPPENING HERE

#1: Gridspec, check out documentation. It's the best way to arrange multiple plots

#2: Each plot is a timeseries of the number of cases each day

#3 We're writing things in the plot using ax.text. For example, In this case to mention the state
# But you can write anything. check ax.text

df = dft_ct_new_cases
df.loc['Total'] = df.sum()
n = 10
ax = []
fig = plt.figure(figsize = (16,32))
gs = fig.add_gridspec(n+2, 3)
# gs = fig.add_gridspec(2, 3)
ax1 = fig.add_subplot(gs[0, :])
ef = df.loc['Total'].reset_index()
ef['date'] = ef['date'].astype('datetime64[ns]')
ax1.bar(ef.date,ef.Total ,color = 'r',alpha=0.5)
ax1.xaxis.set_major_locator(mdates.WeekdayLocator())
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax1.text(0.02, 0.5,'Daily case count', transform = ax1.transAxes, fontsize=25);


ax2 = fig.add_subplot(gs[1,0])
ef = df.loc['Maharashtra'].reset_index()
ef['date'] = ef['date'].astype('datetime64[ns]')
ax2.bar(ef.date, ef.Maharashtra,color = 'r',alpha=0.5)
ax2.xaxis.set_major_locator(mdates.WeekdayLocator())
ax2.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
maxyval = ef.Maharashtra.max()
ax2.set_ylim([0,maxyval])
ax2.text(0.05, 0.5,'Maharashtra', transform = ax2.transAxes, fontsize=20);


ax3 = fig.add_subplot(gs[1,1])
ef = df.loc['Gujarat'].reset_index()
ef['date'] = ef['date'].astype('datetime64[ns]')
ax3.bar(ef.date, ef.Gujarat,color = 'r',alpha=0.5)
ax3.xaxis.set_major_locator(mdates.WeekdayLocator())
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax3.set_ylim([0,maxyval])
ax3.text(0.05, 0.5,'Gujarat', transform = ax3.transAxes, fontsize=20);

ax4 = fig.add_subplot(gs[1,2])
ef = df.loc['Delhi'].reset_index()
ef['date'] = ef['date'].astype('datetime64[ns]')
ax4.bar(ef.date, ef.Delhi,color = 'r',alpha=0.5)
ax4.xaxis.set_major_locator(mdates.WeekdayLocator())
ax4.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
ax4.set_ylim([0,maxyval])
ax4.text(0.05, 0.5,'Delhi', transform = ax4.transAxes, fontsize=20);

for i in range(n):
    
    ax.append(fig.add_subplot(gs[i+2,:]))
    ef = df.iloc[i+3].reset_index()
    ef['date'] = ef['date'].astype('datetime64[ns]')
    ax[i].bar(ef.date,ef.iloc[:,-1],color = 'r',alpha=0.5)
    ax[i].text(0.02,0.5,f'{ef.columns.values[-1]}',transform = ax[i].transAxes, fontsize = 20);
    ax[i].xaxis.set_major_locator(mdates.WeekdayLocator())
    ax[i].xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
    ax[i].set_ylim([0,200])

sns.despine();
#hide_input
#Finally, not everything can be represented by graph, so a simple print of the dataframe
# for a raw numbers view of the table
print(df_table.head(20).to_string(index=False))<jupyter_output>states  Cases  Deaths  PCases  PDeaths  Cases (+)  Deaths (+)  Fatality Rate
       Maharashtra   8068     341    7628      322        440          19           4.23
           Gujarat   3301     151    3071      133        230          18           4.57
             Delhi   2918      54    2625       54        293           0           1.85
         Rajasthan   2185      41    2083       34        102           7           1.88
    Madhya Pradesh   2090     104    1945      100        145           4           4.98
        Tamil Nadu   1885      24    1821       23         64           1           1.27
     Uttar Pradesh   1873      30    1793       27         80           3           1.60
    <NAME>   1097      31    1016       31         81           0           2.83
         Telangana   1001      25     990       25         11           0           2.50
       West Bengal    611      20     571       18         40           2           3.27
 Jammu and Kashmir   [...]
{'fraction_non_alphanumeric': 0.1523809523809524, 'fraction_numerical': 0.040336134453781515, 'mean_word_length': 3.7453482190324294, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 26, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '255759', 'license': 'Apache-2.0', 'n_tokens_mistral': 3487, 'n_tokens_neox': 3231, 'n_words': 762, 'path': '_notebooks/2020-03-21-DashboardV1.ipynb', 'repo_name': '4722794/fastpages-netlify', 'size': '8940'}