nc-state logo
Dash
Christopher G. Healey

Introduction

This module will introduce you to Dash, a framework that integrates Python, plotly, and Flask to construct web-based dashboard in Python. We will work through a number of simple examples of loading data, visualizing it with Python's built-in graphics operations, then integrating those visualizations into an interactive Dash web dashboard, which can be viewed online by anyone with a web browser.

In order to fully understand how to use Dash, you need to also understand basic HTML5, plotly, and Python. HTML5 is used to define the layout of the elements in your dashboard. plotly is used to construct graphical components (visualizations) within your dashboard. Finally, Python ties everything together to control used interaction and changes on the dashboard based on the user's interaction choices.

Setup

In order to use the Dash examples in this tutorial, you will need to add the dash Python package. To do this, run the Anaconda Prompt as an administrator, and at the command line enter the following.

(base) C:> conda install -c conda-forge dash

HTML5

HTML (Hypertext Markup Language) is used to create the layout (sections, paragraphs, headings, links, and so on) for web pages and web applications. In 1989, Tim Berners-Lee developed the concept for HTML while working in the computer services section of CERN, the European Laboratory for Particle Physics in Geneva. Berners-Lee suggested that, rather than downloading research documents as files from individual computers, you could instead link to the text of the files themselves. This would form a cross-reference system between research documents. From one research paper, you could display the content of another paper that held relevant text or diagrams. The cross-reference system could be seen as a web of information held across computers throughout the world.

Berners-Lee's idea was followed by Hypercard, a filing-card type application for the Apple Macintosh built by Bill Atkinson. The main limitation of this system was that hypertext jumps could only be made on files held on the same computer. In the mid 90s, the Internet developed the Domain Name System (DNS) that mapped easy-to-remember names like www.ncsu.edu to their corresponding IP addresses, a unique locator for a given domain. Berners-Lee built on this to develop the HyperText Transfer Protocol (HTTP) and the HyperText Markup Language (HTML), defining a method to transmit HTML documents between computers using HTTP.

Up to this point, HTML was mostly a research-centered idea. In 1992, the National Center for Supercomputer Applications (NCSA) at the University of Illinois-Urbana Champaign (UIUC) developed the first web browser, Mosaic. Mosaic was released on Sun Microsystems workstations in 1993. This was followed by Netscape in 1994, built by Marc Andreessen and Jim Clark. Next came Internet Explorer in 1995, Google Chrome in 1998, and the Mozilla foundation (the precursor to Firefox) in 2003.

It is beyond the scope of our module to discuss all aspects of HTML, and for Dash, it is not required, since all we're concerned about is how different HTML tags affect the layout of information. To this end, we list below some of the common HTML tags important to Dash and their corresponding purpose. A complete list of tags is available online.

•  div
A section of a document
•  H1, H2, H3, H4, H5, H6
Headers in decreasing priority
•  p
A paragraph
•  span
A block of text, usually with properties different from the surrounding text
•  br
A line break
•  ol
An ordered list
•  ul
An unordered list
•  li
A list item in an ordered or unordered list
•  input
An input component, where the type of input is defined by the input's type attribute: input box, checkboxes, radiobuttons, and so on.
•  table
A table
•  tbody
Definition of where a table's body starts and stops
•  tr
A single table row
•  td
A single table column value (embedded within a table row defined with <tr>)
•  thead
Definition of where a table's header starts and stops
•  th
A single table column value in the table's header (embedded within a table row defined with <tr>)
•  tfoot
Definition of where a table's footer starts and stops
•  tf
A single table column value in the table's footer (embedded within a table row defined with <tr>)

Here is a very simple web page demonstrating some of these tags in use.

<html>
<body>

<div>
<p>This is a paragraph</p>
<p>This is a paragraph <span style='font-size: 1.25em'>with a larger font</span> inside it.</p>

<ol>
<li>This is the first item of a list ordered 1,2,...</li>
<li>This is the second item</li>
</ol>

<ul>
<li>This is a bullet list (unordered)</li>
</ul>

<table style='border-collapse: collapse;'>
<tr>
<th style='border: 1px solid black;'>Header Column One</th>
<th style='border: 1px solid black;'>Header Column Two</th>
</tr>

