Seaborn 散点图设置空心标记而不是填充标记

Seaborn scatterplot set hollow markers instead of filled markers

如何使用 Seaborn 散点图将标记设置为空心圆而不是实心圆?

这是一个简单的例子:

import pandas as pd
import seaborn as sns

df = pd.DataFrame(
    {'x': [3,2,5,1,1,0],
     'y': [1,1,2,3,0,2],
     'cat': ['a','a','a','b','b','b']}
)

sns.scatterplot(data=df, x='x', y='y', hue='cat')

我尝试了以下方法但没有成功;其中大部分不会抛出错误,而是产生与上面相同的图。我认为这些不起作用,因为颜色是使用 hue 参数设置的,但我不确定修复方法是什么。

sns.scatterplot(data=df, x='x', y='y', hue='cat', facecolors = 'none')
sns.scatterplot(data=df, x='x', y='y', hue='cat', facecolors = None)
sns.scatterplot(data=df, x='x', y='y', hue='cat', markerfacecolor = 'none')
sns.scatterplot(data=df, x='x', y='y', hue='cat', markerfacecolor = None)

with sns.plotting_context(rc={"markerfacecolor": None}):
    sns.scatterplot(data=df, x='x', y='y', hue='cat')

我使用 seaborn.FacetGrid 修改了您的代码,如下所示(基于 @cphlewis 此处提供的 ):

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

x1 = [3,2,5]
y1 = [1,1,2]
x2 = [1,1,0]
y2 = [3,0,2]

df1 = pd.DataFrame({'x1':x1, 'y1':y1})
df2 = pd.DataFrame({'x2':x2, 'y2':y2})

df = pd.concat([df1.rename(columns={'x1':'x','y1':'y'})
                .join(pd.Series(['a']*len(df1), name='df')),
                df2.rename(columns={'x2':'x','y2':'y'})
                .join(pd.Series(['b']*len(df2), name='df'))],
               ignore_index=True)

pal = dict(a="blue", b="red")

g = sns.FacetGrid(df, hue='df', palette=pal, size=5)

g.map(plt.scatter, "x", "y", s=50, alpha=.7,
      linewidth=.5,
      facecolors = 'none',
      edgecolor=['red', 'blue'])

markers = [plt.Line2D([0,0],[0,0], markeredgecolor=pal[key],
                      marker='o', markerfacecolor='none',
                      mew=0.3,
                      linestyle='')
            for key in pal]

plt.legend(markers, pal.keys(), numpoints=1, title="cat")
plt.show()

结果如下:

原则上,您应该能够使用 fillstyle="none" 创建一个圆形标记,但那里有一些 deep complications,但它目前无法像您希望的那样工作。

最简单的纯 seaborn 解决方案是利用您可以使用任意乳胶符号作为标记这一事实:

sns.scatterplot(data=df, x='x', y='y', hue="cat", marker="$\circ$", ec="face", s=100)

这有点受限,因为您无法控制圆的厚度。

混合 seaborn-matplotlib 方法更灵活,但也更麻烦(您需要自己创建图例):

palette = {"a": "C0", "b": "C1"}
kws = {"s": 70, "facecolor": "none", "linewidth": 1.5}

ax = sns.scatterplot(
    data=df, x='x', y='y',
    edgecolor=df["cat"].map(palette),
    **kws,
)
handles, labels = zip(*[
    (plt.scatter([], [], ec=color, **kws), key) for key, color in palette.items()
])
ax.legend(handles, labels, title="cat")

第三个选项是使用 FacetGrid。这不太灵活,因为情节必须在它自己的图中。但这相当简单;另一个答案使用 FacetGrid 但它有点过度设计,因为它忘记了 hue_kws 参数:

palette = ["C0", "C1"]
g = sns.FacetGrid(
    data=df, hue="cat",
    height=4, aspect=1.25,
    hue_kws={"edgecolor": palette},
)
g.map(plt.scatter, "x", "y", facecolor="none", lw=1.5)
g.add_legend()

有时*,退回到 matplotlib 功能更容易:

import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({'x': [3,2,5,2,1,0],
                   'y': [1,1,2,3,0,2],
                   'cat': ['a','a','a','b','b','b']})

fig, ax = plt.subplots()
m_colors = ["blue", "tab:orange", "red", "green"]

for (cat, group), col in zip(df.groupby("cat"), m_colors):
    ax.scatter(group.x, group.y, edgecolors=col, facecolors="none", alpha=0.7, label=cat)

ax.legend(title="empty circles")
plt.show()

示例输出:

*seaborn 和 pandas 非常适合他们提供的东西。但我看到很多例子,人们认为“这很简单,所以简单地添加 XYZ 功能应该很容易”,然后最终得到复杂的代码,如果他们首先在基础 matplotlib 中编写代码会简单得多地方。

基于类似的问题 (),这里有一个很好的新方法来处理这个问题:

首先,定义你的颜色图。其次,设置合适的色调并在 palette 和 edgecolor 参数中指定颜色图。

penguins = sns.load_dataset("penguins")

colormap = {"Adelie": "purple", "Chinstrap": "orange", "Gentoo": "green"}

sns.jointplot(
    data=penguins,
    x="bill_length_mm",
    y="bill_depth_mm",
    hue="species",
    palette=colormap,
    ec=penguins["species"].map(colormap),
    fc="none",
)