如何为 sns.histplot 中的所有子图制作一个图例而不使用重复标签?

问题描述 投票:0回答:1

我有一个数据框,我正在使用 sns.histplot 绘制约 9 个子图。我不希望这个人物有 9 个图例。我想要整个人物的 1 个图例,因为无论如何大多数图例都是相同的。然而,由于并非所有子图都 100% 相同(有些子图有额外的“裁剪”或缺少一个),所以我不能简单地删除除 1 之外的所有图例。

我能够对 sns.barplot 执行此操作,但相同的代码行似乎不适用于 sns.histplot (并且这些数据需要使用 sns.histplot 进行绘制)。

我下面有一些虚拟代码可以修改。我注释掉了我的行

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

# Create a dummy DataFrame with random data

crops = [
    "apple",
    "banana",
    "orange",
    "potato",
    "zucchini",
    "kale",
    "strawberry",
    "raspberry",
    "turnip",
    "onion",
]
farms = [
    "n",
    "s",
    "e",
    "w",
]

np.random.seed(42)
n_samples = 100
counts = np.random.randint(1, 200, size=n_samples)
choices = np.random.choice(crops, size=n_samples)
locations = np.random.choice(farms, size=n_samples)

df = pd.DataFrame({"counts": counts, "crops": choices, "farms": locations})

fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)

h = []  # Initialize an empty list to collect handles
l = []  # Initialize an empty list to collect labels

rowindex = [0, 0, 1, 1]
colindex = [0, 1, 0, 1]

selection = ["farms == 'n'", "farms == 's'", "farms == 'e'", "farms == 'w'"]

for row, col, sele in zip(rowindex, colindex, selection):
    g = sns.histplot(
        data=df.query(sele),
        x="counts",
        kde=True,
        stat="count",
        hue="crops",  # Add the hue parameter
        ax=axes[row, col],
    )

    handles, labels = g.get_legend_handles_labels()
    h.extend(handles)
    l.extend(labels)
    axes[row, col].get_legend().remove()  # Remove individual legends from subplots

# Create a single legend for the entire figure
by_label = dict(zip(l, h))
g.legend(by_label.values(), by_label.keys(), bbox_to_anchor=(0.9,0.65), loc='upper right')


plt.show()

通过以下几行,我尝试收集每个子图中的所有图例并将其放入handles[]和labels[]中,然后将其转换为字典(以删除重复的条目)。我尝试过

g.get_legend_handles_labels()
axes[row,col].get_legend_handles_labels()
,以及
fig.
ax.
,但是当我打印手柄和标签的内容时,每个东西都变成空的。我真的很困惑,因为这确实适用于 sns.barplot (但也许他们对艺术家的定义不同?或者以不同的方式存储它们?)。

handles, labels = g.get_legend_handles_labels()
    h.extend(handles)
    l.extend(labels)
    axes[row, col].get_legend().remove()  # Remove individual legends from subplots

# Create a single legend for the entire figure
by_label = dict(zip(l, h))
g.legend(by_label.values(), by_label.keys(), bbox_to_anchor=(0.9,0.65), loc='upper right')

将这些行注释掉,这就是情节:

enter image description here

代码中的这些行,我没有得到图例:

enter image description here

理想情况下,我有 1 个图例(在图之外),其中所有“作物”都列出一次(顺序并不重要)。有谁知道我做错了什么?


如前所述,这适用于 sns.barplot。如果将

g =
位更改为:

g = sns.barplot(
        data=df.query(sele),
        x="counts",
        y="counts",
        hue="crops",  # Add the hue parameter
        ax=axes[row, col],
    )

保持其余部分相同,然后我得到如下图所示的图形,这基本上就是我想要的:1 个图例,其中所有“作物”仅列出一次,并带有各自的标签标记。所以它必须可以通过 sns.histplot ....对吗?

enter image description here

python matplotlib seaborn legend histplot
1个回答
0
投票

创建此类图的推荐方法是使用“图形级别”版本的图。对于

sns.histplot()
,这是
sns.displot(kind='hist', ...)

请注意,

sns.histplot()
返回已创建绘图的子图(
ax
),而
sns.displot()
返回
FacetGrid
,即子图网格。 使用图形级函数时,不应创建图形或轴。

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

# Create a dummy DataFrame with random data

crops = ["apple", "banana", "orange", "potato", "zucchini", "kale", "strawberry", "raspberry", "turnip", "onion"]
farms = ["n", "s", "e", "w"]

np.random.seed(42)
n_samples = 100
counts = np.random.randint(1, 200, size=n_samples)
choices = np.random.choice(crops, size=n_samples)
locations = np.random.choice(farms, size=n_samples)

df = pd.DataFrame({"counts": counts, "crops": choices, "farms": locations})

g = sns.displot(kind='hist', data=df, x="counts", hue="crops", col="farms", col_wrap=2)

plt.show()
© www.soinside.com 2019 - 2024. All rights reserved.