<tr>
<td style='border: 1px solid black;'>Normal Table Cell One</td>
<td style='border: 1px solid black;'>Normal Table Cell Two</td>
</tr>
</table>
</div>

</body>
</html>
The result of executing the HTML code in a web browser
html-ex

plotly

plotly is a Python library designed to provide a wide range of standard charts. plotly is built on the Plotly JavaScript library, and is meant to support creation of interactive web-based visualizations. As an example, here is a plotly program that can be run in a Jupyter notebook.

>>> import plotly.graph_objects as go
>>>
>>> fig = go.Figure( data=go.Bar( y=[2,3,1] ) )
>>> fig.show()

This produces the following simple bar chart.

A plotly bar chart
plotly-01

Charts

Although plotly can be used directly to create charts, it is generally recommended to start with Plotly Express, which sits on top of plotly and allows entire figures to be created in a more efficient fashion. Plotly Express functions use plotly graph objects internally and return a plotly.graph_object.figure instance. plotly's documentation shows examples of how to build a graph in Plotly Express, followed by code to build the equivalent graph in plotly. Since Plotly Express returns a basic plotly figure instance, modifications of the Plotly Express charts can be done in way that is identical to how plotly's charts are changed. We will use Plotly Express code whenever it is possible.

To provide you with examples of how to build the basic charts and graphs you will want to use in your dashboards, we provide a collection of code and resulting graphs that Plotly Express supports.

Bar Graph

Plotly Express supports bar charts, stacked bar charts, and side-by-side bar charts.

>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = px.data.medals_long()
>>>
>>> canada = df[ df[ 'nation' ] == 'Canada' ]
>>> fig = px.bar( canada, x='medal', y='count' )
>>> fig.show()
>>>
>>> fig = px.bar( df, x='nation', y='count', color='medal' )
>>> fig.show()
>>>
>>> fig = px.bar( df, x='nation', y='count', color='medal', barmode='group' )
>>> fig.show()
A plotly bar chart, stacked bar chart, and side-by-side bar chart
plotly-02

Line Graph

Plotly Express supports line graphs with one or more lines.

>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> canada = px.data.gapminder().query( 'country=="Canada"' )
>>> US = px.data.gapminder().query( 'country=="United States"' )
>>>
>>> fig = px.line( canada, x='year', y='lifeExp' )
>>> fig.show()
>>>
>>> df = pd.concat( [ canada, US ] )
>>> fig = px.line( df, x='year', y='lifeExp', color='country' )
>>> fig.show()
>>>
>>> fig.update_traces( mode='markers+lines' )
>>> fig.show()
A plotly single trace line chart, double trace line chart, and double trace line chart with markers at each sample point bar chart
plotly-03

Notice the command fig.update_traces( 'markers+lines' ) that adds markers to the traces at each sample point, in addition to the default connected lines. This is an example of a plotly command used to augment a figure returned from Plotly Express. You might wonder what a "trace" is. From the plotly documentation, "a trace is just the name we give a collection of data and the specifications of which we want that data to be plotted." In other words, a trace is a visualization, or a component of a visualization. If you're curious, the same two-trace line chart with markers would be created in plotly as follows.

>>> import plotly.express as px
>>> import plotly.graph_objects as go
>>> import pandas as pd
>>>
>>> canada = px.data.gapminder().query( 'country=="Canada"' )
>>> US = px.data.gapminder().query( 'country=="United States"' )
>>>
>>> fig = go.Figure()
>>> fig.add_trace( go.Scatter( x=canada[ 'year' ], y=canada[ 'lifeExp' ], mode='markers+lines', name='Canada' ) )
>>> fig.add_trace( go.Scatter( x=US[ 'year' ], y=US[ 'lifeExp' ], mode='markers+lines', name='United States' ) )
>>>
>>> fig.show()

Pie Chart

Plotly Express supports standard line charts.

>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = px.data.gapminder().query( 'year==2007' ).query( 'continent=="Americas"' )
>>> df_small = df[ df[ 'pop' ] < 5000000 ]
>>>
>>> fig = px.pie( df_small, values='pop', names='country' )
>>> fig.show()
A plotly pie charts, default and augmented with update_traces
plotly-04

The pie chart can be improved using fig.update_traces, for example, to order slices largest-to-smallest clockwise from the top of the chart, and to change the colours to ones more appropriate for a discrete six-value sequence.

