mirror of
https://github.com/bluxmit/alnoda-workspaces.git
synced 2024-09-28 23:41:30 +12:00
312 lines
11 KiB
Text
312 lines
11 KiB
Text
|
{
|
||
|
"cells": [
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"# JupyterDash\n",
|
||
|
"The `jupyter-dash` package makes it easy to develop Plotly Dash apps from the Jupyter Notebook and JupyterLab.\n",
|
||
|
"\n",
|
||
|
"Just replace the standard `dash.Dash` class with the `jupyter_dash.JupyterDash` subclass."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from jupyter_dash import JupyterDash"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"import dash\n",
|
||
|
"from dash import dcc\n",
|
||
|
"from dash import html\n",
|
||
|
"import pandas as pd"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"When running in JupyterHub or Binder, call the `infer_jupyter_config` function to detect the proxy configuration."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"JupyterDash.infer_jupyter_proxy_config()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Load and preprocess data"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')\n",
|
||
|
"available_indicators = df['Indicator Name'].unique()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Construct the app and callbacks"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n",
|
||
|
"\n",
|
||
|
"app = JupyterDash(__name__, external_stylesheets=external_stylesheets)\n",
|
||
|
"\n",
|
||
|
"# Create server variable with Flask server object for use with gunicorn\n",
|
||
|
"server = app.server\n",
|
||
|
"\n",
|
||
|
"app.layout = html.Div([\n",
|
||
|
" html.Div([\n",
|
||
|
"\n",
|
||
|
" html.Div([\n",
|
||
|
" dcc.Dropdown(\n",
|
||
|
" id='crossfilter-xaxis-column',\n",
|
||
|
" options=[{'label': i, 'value': i} for i in available_indicators],\n",
|
||
|
" value='Fertility rate, total (births per woman)'\n",
|
||
|
" ),\n",
|
||
|
" dcc.RadioItems(\n",
|
||
|
" id='crossfilter-xaxis-type',\n",
|
||
|
" options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n",
|
||
|
" value='Linear',\n",
|
||
|
" labelStyle={'display': 'inline-block'}\n",
|
||
|
" )\n",
|
||
|
" ],\n",
|
||
|
" style={'width': '49%', 'display': 'inline-block'}),\n",
|
||
|
"\n",
|
||
|
" html.Div([\n",
|
||
|
" dcc.Dropdown(\n",
|
||
|
" id='crossfilter-yaxis-column',\n",
|
||
|
" options=[{'label': i, 'value': i} for i in available_indicators],\n",
|
||
|
" value='Life expectancy at birth, total (years)'\n",
|
||
|
" ),\n",
|
||
|
" dcc.RadioItems(\n",
|
||
|
" id='crossfilter-yaxis-type',\n",
|
||
|
" options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n",
|
||
|
" value='Linear',\n",
|
||
|
" labelStyle={'display': 'inline-block'}\n",
|
||
|
" )\n",
|
||
|
" ], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})\n",
|
||
|
" ], style={\n",
|
||
|
" 'borderBottom': 'thin lightgrey solid',\n",
|
||
|
" 'backgroundColor': 'rgb(250, 250, 250)',\n",
|
||
|
" 'padding': '10px 5px'\n",
|
||
|
" }),\n",
|
||
|
"\n",
|
||
|
" html.Div([\n",
|
||
|
" dcc.Graph(\n",
|
||
|
" id='crossfilter-indicator-scatter',\n",
|
||
|
" hoverData={'points': [{'customdata': 'Japan'}]}\n",
|
||
|
" )\n",
|
||
|
" ], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),\n",
|
||
|
" html.Div([\n",
|
||
|
" dcc.Graph(id='x-time-series'),\n",
|
||
|
" dcc.Graph(id='y-time-series'),\n",
|
||
|
" ], style={'display': 'inline-block', 'width': '49%'}),\n",
|
||
|
"\n",
|
||
|
" html.Div(dcc.Slider(\n",
|
||
|
" id='crossfilter-year--slider',\n",
|
||
|
" min=df['Year'].min(),\n",
|
||
|
" max=df['Year'].max(),\n",
|
||
|
" value=df['Year'].max(),\n",
|
||
|
" marks={str(year): str(year) for year in df['Year'].unique()},\n",
|
||
|
" step=None\n",
|
||
|
" ), style={'width': '49%', 'padding': '0px 20px 20px 20px'})\n",
|
||
|
"])\n",
|
||
|
"\n",
|
||
|
"\n",
|
||
|
"@app.callback(\n",
|
||
|
" dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),\n",
|
||
|
" [dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-xaxis-type', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-yaxis-type', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-year--slider', 'value')])\n",
|
||
|
"def update_graph(xaxis_column_name, yaxis_column_name,\n",
|
||
|
" xaxis_type, yaxis_type,\n",
|
||
|
" year_value):\n",
|
||
|
" dff = df[df['Year'] == year_value]\n",
|
||
|
"\n",
|
||
|
" return {\n",
|
||
|
" 'data': [dict(\n",
|
||
|
" x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],\n",
|
||
|
" y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],\n",
|
||
|
" text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n",
|
||
|
" customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n",
|
||
|
" mode='markers',\n",
|
||
|
" marker={\n",
|
||
|
" 'size': 25,\n",
|
||
|
" 'opacity': 0.7,\n",
|
||
|
" 'color': 'orange',\n",
|
||
|
" 'line': {'width': 2, 'color': 'purple'}\n",
|
||
|
" }\n",
|
||
|
" )],\n",
|
||
|
" 'layout': dict(\n",
|
||
|
" xaxis={\n",
|
||
|
" 'title': xaxis_column_name,\n",
|
||
|
" 'type': 'linear' if xaxis_type == 'Linear' else 'log'\n",
|
||
|
" },\n",
|
||
|
" yaxis={\n",
|
||
|
" 'title': yaxis_column_name,\n",
|
||
|
" 'type': 'linear' if yaxis_type == 'Linear' else 'log'\n",
|
||
|
" },\n",
|
||
|
" margin={'l': 40, 'b': 30, 't': 10, 'r': 0},\n",
|
||
|
" height=450,\n",
|
||
|
" hovermode='closest'\n",
|
||
|
" )\n",
|
||
|
" }\n",
|
||
|
"\n",
|
||
|
"\n",
|
||
|
"def create_time_series(dff, axis_type, title):\n",
|
||
|
" return {\n",
|
||
|
" 'data': [dict(\n",
|
||
|
" x=dff['Year'],\n",
|
||
|
" y=dff['Value'],\n",
|
||
|
" mode='lines+markers'\n",
|
||
|
" )],\n",
|
||
|
" 'layout': {\n",
|
||
|
" 'height': 225,\n",
|
||
|
" 'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},\n",
|
||
|
" 'annotations': [{\n",
|
||
|
" 'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',\n",
|
||
|
" 'xref': 'paper', 'yref': 'paper', 'showarrow': False,\n",
|
||
|
" 'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',\n",
|
||
|
" 'text': title\n",
|
||
|
" }],\n",
|
||
|
" 'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},\n",
|
||
|
" 'xaxis': {'showgrid': False}\n",
|
||
|
" }\n",
|
||
|
" }\n",
|
||
|
"\n",
|
||
|
"\n",
|
||
|
"@app.callback(\n",
|
||
|
" dash.dependencies.Output('x-time-series', 'figure'),\n",
|
||
|
" [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-xaxis-type', 'value')])\n",
|
||
|
"def update_y_timeseries(hoverData, xaxis_column_name, axis_type):\n",
|
||
|
" country_name = hoverData['points'][0]['customdata']\n",
|
||
|
" dff = df[df['Country Name'] == country_name]\n",
|
||
|
" dff = dff[dff['Indicator Name'] == xaxis_column_name]\n",
|
||
|
" title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)\n",
|
||
|
" return create_time_series(dff, axis_type, title)\n",
|
||
|
"\n",
|
||
|
"\n",
|
||
|
"@app.callback(\n",
|
||
|
" dash.dependencies.Output('y-time-series', 'figure'),\n",
|
||
|
" [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n",
|
||
|
" dash.dependencies.Input('crossfilter-yaxis-type', 'value')])\n",
|
||
|
"def update_x_timeseries(hoverData, yaxis_column_name, axis_type):\n",
|
||
|
" dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]\n",
|
||
|
" dff = dff[dff['Indicator Name'] == yaxis_column_name]\n",
|
||
|
" return create_time_series(dff, axis_type, yaxis_column_name)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"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.\n",
|
||
|
"\n",
|
||
|
"This makes it possible to iteratively update the app without rerunning the potentially expensive data processing steps."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"app.run_server()"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"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."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"app.run_server(mode=\"inline\")"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"When running in JupyterLab, with the `jupyterlab-dash` extension, setting `mode=\"jupyterlab\"` will open the app in a tab in JupyterLab.\n",
|
||
|
"\n",
|
||
|
"```python\n",
|
||
|
"app.run_server(mode=\"jupyterlab\")\n",
|
||
|
"```"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": null,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": []
|
||
|
}
|
||
|
],
|
||
|
"metadata": {
|
||
|
"jupytext": {
|
||
|
"formats": "ipynb,py:percent"
|
||
|
},
|
||
|
"kernelspec": {
|
||
|
"display_name": "Python 3",
|
||
|
"language": "python",
|
||
|
"name": "python3"
|
||
|
},
|
||
|
"language_info": {
|
||
|
"codemirror_mode": {
|
||
|
"name": "ipython",
|
||
|
"version": 3
|
||
|
},
|
||
|
"file_extension": ".py",
|
||
|
"mimetype": "text/x-python",
|
||
|
"name": "python",
|
||
|
"nbconvert_exporter": "python",
|
||
|
"pygments_lexer": "ipython3",
|
||
|
"version": "3.7.7"
|
||
|
}
|
||
|
},
|
||
|
"nbformat": 4,
|
||
|
"nbformat_minor": 4
|
||
|
}
|