Plotly: How to toggle traces with a button similar to clicking them in legend?

We Are Going To Discuss About Plotly: How to toggle traces with a button similar to clicking them in legend?. So lets Start this Python Article.

Plotly: How to toggle traces with a button similar to clicking them in legend?

  1. How to solve Plotly: How to toggle traces with a button similar to clicking them in legend?

    In order to make it possible to toggle any trace on and off without affecting the others, it seems you'll have to include one updatemenu per button. There might be other ways to do it, but the code snippet below will produce the following plot:
    Plot 1 – At launch all are selected

    Plot 2 – C and D toggled off

    Plot 3 – All off

    Plot 4 – All on

    Complete code:
    import numpy as np import pandas as pd import plotly.graph_objects as go import datetime import plotly.express as px periods = 200 cols = list('ABCD') np.random.seed(123) df = pd.DataFrame(np.random.randint(-10, 12, size=(periods, len(cols))), columns=cols) datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'), periods=periods).tolist() df['dates'] = datelist df = df.set_index(['dates']) df.index = pd.to_datetime(df.index) df.iloc[0] = 0 df = df.cumsum() # # plotly fig = go.Figure() colors = px.colors.qualitative.Plotly # set up multiple traces for col in df.columns: fig.add_trace(go.Scatter(x=df.index, y=df[col], name = col, visible=True ) ) um = [ {} for _ in range(len(df.columns)) ] buttons = [] menuadjustment = 0.15 buttonX = -0.1 buttonY = 1 + menuadjustment for i, col in enumerate(df.columns): button = dict(method='restyle', label=col, visible=True, args=[{'visible':True, 'line.color' : colors[i]}, [i]], args2 = [{'visible': False, 'line.color' : colors[i]}, [i]], ) # adjust some button features buttonY = buttonY-menuadjustment um[i]['buttons'] = [button] um[i]['showactive'] = False um[i]['y'] = buttonY um[i]['x'] = buttonX # add a button to toggle all traces on and off button2 = dict(method='restyle', label='All', visible=True, args=[{'visible':True}], args2 = [{'visible': False}], ) # assign button2 to an updatemenu and make some adjustments um.append(dict()) um[i+1]['buttons'] = [button2] um[i+1]['showactive'] = True um[i+1]['y']=buttonY - menuadjustment um[i+1]['x'] = buttonX # add dropdown menus to the figure fig.update_layout(showlegend=True, updatemenus=um) # adjust button type for m in fig.layout.updatemenus: m['type'] = 'buttons' f = fig.full_figure_for_development(warn=False) fig.show()

  2. Plotly: How to toggle traces with a button similar to clicking them in legend?

    In order to make it possible to toggle any trace on and off without affecting the others, it seems you'll have to include one updatemenu per button. There might be other ways to do it, but the code snippet below will produce the following plot:
    Plot 1 – At launch all are selected

    Plot 2 – C and D toggled off

    Plot 3 – All off

    Plot 4 – All on

    Complete code:
    import numpy as np import pandas as pd import plotly.graph_objects as go import datetime import plotly.express as px periods = 200 cols = list('ABCD') np.random.seed(123) df = pd.DataFrame(np.random.randint(-10, 12, size=(periods, len(cols))), columns=cols) datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'), periods=periods).tolist() df['dates'] = datelist df = df.set_index(['dates']) df.index = pd.to_datetime(df.index) df.iloc[0] = 0 df = df.cumsum() # # plotly fig = go.Figure() colors = px.colors.qualitative.Plotly # set up multiple traces for col in df.columns: fig.add_trace(go.Scatter(x=df.index, y=df[col], name = col, visible=True ) ) um = [ {} for _ in range(len(df.columns)) ] buttons = [] menuadjustment = 0.15 buttonX = -0.1 buttonY = 1 + menuadjustment for i, col in enumerate(df.columns): button = dict(method='restyle', label=col, visible=True, args=[{'visible':True, 'line.color' : colors[i]}, [i]], args2 = [{'visible': False, 'line.color' : colors[i]}, [i]], ) # adjust some button features buttonY = buttonY-menuadjustment um[i]['buttons'] = [button] um[i]['showactive'] = False um[i]['y'] = buttonY um[i]['x'] = buttonX # add a button to toggle all traces on and off button2 = dict(method='restyle', label='All', visible=True, args=[{'visible':True}], args2 = [{'visible': False}], ) # assign button2 to an updatemenu and make some adjustments um.append(dict()) um[i+1]['buttons'] = [button2] um[i+1]['showactive'] = True um[i+1]['y']=buttonY - menuadjustment um[i+1]['x'] = buttonX # add dropdown menus to the figure fig.update_layout(showlegend=True, updatemenus=um) # adjust button type for m in fig.layout.updatemenus: m['type'] = 'buttons' f = fig.full_figure_for_development(warn=False) fig.show()

Solution 1

In order to make it possible to toggle any trace on and off without affecting the others, it seems you’ll have to include one updatemenu per button. There might be other ways to do it, but the code snippet below will produce the following plot:

Plot 1 – At launch all are selected

enter image description here

Plot 2 – C and D toggled off