>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = px.data.gapminder().query( 'year==2007' ).query( 'continent=="Americas"' )
>>> df_small = df[ df[ 'pop' ] < 5000000 ]
>>>
>>> fig = px.pie( df_small, values='pop', names='country' )
>>>
>>> colours=[ '#edf8fb', '#8856a7', '#8c96c6', '#bfd3e6', '#810f7c', '#9ebcda' ]
>>> fig.update_traces( direction='clockwise', sort=True, marker_colors=colours, textinfo='label+percent', showlegend=False )
>>> fig.show()

Scatterplot

Plotly Express supports scatterplots, including linear and non-linear locally weighted scatterplot smoothing (LOWESS) trendlines.

>>> import plotly.express as px
>>>
>>> fig = px.scatter( x=[0,1,2,3,4], y=[0,1,4,9,16] )
>>> fig.show()
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = px.data.iris()
>>>
>>> fig = px.scatter( df, x='sepal_width', y='sepal_length', color='species', size='petal_length', trendline='ols' )
>>> fig.show()
A plotly scatterplot, scatterplot with ordinary least squares trend lines, and scatterplot with non-linear cubic trend lines
plotly-05

More sophisticated non-linear trendlines (e.g., a cubic polynomial fit) can be constructed and added using numpy and plotly.

>>> import plotly.express as px
>>> import plotly.graph_objects as go
>>> import numpy as np
>>> import pandas as pd
>>>
>>> def cubic_fit( df, type, prop_x, prop_y ):
... x = df[ df[ 'species' ] == type ][ prop_x ].to_numpy()
... y = df[ df[ 'species' ] == type ][ prop_y ].to_numpy()
... z = np.polyfit( x, y, 3 )
... f = np.poly1d( z )
...
... x_fit = np.linspace( x.min(), x.max(), 100 )
... y_fit = f( x_fit )
... return (x_fit,y_fit)
>>>
>>> df = px.data.iris()
>>> fig = px.scatter( df, x='sepal_width', y='sepal_length', color='species', size='petal_length' )
>>>
>>> x_fit_setosa,y_fit_setosa = cubic_fit( df, 'setosa', 'sepal_width', 'sepal_length' )
>>> x_fit_versicolor,y_fit_versicolor = cubic_fit( df, 'versicolor', 'sepal_width', 'sepal_length' )
>>> x_fit_virginica,y_fit_virginica = cubic_fit( df, 'virginica', 'sepal_width', 'sepal_length' )
>>>
>>> fig.add_trace( go.Scatter( x=x_fit_setosa, y=y_fit_setosa, name='Setosa Fit', marker=go.Marker( color='rgb(100,100,255)' ) ) )
>>> fig.add_trace( go.Scatter( x=x_fit_versicolor, y=y_fit_versicolor, name='Versicolor Fit', marker=go.Marker( color='rgb(255,100,100)' ) ) )
>>> fig.add_trace( go.Scatter( x=x_fit_virginica, y=y_fit_virginica, name='Virginica Fit', marker=go.Marker( color='rgb(100,200,100)' ) ) )
>>> fig.show()

Histogram

Plotly Express supports histograms for both continuous and categorical (discrete) data.

>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = px.data.tips()
>>> fig = px.histogram( df, x='total_bill' )
>>> fig.show()
>>>
>>> fig = px.histogram( df, x='day' )
>>> fig.show()
>>>
>>> df[ 'tip_pct' ] = df[ 'tip' ] / df[ 'total_bill' ] * 100.0
>>> fig = px.histogram( df, x='tip_pct', color='smoker', marginal='box' )
>>> fig.show()
A plotly continuous data histogram, categorical data histogram, and continuous data histogram coloured by smoker, with a boxplot by smoker shown as a marginal visualization above the histogram
plotly-06

Boxplot

Plotly Express supports boxplots, including outlier visualization and different methods of quartile computation.

>>> import plotly.express as px
>>>
>>> df = px.data.iris()
>>> fig = px.box( df, y='sepal_width' )
>>> fig.show()
>>>
>>> fig = px.box( df, y='sepal_width', color='species' )
>>> fig.show()
>>>
>>> df = px.data.tips()
>>> fig = px.box( df, x='time', y='total_bill', color='smoker', notched=True )
>>> fig.show()
A plotly box plot with outliers, boxplot coloured by category, and boxplot by multiple categories, and notched boxplots used to indicate significant differences
plotly-07

