0
votes

Some plotting tools such as seaborn automatically label plots and add a legend. Removing the legend is fairly easy with ax.get_legend().remove(), but the handles and labels are still stored somewhere in the axes object. Thus when adding another line or other plot type, for which I want to have a legend, the "old" legend handles and labels are also displayed. In other words: ax.get_legend_handles_labels() still returns the labels and handles introduced by f.i. seaborn.

Is there any way to completely remove the handles and labels to be used for a legend from an axis? Such that ax.get_legend_handles_labels() returns ([], [])?

I know that I can set sns.lineplot(..., legend=False) in seaborn. I am just using seaborn to produce a nice example. The question is how to remove existing legend labels and handles in general.

Here is a minimum working example:

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

df = pd.DataFrame(data={
    'x': [0, 1, 0, 1], 
    'y': [7.2, 10., 11.2, 3.],
    'id': [4, 4, 7, 7]
})
# get mean per x location:
grpd = df.groupby('x').mean()

ax = sns.lineplot(data=df, x='x', y='y', hue='id', palette=['b', 'g'])
ax.get_legend().remove()

# this still returns the handles and labels:
print(ax.get_legend_handles_labels())
# Out: 
# ([<matplotlib.lines.Line2D at 0x17c1c208f88>,
#   <matplotlib.lines.Line2D at 0x17c1c1f8888>,
#   <matplotlib.lines.Line2D at 0x17c1c20af88>],
#  ['id', '4', '7'])

# plot some other lines, for which I want to have a legend:
ax.plot(grpd.index, grpd['y'], label='mean')
# add legend, ONLY FOR MEAN, if possible:
ax.legend()

I tried manipulating the axes ax.get_legend() and ax.legend_ objects, f.i. with ax.legend_.legendHandles = [], setting the labels, texts and everything else, but the "old" seaborn entries keep reappiering each time.

Since this is meant to be used within a module, it is no applicable to explicitly set a legend only containing the "mean"-entry. I need to "clear" everything happening in the backend to provide a clean API to the user.

2

2 Answers

1
votes

Change:

ax = sns.lineplot(data=df, x='x', y='y', hue='id', palette=['b', 'g'])

to:

ax = sns.lineplot(data=df, x='x', y='y', hue='id', palette=['b', 'g'], legend=False)

That way you also don't need to make the call to remove the legend as none is created in the first place.

EDIT UPDATE:

If you want to clear the legend handles and label, I think you'll have to do something like:

for line in ax.lines: # put this before you call the 'mean' plot function.
    line.set_label(s='')

This will loop over the lines draw on the plot and then set their label to be empty. That way the legend doesn't consider them and the print(ax.get_legend_handles_labels()) will return empty iterables.

1
votes

Thanks at mullinscr for the answer considering line plots. To remove legend labels in general, the labels of lines, collections (f.i. scatter plots), patches (boxplots etc) and images (imshow etc.) must be cleared:

for _artist in ax.lines + ax.collections + ax.patches + ax.images:
    _artist.set_label(s=None)

Please tell me, if I forgot to mention a specific kind of plot.