Interactive Plots

Overview

Teaching: 30 min
Exercises: 15 min
Questions
  • How can a Plot Respond to an Input

Objectives
  • Interactive Elements

  • Callbacks

So far, plots and diagrams, generated using plotly, have been static. But with Dash we can make these plots interactive.

Callbacks

To begin, we need to provide a means to give some sort of input for the dashboard. For starters let’s use a text input.

from dash import Dash, dcc, html, Input, Output

...

app.layout = dbc.Container(
      [
            ...

            html.Div([
                  "Input: ",
                  dcc.Input(id='my-input', value='initial value', type='text')
            ]),
            html.Br(),
            html.Div(id='my-output'),

            dbc.Tabs(
                  ...
            )
      ]
)

We’ve created our input field but we now need to use that input to create some form of response on the dashboard.

@app.callback(
      Output(component_id='my-output', component-property='children'),
      Input(component_id='my-input', component-property='value')
)
def update_output(input_value):
      return f'Output: {input_value}'

The app.callback decorator is defining the use of the function update_output and dynamically passing in and out of it values, or entire elements.

The dcc.Input is one such input, a field, with a defined initial value and type. But we can use others.

Figures and Sliders

from dash import Dash, dcc, html, Input, Output
import plotly.express as px

import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')


app = Dash(__name__)

app.layout = html.Div([
    dcc.Graph(id='graph-with-slider'),
    dcc.Slider(
        df['year'].min(),
        df['year'].max(),
        step=None,
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        id='year-slider'
    )
])


@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
def update_figure(selected_year):
    filtered_df = df[df.year == selected_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


if __name__ == '__main__':
    app.run_server(debug=True)

If we look at our new layout (which uses no bootstrap components), we return to dcc.Graph the figure object created by the callback function.

Our input to that callback is dcc.Slider, with passes the value of the slider.

Multiple Inputs

Building on our example above we can generate a list of continents for use in the dropdown, and use the dropdown as a means to filter the data of the dataframe that is used to form the plot

from dash import Dash, dcc, html, Input, Output
import plotly.express as px

import pandas as pd
import numpy as np

df = pd.read_csv(
    'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')


continent = df.continent.unique()
continent = np.insert(continent, 0, 'All')


app = Dash(__name__)

app.layout = html.Div([
    dcc.Graph(id='graph-with-slider'),
    dcc.Slider(
        df['year'].min(),
        df['year'].max(),
        step=None,
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        id='year-slider'
    ),
    dcc.Dropdown(continent, id='continent_choice', value='All')
])


@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'),
    Input('continent_choice', 'value'))
def update_figure(selected_year, continent_choice):
    filtered_df = df[df.year == selected_year]
    if continent_choice == 'All':
        filtered_df2 = filtered_df
    else:
        filtered_df2 = filtered_df[filtered_df.continent == continent_choice]

    fig = px.scatter(filtered_df2, 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


if __name__ == '__main__':
    app.run_server(debug=True)

Key Points

  • ???