Maps

plotly supports maps using Mapbox, a map and location service for developers. Some plotly maps may require a Mapbox account and public Mapbox access token. Others do not. The documentation identifies when a public token is needed.

To start, we show an example of a choropleth map visualizing unemployment rate by continental US county. Choropleth maps require two arguments.

  1. A GeoJSON-formatted geometry variable, where each geographic feature has a specific ID field: the field identifier.
  2. A list of values to visualize, where each value has a specific field identifier identical to the type used in the GeoJSON data.

The GeoJSON data is passed as a geojson argument, and the data to visualize within the geography is passed as the color argument of a px.choropleth_mapbox object. The geography and value to visualize are mapped to one another using the field identifier, which must exist in both the GeoJSON and data structures. In our example, a county's Federal Information Processing Standards (FIPS) code is used as the field identifier, and exists in both the GeoJSON object and the unemployment data frame.

>>> import json
>>> import pandas as pd
>>> import plotly.express as px
>>> from urllib.request import urlopen
>>>
>>> with urlopen( 'https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json' ) as response:
... counties = json.load( response )
>>>
>>> df = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv', dtype={ 'fips': str } )
>>>
>>> fig = px.choropleth_mapbox(
>>>   df,
>>>   geojson=counties,
>>>   locations='fips',
>>>   color='unemp',
>>>   color_continuous_scale='Viridis',
>>>   range_color=(0,12),
>>>   mapbox_style='carto-positron',
>>>   zoom=3,
>>>   center={'lat': 37.0902, 'lon': -95.7129},
>>>   opacity=0.5,
>>>   labels={'unemp': 'unemployment rate' }
>>> )
>>> fig.update_layout( margin={ 'r': 0, 't': 0, 'l': 0, 'b': 0 } )
>>> fig.show()
A plotly choropleth map of unemployment by US country
plotly-08

Mapbox maps do not support changing the map projection. To do this, outline-based Geo maps must be used instead. A Geo map's projection type can be changed with fig.update_geos( projection_type='mercator' ). Numerous projects types are supported.

The example code below creates a population proportional dot map for the United States using a Geo map created with the go.Scattergeo plotly command.

>>> import plotly.graph_objects as go
>>> import numpy as np
>>> import pandas as pd
>>>
>>> df = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/2014_us_cities.csv' )
>>> scale = 5000
>>>
>>> fig = go.Figure()
>>>
>>> fig.add_trace( go.Scattergeo(
... lon=df[ 'lon' ],
... lat=df[ 'lat' ],
... marker = {
...   'size': df[ 'pop' ] / scale,
...   'colorscale': 'Viridis',
...   'color': np.log2( df[ 'pop' ] ),
...   'line_color': 'rgb(200,200,200)',
...   'line_width': 0.5,
...   'sizemode': 'area',
...   'colorbar': {
...     'title': 'Pop',
...     'titleside': 'top'
...   }
... }
... ) )
>>>
>>> fig.update_layout(
... geo = dict(
...   showland=True,
...   lataxis = dict( range=[ 18, 51 ] ),
...   lonaxis = dict( range=[ -124, -66 ] ),
...   countrycolor = 'rgb(217,217,217)',
...   countrywidth = 0.5
... )
... )
>>>
>>> fig.update_geos( projection_type='albers usa' )
>>>
>>> fig.show()
A plotly proportional dot map of city location and population
plotly-09

Dash

Finally, we can start our discussion of Dash dashboards. As we initially noted, Dash dashboards are made up of two parts: a layout that defines how elements are position on a web page, and interactivity that defines how users can manipulate elements of the web page to change what it displays.

As a simple example, below is a Dash dashboard that displays a side-by-side bar graph of the number of fruit associated with the cities SF and Montreal. Dash dashboards are normally run as stand-alone python code, and not within a Jupyter notebook.

  1. Enter the Python code for the dashboard in an external file.
  2. Use python to execute the file.
  3. By default, Dash places the dashboard at http://127.0.0.1:8050/, which is shorthand for the computer being used to access the address (i.e., your computer, or localhost in technical terms) over port 8050. By visiting that URL, you can view and interact with the dashboard defined in your Python file.
