Search code examples
python-3.xplotlylegend

Plotly: associate a trace to multiple legendgroup


I'm using plotly and python 3.9 to display traces on a plot.

My figure have multiple subplots:

fig = make_subplots(rows=numberOfRows, cols=1, 
                    horizontal_spacing = 0.04,
                    vertical_spacing=0.04,
                    shared_xaxes=True)
for key, value in filtered_dict.items():
    fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y)[key], hovertemplate=hoverY1,
                                     marker=dict(color=colorList[c]), name=key, legendgroup=key),
                                      row=1, col=1,)
    fig.add_trace(go.Scatter(x=eval(data_to_plot_X)[key], y=eval(data_to_plot_Y2)[key], hovertemplate=hoverY2, 
                                         marker=dict(color=colorList[c]), name=key, legendgroup=key, showlegend=False),
                                          row=2, col=1,)
fig.update_layout(hovermode="x")
fig.show()    

So for each key (corresponding to tests), there is a trace on each subplot and only 1 legend for each key. With the legendgroup, I can activate or deactivate the trace of a key in all subplots in a single click.

Now, I have a lot of keys in my dict, corresponding to multiple groups of tests. I would like it if, with a single click on the legend, I could also deactivate or activate all traces of a group of tests.

But so far I have not managed to succeed, it seems that each trace can only have 1 legend group. Is there a way to achieve that? Thanks


Solution

  • In Plotly, you can only assign a trace to one legendgroup, but you can achieve your goal by using nested legend groups with Plotly buttons. This allows you to group traces by multiple criteria and toggle them collectively.

    import plotly.graph_objects as go
    from plotly.subplots import make_subplots
    
    
    data_to_plot_X = {'test1': [1, 2, 3], 'test2': [1, 2, 3], 'test3': [1, 2, 3]}
    data_to_plot_Y = {'test1': [2, 4, 6], 'test2': [3, 6, 9], 'test3': [4, 8, 12]}
    data_to_plot_Y2 = {'test1': [1, 3, 5], 'test2': [2, 4, 6], 'test3': [3, 5, 7]}
    
    group_dict = {
        'group1': ['test1', 'test2'],
        'group2': ['test3']
    }
    
    colorList = ['blue', 'green', 'red']
    
    fig = make_subplots(rows=2, cols=1, shared_xaxes=True)
    
    c = 0
    for key in data_to_plot_X.keys():
        fig.add_trace(go.Scatter(
            x=data_to_plot_X[key], 
            y=data_to_plot_Y[key], 
            hovertemplate='Y1', 
            marker=dict(color=colorList[c]), 
            name=key, 
            legendgroup=key  # Control visibility by key across subplots
        ), row=1, col=1)
    
        fig.add_trace(go.Scatter(
            x=data_to_plot_X[key], 
            y=data_to_plot_Y2[key], 
            hovertemplate='Y2', 
            marker=dict(color=colorList[c]), 
            name=key, 
            legendgroup=key, 
            showlegend=False  # Hide duplicate legends in the second subplot
        ), row=2, col=1)
        
        c += 1
    
    buttons = []
    for group_name, tests in group_dict.items():
        # Create a list of visibility booleans for traces in the group
        visibility = []
        for key in data_to_plot_X.keys():
            if key in tests:
                visibility += [True, True]  # Show both traces (one for each subplot)
            else:
                visibility += [False, False]  # Hide both traces
        buttons.append(dict(label=group_name,
                            method='update',
                            args=[{'visible': visibility}]))
    
    # Add buttons to the layout
    fig.update_layout(
        updatemenus=[dict(
            type="buttons",
            direction="down",
            buttons=buttons,
            showactive=True
        )],
        hovermode="x"
    )
    
    fig.show()