Plotly: How to make a plotly dropdown menu for figures with wholly different data and layouts?

Viewed 1566

I am trying to make an interactive plot with a dropdown menu that selects from a series of wholly unrelated figures (i.e. plots that rely on different data structures and that have very different layouts). All of the dropdown menu examples I have seen are based on either a single set of data or multiple datasets but that use a relatively simple plot layout. This is not applicable to my case where I am trying to merge dozens of plots with very different layouts and underlying data. Below is a working example of the plots that I am trying to merge. The layouts are highly different across each plot:

import plotly.graph_objs as go
import plotly.express as px
import pandas as pd

# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
    bar_y = ['Bar1', 'Bar2'],
    bar_x = [2,3],
    bar_z = [1,2]
))

# Make bar graph
fig1 = px.bar(df1, 
              x="bar_x", 
              y='bar_y',
              color='bar_z',
              orientation='h',
)    

# Add layout attributes
fig1.update_layout(
    xaxis_title="<b> Bar graph title <b>",
    yaxis_title="<b> Bar x axis <b>",
    legend_title="<b> Bar y axis <b>",        
    xaxis = dict(
        showgrid=True,
        ticks="",
        showline = False,
        gridcolor = 'white'
    )
)       

# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
    line_y = [3,2,1, 1,2,3],
    line_x = [1,2,3,1,2,3],
    line_group = ['line1','line1','line1','line2','line2','line2']
))

# Make an ugly line graph
fig2 = px.line(
    df2,
    x= 'line_x',
    y= 'line_y',
    color = 'line_group'
)

# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
    shapes=[dict(
      type= 'line',
      fillcolor = 'black',
      line_width=2,
      yref= 'y', y0= 0, y1= 0,
      xref= 'x', x0= 1, x1= 3,
    )],
    xaxis_title="<b> Line graph title <b>",
    yaxis_title="<b> Line x axis <b>",
    legend_title="<b> Line y axis <b>",
    template='simple_white',
    hoverlabel=dict(bgcolor="white")
)


# Create a dropdown menu. Below is close to what I'd like to do, but the data statements are not working correctly and the shape in fig2 is causing problems...
fig3 = go.Figure()
fig3.update_layout(
    updatemenus=[
        dict(
            active=0,
            buttons=list([
                dict(label="Bar Graph",
                     method="update",
                     args=[fig1.to_dict()['data'],
                           fig1.to_dict()['layout']]
                    ),
                dict(label="Line Graph",
                     method="update",
                     args=[fig2.to_dict()['data'],
                           fig2.to_dict()['layout']]
                    ),
        ]))
    ]                           
)

It appears that I am almost able to correctly update the layout of each dropdown constituent plot based on the layout of each original graph. However, is it possible to update the data via this sort of method as well?

1 Answers

I may be missing the point completely here. And it may also be overkill to unleash a Dash app in this case. But I would like to show you how the following setup can enable you to return completely different figure objects using a dcc.Dropdown(). The code snippet below will produce the following app:

enter image description here

If you now select fig2, you'll get this:

enter image description here

We can talk more about the details if this is something you can use. Also, the design with the very wide dropdown button is admittedly not the prettiest one, but I assume that design isn't the primary objective here.

Complete code:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px

# Prep some fake data for a bar graph
df1 = pd.DataFrame(dict(
    bar_y = ['Bar1', 'Bar2'],
    bar_x = [2,3],
    bar_z = [1,2]
))

# Make bar graph
fig1 = px.bar(df1, 
              x="bar_x", 
              y='bar_y',
              color='bar_z',
              orientation='h',
)    

# Add layout attributes
fig1.update_layout(
    xaxis_title="<b> Bar graph title <b>",
    yaxis_title="<b> Bar x axis <b>",
    legend_title="<b> Bar y axis <b>",        
    xaxis = dict(
        showgrid=True,
        ticks="",
        showline = False,
        gridcolor = 'white'
    )
)       

# Prep some fake data for a line graph
df2 = pd.DataFrame(dict(
    line_y = [3,2,1, 1,2,3],
    line_x = [1,2,3,1,2,3],
    line_group = ['line1','line1','line1','line2','line2','line2']
))

# Make an ugly line graph
fig2 = px.line(
    df2,
    x= 'line_x',
    y= 'line_y',
    color = 'line_group'
)

# Add a number of layout attributes that are distinct from those above
fig2.update_layout(
    shapes=[dict(
      type= 'line',
      fillcolor = 'black',
      line_width=2,
      yref= 'y', y0= 0, y1= 0,
      xref= 'x', x0= 1, x1= 3,
    )],
    xaxis_title="<b> Line graph title <b>",
    yaxis_title="<b> Line x axis <b>",
    legend_title="<b> Line y axis <b>",
    template='simple_white',
    hoverlabel=dict(bgcolor="white")
)

# app = JupyterDash(__name__)
app = dash.Dash()
figs = ['fig1', 'fig2']

app.layout = html.Div([
    html.Div([
        dcc.Graph(id='plot'),

        html.Div([
            dcc.Dropdown(
                id='variables',
                options=[{'label': i, 'value': i} for i in figs],
                value=figs[0]
            )
        ])
    ])
])

@app.callback(
    Output('plot', 'figure'),
    [Input('variables', 'value')])

def update_graph(fig_name):

    if fig_name == 'fig1':
#         fig=go.Figure(go.Scatter(x=[1,2,3], y = [3,2,1]))
        return fig1


    if fig_name == 'fig2':
#         fig=go.Figure(go.Bar(x=[1,2,3], y = [3,2,1]))
        return fig2
        
# app.run_server(mode='external', debug=True)
app.run_server(debug=True,
           use_reloader=False # Turn off reloader if inside Jupyter
          )  
Related