Sergey Antopolskiy
Sergey Antopolskiy

Reputation: 4290

How to remove the duplicate legend when overlaying boxplot and stripplot

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.

Upvotes: 48

Views: 29789

Answers (3)

Trenton McKinney
Trenton McKinney

Reputation: 62403

  • 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

Upvotes: 4

Ffisegydd
Ffisegydd

Reputation: 53678

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

Upvotes: 57

BossaNova
BossaNova

Reputation: 1527

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()

Upvotes: 9

Related Questions