Create a dynamic dashboard using Plotly, Dash and Python (absolute beginner guide)

OOLy
5 min readJun 24, 2020

--

If you want to create a beautiful dashboard using Python, Dash is amazing! But it can be difficult to dig into it, especially for people without a dev background. Here an example to learn how to create a very simple app with a graph that will be update through dropdown lists.

In order to follow this tutorial, you will need to import the following libraries:

import pandas as pd
import numpy as np
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go

First, we will load data. We will use the famous iris dataset.

file_name = "https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv"
df = pd.read_csv(file_name)

We will now create a very simple graph using plotly. This graph will show us the average sepal_length by species. We first group data by species and compute the mean, then create a simple barplot.

function=’mean’
group_df=df.groupby([‘species’]).agg(function).reset_index()
var='petal_length'
fig = go.Figure([go.Bar(x=group_df.species, y=group_df[var])])
fig.update_layout(title="My initial graph ({},{})".format(var,function))
fig.show()

You should get something like this:

Initial graph

Ok. Now that we have our graph, we will create our app. First, we initialize the app. At this step, we can use a template to initialize the app. We will use the boostrap template. Dash boostrap provides components that makes the app creation a lot easier.

my_simple_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

Ok. Now, we need to create the layout. I want something like this:

App layout

I will create my layout:

my_simple_app.layout = html.Div(
children=[
#First row with title
dbc.Row(html.H1("My app name"), justify="center", align="center", className="h-50"),

#Thematic break
html.Hr(),

#Second row
dbc.Row(children=[
#First column: dropdown
dbc.Col(html.Span("It will be my dropdown"),width=3),
#Second column: graph
dbc.Col(html.Span("It will be my graph"),width=9)
])
])

To run the app, you simply need to run these lines: the app will run on a development server.

if __name__ == "__main__":
my_simple_app.run_server()

You should get this message:

Dash is running on http://127.0.0.1:8050/

Warning: This is a development server. Do not use app.run_server in production, use a production WSGI server like gunicorn instead.

If you click on the link, you should see your app in the browser. It should be something like this:

App layout

For the width of each column, consider that we have a 12 columns grids in the second row. So if we want to have dropdown: 1/4 of the page, and graph: 3/4 of the page, we need to set up width=3 (1/4*12) for the dropdown menu column, and width=9 (3/4*12) for the graph column.

Now we will add our dropdown menu, and our graph.

my_simple_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])#Create layout
my_simple_app.layout = html.Div(

children=[
#Title
dbc.Row(html.H1("My simple name"), justify="center", align="center", className="h-50"),

#Thematic break
html.Hr(),

dbc.Row(children=[
#First column
dbc.Col(
children=[
#Message above the dropdown menu
html.Span("Select a value"),

'''Dropdown menu populated with the Iris df columns values (except species):
sepal_length,sepal_width,petal_length,petal_width'''
dcc.Dropdown(id='my_dropdown',
options=[{'label': i, 'value': i} for i in group_df.columns[1:]]),
],
width=3),

#Second column: I add my graph
dbc.Col(dcc.Graph(id="my_graph",figure=fig),width=9)
])
])
#Run app
if __name__ == "__main__":
my_simple_app.run_server()

You should see something like this:

By the way, if you click on the dropdown menu, you will se that the graph is not updated accordingly. So you need to create a callback. The callback will make you app interactive, taking input values and providing an output. In our case, the input will be the dropdown menu value, and the output an updated graph.

@my_simple_app.callback(Output("my_graph",'figure'),
[Input('my_dropdown', 'value')]
def update_graph(dropdown_value):
group_df=df.groupby(['species']).agg('mean').reset_index()
fig1 = go.Figure([go.Bar(x=group_df.species,
y=group_df[dropdown_value])])
fig1.update_layout(title="Mean {} by species".format(dropdown_value))

return fig1

Ok, so if we put all together, we got this:

my_simple_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])#Create layout
my_simple_app.layout = html.Div(

children=[
#Title
dbc.Row(html.H1("My simple app"), justify="center", align="center", className="h-50"),

#Thematic break
html.Hr(),

dbc.Row(children=[
#First column
dbc.Col(
children=[
#Message above the dropdown menu
html.Span("Select a value"),

#Dropdown menu populated with the Iris df column values (except species):sepal_length,sepal_width,petal_length,
#petal_width
dcc.Dropdown(id='my_dropdown',
options=[{'label': i, 'value': i} for i in group_df.columns[1:]]),
],
width=3),

#Second column: I add my graph
dbc.Col(dcc.Graph(id="my_graph",figure=fig),width=9)
])
])
@my_simple_app.callback(Output("my_graph",'figure'),
[Input('my_dropdown', 'value')])
def update_graph(dropdown_value):
group_df=df.groupby(['species']).agg('mean').reset_index()
fig1 = go.Figure([go.Bar(x=group_df.species,
y=group_df[dropdown_value])])
fig1.update_layout(title="Mean {} by species".format(dropdown_value))

return fig1
#Run app
if __name__ == "__main__":
my_simple_app.run_server()

Give it a try. Your app should update the graph dynamically.

And what if I got more inputs? Two dropdowns by example, one to choose the variable and one to choose the function to apply? You will need to add inputs in the callback. When you create you function, you will provide the two arguments. Keep the same order than the one provided in the inputs! Here an example:

my_simple_app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])my_simple_app.layout = html.Div(
children=[
dbc.Row(html.H1("My simple app"), justify="center", align="center", className="h-50"),

html.Hr(),

dbc.Row(children=[
dbc.Col(
children=[
html.Span("Select a value"),
dcc.Dropdown(id='my_dropdown',
options=[{'label': i, 'value': i} for i in group_df.columns[1:]]),
html.Span("Select a function"),
dcc.Dropdown(id='my_dropdown2',
options=[{'label': 'Average', 'value': 'mean'},
{'label': 'Maximum', 'value': 'max'}]),
],
width=3),

dbc.Col(dcc.Graph(id="my_graph",figure=fig),width=9)
])
])
#Add callback
@my_simple_app.callback(Output("my_graph",'figure'),
[Input('my_dropdown', 'value'),#First input
Input('my_dropdown2', 'value')])#Second imput
def update_graph(dropdown_value,dropdown2_value):
group_df=df.groupby(['species']).
agg(dropdown2_value).reset_index()
fig1 = go.Figure([go.Bar(x=group_df.species,
y=group_df[dropdown_value])])
main='Maximum' if dropdown2_value=='max' else 'Average'
fig1.update_layout(title="My updated graph: {} {} by species".format(main,dropdown_value))
return fig1
#Run app
if __name__ == "__main__":
my_simple_app.run_server()

You should get something like this.

App with two inputs

So this was a small introduction to Dash, and Dash callback. I hope it has been usefull!

--

--