How to remove the duplicate legend when overlaying boxplot and stripplot

Question:

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.

Asked By: Sergey Antopolskiy

||

Answers:

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

Answered By: Ffisegydd

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()
Answered By: BossaNova
  • 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)

sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, ax=ax)

sns.move_legend(ax, bbox_to_anchor=(1, 1.02), loc='upper left')

enter image description here

Answered By: Trenton McKinney
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.