Development of Web Dashboard Visualizations with Python and Dash

Alberto García Robledo, Ph.D.

CentroGeo

Escuela de Verano CentroGeo 2021

Content

  • What is Dash?
  • Dash layouts
  • Dash callbacks
  • Third-party Dash components
  • Exercises

Requirements

  • Knowledge

    • HTML and CSS (basic)

    • Python 3 (intermediate)

    • Pandas (basic)

  • Tools

    • A laptop with Internet connection

    • A Google e-mail account

    • Any OS (Windows, Linux, MacOS)

    • Any modern browser (Chrome, Firefox, Edge, Safari)

What is Dash?

What is Dash?

  • Dash is a productive Python framework for building web analytic applications

  • Written on top of Flask, Plotly.js, and React.js

  • Abstracts away all of the technologies and protocols that are required to build an interactive web-based application

What is Dash?

  • Dash apps are rendered in the web browser

  • You can deploy your apps to servers and then share them through URLs

  • Dash is an open source library, released under the permissive MIT license

  • Available for Python, R, Julia, and .NET

Installation

  • In your terminal, install dash with pip:

pip install dash
  • If you prefer Jupyter notebook or JupyterLab as your development environment, install jupyter-dash:

pip install jupyter-dash

Dash Layouts

Dash layouts

  • Dash apps are composed of two parts:

    1. ​The first part describes the layout

    2. The second part describes the interactivity

  • Dash provides Python classes for all of the visual components of the application:

Example 1: Hello Dash 

Example 1: Hello Dash 

import dash_html_components as html
from jupyter_dash import JupyterDash

app = JupyterDash(__name__)

app.layout = html.Div(
    html.H1('Hello Dash')
)

app.run_server()

Example 1 Take Aways

  • The dash_html_components library contains a component class for every HTML tag as well as keyword arguments for all of the HTML arguments

  • The layout is made out of nested Dash components:

    • A layout resembles the HTML DOM tree

  • The layout root is assigned to app.layout 

Example 2: Hello Dash with Styles

Example 2: Hello Dash with Styles

from jupyter_dash import JupyterDash
import dash_html_components as html

EXTERNA_STYLESHEETS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
BACKGROUND_COLOR = '#111111'
TEXT_COLOR = '#7FDBFF'
TEXT_ALIGN = 'center'

app = JupyterDash(__name__, external_stylesheets=EXTERNA_STYLESHEETS)

app.layout = \
    html.Div(style={'backgroundColor': BACKGROUND_COLOR}, children=[
        html.H1(children='Hello Dash', style={
            'textAlign': TEXT_ALIGN,
            'color': TEXT_COLOR
        }),
        html.Div(children='A Dash web app.', style={
            'textAlign': TEXT_ALIGN,
            'color': TEXT_COLOR
        })
    ])

app.run_server()

Example 2 Take Aways

  • The children of the HTML tag are specified through the children keyword argument
  • The style property in Dash is a dictionary
  • The keys in the style dictionary are camelCased
    • So, instead of text-align, it's textAlign
  • The HTML class attribute is className in Dash
     

Example 3: Markdown

  • Markdown is a lightweight markup language for creating formatted text using a plain-text editor

Example 3: Markdown

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

markdown_text = '''
### Dash and Markdown

Dash apps can be written in Markdown.
Dash uses the [CommonMark](http://commonmark.org/)
specification of Markdown.
Check out their [60 Second Markdown Tutorial](http://commonmark.org/help/)
if this is your first introduction to Markdown!
'''

app.layout = html.Div([
    dcc.Markdown(children=markdown_text)
])

app.run_server()

Example 3 Take Aways

  • While Dash exposes HTML through the dash_html_components library, it can be tedious to write your copy in HTML

  • For writing blocks of text, you can use the Markdown component in the dash_core_components library

Example 4: Hello Graph

Example 4: Hello Graph

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html

EXTERNAL_STYLESHEETS = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

