<jupyter_start><jupyter_text>Cases in Malaysia> Updates on the respiratory illness that has infected more than one million people and killed tens of thousands.- toc:false- branch: master- badges: false- hide: false- comments: false- permalink:/covid-my-overview/<jupyter_code>#hide
#@title Import modules
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# import ipywidgets as widgets
# from jinja2 import Template
#hide
#@title Load datasets from source and define functions
#@markdown source: [Novel Coronavirus (COVID-19) Cases, provided by JHU CSSE](https://github.com/CSSEGISandData/COVID-19)
#@markdown * `cases_global` Global dataset by type
#@markdown * `country_cases_df` Filter cases by country and dataset type

# source data
# [Novel Coronavirus (COVID-19) Cases, provided by JHU CSSE](https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_time_series)
base_url = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data'
time_series_url = f'{base_url}/csse_covid_19_time_series'
covid_time_series_df = lambda type: pd.read_csv(f'{time_series_url}/time_series_covid19_{type}_global.csv')

# confirmed, deaths, recovered
def cases_global(type):
  _dff = covid_time_series_df(type)
  _cols = _dff.columns[~_dff.columns.isin(['Province/State', 'Country/Region', 'Lat', 'Long'])]
  _dff = (_dff.groupby('Country/Region')[_cols].sum().stack().reset_index(name = 'Cases')
        .rename(columns = {'level_1': 'Date', 'Country/Region': 'Country'}))
  _dff['Date'] = pd.to_datetime(_dff['Date'], format='%m/%d/%y')
  return _dff

country_cases_df = lambda _df, country: _df[_df['Country'] == country][['Date', 'Cases']].reset_index(drop=True)
#hide
#@title Prepare datasets
# Country filter
global_cases = {
    'confirmed': cases_global('confirmed'),
    'deaths': cases_global('deaths'),
    'recovered': cases_global('recovered')
}

def country_cases(country): 
  confirmed_df = country_cases_df(global_cases['confirmed'], country)
  deaths_df = country_cases_df(global_cases['deaths'], country)
  recovered_df = country_cases_df(global_cases['recovered'], country)

  active_df = pd.concat([confirmed_df['Date'], confirmed_df['Cases'] - deaths_df['Cases'] - recovered_df['Cases']], axis=1)
  
  return {
    'country': country,
    'active': active_df,
    'confirmed': confirmed_df,
    'deaths': deaths_df,
    'recovered': recovered_df
  }

cases_summary = lambda c: { # c country_cases(country)
    'country': c['country'],
    'date_latest': c['confirmed']['Date'].max(),
    's_date_latest': c['confirmed']['Date'].max().strftime("%B %d, %Y"), #.strftime('%m/%d/%Y')
    'total_active': c['active'].iloc[-1]['Cases'],
    'total_confirmed': c['confirmed'].iloc[-1]['Cases'],
    'total_deaths': c['deaths'].iloc[-1]['Cases'],
    'total_recovered': c['recovered'].iloc[-1]['Cases'],
    'new_active': c['active'].diff().iloc[-1]['Cases'],
    'new_confirmed': c['confirmed'].diff().iloc[-1]['Cases'],
    'new_deaths': c['deaths'].diff().iloc[-1]['Cases'],
    'new_recovered': c['recovered'].diff().iloc[-1]['Cases'],
}
#hide
#@title Define plot functions { form-width: "100px" }
# def show_summary(country_cases):
#   summary = cases_summary(country_cases)
#   return HTML(
#     f'<div style="height: 30px; width: 600px; margin: 0 auto;"><span style="font-size:0.8em;">Summary of {summary["country"]} COVID19 cases as of {summary["s_date_latest"]} 12PM</span></div>'
#     f'<div style="width: 600px; margin: 0 auto;">'
#     f'<div style="float: left; width: 150px">Confirmed Cases<br/><h1>{summary["total_confirmed"]:,}</h1>(+{summary["new_confirmed"]:,.0f})</div>'
#     f'<div style="float: right; width: 150px">Deaths<br/><h1>{summary["total_deaths"]:,}</h1>{summary["total_deaths"]/summary["total_confirmed"]:.2%} (+{summary["new_deaths"]:,.0f})</div>'
#     f'<div style="float: right; width: 150px">Recovered<br/><h1>{summary["total_recovered"]:,}</h1>{summary["total_recovered"]/summary["total_confirmed"]:.2%} (+{summary["new_recovered"]:,.0f})</div>'
#     f'<div style="float: right; width: 150px">Active<br/><h1>{summary["total_active"]:,}</h1>{summary["total_active"]/summary["total_confirmed"]:.2%} ({summary["new_active"]:,.0f})</div>'
#     f'</div>'
#   )

