Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit 0d3d3a2

Browse files
committed
Pair getting_started.ipynb with getting_started.py using Jupytext
1 parent 03cafdc commit 0d3d3a2

File tree

2 files changed

+241
-40
lines changed

2 files changed

+241
-40
lines changed

notebooks/getting_started.ipynb

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
{
1414
"cell_type": "code",
15-
"execution_count": 25,
15+
"execution_count": null,
1616
"metadata": {},
1717
"outputs": [],
1818
"source": [
@@ -21,7 +21,7 @@
2121
},
2222
{
2323
"cell_type": "code",
24-
"execution_count": 26,
24+
"execution_count": null,
2525
"metadata": {},
2626
"outputs": [],
2727
"source": [
@@ -40,7 +40,7 @@
4040
},
4141
{
4242
"cell_type": "code",
43-
"execution_count": 28,
43+
"execution_count": null,
4444
"metadata": {},
4545
"outputs": [],
4646
"source": [
@@ -56,7 +56,7 @@
5656
},
5757
{
5858
"cell_type": "code",
59-
"execution_count": 29,
59+
"execution_count": null,
6060
"metadata": {},
6161
"outputs": [],
6262
"source": [
@@ -73,14 +73,17 @@
7373
},
7474
{
7575
"cell_type": "code",
76-
"execution_count": 30,
76+
"execution_count": null,
7777
"metadata": {},
7878
"outputs": [],
7979
"source": [
8080
"external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n",
8181
"\n",
8282
"app = JupyterDash(__name__, external_stylesheets=external_stylesheets)\n",
8383
"\n",
84+
"# Create server variable with Flask server object for use with gunicorn\n",
85+
"server = app.server\n",
86+
"\n",
8487
"app.layout = html.Div([\n",
8588
" html.Div([\n",
8689
"\n",
@@ -161,7 +164,8 @@
161164
" mode='markers',\n",
162165
" marker={\n",
163166
" 'size': 25,\n",
164-
" 'opacity': 0.5,\n",
167+
" 'opacity': 0.7,\n",
168+
" 'color': 'orange',\n",
165169
" 'line': {'width': 2, 'color': 'purple'}\n",
166170
" }\n",
167171
" )],\n",
@@ -238,17 +242,9 @@
238242
},
239243
{
240244
"cell_type": "code",
241-
"execution_count": 32,
245+
"execution_count": null,
242246
"metadata": {},
243-
"outputs": [
244-
{
245-
"name": "stdout",
246-
"output_type": "stream",
247-
"text": [
248-
"Dash app running on http://127.0.0.1:8889/proxy/8050/\n"
249-
]
250-
}
251-
],
247+
"outputs": [],
252248
"source": [
253249
"app.run_server()"
254250
]
@@ -262,30 +258,9 @@
262258
},
263259
{
264260
"cell_type": "code",
265-
"execution_count": 33,
261+
"execution_count": null,
266262
"metadata": {},
267-
"outputs": [
268-
{
269-
"data": {
270-
"text/html": [
271-
"\n",
272-
" <iframe\n",
273-
" width=\"800\"\n",
274-
" height=\"650\"\n",
275-
" src=\"http://127.0.0.1:8889/proxy/8050/\"\n",
276-
" frameborder=\"0\"\n",
277-
" allowfullscreen\n",
278-
" ></iframe>\n",
279-
" "
280-
],
281-
"text/plain": [
282-
"<IPython.lib.display.IFrame at 0x7f71ccd63fd0>"
283-
]
284-
},
285-
"metadata": {},
286-
"output_type": "display_data"
287-
}
288-
],
263+
"outputs": [],
289264
"source": [
290265
"app.run_server(mode=\"inline\")"
291266
]
@@ -310,6 +285,9 @@
310285
}
311286
],
312287
"metadata": {
288+
"jupytext": {
289+
"formats": "ipynb,py:percent"
290+
},
313291
"kernelspec": {
314292
"display_name": "Python 3",
315293
"language": "python",
@@ -325,7 +303,7 @@
325303
"name": "python",
326304
"nbconvert_exporter": "python",
327305
"pygments_lexer": "ipython3",
328-
"version": "3.7.6"
306+
"version": "3.7.7"
329307
}
330308
},
331309
"nbformat": 4,

notebooks/getting_started.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# ---
2+
# jupyter:
3+
# jupytext:
4+
# formats: ipynb,py:percent
5+
# text_representation:
6+
# extension: .py
7+
# format_name: percent
8+
# format_version: '1.3'
9+
# jupytext_version: 1.4.2
10+
# kernelspec:
11+
# display_name: Python 3
12+
# language: python
13+
# name: python3
14+
# ---
15+
16+
# %% [markdown]
17+
# # JupyterDash
18+
# The `jupyter-dash` package makes it easy to develop Plotly Dash apps from the Jupyter Notebook and JupyterLab.
19+
#
20+
# Just replace the standard `dash.Dash` class with the `jupyter_dash.JupyterDash` subclass.
21+
22+
# %%
23+
from jupyter_dash import JupyterDash
24+
25+
# %%
26+
import dash
27+
import dash_core_components as dcc
28+
import dash_html_components as html
29+
import pandas as pd
30+
31+
# %% [markdown]
32+
# When running in JupyterHub or Binder, call the `infer_jupyter_config` function to detect the proxy configuration.
33+
34+
# %%
35+
JupyterDash.infer_jupyter_config()
36+
37+
# %% [markdown]
38+
# Load and preprocess data
39+
40+
# %%
41+
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
42+
available_indicators = df['Indicator Name'].unique()
43+
44+
# %% [markdown]
45+
# Construct the app and callbacks
46+
47+
# %%
48+
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
49+
50+
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
51+
52+
# Create server variable with Flask server object for use with gunicorn
53+
server = app.server
54+
55+
app.layout = html.Div([
56+
html.Div([
57+
58+
html.Div([
59+
dcc.Dropdown(
60+
id='crossfilter-xaxis-column',
61+
options=[{'label': i, 'value': i} for i in available_indicators],
62+
value='Fertility rate, total (births per woman)'
63+
),
64+
dcc.RadioItems(
65+
id='crossfilter-xaxis-type',
66+
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
67+
value='Linear',
68+
labelStyle={'display': 'inline-block'}
69+
)
70+
],
71+
style={'width': '49%', 'display': 'inline-block'}),
72+
73+
html.Div([
74+
dcc.Dropdown(
75+
id='crossfilter-yaxis-column',
76+
options=[{'label': i, 'value': i} for i in available_indicators],
77+
value='Life expectancy at birth, total (years)'
78+
),
79+
dcc.RadioItems(
80+
id='crossfilter-yaxis-type',
81+
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
82+
value='Linear',
83+
labelStyle={'display': 'inline-block'}
84+
)
85+
], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
86+
], style={
87+
'borderBottom': 'thin lightgrey solid',
88+
'backgroundColor': 'rgb(250, 250, 250)',
89+
'padding': '10px 5px'
90+
}),
91+
92+
html.Div([
93+
dcc.Graph(
94+
id='crossfilter-indicator-scatter',
95+
hoverData={'points': [{'customdata': 'Japan'}]}
96+
)
97+
], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
98+
html.Div([
99+
dcc.Graph(id='x-time-series'),
100+
dcc.Graph(id='y-time-series'),
101+
], style={'display': 'inline-block', 'width': '49%'}),
102+
103+
html.Div(dcc.Slider(
104+
id='crossfilter-year--slider',
105+
min=df['Year'].min(),
106+
max=df['Year'].max(),
107+
value=df['Year'].max(),
108+
marks={str(year): str(year) for year in df['Year'].unique()},
109+
step=None
110+
), style={'width': '49%', 'padding': '0px 20px 20px 20px'})
111+
])
112+
113+
114+
@app.callback(
115+
dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),
116+
[dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
117+
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
118+
dash.dependencies.Input('crossfilter-xaxis-type', 'value'),
119+
dash.dependencies.Input('crossfilter-yaxis-type', 'value'),
120+
dash.dependencies.Input('crossfilter-year--slider', 'value')])
121+
def update_graph(xaxis_column_name, yaxis_column_name,
122+
xaxis_type, yaxis_type,
123+
year_value):
124+
dff = df[df['Year'] == year_value]
125+
126+
return {
127+
'data': [dict(
128+
x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
129+
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
130+
text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
131+
customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],
132+
mode='markers',
133+
marker={
134+
'size': 25,
135+
'opacity': 0.7,
136+
'color': 'orange',
137+
'line': {'width': 2, 'color': 'purple'}
138+
}
139+
)],
140+
'layout': dict(
141+
xaxis={
142+
'title': xaxis_column_name,
143+
'type': 'linear' if xaxis_type == 'Linear' else 'log'
144+
},
145+
yaxis={
146+
'title': yaxis_column_name,
147+
'type': 'linear' if yaxis_type == 'Linear' else 'log'
148+
},
149+
margin={'l': 40, 'b': 30, 't': 10, 'r': 0},
150+
height=450,
151+
hovermode='closest'
152+
)
153+
}
154+
155+
156+
def create_time_series(dff, axis_type, title):
157+
return {
158+
'data': [dict(
159+
x=dff['Year'],
160+
y=dff['Value'],
161+
mode='lines+markers'
162+
)],
163+
'layout': {
164+
'height': 225,
165+
'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},
166+
'annotations': [{
167+
'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',
168+
'xref': 'paper', 'yref': 'paper', 'showarrow': False,
169+
'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',
170+
'text': title
171+
}],
172+
'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},
173+
'xaxis': {'showgrid': False}
174+
}
175+
}
176+
177+
178+
@app.callback(
179+
dash.dependencies.Output('x-time-series', 'figure'),
180+
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
181+
dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
182+
dash.dependencies.Input('crossfilter-xaxis-type', 'value')])
183+
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
184+
country_name = hoverData['points'][0]['customdata']
185+
dff = df[df['Country Name'] == country_name]
186+
dff = dff[dff['Indicator Name'] == xaxis_column_name]
187+
title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)
188+
return create_time_series(dff, axis_type, title)
189+
190+
191+
@app.callback(
192+
dash.dependencies.Output('y-time-series', 'figure'),
193+
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
194+
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
195+
dash.dependencies.Input('crossfilter-yaxis-type', 'value')])
196+
def update_x_timeseries(hoverData, yaxis_column_name, axis_type):
197+
dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]
198+
dff = dff[dff['Indicator Name'] == yaxis_column_name]
199+
return create_time_series(dff, axis_type, yaxis_column_name)
200+
201+
202+
# %% [markdown]
203+
# 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.
204+
#
205+
# This makes it possible to iterativly update the app without rerunning the potentially expensive data processing steps.
206+
207+
# %%
208+
app.run_server()
209+
210+
# %% [markdown]
211+
# 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.
212+
213+
# %%
214+
app.run_server(mode="inline")
215+
216+
# %% [markdown]
217+
# When running in JupyterLab, with the `jupyterlab-dash` extension, setting `mode="jupyterlab"` will open the app in a tab in JupyterLab.
218+
#
219+
# ```python
220+
# app.run_server(mode="jupyterlab")
221+
# ```
222+
223+
# %%

0 commit comments

Comments
 (0)