Home > Net >  How to change the marker in a stripplot legend in seaborn?
How to change the marker in a stripplot legend in seaborn?

Time:09-24

The legend of a seaborn stripplot only shows the colored circles, however, the marker shapes are not respecting the marker that I set.

Code for reproduction:

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

letters = list('abcdefghijklmnopqrstuvwxyz')
place = ['North', 'South', 'East', 'West']

letter_set1 = set("abcdefghijklmn")
letter_set2 = set("opqrstuvwxyz")

data_size = 100

df_dict = {'letter': np.random.choice(letters, data_size), 
           'place': np.random.choice(place, data_size),
           "height": np.random.randint(low=40, high=100, size=data_size),
          "weight": np.random.randint(low=150, high=210, size=data_size),}

df = pd.DataFrame(df_dict)
print(df)

fig, ax = plt.subplots(1, 1, figsize=(10, 7))

# We can ignore the violinplot
sns.violinplot(x='place', y="weight", data=df, scale="width", inner="quartile", bw=0.2, linewidth=1,
)
for violin in ax.collections:
    violin.set_alpha(0.1)

set1_df = df[df['letter'].isin(letter_set1)]
set2_df = df[df['letter'].isin(letter_set2)]

sns.stripplot(data=set1_df, x='place', y="weight", hue="letter", palette="Set1", size=10, linewidth=0.05, marker='^', ax=ax
)
sns.stripplot(data=set2_df, x='place', y="weight", hue="letter", palette="Set2", size=10, linewidth=0.05, marker='D', ax=ax
)

# Update the legend oreder
handles, labels = ax.get_legend_handles_labels()
zipped_list = zip(handles, labels)
sorted_zipped_list = sorted(zipped_list, key=lambda x: x[1])
ordered_handles, ordered_labels = [x[0] for x in sorted_zipped_list], [x[1] for x in sorted_zipped_list]

ax.legend(
    handles=ordered_handles,
    labels=ordered_labels,
    title="Letter",
    bbox_to_anchor=(1.02, 1),
    loc="upper left",
    )
plt.tight_layout()

plt.show()
plt.close()

Figure output: output

Goal: change the colored circles in the legend to diamonds and triangles.

The legend handles are all matplotlib.collections.PathCollection objects, which do not have obvious way to change. I also found an open issue on GitHub: stripplot with custom markers and updated legend

CodePudding user response:

Followed by the hint from the comments in the open issue #940 on GitHub, I modified the code accordingly to have a temporary solution.

I modified this file: /<your_python_dir>/lib/python3.8/site-packages/seaborn/categorical.py

Around L1084: add kws=None to the input argument, add marker=kws['marker'], to the input argument passed into ax.scatter():

def add_legend_data(self, ax, kws=None):
"""Add empty scatterplot artists with labels for the legend."""
    if self.hue_names is not None:
        for rgb, label in zip(self.colors, self.hue_names):
            ax.scatter([], [],
                       color=mpl.colors.rgb2hex(rgb),
                       label=label,
                       marker=kws['marker'],
                       s=60)

Around L1162, add kws to self.add_legend_data(ax):

def plot(self, ax, kws):
    """Make the plot."""
    self.draw_stripplot(ax, kws)
    self.add_legend_data(ax, kws)
    self.annotate_axes(ax)
    if self.orient == "h":
        ax.invert_yaxis()

The downside could be, you may have to pass the marker argument to the stripplot() function every time, otherwise we get a KeyError.

Proceed at your own risk.

  • Related