>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> df = pd.DataFrame( {
>>>   'Fruit': ['Apples','Oranges','Bananas','Apples','Oranges','Bananas'],
>>>   'Amount': [4,1,2,2,4,5],
>>>   'City': ['SF','SF','SF','Montreal','Montreal','Montreal']
>>> } )
>>>
>>> fig = px.bar( df, x='Fruit', y='Amount', color='City', barmode='group' )
>>>
>>> app.layout = html.Div(
>>>   children=[
>>>     html.H1( children='Dash dashboard example' ),
>>>     html.Div( children='Fruit Counts for SF and Montreal' ),
>>>
>>>     dcc.Graph( id='ex_graph', figure=fig )
>>> ])
>>>
>>> app.run_server(debug=True)

Running this code, then visiting URL http://127.0.0.1:8050 produces the following (non-interactive) visualization in a web browser.

A Dash dashboard showing the number of three fruit for two different cities, visualized as a side-by-side bar chart
dash-01

To end the dashboard, terminate the Python program running dash-app.py. A very simple explanation of the components of this program is as follows.

One clarifying note about setting debug=True in the run_server function: when debug is False, changes to the Python code will not update the dashboard. When debug is True, changes to the Python code will automatically be detected and used to update the dashboard in real-time. If you are using debug=True you can run your code (based on my testing) from an Anaconda Prompt or the Spyder IDE. We DO NOT recommend running the code in a Jupyter notebook. Although it will run correctly the first time, there is no easy way to terminate the dashboard if you want to re-run it.

Setting debug=True will also add a blue button containing < > at the bottom-right of the screen. Clicking on this button allows you to check the status of the Dash server, view any errors that may have occurred, and visualize the callback graph for the dashboard.

HTML objects have default styles that can be changed in various ways. Dash supports this ability by providing access to a tag's style properties through update_layout() for figures and style for Dash layouts. The following code is a modification of the original example.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>> colours = { 'background': '#111111', 'text': '#7fdbff' }
>>>
>>> df = pd.DataFrame( {
>>>   'Fruit': ['Apples','Oranges','Bananas','Apples','Oranges','Bananas'],
>>>   'Amount': [4,1,2,2,4,5],
>>>   'City': ['SF','SF','SF','Montreal','Montreal','Montreal']
>>> } )
>>>
>>> fig = px.bar( df, x='Fruit', y='Amount', color='City', barmode='group' )
>>> fig.update_layout(
>>>   plot_bgcolor=colours[ 'background' ],
>>>   paper_bgcolor=colours[ 'background' ],
>>>   font_color=colours[ 'text' ]
>>> )
>>>
>>> app.layout = html.Div(
>>>   style={ 'backgroundColor': colours[ 'background' ] },
>>>   children=[
>>>     html.H1(
>>>       children='Dash dashboard example',
>>>       style={ 'textAlign': 'center', 'color': colours[ 'text' ] }
>>>     ),
>>>     html.Div(
>>>       children='Fruit Counts for SF and Montreal',
>>>       style={ 'textAlign': 'center', 'color': colours[ 'text' ] }
>>>     ),
>>>
>>>     dcc.Graph( id='ex_graph', figure=fig )
>>>   ]
>>> )
>>>
>>> app.run_server(debug=True)
A Dash dashboard showing the number of three fruit for two different cities, visualized as a side-by-side bar chart, with HTML styles applied to control dashboard colours
dash-02

Interactive Widgets

