From 7f440fd11cd45216fb2617c4829de234c82c6a24 Mon Sep 17 00:00:00 2001 From: Daniel Probst Date: Wed, 25 Mar 2020 22:55:00 +0100 Subject: [PATCH] Feature Update 1 --- app.py | 130 ++++++++++++++++++++++++++++++++++++--- dashcoch/data_loader.py | 26 +++++++- dashcoch/style_loader.py | 29 +++++++++ 3 files changed, 175 insertions(+), 10 deletions(-) diff --git a/app.py b/app.py index d9f2de0..33ca4c2 100644 --- a/app.py +++ b/app.py @@ -84,7 +84,10 @@ html.Div( className="total-container", children=[ - html.P(className="total-title", children="Total Cases"), + html.P( + className="total-title", + children="Total Reported Cases", + ), html.Div( className="total-content", children=str(int(data.total_swiss_cases)), @@ -95,7 +98,8 @@ className="total-container", children=[ html.P( - className="total-title", children="New Cases Today" + className="total-title", + children="Reported Cases Today", ), html.Div( className="total-content", @@ -127,9 +131,11 @@ dcc.RadioItems( id="radio-prevalence", options=[ - {"label": "Number of Cases", "value": "number"}, + {"label": "Total Reported Cases", "value": "number"}, + {"label": "Newly Reported Cases", "value": "new"}, {"label": "Prevalence (per 10,000)", "value": "prevalence"}, - {"label": "Number of Fatalities", "value": "fatalities"}, + {"label": "New Fatalities", "value": "new_fatalities"}, + {"label": "Total Fatalities", "value": "fatalities"}, ], value="number", labelStyle={ @@ -261,6 +267,22 @@ ], ), html.Br(), + html.H4( + children="Demographic Correlations", style={"color": style.theme["accent"]} + ), + html.Div( + className="row", + children=[ + html.Div( + className="six columns", + children=[dcc.Graph(id="prevalence-density-graph")], + ), + html.Div( + className="six columns", children=[dcc.Graph(id="cfr-age-graph")] + ), + ], + ), + html.Br(), html.H4(children="Raw Data", style={"color": style.theme["accent"]}), dash_table.DataTable( id="table", @@ -300,6 +322,22 @@ def update_graph_map(selected_date_index, mode): else "" for canton in data.cantonal_centres ] + elif mode == "new": + map_data = data.swiss_cases_by_date_diff + labels = [ + canton + ": " + str(int(map_data[canton][date])) + if not math.isnan(float(map_data[canton][date])) + else "" + for canton in data.cantonal_centres + ] + elif mode == "new_fatalities": + map_data = data.swiss_fatalities_by_date_diff + labels = [ + canton + ": " + str(int(map_data[canton][date])) + if not math.isnan(float(map_data[canton][date])) + else "" + for canton in data.cantonal_centres + ] return { "data": [ @@ -526,9 +564,9 @@ def update_case_graph(selected_cantons, selected_scale): "x": data.swiss_cases_as_dict["Date"], "y": data.swiss_cases_as_dict[canton], "name": canton, - "marker": {"color": style.colors[i - 1]}, + "marker": {"color": style.canton_colors[canton]}, } - for i, canton in enumerate(data.swiss_cases_as_dict) + for _, canton in enumerate(data.swiss_cases_as_dict) if canton in selected_cantons ], "layout": { @@ -559,9 +597,9 @@ def update_case_pc_graph(selected_cantons, selected_scale): "x": data.swiss_cases_normalized_as_dict["Date"], "y": data.swiss_cases_normalized_as_dict[canton], "name": canton, - "marker": {"color": style.colors[i - 1]}, + "marker": {"color": style.canton_colors[canton]}, } - for i, canton in enumerate(data.swiss_cases_normalized_as_dict) + for _, canton in enumerate(data.swiss_cases_normalized_as_dict) if canton in selected_cantons ], "layout": { @@ -612,7 +650,7 @@ def update_case_graph_diff(selected_cantons, selected_scale): for i, j in zip(data_non_nan[canton][:-1], data_non_nan[canton][1:]) ], "name": canton, - "marker": {"color": style.colors[i - 1]}, + "marker": {"color": style.canton_colors[canton]}, "type": "bar", } for i, canton in enumerate(data.swiss_cases_as_dict) @@ -636,6 +674,80 @@ def update_case_graph_diff(selected_cantons, selected_scale): } +# +# Demographic Correlations +# +@app.callback( + Output("prevalence-density-graph", "figure"), [Input("dropdown-cantons", "value")], +) +def update_prevalence_density_graph(selected_cantons): + return { + "data": [ + { + "x": [data.swiss_demography["Density"][canton]], + "y": [data.swiss_cases_by_date_filled_per_capita.iloc[-1][canton]], + "name": canton, + "mode": "markers", + "marker": {"color": style.canton_colors[canton], "size": 10.0}, + } + for _, canton in enumerate(data.swiss_cases_as_dict) + if canton in selected_cantons + ], + "layout": { + "title": "Prevalence vs Population Density", + "hovermode": "closest", + "height": 750, + "xaxis": { + "showgrid": True, + "color": "#ffffff", + "title": "Population Density [Inhabitants/km2]", + }, + "yaxis": {"showgrid": True, "color": "#ffffff", "title": "Prevalence",}, + "plot_bgcolor": style.theme["background"], + "paper_bgcolor": style.theme["background"], + "font": {"color": style.theme["foreground"]}, + }, + } + + +@app.callback( + Output("cfr-age-graph", "figure"), [Input("dropdown-cantons", "value")], +) +def update_cfr_age_graph(selected_cantons): + return { + "data": [ + { + "x": [data.swiss_demography["O65"][canton] * 100], + "y": [data.swiss_case_fatality_rates.iloc[-1][canton]], + "name": canton, + "mode": "markers", + "marker": {"color": style.canton_colors[canton], "size": 10.0}, + } + for _, canton in enumerate(data.swiss_cases_normalized_as_dict) + if canton in selected_cantons + ], + "layout": { + "title": "Case Fatality Rate vs Population over 65", + "hovermode": "closest", + "height": 750, + "xaxis": { + "showgrid": True, + "color": "#ffffff", + "title": "Population over 65 [%]", + }, + "yaxis": { + "type": "linear", + "showgrid": True, + "color": "#ffffff", + "title": "Case Fatality Rate", + }, + "plot_bgcolor": style.theme["background"], + "paper_bgcolor": style.theme["background"], + "font": {"color": style.theme["foreground"]}, + }, + } + + if __name__ == "__main__": app.run_server( # debug=True, diff --git a/dashcoch/data_loader.py b/dashcoch/data_loader.py index 634eee4..2b9b5cd 100644 --- a/dashcoch/data_loader.py +++ b/dashcoch/data_loader.py @@ -22,10 +22,25 @@ def __init__(self, parser: ConfigParser): self.swiss_cases_by_date = self.swiss_cases.set_index("Date") self.swiss_fatalities_by_date = self.swiss_fatalities.set_index("Date") + self.swiss_cases_by_date_diff = self.swiss_cases_by_date.diff().replace( + 0, float("nan") + ) + self.swiss_fatalities_by_date_diff = self.swiss_fatalities_by_date.diff().replace( + 0, float("nan") + ) + self.swiss_cases_by_date_filled = self.swiss_cases_by_date.fillna( method="ffill", axis=0 ) + self.swiss_fatalities_by_date_filled = self.swiss_fatalities_by_date.fillna( + method="ffill", axis=0 + ) + + self.swiss_case_fatality_rates = ( + self.swiss_fatalities_by_date_filled / self.swiss_cases_by_date_filled + ) + self.swiss_cases_by_date_filled_per_capita = ( self.__get_swiss_cases_by_date_filled_per_capita() ) @@ -138,7 +153,15 @@ def __simplify_world_data(self, df: pd.DataFrame): df = df.T df.drop( df.columns.difference( - ["France", "Germany", "Italy", "Spain", "United Kingdom", "US"] + [ + "France", + "Germany", + "Italy", + "Korea, South", + "Spain", + "United Kingdom", + "US", + ] ), 1, inplace=True, @@ -170,6 +193,7 @@ def __get_world_population(self): "US": 331002651, "United Kingdom": 67886011, "Switzerland": 8654622, + "Korea, South": 51269185, } def __get_cantonal_centres(self): diff --git a/dashcoch/style_loader.py b/dashcoch/style_loader.py index 3e020f9..d785352 100644 --- a/dashcoch/style_loader.py +++ b/dashcoch/style_loader.py @@ -48,6 +48,35 @@ def __init__(self): "#10523e", ] + self.canton_colors = { + "AG": "#87ceeb", + "AI": "#57606f", + "AR": "#57606f", + "BE": "#EE5A24", + "BL": "#e9403c", + "BS": "#F8EFBA", + "FR": "#ffffff", + "GE": "#ffd134", + "GL": "#B53471", + "GR": "#eccc68", + "JU": "#6D214F", + "LU": "#258bcc", + "NE": "#17a74e", + "NW": "#e7423e", + "OW": "#D980FA", + "SG": "#17a74e", + "SH": "#ffd829", + "SO": "#e8423f", + "SZ": "#ff0000", + "TG": "#12CBC4", + "TI": "#0271ac", + "UR": "#ffd72e", + "VD": "#007f01", + "VS": "#e8423f", + "ZG": "#278bcc", + "ZH": "#0abde3", + } + self.theme = { "background": "#252e3f", "foreground": "#2cfec1",