# def plot_cases(cases_df):
#   _plot = alt.Chart(cases_df).mark_bar().encode(
#     x='Date:T',
#     y='Cases:Q',
#     tooltip=list(cases_df)
#   )
#   return _plot

def fig_summary(country_cases):
  fig = go.Figure()

  summary = cases_summary(country_cases)

  fig.add_trace(go.Indicator(
      mode = "number+delta",
      value = summary['total_confirmed'],
      number = {'valueformat': ','},
      align = "left",
      title = {"text": f'Confirmed', "align": "left", "font": {"family": "sans-serif"}},
      delta = {'reference': summary['total_confirmed'] - summary['new_confirmed'], 'position': 'right', 'valueformat': ','},
      domain = {'x': [0, .2], 'y': [.5, 1]}))

  fig.add_trace(go.Indicator(
      mode = "number+delta",
      value = summary['total_deaths'],
      number = {'valueformat': ','},
      align = "left",
      title = {"text": f'Deaths <span style="font-size:0.8em;color:gray">{summary["total_deaths"]/summary["total_confirmed"]:.2%}</span>', "align": "left", "font": {"family": "sans-serif"}},
      delta = {'reference': summary['total_deaths'] - summary['new_deaths'], 'position': 'right', 'valueformat': ','},
      domain = {'x': [0, .2], 'y': [0, .5]}))

  fig.add_trace(go.Indicator(
      mode = "number+delta",
      value = summary['total_recovered'],
      number = {'valueformat': ','},
      align = "left",
      title = {"text": f'Recovered <span style="font-size:0.8em;color:gray">{summary["total_recovered"]/summary["total_confirmed"]:.2%}</span>', "align": "left", "font": {"family": "sans-serif"}},
      delta = {'reference': summary['total_recovered'] - summary['new_recovered'], 'position': 'right', 'valueformat': ','},
      domain = {'x': [.4, .6], 'y': [0, .5]}))

  fig.add_trace(go.Indicator(
      mode = "number+delta",
      value = summary['total_active'],
      number = {'valueformat': ','},
      align = "left",
      title = {"text": f'Active <span style="font-size:0.8em;color:gray">{summary["total_active"]/summary["total_confirmed"]:.2%}</span>', "align": "left", "font": {"family": "sans-serif"}},
      delta = {'reference': summary['total_active'] - summary['new_active'], 'position': 'right', 'valueformat': ','},
      domain = {'x': [.4, .6], 'y': [.5, 1]}))

  fig.update_layout(title=f'Summary of {summary["country"]} COVID19 cases as of {summary["s_date_latest"]} 12PM')
  return fig, summary<jupyter_output><empty_output><jupyter_text>COVID-19: Malaysia at a Glance  Malaysia Movement Control Order> The 2020 Malaysia movement control order, commonly referred to as the MCO, is a cordon sanitaire implemented as a preventive measure by the federal government of Malaysia in response to the COVID-19 pandemic in the country on 18 March 2020.<jupyter_code>#hide
#@title Define functions for MCO { form-width: "100px" }
#@markdown * my_cases
#@markdown * my_summary

#country = 'Malaysia'
my_cases = country_cases('Malaysia')
my_summary = cases_summary(my_cases)

# def plot_mco_date(date, label, max):
#   _df = pd.DataFrame({'Date': [date, date], 'Cases': [0, max]})
#   _base_ref = alt.Chart(_df).encode(x='Date:T', y='Cases:Q')
#   return (_base_ref.mark_line(color='black', opacity=.5, strokeDash=[3,3])  + 
#           _base_ref.transform_filter(alt.datum['Cases'] > 0).mark_text(text=label, dx=-20, dy=-10, angle=270))

