mirror of
https://github.com/bluxmit/alnoda-workspaces.git
synced 2024-09-28 23:41:30 +12:00
223 lines
7.7 KiB
Python
223 lines
7.7 KiB
Python
# ---
|
|
# jupyter:
|
|
# jupytext:
|
|
# formats: ipynb,py:percent
|
|
# text_representation:
|
|
# extension: .py
|
|
# format_name: percent
|
|
# format_version: '1.3'
|
|
# jupytext_version: 1.4.2
|
|
# kernelspec:
|
|
# display_name: Python 3
|
|
# language: python
|
|
# name: python3
|
|
# ---
|
|
|
|
# %% [markdown]
|
|
# # JupyterDash
|
|
# The `jupyter-dash` package makes it easy to develop Plotly Dash apps from the Jupyter Notebook and JupyterLab.
|
|
#
|
|
# Just replace the standard `dash.Dash` class with the `jupyter_dash.JupyterDash` subclass.
|
|
|
|
# %%
|
|
from jupyter_dash import JupyterDash
|
|
|
|
# %%
|
|
import dash
|
|
import dash_core_components as dcc
|
|
import dash_html_components as html
|
|
import pandas as pd
|
|
|
|
# %% [markdown]
|
|
# When running in JupyterHub or Binder, call the `infer_jupyter_config` function to detect the proxy configuration.
|
|
|
|
# %%
|
|
JupyterDash.infer_jupyter_proxy_config()
|
|
|
|
# %% [markdown]
|
|
# Load and preprocess data
|
|
|
|
# %%
|
|
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
|
|
available_indicators = df['Indicator Name'].unique()
|
|
|
|
# %% [markdown]
|
|
# Construct the app and callbacks
|
|
|
|
# %%
|
|
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
|
|
|
|
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
|
|
|
|
# Create server variable with Flask server object for use with gunicorn
|
|
server = app.server
|
|
|
|
app.layout = html.Div([
|
|
html.Div([
|
|
|
|
html.Div([
|
|
dcc.Dropdown(
|
|
id='crossfilter-xaxis-column',
|
|
options=[{'label': i, 'value': i} for i in available_indicators],
|
|
value='Fertility rate, total (births per woman)'
|
|
),
|
|
dcc.RadioItems(
|
|
id='crossfilter-xaxis-type',
|
|
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
|
|
value='Linear',
|
|
labelStyle={'display': 'inline-block'}
|
|
)
|
|
],
|
|
style={'width': '49%', 'display': 'inline-block'}),
|
|
|
|
html.Div([
|
|
dcc.Dropdown(
|
|
id='crossfilter-yaxis-column',
|
|
options=[{'label': i, 'value': i} for i in available_indicators],
|
|
value='Life expectancy at birth, total (years)'
|
|
),
|
|
dcc.RadioItems(
|
|
id='crossfilter-yaxis-type',
|
|
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
|
|
value='Linear',
|
|
labelStyle={'display': 'inline-block'}
|
|
)
|
|
], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
|
|
], style={
|
|
'borderBottom': 'thin lightgrey solid',
|
|
'backgroundColor': 'rgb(250, 250, 250)',
|
|
'padding': '10px 5px'
|
|
}),
|
|
|
|
html.Div([
|
|
dcc.Graph(
|
|
id='crossfilter-indicator-scatter',
|
|
hoverData={'points': [{'customdata': 'Japan'}]}
|
|
)
|
|
], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
|
|
html.Div([
|
|
dcc.Graph(id='x-time-series'),
|
|
dcc.Graph(id='y-time-series'),
|
|
], style={'display': 'inline-block', 'width': '49%'}),
|
|
|
|
html.Div(dcc.Slider(
|
|
id='crossfilter-year--slider',
|
|
min=df['Year'].min(),
|
|
max=df['Year'].max(),
|
|
value=df['Year'].max(),
|
|
marks={str(year): str(year) for year in df['Year'].unique()},
|
|
step=None
|
|
), style={'width': '49%', 'padding': '0px 20px 20px 20px'})
|
|
])
|
|
|
|
|
|
@app.callback(
|
|
dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),
|
|
[dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
|
|
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
|
|
dash.dependencies.Input('crossfilter-xaxis-type', 'value'),
|
|
dash.dependencies.Input('crossfilter-yaxis-type', 'value'),
|
|
dash.dependencies.Input('crossfilter-year--slider', 'value')])
|
|
def update_graph(xaxis_column_name, yaxis_column_name,
|
|
xaxis_type, yaxis_type,
|
|
year_value):
|
|
dff = df[df['Year'] == year_value]
|
|
|
|
return {
|
|
'data': [dict(
|
|
x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
|
|
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
|
|
text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
|
|
customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
|
|
mode='markers',
|
|
marker={
|
|
'size': 25,
|
|
'opacity': 0.7,
|
|
'color': 'orange',
|
|
'line': {'width': 2, 'color': 'purple'}
|
|
}
|
|
)],
|
|
'layout': dict(
|
|
xaxis={
|
|
'title': xaxis_column_name,
|
|
'type': 'linear' if xaxis_type == 'Linear' else 'log'
|
|
},
|
|
yaxis={
|
|
'title': yaxis_column_name,
|
|
'type': 'linear' if yaxis_type == 'Linear' else 'log'
|
|
},
|
|
margin={'l': 40, 'b': 30, 't': 10, 'r': 0},
|
|
height=450,
|
|
hovermode='closest'
|
|
)
|
|
}
|
|
|
|
|
|
def create_time_series(dff, axis_type, title):
|
|
return {
|
|
'data': [dict(
|
|
x=dff['Year'],
|
|
y=dff['Value'],
|
|
mode='lines+markers'
|
|
)],
|
|
'layout': {
|
|
'height': 225,
|
|
'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},
|
|
'annotations': [{
|
|
'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',
|
|
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
|
|
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
|
|
'text': title
|
|
}],
|
|
'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},
|
|
'xaxis': {'showgrid': False}
|
|
}
|
|
}
|
|
|
|
|
|
@app.callback(
|
|
dash.dependencies.Output('x-time-series', 'figure'),
|
|
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
|
|
dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
|
|
dash.dependencies.Input('crossfilter-xaxis-type', 'value')])
|
|
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
|
|
country_name = hoverData['points'][0]['customdata']
|
|
dff = df[df['Country Name'] == country_name]
|
|
dff = dff[dff['Indicator Name'] == xaxis_column_name]
|
|
title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)
|
|
return create_time_series(dff, axis_type, title)
|
|
|
|
|
|
@app.callback(
|
|
dash.dependencies.Output('y-time-series', 'figure'),
|
|
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
|
|
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
|
|
dash.dependencies.Input('crossfilter-yaxis-type', 'value')])
|
|
def update_x_timeseries(hoverData, yaxis_column_name, axis_type):
|
|
dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]
|
|
dff = dff[dff['Indicator Name'] == yaxis_column_name]
|
|
return create_time_series(dff, axis_type, yaxis_column_name)
|
|
|
|
|
|
# %% [markdown]
|
|
# Serve the app using `run_server`. Unlike the standard `Dash.run_server` method, the `JupyterDash.run_server` method doesn't block execution of the notebook. It serves the app in a background thread, making it possible to run other notebook calculations while the app is running.
|
|
#
|
|
# This makes it possible to iteratively update the app without rerunning the potentially expensive data processing steps.
|
|
|
|
# %%
|
|
app.run_server(host="0.0.0.0", port=8039)
|
|
|
|
# %% [markdown]
|
|
# By default, `run_server` displays a URL that you can click on to open the app in a browser tab. The `mode` argument to `run_server` can be used to change this behavior. Setting `mode="inline"` will display the app directly in the notebook output cell.
|
|
|
|
# %%
|
|
app.run_server(mode="inline", host="0.0.0.0", port=8040)
|
|
|
|
# %% [markdown]
|
|
# When running in JupyterLab, with the `jupyterlab-dash` extension, setting `mode="jupyterlab"` will open the app in a tab in JupyterLab.
|
|
#
|
|
# ```python
|
|
# app.run_server(mode="jupyterlab")
|
|
# ```
|
|
|
|
# %%
|