Skip to content

Instantly share code, notes, and snippets.

@xhluca
Last active November 5, 2020 16:43
Show Gist options
  • Save xhluca/6a6ced8abf4fa5c2eb14738cba4a75f3 to your computer and use it in GitHub Desktop.
Save xhluca/6a6ced8abf4fa5c2eb14738cba4a75f3 to your computer and use it in GitHub Desktop.
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
available_indicators = df['Indicator Name'].unique()
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(
id='xaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Fertility rate, total (births per woman)'
),
dcc.RadioItems(
id='xaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],
style={'width': '48%', 'display': 'inline-block'}),
html.Div([
dcc.Dropdown(
id='yaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Life expectancy at birth, total (years)'
),
dcc.RadioItems(
id='yaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],style={'width': '48%', 'float': 'right', 'display': 'inline-block'})
]),
dcc.Graph(id='indicator-graphic'),
dcc.Slider(
id='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
)
])
@app.callback(
Output('indicator-graphic', 'figure'),
[Input('xaxis-column', 'value'),
Input('yaxis-column', 'value'),
Input('xaxis-type', 'value'),
Input('yaxis-type', 'value'),
Input('year--slider', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
fig.update_xaxes(title=xaxis_column_name,
type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name,
type='linear' if yaxis_type == 'Linear' else 'log')
return fig
if __name__ == '__main__':
app.run_server(debug=True)
import inspect
import types
import sys
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash
from dash.dependencies import Input, Output, State
class Row(dbc.Row):
def add(*components):
"""
Add one or more components as new column to this row.
"""
new_cols = [dbc.Col(c) for c in components]
self.children.extend(new_cols)
def append(component):
"""
Directly append single new component to the list of children
"""
self.children.append(component)
class UserInterface:
def __init__(self, bootstrap=True, external_stylesheets=[]):
if bootstrap:
external_stylesheets.append(dbc.themes.BOOTSTRAP)
# Define the app and layout
self.app = dash.Dash(external_stylesheets=external_stylesheets)
self.layout = dbc.Container([], fluid=True)
self.app.layout = self.layout
self.ids = []
def __check_id(self, component):
if not hasattr(component, 'id'):
component.id = f"component-{len(self.ids)}"
if component.id in self.ids:
raise Exception("This component already exists. Please add a new component.")
self.ids.append(component.id)
if hasattr(component, 'children') and type(component.children) is list:
for child in component.children:
if type(child) is not str:
self.__check_id(child)
def add(self, *components):
"""
Add a component to the current layout
"""
for c in components:
self.__check_id(c)
self.layout.children.extend(components)
return self.layout.children
def add_row(self, *components):
"""
Add a new row with all the components specified.
"""
for c in components:
self.__check_id(c)
row = Row([dbc.Col(c) for c in components])
self.layout.children.append(row)
return row
def add_func(self, fn):
"""
Add an interaction between various components
"""
inputs = []
states = []
sig = inspect.signature(fn)
mode = 'input'
for param in sig.parameters.values():
annot = param.annotation
# Convert string into Inputs
if type(annot) is str:
annot = I(annot)
# If it's an input, then we add it to the inputs list
if type(annot) is Input:
if mode == 'state':
raise Exception("Inputs and States need to be separate")
inputs.append(annot)
elif type(annot) is State:
states.append(annot)
mode = 'state'
else:
raise Exception("The function annotation can only be an Input or State. Please use I and S.")
if type(sig.return_annotation) is str:
outputs = O(sig.return_annotation)
elif type(sig.return_annotation) is O:
outputs = sig.return_annotation
else:
outputs = [O(annot) for annot in sig.return_annotation]
self.app.callback(outputs, inputs)(fn)
def run(self, **kwargs):
self.app.run_server(**kwargs)
def __extract_content(st):
var, attr = st.split(".")
component = getattr(sys.modules['__main__'], var)
return component.id, attr
def I(st):
return Input(*__extract_content(st))
def S(st):
return State(*__extract_content(st))
def O(st):
return Output(*__extract_content(st))
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_express as dx
from dash_express import S
import plotly.express as px
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
ui = dx.UserInterface(bootstrap=True)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
available_indicators = df['Indicator Name'].unique()
# Define the components
xaxis_column = dcc.Dropdown(
options=[{'label': i, 'value': i} for i in available_indicators],
value='Fertility rate, total (births per woman)'
)
xaxis_type = dcc.RadioItems(
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
yaxis_column = dcc.Dropdown(
options=[{'label': i, 'value': i} for i in available_indicators],
value='Life expectancy at birth, total (years)'
)
yaxis_type = dcc.RadioItems(
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
year_slider = dcc.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
)
indicator_graphic = dcc.Graph()
ui.add_row(xaxis_column, yaxis_column)
ui.add_row(xaxis_type, yaxis_type)
ui.add(indicator_graphic)
ui.add(year_slider)
def update_graph(
xaxis_column_name: 'xaxis_column.value',
yaxis_column_name: 'yaxis_column.value',
xaxis_type: 'xaxis_type.value',
yaxis_type: 'yaxis_type.value',
year_value: 'year_slider.value'
) -> 'indicator_graphic.figure':
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
fig.update_xaxes(title=xaxis_column_name,
type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name,
type='linear' if yaxis_type == 'Linear' else 'log')
return fig
ui.add_func(update_graph)
if __name__ == '__main__':
ui.run(debug=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment