How can I allow users to select which comparison variables to display on a chart?

Let’s say that I have a dataset showing Delta, United, American, and Southwest Airlines traffic at the top 5 airports in the US, and that this dataset also distinguishes between narrowbody and widebody aircraft.

I would like to create an interactive bar chart in which users, by default, see a single bar (which would represent all airline traffic). However, I would then want to allow users to select up to 3 comparison variables. For instance, if they chose to compare this data by airline (but nothing else), they would see 4 bars, one for each airline. If they then chose to compare this data by (1) airport and (2) airline, they would then see 20 bars, each of which would show one airline’s traffic at one of the 5 airports.

Within Tableau, I could accomplish this by creating 3 different parameters and then letting the user select which comparison variable, if any, to show for each parameter. However, I’d love to learn to do this using Plotly.

The dash-pivottable library looks like a promising method, but would there be a way to accomplish this in Plotly without using that component? (If not, that’s totally fine–I’m really new to Plotly and am just trying to learn different tools for creating charts like this.)

Thanks in advance for your help! If my question is unclear, I could try to create a prototype of my desired visualization in Tableau.

Hi @KBurchfiel and welcome to the Dash community :slight_smile:

This is easy to do in Dash. You can find lots of examples where user selections update a figure:

This one has two dropdowns and a pie chart - but easy enough to change to a bar chart:

If it’s still unclear, I could help you make this example - it might be a good candidate to include in the Example Index. Do you have a sample dataset?

Hello @KBurchfiel,

Welcome to the community. Do you have a data set that we can use to build this?

Hi @AnnMarieW and @jinnyzor ,

Thanks so much for your prompt response! I’ve now created the sample dataset that could be used for the interactive bar charts. It’s available here: kburchfiel-dash-plotly-demo/t5_airports_t4_airlines_2018.csv at master · kburchfiel/kburchfiel-dash-plotly-demo · GitHub

The data is based on public-domain Bureau of Transportation Statistics data, and I’m releasing this modified version into the public domain as well. It shows a breakdown of domestic and international traffic by the top 4 US airlines at the top 5 US airports in 2018.

It would be great to have an example like this in the public gallery! Feel free to use it however you’d like :slight_smile: I’ll also plan to work on my own solution using Plotly as time allows! My real will be to allow the user to compare the data on any combination of the 3 parameters (airline, airport, and domestic vs international traffic) that they want (or no combination at all). Thus, there would be 8 different potential chart outputs (222 in total).

@KBurchfiel,

When you add to the drop-down, the goal is to make groupings of the smaller sets, is that correct?

Is there a specific order to the way that the groupings would work, or whatever order is presented in the drop-down?

1 Like

@jinnyzor , that’s right–the idea would be to create a series of grouped bar charts (or perhaps stacked charts for the first comparison option) depending on the user’s desired input.

In Tableau, I would set up a “1st comparison,” “2nd comparison,” and “3rd comparison” dropdown menu, each of which would have 4 options (Airline, Airport, Domestic/International Route Type, and ‘None’). Each would be set to ‘None’ by default, resulting in just one big bar representing all air traffic. However, users could then select different options for each to produce their desired chart.

For example, if you selected ‘Airline’ for the first comparison but left the others blank, you would see 4 bars (for AA, UA, DL, and WN (Southwest)). But if you then selected ‘Airport’ for the 2nd comparison, each of these airline bars would be split into 5 bars to represent each airport. This split could be horizontal (for grouped bars) or vertical (for stacked bars).

Consolidating all 3 comparison options into a single multi-select drop down menu could work also–if that’s harder to pull off, though, individual dropdown menus for each comparison option also works fine.

Looking forward to trying this all out in Plotly!

Here is a really simple one:

import plotly.express as px
import dash
from dash import html, dcc, Input, Output
import dash_bootstrap_components as dbc
import pandas as pd

df = pd.read_csv('data.csv')

