Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display fatalities in dashboard #95

Merged
merged 9 commits into from
Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@
className="pure-u-1 pure-u-lg-1 pure-u-xl-12-24",
),
html.Div([
dcc.RadioItems(id='radio-cases',
options=[
{'label':'Confirmed cases', 'value': 'active'},
{'label': 'Fatalities', 'value': 'death'},
],
value='active',
labelStyle={'display': 'inline-block',
'padding-right': '0.5em'}
),
dcc.RadioItems(id='log-lin',
options=[
{'label':'log', 'value': 'log'},
{'label': 'linear', 'value': 'linear'},
],
value='linear',
labelStyle={'display': 'inline-block',
'padding-right': '0.5em'}
),

dcc.Graph(
id='plot', figure=fig2,
config={
Expand Down Expand Up @@ -166,19 +185,6 @@
# in order to transform the app into static html pages
# javascript functions are defined in assets/callbacks.js

app.clientside_callback(
ClientsideFunction(
namespace='clientside',
function_name='update_store_data'
),
output=Output('plot', 'figure'),
inputs=[
Input('table', "data"),
Input('table', "selected_rows")],
state=[State('store', 'data')],
)


app.clientside_callback(
ClientsideFunction(
namespace='clientside3',
Expand All @@ -195,5 +201,21 @@
)


app.clientside_callback(
ClientsideFunction(
namespace='clientside',
function_name='update_store_data'
),
output=Output('plot', 'figure'),
inputs=[
Input('table', "data"),
Input('table', "selected_rows"),
Input('radio-cases', 'value'),
Input('log-lin', 'value')],
state=[State('store', 'data')],
)



if __name__ == '__main__':
app.run_server(debug=debug)
73 changes: 67 additions & 6 deletions assets/callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ if (!window.dash_clientside) {

window.dash_clientside.clientside3 = {
update_table: function(clickdata, selecteddata, table_data, selectedrows, store) {
/**
* Update selected rows in table when clicking or selecting in map
* chart
*
* Parameters
* ----------
*
* clickdata: object (dict)
* clicked points
* selected: object (dict)
* box-selected points
* table_data: list of dict
* data of the table
* selectedrows: list of indices
* list of selected countries to be updated
* store: list
* store[1] is the list of countries to be used when initializing
* the app
*/
if ((!selecteddata) && (!clickdata)) {
// this is only visited when initializing the app
// we use a pre-defined list of indices
Expand Down Expand Up @@ -46,25 +65,67 @@ window.dash_clientside.clientside3 = {


window.dash_clientside.clientside = {
update_store_data: function(rows, selectedrows, store) {
update_store_data: function(rows, selectedrows, cases_type, log_or_lin, store) {
/**
* Update timeseries figure when selected countries change,
* or type of cases (active cases or fatalities)
*
* Parameters
* ----------
*
* rows: list of dicts
* data of the table
* selectedrows: list of indices
* indices of selected countries
* cases_type: str
* active or death
* log_or_lin: str
* log or linear axis
* store: list
* store[0]: plotly-figure-dict, containing all the traces (all
* countries, data and prediction, for active cases and deaths)
* store[1]: list of countries to be used at initialization
*/
var fig = store[0];
if (!rows) {
throw "Figure data not loaded, aborting update."
}
var new_fig = {};
new_fig['data'] = [];
new_fig['layout'] = fig['layout'];

var countries = [];
for (i = 0; i < selectedrows.length; i++) {
countries.push(rows[selectedrows[i]]["country_region"]);
}
for (i = 0; i < fig['data'].length; i++) {
var name = fig['data'][i]['name'];
if (countries.includes(name) || countries.includes(name.substring(1))){
new_fig['data'].push(fig['data'][i]);
if (cases_type === 'active'){
new_fig['layout']['annotations'][0]['visible'] = false;
new_fig['layout']['annotations'][1]['visible'] = true;
for (i = 0; i < fig['data'].length; i++) {
var name = fig['data'][i]['name'];
if (countries.includes(name) || countries.includes(name.substring(1))){
new_fig['data'].push(fig['data'][i]);
}
}
}
else{
new_fig['layout']['annotations'][0]['visible'] = true;
new_fig['layout']['annotations'][1]['visible'] = false;
for (i = 0; i < fig['data'].length; i++) {
var name = fig['data'][i]['name'];
if (countries.includes(name.substring(2))){
new_fig['data'].push(fig['data'][i]);
}
}
}
new_fig['layout']['yaxis']['type'] = log_or_lin;
if (log_or_lin === 'log'){
new_fig['layout']['legend']['x'] = .65;
new_fig['layout']['legend']['y'] = .1;
}
else{
new_fig['layout']['legend']['x'] = .05;
new_fig['layout']['legend']['y'] = .8;
}
return new_fig;
}
};
Expand Down
10 changes: 10 additions & 0 deletions assets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,13 @@ div.date {
padding: 3px;
color: #444;
}

#radio-cases {
display: inline-block;
padding-right: 2em;
}

#log-lin {
text-align: right;
display: inline-block;
}
144 changes: 79 additions & 65 deletions make_figures.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def make_map(df, df_fatalities):
Parameters
----------
df: pandas DataFrame
Our cases to plot
pop: pandas DataFrame
The population, used to normalize
Tidt dataframe of confirmed cases
df_fatalities: pandas DataFrame
Tidy dataframe of fatalities
"""
normalized_values = normalize_by_population(df)
# Plot per Million individual
Expand Down Expand Up @@ -70,7 +70,11 @@ def make_timeplot(df_measure, df_prediction, countries=None):

df_prediction: pandas DataFrame
DataFrame of predictions, with similar structure as df_measure

countries: list or None (default)
list of countries to use for the figure. If None, all countries are used.
"""
# active cases
mode = 'confirmed'
df_measure_confirmed = df_measure[mode]
df_measure_confirmed = normalize_by_population_wide(df_measure_confirmed)
Expand All @@ -93,6 +97,8 @@ def make_timeplot(df_measure, df_prediction, countries=None):
meta=country[1],
hovertemplate=hovertemplate_measure,
visible=True))

# predictions
prediction = df_prediction['prediction']
upper_bound = df_prediction['upper_bound']
lower_bound = df_prediction['lower_bound']
Expand Down Expand Up @@ -135,57 +141,37 @@ def make_timeplot(df_measure, df_prediction, countries=None):
visible=True,
hoverinfo='skip',
line_width=.8))
# fatalities
mode = 'death'
df_measure_death = df_measure[mode]
df_measure_death = normalize_by_population_wide(df_measure_death)
# Plot per million
df_measure_death *= 1e6
colors = px.colors.qualitative.Dark24
n_colors = len(colors)
hovertemplate_fatalities = '<b>%{meta}<br>fatalities</b><br>%{x}<br>%{y:.0f} per Million<extra></extra>'
for i, country in enumerate(df_measure_death.columns):
if countries and country[1] not in countries:
continue
fig.add_trace(go.Scatter(x=df_measure_death.index,
y=df_measure_death[country],
name=' ' + country[1], mode='markers+lines',
marker_symbol = SymbolValidator().values[i],
marker_color=colors[i%n_colors],
line_color=colors[i%n_colors],
meta=country[1],
hovertemplate=hovertemplate_fatalities,
visible=True))

last_day = df_measure_confirmed.index.max()
day = pd.DateOffset(days=1)
fig.update_layout(title='',
xaxis=dict(rangeslider_visible=True,
range=(last_day - 10 * day,
last_day + 4 * day)))
fig.update_layout(
showlegend=True,
updatemenus=[
dict(
type = "buttons",
direction = "left",
buttons=list([
dict(
args=[{'yaxis': {'type':'log'},
"legend": {'x':0.65, 'y':0.1,
"font":{"size":18},
}}],
label="log",
method="relayout",
),
dict(
args=[{'yaxis': {'type':'linear'},
"legend": {'x':0.05, 'y':0.8,
"font":{"size":18},
}}],
label="linear",
method="relayout",
),

]),
pad={"r": 10, "t": 0, "b": 0},
showactive=True,
x=0.05,
xanchor="left",
y=1.05,
yanchor="top",
font_color='black',
),
],
xaxis_tickfont_size=LABEL_FONT_SIZE - 4,
yaxis_tickfont_size=LABEL_FONT_SIZE - 4,
height=FIRST_LINE_HEIGHT,
margin=dict(t=0, b=0.02),
# The legend position + font size
# See https://plot.ly/python/legend/#style-legend
legend=dict(x=.05, y=.8, font_size=LABEL_FONT_SIZE,
)
)
# vartical line to seprate the last day of measurements from prediction


# # vertical line to separate the last day of measurements from prediction
fig.add_shape(
# Line Vertical
dict(
Expand All @@ -203,24 +189,52 @@ def make_timeplot(df_measure, df_prediction, countries=None):
)
))

fig.add_annotation(
x=0.1,
y=0.95,
xref='paper',
yref='paper',
showarrow=False,
font_size=LABEL_FONT_SIZE,
text="Confirmed cases per Million")
fig.add_annotation(
x=1,
y=-0.13,
xref='paper',
yref='paper',
showarrow=False,
font_size=LABEL_FONT_SIZE - 6,
font_color="DarkSlateGray",
text="Drag handles below to change time window",
align="right")

fatalities_annotation = dict(x=0.1,
y=0.95,
xref='paper',
yref='paper',
showarrow=False,
font_size=LABEL_FONT_SIZE,
text='Fatalities per Million',
visible=False,
)
confirmed_annotation = dict(x=0.1,
y=0.95,
xref='paper',
yref='paper',
showarrow=False,
font_size=LABEL_FONT_SIZE,
text='Confirmed cases per Million',
visible=True,
)
drag_handle_annotation = dict(x=1,
y=-0.1,
xref='paper',
yref='paper',
showarrow=False,
font_size=LABEL_FONT_SIZE - 6,
font_color="DarkSlateGray",
text="Drag handles below to change time window",
align="right")


fig.update_layout(
showlegend=True,
annotations=[fatalities_annotation,
confirmed_annotation,
drag_handle_annotation],
xaxis_tickfont_size=LABEL_FONT_SIZE - 4,
yaxis_tickfont_size=LABEL_FONT_SIZE - 4,
yaxis_type='linear',
height=FIRST_LINE_HEIGHT,
margin=dict(t=0, b=0.02),
# The legend position + font size
# See https://plot.ly/python/legend/#style-legend
legend=dict(x=.05, y=.8, font_size=LABEL_FONT_SIZE)
)



return fig

Expand Down