DATA = [
    {
        'x': [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
              2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
        'y':[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
             350, 430, 474, 526, 488, 537, 500, 439],
        'name':'Rest of world',
        'marker':{
            'color': 'rgb(55, 83, 109)'
        }
    },
    {
        'x': [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
              2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
        'y':[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
             299, 340, 403, 549, 499],
        'name':'China',
        'marker':{
            'color': 'rgb(26, 118, 255)'
        }
    }
]

app = JupyterDash(__name__, external_stylesheets=EXTERNAL_STYLESHEETS)

figure = {
    'data': DATA,
    'layout': {
        'title': 'US Export of Plastic Scrap',
        'showlegend': True,
        'legend': {
            'x': 0,
            'y': 1.0
        },
        'margin': {'l': 40, 'r': 0, 't': 40, 'b': 30}
    }
}

app.layout = html.Div([
    dcc.Graph(
        figure=figure,
        style={'height': 300},
    )
])

app.run_server()

Example 4 Take Aways

  • The dcc.Graph component can be used to render any plotly-powered data visualization, passed as the figure argument

  • The Plotly Graphing Library generates "figures".
    • These are used in dcc.Graph with e.g. dcc.Graph(figure=fig) with fig a plotly figure

Dash Callbacks

Dash Callbacks

  • Custom behavior at the user interaction is defined by callbacks

  • Every Dash component in a layout has an unique id

  • A Dash callback has inputs and outputs

    • An input/output is associated to a component's id and a component's property

  • States are a special kind of input

Example 6: Simple Callback

Example 6: Simple Callback

import dash_html_components as html
import dash_core_components as dcc
from jupyter_dash import JupyterDash
from dash.dependencies import Input, Output

app = JupyterDash(__name__)

app.layout = html.Div([    
    html.Div(['Text:',
              dcc.Input(id='text-input', 
                        value='', 
                        type='text')]),
    html.Div(id='echo-div')
])

@app.callback(
    Output('echo-div', 'children'),
    Input('text-input', 'value')
)
def update_echo_div_callback(text_value):
  if text_value:
    return f'Echo: {text_value}'

app.run_server()

Example 6 Take Aways

  • You need to manually set a unique id to any component you want to refer to in a callback

  • A given Output can only have one callback

  • A given Input can have more than one callback, tho

  • All callbacks are triggered at the app startup

    • You need to manually check that inputs are not None

Example 7: Multiple Inputs and Outputs

xy+c\text{\,\,\,for\,\,\,}y=1...5

Example 7: Multiple Inputs and Outputs

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

app = JupyterDash(__name__)

app.layout = html.Div([
    dcc.Input(
        id='x',
        type='number',
        value=1
    ),
    dcc.Input(
        id='c',
        type='number',
        value=0
    ),
    html.Table([
        html.Tr(html.Td(id='y-1-td')),
        html.Tr(html.Td(id='y-2-td')),
        html.Tr(html.Td(id='y-3-td')),
        html.Tr(html.Td(id='y-4-td')),
        html.Tr(html.Td(id='y-5-td'))        
    ])
])

@app.callback(
    Output('y-1-td', 'children'),
    Output('y-2-td', 'children'),
    Output('y-3-td', 'children'),
    Output('y-4-td', 'children'),
    Output('y-5-td', 'children'),
    Input('x', 'value'),
    Input('c', 'value'))
def compute_callback(x, c):
  if (not x is None) and (not c is None):
    return x*1+c, x*2+c, x*3+c, x*4+c, x*5+c

app.run_server(debug=True)

Example 8: States

Example 8: States

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
 
app = JupyterDash(__name__)
 
app.layout = html.Div([
    dcc.Input(
        id='name-input',
        type='text',
        value=''
    ),
    dcc.Input(
        id='lastname-input',
        type='text',
        value=''
    ),
    dcc.Input(
        id='age-input',
        type='number',
        value=18
    ),
    html.Button(id='submit-button', children='Submit'),
    html.Table([
        html.Tr(html.Td(id='name-td')),
        html.Tr(html.Td(id='lastname-td')),
        html.Tr(html.Td(id='age-td'))        
    ])
])
 
@app.callback(
    Output('name-td', 'children'),
    Output('lastname-td', 'children'),
    Output('age-td', 'children'),
    Input('submit-button', 'n_clicks'),   
    State('name-input', 'value'),
    State('lastname-input', 'value'),
    State('age-input', 'value'))
def compute_callback(n_clicks, name_input, lastname_input, age_input):
  if n_clicks and name_input and lastname_input and age_input:
    return name_input, lastname_input, age_input
  else:
    return '', '', ''
 
app.run_server()

Example 8 Take Aways

  • States allow us to gather values from components without triggering a callback

  • States allow us to pass extra values along with the trigger input values to a callback

  • Don't forget to provide default values for all outputs at startup time

Third-party Dash components

Third-party Dash components

  • Dash provides the tools to develop your own components

  • Dash components are basically Python wrappers of React.js components

  • Dash has a large community of developers of third-party open-source Dash components

dash-extensions

Dash Bootstrap Components

  • Provides UI components based on the Bootstrap 4 framework:

    • Modal

    • Cards

    • Forms

    • Jumbotron

    • Navbar

Dash Sylvereye

  • Custom Dash library developed at CentroGeo

  • Visualization of large-scale street networks with WebGL

  • Customizable visualizations

  • Click events for junctions, roads, and markers

Example 9: Google Play Top

Example 9: Google Play Top

Example 9: Google Play Top

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd

df = pd.read_csv(
    'https://raw.githubusercontent.com/jasonchang0/kaggle-google-apps/master/google-play-store-apps/googleplaystore.csv', delimiter=',')

app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

navbar = dbc.NavbarSimple(
    brand="Google Play Top",
    brand_href="#",
    color="primary",
    dark=True,
)

controls = dbc.Card(
    [
        dbc.FormGroup(
            [
                dbc.Label('Category'),
                dcc.Dropdown(
                    id='category-dropdown',
                    options=[
                        {'label': col, 'value': col} for col in df['Category'].unique()
                    ],
                    value='ART_AND_DESIGN',
                ),
            ]
        ),
        dbc.FormGroup(
            [
                dbc.Label('Top'),
                dcc.Dropdown(
                    id='top-dropdown',
                    options=[
                        {'label': 5, 'value': 5},
                        {'label': 10, 'value': 10},
                        {'label': 20, 'value': 20},
                        {'label': 30, 'value': 30},
                        {'label': 40, 'value': 40},
                        {'label': 50, 'value': 50},
                    ],
                    value=5,
                ),
            ]
        )
    ],
    body=True
)

app.layout = dbc.Container(
    [
        navbar,
        dbc.Row(
            [
                dbc.Col(controls, md=4),
                dbc.Col(dcc.Graph(id='top-graph'), md=8),
            ],
            align='center'
        ),
    ]
)

@app.callback(
    Output('top-graph', 'figure'),
    Input('category-dropdown', 'value'),
    Input('top-dropdown', 'value')
)
def update_callback(category, top):
    df2 = df[df['Category'] == category]
    df2 = df2.sort_values(by='Rating', ascending=False).head(top)
    fig = px.bar(df2, x='App', y='Rating')
    return fig

app.run_server()

Example 6 Take Aways

  • You can use Pandas DataFrames as a data source of your Dash apps

  • Dash provides the Plotly Express module, which contains functions that can create entire figures at once

  • Dash Bootstrap Components provide the Navbar, Card, and FormGroup components, which are based on Boostrap 4

Activity: Visualizing Correlations

Activity: Visualizing Correlations

  1. Pick a dataset from kaggle.com

  2. Develop a simple Dash app that displays a Plotly Express scatterplot to visually explore potential correlations between any two numerical attributes

  3. The dash app must use Dash Bootstrap Components for the UI: two columns and a navbar

Activity: Visualizing Correlations

Thank you!

agarcia@centrogeo.edu.mx

 

Made with Slides.com