Sesión 9:

Visualización Interactiva de Datos con Dash

Alberto García Robledo
Visualización de Datos Aplicada

16 de noviembre de 2021

Contenido

  • ¿Qué es Dash?
  • Layouts de Dash
  • Callbacks de Dash
  • Componentes Dash de terceros
  • Actividad

¿Qué es Dash?

¿Qué es Dash?

  • Dash es un framework web escrito en Python para el desarrollo de tableros de visualización.

  • Está construido sobre Flask, Plotly.js y React.js

  • Permite el desarrollo de aplicaciones web de visualización de datos sin la necesidad de escribir HTML, JavaScript o CSS.

¿Qué es Dash?

  • Dash es código abierto, publicado bajo la licencia MIT.

  • Está disponible para múltiples lenguajes:

    • Python

    • R

    • Julia

    • .NET

Instalación

  • En una terminal, instala Dash a través de pip:

pip install dash
  • Si prefieres desarrollar en Jupyter Notebook o Jupyter Lab, instala jupyter-dash:

pip install jupyter-dash

Layouts de Dash

Layouts de Dash

  • Una aplicación Dash está integrada de dos partes:

    1. La descripción del UI (layout)

    2. La descripción de la interactividad (callbacks)

  • Un layout de Dash está integrado de uno o más componentes Dash anidados. Los hay de dos tipos:

Ejemplo 1: Hello Dash

Ejemplo 1: Hello Dash

import dash_html_components as html
from jupyter_dash import JupyterDash

app = JupyterDash(__name__)

app.layout = html.Div([
    html.H1('H1 header!'),
    html.H2('H2 header!'),
    html.H3('H3 header!'),
    html.H4('H4 header!'),
    html.H5('H5 header!'),
    html.H6('H6 header!'),
])

app.run_server()

Ejemplo 1: Para recordar

  • dash_html_components provee un componente Dash por cada etiqueta HTML.

  • Un layout está compuesto por Dash components anidados:

    • Un layout de Dash es parecido a un árbol DOM.

  • El componente raíz de un layout es asignado a app.layout.

Ejemplo 2: Markdown

Ejemplo 2: Markdown

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

app = JupyterDash(__name__)

markdown = '''
# Lorem Ipsum

**Lorem ipsum dolor sit amet**, consectetur adipiscing elit. 

1. Duis ullamcorper nisl eget diam laoreet, ac rutrum enim aliquet. 
2. Curabitur aliquam libero et leo consectetur, at pretium urna mollis. 
3. Cras et vestibulum nisi, sit amet sollicitudin turpis. 

***Nam imperdiet porttitor venenatis***. Aenean vitae maximus enim, ac consectetur augue. 

## Vestibulum nec felis ac erat auctor pharetra. 

> Vestibulum vitae velit at nisl pretium ultricies. 
>> Suspendisse mi urna, lobortis sed ante sit amet, porta venenatis ligula. 

Text generated by the [Lorem Ipsum generator](https://www.lipsum.com/).
'''

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

app.run_server()

Ejemplo 2: Para recordar

  • Puede ser tedioso construir un layout utilizando únicamente los dash_html_components.

  • Para desplegar bloques de texto con formato de manera rápida, puedes usar el componente Markdown del dash_core_components.

Ejemplo 3: Hello Graph

Ejemplo 3: Hello Graph

Ejemplo 3: Hello Graph

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import plotly.express as px

df = px.data.iris() # iris is a pandas DataFrame

app = JupyterDash(__name__)

fig = px.scatter(df, x='petal_length', y='sepal_length') 
 
app.layout = html.Div([
    dcc.Graph(
        figure=fig,
        style={'height': 300},
    )
])

app.run_server()

Ejemplo 3: Para recordar

  • Puedes utilizar DataFrames de Pandas como la fuente de datos de tus tableros Dash.

  • Dash provee Plotly Express, el cual contiene funciones para crear gráficas ricas y complejas de manera fácil.

  • El componente dcc.Graph puede visualizar figuras generadas por Plotly y Plotly Express:

    • Sólo hay que pasar la figura Plotly al componente dcc.Graph a través del argumento "figure"

Callbacks de Dash

Callbacks de Dash

  • Un callback es una función de Python que permite definir la interacción de un tablero Dash.

  • Cada componente Dash puede tener un id único

  • Un callback contiene uno o más Input y Output:

    • Cada entrada/salida está asociada al id de un componente y a un atributo del componente.

Ejemplo 4: Callback simple

Ejemplo 4: Callback simple

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()

Ejemplo 4: Para recordar

  • Es necesario asignar un id único a un componente si deseamos hacer referencia a éste en un callback.

  • Un Output dado sólo puede aparecer en un callback.

  • Pero un Input dado puede aparecer en más de un callback.

  • Los callbacks son disparados de manera automática al iniciar la aplicación.

Ejemplo 5: Múltiples Inputs y Outputs

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