enter image description here

Plot 3 – All off

enter image description here

Plot 4 – All on

enter image description here

Complete code:

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import datetime
import plotly.express as px

periods = 200
cols = list('ABCD')

np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(periods, len(cols))),
                  columns=cols)
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),
                         periods=periods).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum()

# # plotly
fig = go.Figure()
colors = px.colors.qualitative.Plotly

# set up multiple traces
for col in df.columns:
    fig.add_trace(go.Scatter(x=df.index,
                             y=df[col],
                             name  = col,
                             visible=True
                            )
                 )

um = [ {} for _ in range(len(df.columns)) ]
buttons = []
menuadjustment = 0.15

buttonX = -0.1
buttonY = 1 + menuadjustment
for i, col in enumerate(df.columns):
    button = dict(method='restyle',
                  label=col,
                  visible=True,
                  args=[{'visible':True,
                         'line.color' : colors[i]}, [i]],
                  args2 = [{'visible': False,
                            'line.color' : colors[i]}, [i]],
                 )
    
    # adjust some button features
    buttonY = buttonY-menuadjustment
    um[i]['buttons'] = [button]
    um[i]['showactive'] = False
    um[i]['y'] = buttonY
    um[i]['x'] = buttonX

# add a button to toggle all traces on and off
button2 = dict(method='restyle',
               label='All',
               visible=True,
               args=[{'visible':True}],
               args2 = [{'visible': False}],
               )
# assign button2 to an updatemenu and make some adjustments
um.append(dict())
um[i+1]['buttons'] = [button2]
um[i+1]['showactive'] = True
um[i+1]['y']=buttonY - menuadjustment
um[i+1]['x'] = buttonX
    
# add dropdown menus to the figure
fig.update_layout(showlegend=True, updatemenus=um)

# adjust button type
for m in fig.layout.updatemenus:
    m['type'] = 'buttons'

f = fig.full_figure_for_development(warn=False)
fig.show()

Original Author vestland Of This Content

Solution 2

After a decent bit of searching, I have been able to figure it out thanks to this answer on the Plotly forum. I have not been able to find somewhere that lists all of these options yet, but that would be very helpful.

It appears that the list given to ‘visible’ in the args dictionary does not need to be only booleans. In order to keep the items visible in the legend but hidden in the plot, you need to set the values to ‘legendonly’. The legend entries can then still be clicked to toggle individual visibility. That answers the main thrust of my question.

args = [{'visible': True}]
args = [{'visible': 'legendonly'}]
args = [{'visible': False}]

Vestland’s answer helped solve the second part of my question, only modifying the traces I want and leaving everything else the same. It turns out that you can pass a list of indices after the dictionary to args and those args will only apply to the traces at the indices provided. I used list comprehension in the example to find the traces that match the given name. I also added another trace for each column to show how this works for multiple traces.

args = [{'key':arg}, [list of trace indices to apply key:arg to]]

Below is the now working code.

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import datetime

# mimic OP's datasample

NPERIODS = 200

np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(NPERIODS, 4)),
                  columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),
                         periods=NPERIODS).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum()

# set up multiple traces
traces = []
buttons = []
for col in df.columns:
    traces.append(go.Scatter(x=df.index,
                            y=df[col],
                            visible=True,
                            name=col)
                )
    traces.append(go.Scatter(x=df.index,
                y=df[col]+20,
                visible=True,
                name=col)
    )
    buttons.append(dict(method='restyle',
                        label=col,
                        visible=True,
                        args=[{'visible':True},[i for i,x in enumerate(traces) if x.name == col]],
                        args2=[{'visible':'legendonly'},[i for i,x in enumerate(traces) if x.name == col]]
                        )
                )

allButton = [
    dict(
        method='restyle',
        label=col,
        visible=True,
        args=[{'visible':True}],
        args2=[{'visible':'legendonly'}]
    )
]

# create the layout 
layout = go.Layout(
    updatemenus=[
        dict(
            type='buttons',
            direction='right',
            x=0.7,
            y=1.3,
            showactive=True,
            buttons=allButton + buttons
        )
    ],
    title=dict(text='Toggle Traces',x=0.5),
    showlegend=True
)

fig = go.Figure(data=traces,layout=layout)

# add dropdown menus to the figure
fig.show()

This gives the following functionality:

the “All” button can toggle visibility of all traces.

Each other button will only toggle the traces with the matching name. Those traces will still be visible in the legend and can be turned back to visible by clicking on them in the legend or clicking the button again.

starting plot with all traces visible

After clicking the “B” button (twice to hit arg2).

plot after clicking B button twice

And then after clicking the first B trace in the legend.

plot after enabling first B trace again

Original Author edited Feb 2, 2021 at 15:26 Of This Content

Conclusion

So This is all About This Tutorial. Hope This Tutorial Helped You. Thank You.

Also Read,

ittutorial team

I am an Information Technology Engineer. I have Completed my MCA And I have 4 Year Plus Experience, I am a web developer with knowledge of multiple back-end platforms Like PHP, Node.js, Python and frontend JavaScript frameworks Like Angular, React, and Vue.

Leave a Comment