0

I have 2 sets of boxplots, one set in blue color and another in red color. I want the legend to show the label for each set of boxplots, i.e.

Legend: -blue box- A, -red box- B

Added labels='A' and labels='B' within sns.boxplot(), but didn't work with error message "No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument". How do I add the labels?

enter image description here

code for the inserted image:

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

x = list(range(1,13))
n = 40
index = [item for item in x for i in range(n)]

np.random.seed(123)

df = pd.DataFrame({'A': np.random.normal(30, 2, len(index)),
                   'B': np.random.normal(10, 2, len(index))},
                   index=index)

red_diamond = dict(markerfacecolor='r', marker='D')
blue_dot = dict(markerfacecolor='b', marker='o')

plt.figure(figsize=[10,5])
ax = plt.gca()
ax1 = sns.boxplot( x=df.index, y=df['A'], width=0.5, color='red', \
    boxprops=dict(alpha=.5), flierprops=red_diamond, labels='A')
ax2 = sns.boxplot( x=df.index, y=df['B'], width=0.5, color='blue', \
    boxprops=dict(alpha=.5), flierprops=blue_dot, labels='B')
plt.ylabel('Something')
plt.legend(loc="center", fontsize=8, frameon=False)

plt.show()

Here are the software versions I am using: seaborn version 0.11.2. matplotlib version 3.5.1. python version 3.10.1

PeterJames
  • 1,137
  • 1
  • 9
  • 17
  • Since we don't have the data to classify the colors, we need to create a legend handler and labels. Please refer to [How to manually create a legend](https://stackoverflow.com/questions/39500265/how-to-manually-create-a-legend). – r-beginners Feb 22 '22 at 06:46
  • Follow this guide and it works with the following code: `red_patch = mpatches.Patch(color='red', label='A') blue_patch = mpatches.Patch(color='blue', label='B') plt.legend(handles=[red_patch, blue_patch], loc="center right", fontsize=8, frameon=False)` – montvinpeck Feb 24 '22 at 03:05
  • @montvinpeck You could also set alpha and edge color for the patches to make them more similar to the boxplots, e.g. `red_patch = mpatches.Patch(facecolor='red', alpha=0.5, edgecolor='black', label='A')` – JohanC Feb 24 '22 at 08:55

1 Answers1

0

The following approach sets a label via the boxprops, and creates a legend using part of ax.artists. (Note that ax, ax1 and ax2 of the question's code are all pointing to the same subplot, so here only ax is used.)

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

x = np.arange(1, 13)
index = np.repeat(x, 40)
np.random.seed(123)
df = pd.DataFrame({'A': np.random.normal(30, 2, len(index)),
                   'B': np.random.normal(10, 2, len(index))},
                  index=index)
red_diamond = dict(markerfacecolor='r', marker='D')
blue_dot = dict(markerfacecolor='b', marker='o')

plt.figure(figsize=[10, 5])
ax = sns.boxplot(data=df, x=df.index, y='A', width=0.5, color='red',
                 boxprops=dict(alpha=.5, label='A'), flierprops=red_diamond)
sns.boxplot(data=df, x=df.index, y='B', width=0.5, color='blue',
            boxprops=dict(alpha=.5, label='B'), flierprops=blue_dot, ax=ax)
ax.set_ylabel('Something')

handles, labels = ax.get_legend_handles_labels()
handles = [h for h, lbl, prev in zip(handles, labels, [None] + labels) if lbl != prev]
ax.legend(handles=handles, loc="center", fontsize=8, frameon=False)

plt.show()

sns.boxplot legend labels from artists

Alternative approaches could be:

  • pd.melt the dataframe to long form, so hue could be used; a problem here is that then the legend wouldn't take the alpha from the boxprops into account; also setting different fliers wouldn't be supported
  • create a legend from custom handles
JohanC
  • 71,591
  • 8
  • 33
  • 66