Black Lives Matter. Please consider donating to Black Girls Code today.

Can you have 2 dropdown menus with one setting the number of traces that are shown conditional on the first?

I noticed a previous question that asked something similar to what I’m looking for but it didn’t receive an answer. Perhaps someone has solved this question in the meantime.

I’m working on a graph that has a dropdown menu for selecting a state. I’ve been asked if it’s possible to add another dropdown that contain two options for choosing which lines are displayed on the graph. For example, the default option would only show “Total Ed. Credentials” and then the first option, if clicked, would show the lines: “Total BA”, and “Total MA.” The second option would allow you to see the other four lines.
Is this possible?

2 Likes

i bumped into the same problem. looks like we don’t have answer for that. :frowning:

Hi @zhsee,

One approach you can take is using a second dropdown to restyle the visible property of your traces. Here’s an example based on the " Relayout Dropdown" section of https://plot.ly/python/dropdowns/.

import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import iplot

import numpy as np

x0 = np.random.normal(2, 0.4, 400)
y0 = np.random.normal(2, 0.4, 400)
x1 = np.random.normal(3, 0.6, 600)
y1 = np.random.normal(6, 0.4, 400)
x2 = np.random.normal(4, 0.2, 200)
y2 = np.random.normal(4, 0.4, 200)

trace0 = go.Scatter(
    x=x0,
    y=y0,
    mode='markers',
    marker=dict(color='#835AF1')
)
trace1 = go.Scatter(
    x=x1,
    y=y1,
    mode='markers',
    marker=dict(color='#7FA6EE')
)
trace2 = go.Scatter(
    x=x2,
    y=y2,
    mode='markers',
    marker=dict(color='#B8F7D4')
)
data = [trace0, trace1, trace2]

cluster0 = [dict(type='circle',
                 xref='x', yref='y',
                 x0=min(x0), y0=min(y0), 
                 x1=max(x0), y1=max(y0), 
                 opacity=.25,
                 line=dict(color='#835AF1'),
                 fillcolor='#835AF1')]
cluster1 = [dict(type='circle',
                 xref='x', yref='y',
                 x0=min(x1), y0=min(y1), 
                 x1=max(x1), y1=max(y1), 
                 opacity=.25,
                 line=dict(color='#7FA6EE'),
                 fillcolor='#7FA6EE')]
cluster2 = [dict(type='circle',
                 xref='x', yref='y',
                 x0=min(x2), y0=min(y2), 
                 x1=max(x2), y1=max(y2), 
                 opacity=.25,
                 line=dict(color='#B8F7D4'),
                 fillcolor='#B8F7D4')]

updatemenus = list([
    dict(buttons=list([   
            dict(label = 'None',
                 method = 'relayout',
                 args = ['shapes', []]),
            dict(label = 'Cluster 0',
                 method = 'relayout',
                 args = ['shapes', cluster0]),
            dict(label = 'Cluster 1',
                 method = 'relayout',
                 args = ['shapes', cluster1]),
            dict(label = 'Cluster 2',
                 method = 'relayout',
                 args = ['shapes', cluster2]),
            dict(label = 'All',
                 method = 'relayout',
                 args = ['shapes', cluster0+cluster1+cluster2])
        ]),
    ),
    dict(buttons=list([   
            dict(label = 'None',
                 method = 'restyle',
                 args = ['visible', ['legendonly', 'legendonly', 'legendonly']]),
            dict(label = 'Cluster 0',
                 method = 'restyle',
                 args = ['visible', [True, 'legendonly', 'legendonly']]),
            dict(label = 'Cluster 1',
                 method = 'restyle',
                 args = ['visible', ['legendonly', True, 'legendonly']]),
            dict(label = 'Cluster 2',
                 method = 'restyle',
                 args = ['visible', ['legendonly', 'legendonly', True]]),
            dict(label = 'All',
                 method = 'restyle',
                 args = ['visible', [True, True, True]])
        ]),
         y=0.8
    )
])

layout = dict(title='Highlight Clusters',
              showlegend=True,
              updatemenus=updatemenus,
              xaxis={'range': [0, 6]},
              yaxis={'range': [0, 8]})

fig = dict(data=data, layout=layout)
iplot(fig)

I know it will probably take a bit of time to digest this. What I added was the second dict in the updatemenus list. The different dropdown states cause the execution of a restyle command, which lets you set the properties of traces (whereas relayout lets you set properties of the layout).

In this case, I’m setting the visible property of the scatter traces. This property can be True, False, or legendonly (to hide trace in plot but still show in legend). Since I have three traces, I’m using lists with three elements so I can set the visible property separately for each trace.

I’m hoping that playing around with this example will give you some ideas on a way forward for your use case!

1 Like

will it handle this case, looks like it is not :grinning:

let say i have this senario, i would like to create two dropdowns menu (category and scenario) to select the data i want to pick.

data:
categoryA
scene1
scene2
scene3
categoryB
scene1
scene2
scene3
categoryC
scene1
scene2
scene3

No, you’re right, I don’t think it can handle that case.

If you’re working in the Jupyter notebook context you could do this using the plotly.py version 3 FigureWidget and ipywidgets dropdowns. (see installation instructions in the README)

Here’s an example of creating a FigureWidget with a single trace, and then updating that trace based on a collection of ipywidgets: https://github.com/jonmmease/plotly_ipywidget_notebooks/blob/master/notebooks/Interact.ipynb.

You can try out all of the FigureWidget examples in this repo using binder.

One downside of this approach (compared to the updatemenus version) is that it requires a Python kernel, so you can’t export the whole thing to a standalone html file and preserve the interactions.

Hope that helps!
-Jon