为多个seaborn图创建单个图例

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

我正在使用“iris.csv”数据制作箱线图。我试图通过测量(即花瓣长度、花瓣宽度、萼片长度、萼片宽度)将数据分解为多个数据框,然后在 forloop 上绘制箱线图,从而添加子图。

最后,我想一次性为所有箱形图添加一个通用图例。但是,我做不到。我已经使用几个 stackoverflow 问题尝试了几个教程和方法,但我无法修复它。

这是我的代码:

import seaborn as sns 
from matplotlib import pyplot

iris_data = "iris.csv"
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'class']
dataset = read_csv(iris_data, names=names)


# Reindex the dataset by species so it can be pivoted for each species 
reindexed_dataset = dataset.set_index(dataset.groupby('class').cumcount())
cols_to_pivot = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width']

# empty dataframe 
reshaped_dataset = pd.DataFrame()
for var_name in cols_to_pivot:
    pivoted_dataset = reindexed_dataset.pivot(columns='class', values=var_name).rename_axis(None,axis=1)
    pivoted_dataset['measurement'] = var_name
    reshaped_dataset = reshaped_dataset.append(pivoted_dataset, ignore_index=True)


## Now, lets spit the dataframe into groups by-measurements.
grouped_dfs_02 = []
for group in reshaped_dataset.groupby('measurement') :
    grouped_dfs_02.append(group[1])


## make the box plot of several measured variables, compared between species 

pyplot.figure(figsize=(20, 5), dpi=80)
pyplot.suptitle('Distribution of floral traits in the species of iris')

