21

How can I avoid duplicate legend labels in subplots? One way I would go about it in matplotlib would be to pass custom legend labels to an legend object. I couldn't find any documentation for an equivalent option in plotly. Any ideas?

traces = []

colors = {'Iris-setosa': 'rgb(31, 119, 180)', 
          'Iris-versicolor': 'rgb(255, 127, 14)', 
          'Iris-virginica': 'rgb(44, 160, 44)'}

for col in range(4):
    for key in colors:
        traces.append(Histogram(x=X[y==key, col], 
                        opacity=0.75,
                        xaxis='x%s' %(col+1),
                        marker=Marker(color=colors[key]),
                        name=key
                        )
                     )

data = Data(traces)

layout = Layout(barmode='overlay',
                xaxis=XAxis(domain=[0, 0.25], title='sepal length (cm)'),
                xaxis2=XAxis(domain=[0.3, 0.5], title='sepal width (cm)'),
                xaxis3=XAxis(domain=[0.55, 0.75], title='petal length (cm)'),
                xaxis4=XAxis(domain=[0.8, 1], title='petal width (cm)'),
                yaxis=YAxis(title='count'),
                title='Distribution of the different Iris flower features')

fig = Figure(data=data, layout=layout)

py.iplot(fig)

enter image description here

3 Answers3

17

Plotly controls this on the trace level. Try passing in showlegend=False inside the Histogram traces that you don't want to appear in the legend.

Reference: https://plot.ly/python/reference/#Histogram-showlegend

Example: https://plot.ly/python/legend/#Hiding-Legend-Entries

Direct copy-paste from the link above.

import plotly.plotly as py
from plotly.graph_objs import *
# Fill in with your personal username and API key
# or, use this public demo account
py.sign_in('Python-Demo-Account', 'gwt101uhh0')

trace1 = Scatter(
    x=[0, 1, 2],
    y=[1, 2, 3],
    name='First Trace',
    showlegend=False
)
trace2 = Scatter(
    x=[0, 1, 2, 3],
    y=[8, 4, 2, 0],
    name='Second Trace',
    showlegend=True
)
data = Data([trace1, trace2])
plot_url = py.plot(data, filename='show-legend')

The usage you want to see is shown in trace1 above.

WGS
  • 13,969
  • 4
  • 48
  • 51
Chris P
  • 2,888
  • 21
  • 22
  • 8
    @SebastianRaschka But if the legend for `trace1` is hidden, then you can't control or hide `trace1`'s traces. – Jossie Calderon Jul 07 '16 at 16:46
  • 1
    If one need to generate plots in a loop, you can use a `set` to keep track of which trace name have been added to the Figure yet, then use `showlegend=True if tracename not in set_tracenames else False` – LucG Feb 12 '20 at 08:03
  • @JossieCalderon AmourK's answer handles this using `legendgroup`. There's a useful example here: https://plotly.com/python/legend/#grouped-legend-items – mckbrd Jun 19 '23 at 06:35
14

This is a code snippet I came up with which avoids to set showlegend=False manually on each trace with a duplicate name.

names = set()
fig.for_each_trace(
    lambda trace:
        trace.update(showlegend=False)
        if (trace.name in names) else names.add(trace.name))

fig.for_each_trace calls the passed function for each trace. The function keeps track of which legend names already occurred (via the set names, like @LucG proposed in a comment), and hides legend entries for duplicate (or triplicate, ...) names.

The code needs to be run after all traces have been added to the figure, and before it is shown.

A. Donda
  • 8,381
  • 2
  • 20
  • 49
  • 1
    Great answer. @Chris P's answer does not work for plotly timeline, this anwer does. Thank you A. Donda. – Hedge92 Nov 28 '22 at 14:19
10

A better way:

Set the legendgroup option to the legend label you want for each trace. This will allow you to filter everything in the same group.

Hide the remaining traces' legends using the showlegend=False option.

This will give the exact behaviour you want.

Old Solution (Not recommended):

There is a another solution by adding "dummy" traces and hiding the data but only showing the legend. With this method you cannot slice any of the data (which is not a bad thing).

trace_dummy = Scatter(
    x=[0, 0, 0], # Data is irrelevant since it won't be shown
    y=[0, 0, 0],
    name='Whatever Trace',
    showlegend=True,
    visible="legendonly"
)
AmourK
  • 720
  • 1
  • 8
  • 20
  • 2
    I don't understand how one would use `legendgroup` for this purpose. Can you elaborate? – A. Donda Jun 02 '20 at 22:13
  • 1
    Take a look at the grouped legend section of `https://plotly.com/python/legend/#grouped-legend` – AmourK Jun 11 '20 at 13:24
  • 2
    @A.Donda If u wanna filter out data belonging to a legend, using `showlegend=False` will only filter out part of the data, since the other part's legend is hidden. But if u use `legendgroup`, filtering out the part that is shown in the legend will also filter out the hidden one, since they are in the same legend group – tjysdsg Jul 28 '20 at 03:22