# def plot_mco_cases(case, label, max):
#   _df = pd.DataFrame({'Cummulative Cases': [case, case], 'Cases': [0.1, max]})
#   _base_ref = alt.Chart(_df).encode(x='Cummulative Cases:Q', y='Cases:Q')
#   return (_base_ref.mark_line(color='black', opacity=.5, strokeDash=[3,3])  + 
#           _base_ref.transform_filter(alt.datum['Cases'] > 1).mark_text(text=label, dx=-20, dy=-10, angle=270))

# _plot_mco_date = lambda n: plot_mco_date('2020-03-18', 'MCO1', n) + plot_mco_date('2020-04-01', 'MCO2', n) \
#                                         + plot_mco_date('2020-04-15', 'MCO3', n) + plot_mco_date('2020-04-29', 'MCO4', n) \
#                                           + plot_mco_date('2020-05-04', 'CMCO', n)


# _plot_mco_case = plot_mco_cases(790, 'MCO1', 1000) + plot_mco_cases(2908, 'MCO2', 1000) + plot_mco_cases(5072, 'MCO3', 1000) + plot_mco_cases(5945, 'MCO4', 1000) + plot_mco_cases(6353, 'CMCO', 1000)

# Add latest data which not exist in source
_latest_case = {
    'date': np.datetime64('2020-06-12'),
    'confirmed': 8402,
    'deaths': 119,
    'recovered': 7168
}
if len(my_cases['confirmed'][my_cases['confirmed']['Date'] == _latest_case['date']]) == 0:
  my_cases['active'] = my_cases['active'].append(pd.DataFrame([[_latest_case['date'], _latest_case['confirmed'] - _latest_case['deaths'] - _latest_case['recovered']]], columns = ['Date', 'Cases']), ignore_index=True)
  my_cases['confirmed'] = my_cases['confirmed'].append(pd.DataFrame([[_latest_case['date'], _latest_case['confirmed']]], columns = ['Date', 'Cases']), ignore_index=True)
  my_cases['deaths'] = my_cases['deaths'].append(pd.DataFrame([[_latest_case['date'], _latest_case['deaths']]], columns = ['Date', 'Cases']), ignore_index=True)
  my_cases['recovered'] = my_cases['recovered'].append(pd.DataFrame([[_latest_case['date'], _latest_case['recovered']]], columns = ['Date', 'Cases']), ignore_index=True)
#hide_input
#@title Summary
fig,_summary = fig_summary(my_cases)
fig.show()

labels = ['Recovered','Active','Deaths']
colors = ['#2e7d32', '#f9a825', '#c62828']
values = [_summary['total_recovered'], _summary['total_active'], _summary['total_deaths']]

fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
fig.update_traces(hoverinfo='label+percent', textinfo='value+percent', textfont_size=20,
                  marker=dict(colors=colors, line=dict(color='gray', width=2)))
fig.show()
#hide_input
#@title Cummulative cases in Malaysia
mco_periods = [
               {'name': 'MCO1', 'date_start': '2020-03-18', 'cases': 790},
               {'name': 'MCO2', 'date_start': '2020-04-01', 'cases': 2908},
               {'name': 'MCO3', 'date_start': '2020-04-15', 'cases': 5072},
               {'name': 'MCO4', 'date_start': '2020-04-29', 'cases': 5945},
               {'name': 'CMCO', 'date_start': '2020-05-04', 'cases': 6353},
               {'name': 'RMCO', 'date_start': '2020-06-10', 'cases': 8338}
]

def add_mco_periods(fig, line_y_max, text_y_pos):
  for period in mco_periods:
    fig.add_shape(dict(x0=period['date_start'],x1=period['date_start']))
    fig.add_annotation(x=period['date_start'],y=text_y_pos,text=period['name'])

  fig.update_shapes(dict(
        xref='x', 
        yref='y',
        type="line",
        y0=0,
        y1=line_y_max,
        line=dict(
            color='black',
            width=3,
            dash='dot'
        )
      )
  )

  fig.update_layout(
      #showlegend=False,
      annotations=[
          dict(
              xref="x",
              yref="y",
              textangle=270,
              showarrow=False,
              xshift=-10,
              font=dict(color='black')
          )
      ]
  )
  return fig

_data = my_cases['confirmed']
_data_mco = my_cases['confirmed'][my_cases['confirmed']['Date'].isin(['2020-03-18'])]

