Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Seaborn boxplot + stripplot: duplicate legend

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", split=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.);

boxplot+stripplot

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.

like image 763
Sergey Antopolskiy Avatar asked Feb 21 '16 16:02

Sergey Antopolskiy


2 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", split=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

like image 65
Ffisegydd Avatar answered Oct 20 '22 15:10

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()
like image 7
BossaNova Avatar answered Oct 20 '22 16:10

BossaNova