As with most applications, Dash provides a set of standard widgets to allow users to interact with an application. The code below shows a sample of Dash's interactive objects, including dropdown and multi-dropdown menus, radiobuttons, checkboxes, input fields, and sliders.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> app.layout = html.Div([
... html.Label('Dropdown'),
... dcc.Dropdown(
...   options=[
...     {'label': 'New York City', 'value': 'NYC'},
...     {'label': 'Montreal', 'value': 'MTL'},
...     {'label': 'San Francisco', 'value': 'SF'}
... ],
...   value='MTL'
... ),
... html.Div(style={'padding': '20px'}),
...
... html.Label('Multi-Select Dropdown'),
... dcc.Dropdown(
...   options=[
...     {'label': 'New York City', 'value': 'NYC'},
...     {'label': 'Montreal', 'value': 'MTL'},
...     {'label': 'San Francisco', 'value': 'SF'}
...   ],
...   value=['MTL', 'SF'],
...   multi=True
... ),
... html.Div(style={'padding': '20px'}),
...
... html.Label('Radio Items'),
... dcc.RadioItems(
...   options=[
...     {'label': 'New York City', 'value': 'NYC'},
...     {'label': 'Montreal', 'value': 'MTL'},
...     {'label': 'San Francisco', 'value': 'SF'}
...   ],
...   value='MTL'
... ),
... html.Div(style={'padding': '20px'}),
...
... html.Label('Checkboxes'),
... dcc.Checklist(
...   options=[
...     {'label': 'New York City', 'value': 'NYC'},
...     {'label': 'Montreal', 'value': 'MTL'},
...     {'label': 'San Francisco', 'value': 'SF'}
...   ],
...   value=['MTL', 'SF']
... ),
... html.Div(style={'padding': '20px'}),
...
... html.Label('Text Input: '),
... dcc.Input(value='MTL', type='text'),
... html.Div(style={'padding': '20px'}),
...
... html.Label('Slider'),
... dcc.Slider(
...   min=1,
...   max=9,
...   marks={i: 'Label {}'.format(i) if i == 1 else str(i) for i in range(1, 10)},
...   value=5,
... )
>>> ])
>>>
>>> app.run_server(debug=True)
Examples of Dash dropdown, multi-dropdown, radiobutton group, checkbox group, text input, and slider widgets
dash-03

Callbacks

Once interactive widgets are placed on a dashboard, we need a way to recognize when they are changed. This is done using callbacks, a standard method to monitor user interaction in an application. Whenever a widget is manipulated to change its value, a callback is made to the Python program controlling the dashboard. Code in the program captures the callback, so it can examine the new widget value and update the dashboard's content appropriately.

As a very simple example of callbacks, the following Python program creates a Dash dashboard with an input text field, and an output label that displays the current value of the text field.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> app.layout = html.Div( [
>>>   html.H6( 'Change the value in the text box to invoke the callback' ),
>>>   html.Div( [
>>>     'Input: ',
>>>     dcc.Input( id='inp', value='Initial Value', type='text' )
>>>   ] ),
>>>   html.Br(),
>>>   html.Div( id='out' )
>>> ] )
>>>
>>> @app.callback(
>>>   [ Output(component_id='out', component_property='children') ],
>>>   [ Input(component_id='inp', component_property='value') ],
>>> )
>>> def update_output_div( input_value ):
... s = 'Output: {}'.format( input_value )
... return [ s ]
>>>
>>> app.run_server(debug=True)
A Dash input textbox and a callback that displays the current value of the textbox in a div
dash-04

We have already discussed the code used to create the dashboard and its corresponding widgets. The @app.callback function is where a callback to detect changes to the input textbox and modify the output div is contained.