fig = go.Figure(
    data=[go.Bar(x=_data['Date'], y=_data['Cases'])],
    layout_title_text=f'Cummulative COVID-19 cases in Malaysia (n = {my_summary["total_confirmed"]})'
)

add_mco_periods(fig, my_cases['confirmed']['Cases'].max(), 6000)

fig.show()<jupyter_output><empty_output><jupyter_text>Is the curve is flatterning in Malaysia?> Inflection-sensitive chart for detecting successful interventions, from the article "How To Tell If We're Beating COVID-19". Please refer _minutephysics_ for [How To Tell If We're Beating COVID-19](https://youtu.be/54XLXg4fYsc)<jupyter_code>#hide
#@title Define flatterning figure
def fig_flattern(fig, country):
  _data = country_cases(country)['confirmed']
  _data['New Cases'] = _data['Cases'].diff()
  _data['Average New Cases'] = round(_data['New Cases'].rolling(window=7).mean(),0)
  _data = _data[(_data['Cases'] > 100) & (_data['Average New Cases'] > 0)]
  fig.add_trace(go.Scatter(
      name=country,
      x=_data['Cases'], 
      y=_data['Average New Cases']
  ))

  return fig
#hide_input
#@title Plot flatterning curve of countries
fig = go.Figure()

fig_flattern(fig, 'Malaysia')
fig_flattern(fig, 'Singapore')
fig_flattern(fig, 'Korea, South')
fig_flattern(fig, 'Sweden')
fig_flattern(fig, 'China')
fig_flattern(fig, 'Italy')
fig_flattern(fig, 'United Kingdom')
fig_flattern(fig, 'US')
fig_flattern(fig, 'Taiwan*')

fig.update_layout(legend=dict(x=0, y=1, orientation='h'), title=f'Trajectory of COVID-19 Confirmed Cases vs New Cases (7 Days Average)')
fig.update_xaxes(title_text="<b>Cummulative</b> cases (Log scale)", type='log')
fig.update_yaxes(title_text="<b>New</b> cases (Log scale)", type='log')

fig.show()
#hide_input
#@title Plot daily reported cases
rolling_window = 7
_data = my_cases['confirmed'].copy()
_data['New Cases'] = _data['Cases'].diff()
_data['Average New Cases'] = round(_data['New Cases'].rolling(window=rolling_window).mean(),0)


fig = go.Figure()

fig.add_trace(go.Bar(
      name='Daily New Cases',
      x=_data['Date'], 
      y=_data['New Cases']
))

fig.add_trace(go.Scatter(
      name='7 Days Average',
      x=_data['Date'], 
      y=_data['Average New Cases']
))

add_mco_periods(fig, _data['New Cases'].max(), _data['New Cases'].max() - 15)

fig.update_layout(legend=dict(x=0, y=1, orientation='h'),title=f'Daily Reported Cases in Malaysia')
fig.update_xaxes(title_text="Reported Date")
fig.update_yaxes(title_text="<b>New</b> cases (Log scale)")

fig.show()
#hide
global_cases['confirmed']['Country'].unique()
#hide
df_states = pd.read_csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vTzT<KEY>-B2triWGPE74rfUT4XOsF-5qsB1tM6OfMPVKiRHX95tE9tPubdTbxY/pub?gid=1726267961&single=true&output=csv", parse_dates=['Date'])
df_states.set_index('Date').sum(axis=1)
#hide
df_districts = pd.read_csv("https://docs.google.com/spreadsheets/d/e/2PACX-1vTzT9vUJNiKV2yN4sb_VvxKcq-B2triWGPE74rfUT4XOsF-5qsB1tM6OfMPVKiRHX95tE9tPubdTbxY/pub?gid=1667946793&single=true&output=csv")
col_latest = df_districts.columns.values[-1]

df_districts[['Districts', 'State', col_latest]].sort_values(col_latest, ascending=False).reset_index(drop=True).head(10)
#hide
df_districts_last2 = df_districts.set_index(['Districts', 'State']).transpose().tail(2)
df_districts_new = df_districts_last2.diff().tail(1).transpose()
df_districts_new = df_districts_new.sort_values(by=df_districts_new.columns[0], ascending=False).head(10)
df_districts_new[df_districts_new[df_districts_new.columns[0]] > 0]<jupyter_output><empty_output>