Ejemplo 5: Múltiples Inputs y 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)

Ejemplo 5: Para recordar

  • Un callback puede tener múltiples Inputs y Outputs

  • Tienes que asegurarte que todos los Inputs tengan valores válidos:

    • Puedes pasar prevent_initial_call = True al decorador de un callback para evitar que el callback se dispare automáticamente

    • Diseñar callbacks puede ser complicado en un principio ¡No te desanimes!

Ejemplo 6: States

Ejemplo 6: 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()

Ejemplo 6: Para recordar

  • Los "States" nos permiten recolectar valores de componentes sin tener que disparar un callback

  • Nos permiten pasar valores a un callback adicionales al valor disparador

  • No olvides proveer valores por defecto a todas las salidas de un callback para el disparo de inicialización

Componentes Dash de terceros

Componentes Dash de terceros

  • Dash provee herramientas para que puedas desarrollar tus propios componentes

  • Básicamente, un componente Dash no es más que un envoltorio de un componente React.js

  • Dash goza de una nutrida comunidad de desarrolladores que han creado sus propios componentes Dash

dash-extensions

  • Provee varias extensiones útiles al framework Dash:

    • Aplicaciones Dash de múltiples páginas

    • Grupos de Outputs para permitir que un Output pueda ser utilizado en múltiples callbacks

    • Outputs del lado del servidor

  • https://github.com/thedirtyfew/dash-extensions

Dash Bootstrap Components

  • Provee componentes de UI basados en el framework Bootstrap 4:

    • Modal

    • Cards

    • Form

    • Jumbotron

    • Navbar

Ejemplo 7: Google Play Top

Ejemplo 7: Google Play Top

Ejemplo 7: 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()

Ejemplo 7: Para recordar

  • Puedes utilizar archivos CSV como fuente de datos de tus tableros Dash utilizando Pandas.

  • Puedes utilizar Pandas para manipular el dataset dentro de un callback (e.g. filtrarlo u ordenarlo).

  • La biblioteca Dash Bootstrap Components permite la utilización de componentes de Bootstrap 4 en nuestras aplicaciones Dash.

  1. Elige un dataset de kaggle.com

  2. Desarrolla una aplicación de Dash que utilize un Plotly Express scatter plot para visualizar las potenciales correlaciones entre cualesquiera dos variables numéricas

  3. La aplicación de Dash debe utilizar la biblioteca Dash Bootstrap Components para la UI: dos columnas y un navbar

Actividad: Correlaciones

Actividad: Correlaciones

Actividad: Correlaciones

Paso 0. Haz una copia del siguiente notebook:

Actividad: Correlaciones

Paso 1. Asigna a la variable df el dataset Iris provisto por Plotly Express:

Actividad: Correlaciones

df = px.data.iris() # iris is a pandas DataFrame

Actividad: Correlaciones

Paso 2. Define un Dropdown que tenga como options diferentes columnas del dataset. Asígnale el id xaxis-dropdown

dcc.Dropdown(
  id='xaxis-dropdown',
  options=[
    {'label': 'sepal_width', 'value': 'sepal_width'},
    {'label': 'sepal_length', 'value': 'sepal_length'},
    {'label': 'petal_length', 'value': 'petal_length'}
  ],
  value='sepal_width',
)

Actividad: Correlaciones

Paso 3. Define otro Dropdown que tenga como options diferentes columnas del dataset. Asígnale el id yaxis-dropdown:

dcc.Dropdown(
  id='yaxis-dropdown',
  options=[
    {'label': 'sepal_width', 'value': 'sepal_width'},
    {'label': 'sepal_length', 'value': 'sepal_length'},
    {'label': 'petal_length', 'value': 'petal_length'}
  ],
  value='sepal_width',
)

Actividad: Correlaciones

Paso 4. un objeto dcc.Graph en el layout en donde se ubicará el scatterplot. Asígnale el id top-graph:

dbc.Col(dcc.Graph(id='top-graph'), md=8),

Actividad: Correlaciones

Paso 5. Define un callback que tenga como inputs el valor de xaxis-dropdown y yaxis-dropdown ​y como output la figura de top-graph:

@app.callback(
    Output('top-graph', 'figure'),
    Input('xaxis-dropdown', 'value'),
    Input('yaxis-dropdown', 'value'),
)
def update_callback(xaxis_col, yaxis_col):          
    fig = px.scatter(df, x=xaxis_col, y=yaxis_col)    
    return fig

Para saber más

  • Dabbas, E. (2021) Interactive Dashboards and Data Apps with Plotly and Dash, Packt. 

agarcia@centrogeo.edu.mx

albertogarob.mx

Sesión 09 - 16nov21 - Visualización Interactiva de Datos con Dash

By Alberto Garcia-Robledo

Sesión 09 - 16nov21 - Visualización Interactiva de Datos con Dash

  • 297