4

I have been playing a bit with plt.legend() and ax.legend() and legend from seaborn itself and I think I'm missing something.

My first question is, could someone please explain to me how those go together, how they work and if I have subplots, what is superior to what? Meaning can I set a general definition (eg. have this legend in all subplots in this loc) and then overwrite this definition for specific subplots (eg by ax.legend())?

My second question is practical and showing my problems. Let's take the seaborn Smokers data set to illustrate it on:

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

# define sizes for labels, ticks, text, ...
# as defined here https://stackoverflow.com/questions/3899980/how-to-change-the-font-size-on-a-matplotlib-plot
SMALL_SIZE = 10
MEDIUM_SIZE = 14
BIGGER_SIZE = 18

plt.rc('font', size=SMALL_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=BIGGER_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=MEDIUM_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=MEDIUM_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE)    # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title


# create figure
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, figsize=(16,12))
ylim = (0,1)

sns.boxplot(x= 'day', y= 'tip', hue="sex",
                  data=tips, palette="Set2", ax=ax1)
sns.swarmplot(x= 'day', y= 'tip', hue="sex",
                  data=tips, palette="Set2", ax=ax2)
ax2.legend(loc='upper right')

sns.boxplot(x= 'day', y= 'total_bill', hue="sex",
                  data=tips, palette="Set2", ax=ax3)
sns.swarmplot(x= 'day', y= 'total_bill', hue="sex",
                  data=tips, palette="Set2", ax=ax4)


plt.suptitle('Smokers')
plt.legend(loc='upper right')

plt.savefig('test.png', dpi = 150)

Plot example.

If I use simply seaborn, I get a legend as in Subplot 1 and 3 -- it has the 'hue' label and follows defined font size. However, I'm not able to control its location (it has some default, see the difference between 1 and 3). If I use ax.legend() as in Subplot 2, then I can modify specific subplot but I lose the seaborn 'hue' feature (notice that the "sex" disappears) and it does not follow my font definitions. If I use plt.legend(), it only affects the Subplot before it (Subplot 4 in this case). How can I unite all this? Eg. to have one definition for all subplots or how to control the seaborn default? To make clear goal, how to have a legend as in Subplot 1 where the labels come automatically from the data (but I can change them) and the location, font size, ... is set the same for all the subplots (eg. upper right, font size of 10, ...)?

Thank you for help and explanation.

My Work
  • 2,143
  • 2
  • 19
  • 47
  • I don't understand what's wrong with the legend in the second subplot. It has the right labels, the correct location and the defined fontsize, does is not? – ImportanceOfBeingErnest Jan 26 '20 at 12:45
  • No, it does not. First, I would have to add this to all subplots, it's not global. Second, the font should look like in the 1st or 3rd (bigger) and third, the label of the hue is missing, the label of the group ('sex') -- seaborn puts this there automatically. – My Work Jan 26 '20 at 13:42

1 Answers1

6

Seaborn legends are always called with the keyword loc=best. This is hardcoded in the sourcecode. You could change the sourcecode, e.g. in this line and replace by ax.legend(). Then setting the rc parameter in your code like

plt.rc('legend', loc="upper right")

would give the desired output.

The only other option is to create the legend manually, like you do in the second case,

ax2.legend(loc="upper right", title="sex", title_fontsize="x-large")
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks for the link. So why does not plt.legend() override the seaborn default? Or where does it stand? – My Work Jan 27 '20 at 08:11
  • I don't think I understand the question, sorry. – ImportanceOfBeingErnest Jan 27 '20 at 12:26
  • What I mean is that ax.legend() overrides the default, the sourcecode of seaborn. Why plt.legend() does not? One more question. Why does the font size defined by `plt.rc('legend', fontsize=SMALL_SIZE)` affect the legend (change it and see the change in font size) and `plt.rc('legend', loc="upper right")` does not? – My Work Jan 29 '20 at 10:01
  • 1
    Both `ax.legend` and `plt.legend` override the legend created by seaborn, there is no difference. `plt.rc('legend', loc="upper right")` does not change the location of the seaborn legend because seaborn explicitely calls `plt.legend(loc="best")`, as shown in the link in this answer. – ImportanceOfBeingErnest Jan 29 '20 at 13:31