如何在 seaborn 的 stripplot 图例中更改标记
How to change the marker in a stripplot legend in seaborn
seaborn stripplot 的图例仅显示彩色圆圈,但是,标记形状不符合我设置的标记。
复制代码:
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()
图输出:
目标:将图例中的彩色圆圈改为菱形和三角形。
图例句柄都是matplotlib.collections.PathCollection
对象,没有明显的改变方式。我还在 GitHub 上发现了一个未解决的问题:https://github.com/mwaskom/seaborn/issues/940(供参考)。
有没有人知道如何手动设置图例中的标记或其他一些快速的方法?
这似乎是 seaborn 的 github 中的一个 open issue。解决方法可能是手动创建图例句柄:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
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)
fig, ax = plt.subplots(1, 1, figsize=(10, 7))
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)]
marker_set1 = '^'
marker_set2 = 'D'
marker_for_letter = {**{letter: marker_set1 for letter in letter_set1},
**{letter: marker_set2 for letter in letter_set2}}
sns.stripplot(data=set1_df, x='place', y="weight", hue="letter",
palette="Set1", size=10, linewidth=0.05, marker=marker_set1, ax=ax)
sns.stripplot(data=set2_df, x='place', y="weight", hue="letter",
palette="Set2", size=10, linewidth=0.05, marker=marker_set2, ax=ax)
handles, labels = ax.get_legend_handles_labels()
handles = [Line2D([], [], color=h.get_facecolor(), linestyle='',
marker=marker_for_letter[l])
for h, l in zip(handles, labels)]
labels, handles = zip(*sorted(zip(labels, handles)))
ax.legend(handles, labels, title="Letter", bbox_to_anchor=(1.01, 1.01), loc="upper left")
plt.tight_layout()
plt.show()
根据 GitHub 上 comments in the open issue #940 的提示,我相应地修改了代码以获得临时解决方案。
我修改了这个文件://lib/python3.8/site-packages/seaborn/categorical.py
L1084 附近:将 kws={"marker": "o"}
添加到输入参数,将 marker=kws['marker'],
添加到传递给 ax.scatter()
的输入参数:
def add_legend_data(self, ax, kws={"marker": "o"}):
"""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)
L1162附近,在self.add_legend_data(ax)
中添加kws
:
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()
缺点可能是,您可能每次都必须将 marker
参数传递给 stripplot()
函数,否则我们会得到 KeyError。此外,此方法不可移植。您必须在所有机器上以这种方式编辑。
风险自负。
seaborn stripplot 的图例仅显示彩色圆圈,但是,标记形状不符合我设置的标记。
复制代码:
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()
图输出:
目标:将图例中的彩色圆圈改为菱形和三角形。
图例句柄都是matplotlib.collections.PathCollection
对象,没有明显的改变方式。我还在 GitHub 上发现了一个未解决的问题:https://github.com/mwaskom/seaborn/issues/940(供参考)。
有没有人知道如何手动设置图例中的标记或其他一些快速的方法?
这似乎是 seaborn 的 github 中的一个 open issue。解决方法可能是手动创建图例句柄:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
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)
fig, ax = plt.subplots(1, 1, figsize=(10, 7))
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)]
marker_set1 = '^'
marker_set2 = 'D'
marker_for_letter = {**{letter: marker_set1 for letter in letter_set1},
**{letter: marker_set2 for letter in letter_set2}}
sns.stripplot(data=set1_df, x='place', y="weight", hue="letter",
palette="Set1", size=10, linewidth=0.05, marker=marker_set1, ax=ax)
sns.stripplot(data=set2_df, x='place', y="weight", hue="letter",
palette="Set2", size=10, linewidth=0.05, marker=marker_set2, ax=ax)
handles, labels = ax.get_legend_handles_labels()
handles = [Line2D([], [], color=h.get_facecolor(), linestyle='',
marker=marker_for_letter[l])
for h, l in zip(handles, labels)]
labels, handles = zip(*sorted(zip(labels, handles)))
ax.legend(handles, labels, title="Letter", bbox_to_anchor=(1.01, 1.01), loc="upper left")
plt.tight_layout()
plt.show()
根据 GitHub 上 comments in the open issue #940 的提示,我相应地修改了代码以获得临时解决方案。
我修改了这个文件:/
L1084 附近:将 kws={"marker": "o"}
添加到输入参数,将 marker=kws['marker'],
添加到传递给 ax.scatter()
的输入参数:
def add_legend_data(self, ax, kws={"marker": "o"}):
"""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)
L1162附近,在self.add_legend_data(ax)
中添加kws
:
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()
缺点可能是,您可能每次都必须将 marker
参数传递给 stripplot()
函数,否则我们会得到 KeyError。此外,此方法不可移植。您必须在所有机器上以这种方式编辑。
风险自负。