def createFigure(grouping = None):
    if grouping:
        dff = df.groupby(grouping).sum().reset_index()
        dff['sorted'] = dff[grouping].apply(
    lambda x: '-'.join(x.dropna().astype(str)),
    axis=1
)
        fig = px.bar(dff, x='sorted', y='Passengers', color=grouping[0])
    else:
        fig = px.bar(df, x=['All'], y=[df.sum()['Passengers']])
    return fig

app = dash.Dash(__name__)
app.layout = html.Div([
    dbc.Card([dcc.Dropdown(df.columns, id='myDropdown', multi=True),
        dcc.Graph(figure=createFigure(), id='myChart')
    ])
])



@app.callback(
    Output('myChart', 'figure'),
    Input('myDropdown', 'value'),
    prevent_intial_call=True
)
def updateGroupings(v):
    fig = createFigure(v)
    return fig

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

It makes use of the grouping index of the dataframe and then defines the first selection as the color type.

You can remove the Passengers column from the drop down selections and also add some x-axis and y-axis labels.

1 Like

Here is a slightly modified one:

import plotly.express as px
import dash
from dash import html, dcc, Input, Output
import dash_bootstrap_components as dbc
import pandas as pd

df = pd.read_csv('data.csv')

def createFigure(grouping = None):
    if grouping:
        dff = df.groupby(grouping).sum().reset_index()
        if len(grouping) == 1:
            dff['sorted'] = dff[grouping]
            fig = px.bar(dff, x='sorted', y='Passengers', color=grouping[0])
        else:
            dff['sorted'] = dff[grouping[1:]].apply(
        lambda x: '-'.join(x.dropna().astype(str)),
        axis=1
    )
            fig = px.bar(dff, x='sorted', y='Passengers', color=grouping[0], barmode='group')

    else:
        fig = px.bar(df, x=['All'], y=[df.sum()['Passengers']])
    return fig

app = dash.Dash(__name__)
app.layout = html.Div([
    dbc.Card([dcc.Dropdown(df.columns, id='myDropdown', multi=True),
        dcc.Graph(figure=createFigure(), id='myChart')
    ])
])



@app.callback(
    Output('myChart', 'figure'),
    Input('myDropdown', 'value'),
    prevent_intial_call=True
)
def updateGroupings(v):
    fig = createFigure(v)
    return fig

if __name__ == "__main__":
    app.run_server(debug=True)
2 Likes

Wow @jinnyzor, this is amazing! Thank you so much! It will be a very helpful reference for similar projects (as replacing a pre-existing Tableau dashboard I have would involve creating many different charts like these).

Would I be able to incorporate this code into an MIT-licensed project? I would make sure to give you credit (as jinnyzor, Bryan, or another name of your choosing.)

@KBurchfiel,

Sure thing, jinnyzor is fine. :slight_smile:

There might be other ways to solve your problem as well.

1 Like

Wonderful, thank you! I think your code would also be a great addition to the Dash Example Index that @AnnMarieW shared earlier.

Ken

1 Like

Hi @KBurchfiel

I agree @jinnyzor solution is :heart_eyes:.

Here’s another one to try. This one uses sunburst charts which work great for drill downs. Although it doesn’t use bar charts as you requested, there is no need for any data wrangling, so it’s simpler.


from dash import Dash, html, dcc, Input, Output
import plotly.express as px
import pandas as pd
import dash_bootstrap_components as dbc

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

data = "https://raw.githubusercontent.com/kburchfiel/kburchfiel-dash-plotly-demo/master/t5_airports_t4_airlines_2018.csv"
df = pd.read_csv(data)

app.layout = dbc.Card(
    [
        html.Label("Select drill down order", htmlFor="dropdown"),
        dcc.Dropdown(["Airport", "Airline", "Route_Type"], id="dropdown", multi=True),
        html.Div(f"Total Passengers {df.Passengers.sum():,.0f}", className="text-center pt-4"),
        dcc.Graph(id="graph", style={"height": 800}),
    ], body=True, className="m-4"
)


@app.callback(Output("graph", "figure"), Input("dropdown", "value"))
def update_drilldown(value):
    if value is None or value == []:
        return px.pie(values=[df.Passengers.sum()])
    return px.sunburst(df, path=value, values="Passengers")


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

drilldown

2 Likes