How to remove the duplicate legend when overlaying boxplot and stripplot

28.6k views Asked by At

One of the coolest things you can easily make in seaborn is boxplot + stripplot combination:

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

tips = sns.load_dataset("tips")

sns.stripplot(x="day", y="total_bill", hue="smoker",
data=tips, jitter=True,
palette="Set2", dodge=True,linewidth=1,edgecolor='gray')

sns.boxplot(x="day", y="total_bill", hue="smoker",
data=tips,palette="Set2",fliersize=0)

plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.);

enter image description here

Unfortunately, as you can see above, it produced double legend, one for boxplot, one for stripplot. Obviously, it looks ridiculous and redundant. But I cannot seem to find a way to get rid of stripplot legend and only leave boxplot legend. Probably, I can somehow delete items from plt.legend, but I cannot find it in the documentation.

3

There are 3 answers

0
Ffisegydd On BEST ANSWER

You can get what handles/labels should exist in the legend before you actually draw the legend itself. You then draw the legend only with the specific ones you want.

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
    
tips = sns.load_dataset("tips")

sns.stripplot(x="day", y="total_bill", hue="smoker", data=tips, jitter=True, palette="Set2", dodge=True, linewidth=1, edgecolor='gray')

# Get the ax object to use later.
ax = sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set2", fliersize=0)

# Get the handles and labels. For this example it'll be 2 tuples
# of length 4 each.
handles, labels = ax.get_legend_handles_labels()

# When creating the legend, only use the first two elements
# to effectively remove the last two.
l = plt.legend(handles[0:2], labels[0:2], bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)

example plot

0
BossaNova On

I want to add that if you use subplots, the legend handling might be a bit more problematic. The code above, which gives a very nice figure by the way (@Sergey Antopolskiy and @Ffisegydd), will not relocate the legend in a subplot, which keeps appearing very stubbornly. See code above adapted to subplots:

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

tips = sns.load_dataset("tips")

fig, axes = sns.plt.subplots(2,2)

sns.stripplot(x="day", y="total_bill", hue="smoker",
              data=tips, jitter=True, palette="Set2", 
              split=True,linewidth=1,edgecolor='gray', ax = axes[0,0])

ax = sns.boxplot(x="day", y="total_bill", hue="smoker",
                 data=tips,palette="Set2",fliersize=0, ax = axes[0,0])

handles, labels = ax.get_legend_handles_labels()

l = plt.legend(handles[0:2], labels[0:2], bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)

duplicated legend

The original legend remains. In order to erase it, you can add this line:

axes[0,0].legend(handles[:0], labels[:0])

corrected legend

Edit: in recent versions of seaborn (>0.9.0), this used to leave a small white box in the corner as pointed in the comments. To solve it use the answer in this post:

axes[0,0].get_legend().remove()
0
Trenton McKinney On
  • sns.stripplot: set legend=None
  • Tested in python 3.11.2, pandas 2.0.0, matplotlib 3.7.1, seaborn 0.12.2
import seaborn as sns

# sample data
tips = sns.load_dataset("tips")

# plot the stipplot without a legend
ax = sns.stripplot(x="day", y="total_bill", hue="smoker", data=tips, jitter=True, dodge=True, edgecolor='k', linewidth=1, legend=None)

# add the boxplot to the same axes
sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, ax=ax)

# move the legend
sns.move_legend(ax, bbox_to_anchor=(1, 0.5), loc='center left', frameon=False)

# remove spines
ax.spines[['top', 'right']].set_visible(False)

enter image description here