Here is another, more realistic, example that uses a slider and a scatterplot to visualize the relationship between GDP per capita and life expectancy, subdivided by continent.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> df = pd.read_csv( 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv' )
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> app.layout = html.Div( [
>>>   dcc.Graph( id='graph-with-slider' ),
>>>   dcc.Slider(
>>>     id='year-slider',
>>>     min=df['year'].min(),
>>>     max=df['year'].max(),
>>>     value=df['year'].min(),
>>>     marks={str(year): str(year) for year in df['year'].unique()},
>>>     step=None
>>>   )
>>> ] )
>>>
>>> @app.callback(
>>>   [ Output('graph-with-slider', 'figure') ],
>>>   [ Input('year-slider', 'value') ],
>>> )
>>> def update_figure( year ):
... filtered_df = df[ df.year==year ]
...
... fig = px.scatter(
...   filtered_df,
...   x='gdpPercap',
...   y='lifeExp',
...   size='pop',
...   color='continent',
...   hover_name='country',
...   log_x=True,
...   size_max=55
... )
...
... fig.update_layout( transition_duration=500 )
... return [ fig ]
>>>
>>> app.run_server(debug=True)
A Dash scatterplot showing GDP per capita versus life expectancy, categorized by continent. The slider allows users to change the year being visualized from 1952 to 2007
dash-05

Of course, most dashboards will have multiple inputs and outputs. How is this handled in Dash? For inputs, notice that Input is embedded within a list. You can define multiple Input statements in this list. For each Input statement you add, another input argument is included in your callback function.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> df = pd.read_csv( 'https://plotly.github.io/datasets/country_indicators.csv' )
>>>
>>> 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 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 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-graph' ),
>>>
>>>   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-graph', '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_col_nm, yaxis_col_nm, xaxis_type, yaxis_type, year ):
... dff = df[ df['Year']==year ]
...
... fig = px.scatter(
...   x=dff[dff['Indicator Name']==xaxis_col_nm]['Value'],
...   y=dff[dff['Indicator Name']==yaxis_col_nm]['Value'],
...   hover_name=dff[dff['Indicator Name']==yaxis_col_nm]['Country Name'],
... )
...
... fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
... fig.update_xaxes(title=xaxis_col_nm, type='linear' if xaxis_type=='Linear' else 'log')
... fig.update_yaxes(title=yaxis_col_nm, type='linear' if yaxis_type=='Linear' else 'log')
... return [ fig ]
>>>
>>> app.run_server(debug=True)
A Dash scatterplot with variable x and y-axes, as well as the ability to display linear or logarithmic axis spacing, and to choose the year of data to plot
dash-06

In this example, there are five input widgets: two dropdown menus, two radiobutton groups, and a slider. All five are included in the Input list: xaxis-column, yaxis-column, xaxis-type, yaxis-type, and year-slider, respectively. In the update_graph function the values for those five widgets are passed as xaxis_col_nm, yaxis_col_nm, xaxis_type, yaxis_type, and year. Other than this, the function operates identically, returning an updated scatterplot based on the five selected inputs.

Multiple outputs work in a similar way. First, you specify each output as part of the list of Output statements. Then, specify each output element in order in the list returned by the callback function.

>>> from dash import Dash
>>> from dash import html
>>> from dash import dcc
>>> from dash import Input
>>> from dash import Output
>>> import plotly.express as px
>>> import pandas as pd
>>>
>>> ext_SS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
>>> app = Dash(__name__, external_stylesheets=ext_SS)
>>>
>>> df = px.data.gapminder().query( 'year==2007' )
>>> continents = df[ 'continent' ].unique()
>>>
>>> app.layout = html.Div(
>>>   children=[
>>>     dcc.Dropdown(
>>>       id='continent',
>>>       options=[ { 'label': c, 'value': c } for c in continents ],
>>>       value=continents[ 0 ]
>>>     ),
>>>
>>>     dcc.Graph( id='bar' ),
>>>     dcc.Graph( id='map' )
>>> ] )
>>>
>>> @app.callback(
>>>   [ Output('bar', 'figure'),
>>>     Output('map', 'figure') ],
>>>   [ Input('continent', 'value') ]
>>> )
>>> def update_bar_map( continent ):
... dff = df[ df['continent']==continent ]
...
... bar = px.bar( dff, x='country', y='gdpPercap' )
... map = px.treemap(
...   dff,
...   path=['country'],
...   values='pop',
...   color='gdpPercap',
...   color_continuous_scale='RdBu'
... )
...
... return [ bar, map ]
>>>
>>> app.run_server(debug=True)
A Dash bar chart and treemap. The bar chart shows per capita GDP by country for the user-chosen continent. The treemap show population by size and per capita GDP by colour, by country for the user-chosen continent
dash-07

This dashboard shows the GDP per capita for countries in a user-chosen continent. Continent selection is performed with a standard dropdown menu. GDP per capita is shown in two visualizations. The first uses a simple bar chart by country. The second uses a treemap, where the size of the rectangle assigned to a country represents its population, and the rectangle's colour represents the GDP per capita from a red–blue double-ended colour scale.

Publishing

Unfortunately, publishing a Dash dashboard is not as straight forward as it is with other environments like R+Shiny. plotly does not maintain a public cloud to upload dashboards. Various options are suggested by different Dash users.

Dash itself suggests the following documentation for deploying Dash applications. Your milage may vary on this, but it is probably the best starting point for attempt to build a Dash appliation that is accessible from the web.