sp_name=['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
setosa = mpatches.Patch(color='red')
versi = mpatches.Patch(color='green')
virgi = mpatches.Patch(color='blue')

my_pal = {"Iris-versicolor": "g", "Iris-setosa": "r", "Iris-virginica":"b"}
plt_index = 0


# for i, df in enumerate(grouped_dfs_02):
for group_name, df in reshaped_dataset.groupby('measurement'):

    axi = pyplot.subplot(1, len(grouped_dfs_02), plt_index + 1)
    sp_name=['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
    df_melt = df.melt('measurement', var_name='species', value_name='values')

    sns.boxplot(data=df_melt, x='species', y='values', ax = axi, orient="v", palette=my_pal)
    pyplot.title(group_name)
    plt_index += 1


# Move the legend to an empty part of the plot
pyplot.legend(title='species', labels = sp_name, 
         handles=[setosa, versi, virgi], bbox_to_anchor=(19, 4),
           fancybox=True, shadow=True, ncol=5)


pyplot.show()

剧情如下:

如何在主图上、主框架之外、“主副标题”旁边添加一个通用图例?

python pandas matplotlib seaborn boxplot
2个回答
7
投票
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# load iris data
iris = sns.load_dataset("iris")

   sepal_length  sepal_width  petal_length  petal_width species
0           5.1          3.5           1.4          0.2  setosa
1           4.9          3.0           1.4          0.2  setosa
2           4.7          3.2           1.3          0.2  setosa
3           4.6          3.1           1.5          0.2  setosa
4           5.0          3.6           1.4          0.2  setosa

# create figure
fig, axes = plt.subplots(ncols=4, figsize=(20, 5), sharey=True)

# add subplots
for ax, col in zip(axes, iris.columns[:-1]):
    sns.boxplot(x='species', y=col, data=iris, hue='species', dodge=False, ax=ax)
    ax.get_legend().remove()
    ax.set_title(col)

# add legend
handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc='upper right', ncol=3, bbox_to_anchor=(0.8, 1), frameon=False)

# add subtitle
fig.suptitle('Distribution of floral traits in the species of iris')

plt.show()


  • 但是,图例不是必需的,并且冗余地传达相同的信息,因为每个图上的颜色都是相同的,并且每个图例的标签已经在 x 轴上。
  • 更简洁的选项是使用
    pandas.DataFrame.melt
    将数据帧转换为长格式,然后使用
    sns.catplot
    kind='box'
    进行绘图。
dfm = iris.melt(id_vars='species', var_name='parameter', value_name='measurement', ignore_index=True)

  species     parameter  measurement
0  setosa  sepal_length          5.1
1  setosa  sepal_length          4.9
2  setosa  sepal_length          4.7
3  setosa  sepal_length          4.6
4  setosa  sepal_length          5.0

g = sns.catplot(kind='box', data=dfm, x='species', y='measurement', hue='species', col='parameter', dodge=False)
_ = g.fig.suptitle('Distribution of floral traits in the species of iris', y=1.1)

  • (可选)将所有值绘制在单个子图中,这样可以更轻松地比较
    'parameter'
    'species'
g = sns.catplot(kind='box', data=dfm, x='parameter', y='measurement', hue='species', height=4, aspect=2)

_ = g.fig.suptitle('Distribution of floral traits in the species of iris', y=1.1)


1
投票

要定位图例,设置

loc
参数(即锚点)非常重要。 (默认的
loc
'best'
这意味着你事先不知道它会在哪里结束)。位置从
0,0
(当前轴的左下角)到
1,1
(当前轴的左上角)进行测量。这不包括标题等的填充,因此这些值可能会超出
0, 1
范围。 “当前斧头”是最后一个被激活的斧头。

请注意,您还可以使用使用“图形”的

plt.legend
,而不是
plt.gcf().legend
(使用轴)。然后,坐标是完整图(“图”)左下角的
0,0
和右上角的
1,1
。缺点是不会为图例创建额外的空间,因此您需要手动设置顶部填充(例如
plt.gcf().subplots_adjust(top=0.8)
)。缺点是您不能再使用
plt.tight_layout()
,并且将图例与轴对齐会更困难。

import seaborn as sns
from matplotlib import pyplot as plt
from matplotlib import patches as mpatches
import pandas as pd

dataset = sns.load_dataset("iris")

# Reindex the dataset by species so it can be pivoted for each species
reindexed_dataset = dataset.set_index(dataset.groupby('species').cumcount())
cols_to_pivot = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width']

# empty dataframe
reshaped_dataset = pd.DataFrame()
for var_name in cols_to_pivot:
    pivoted_dataset = reindexed_dataset.pivot(columns='species', values=var_name).rename_axis(None, axis=1)
    pivoted_dataset['measurement'] = var_name
    reshaped_dataset = reshaped_dataset.append(pivoted_dataset, ignore_index=True)

## Now, lets spit the dataframe into groups by-measurements.
grouped_dfs_02 = []
for group in reshaped_dataset.groupby('measurement'):
    grouped_dfs_02.append(group[1])

## make the box plot of several measured variables, compared between species
plt.figure(figsize=(20, 5), dpi=80)
plt.suptitle('Distribution of floral traits in the species of iris')

sp_name = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
setosa = mpatches.Patch(color='red')
versi = mpatches.Patch(color='green')
virgi = mpatches.Patch(color='blue')

my_pal = {"versicolor": "g", "setosa": "r", "virginica": "b"}
plt_index = 0

# for i, df in enumerate(grouped_dfs_02):
for group_name, df in reshaped_dataset.groupby('measurement'):
    axi = plt.subplot(1, len(grouped_dfs_02), plt_index + 1)
    sp_name = ['Iris-setosa', 'Iris-versicolor', 'Iris-virginica']
    df_melt = df.melt('measurement', var_name='species', value_name='values')

    sns.boxplot(data=df_melt, x='species', y='values', ax=axi, orient="v", palette=my_pal)
    plt.title(group_name)
    plt_index += 1

# Move the legend to an empty part of the plot
plt.legend(title='species', labels=sp_name,
           handles=[setosa, versi, virgi], bbox_to_anchor=(1, 1.23),
           fancybox=True, shadow=True, ncol=5, loc='upper right')
plt.tight_layout()
plt.show()

© www.soinside.com 2019 - 2024. All rights reserved.