根据值在 Seaborn Grouped Barplot 中设置调色板

Set color-palette in Seaborn Grouped Barplot depending on values

我有一个数据框,其中包含来自三种变量的正值和负值。

    labels  variable    value
0   -10e5        nat     -38
1     2e5        nat      50
2    10e5        nat      16
3   -10e5        agr     -24
4     2e5        agr      35
5    10e5        agr      26
6   -10e5        art     -11
7     2e5        art      43
8    10e5        art      20

当值为负时,我希望条形图遵循颜色顺序:

n_palette = ["#ff0000","#ff0000","#00ff00"]

相反,当我希望它反转调色板时:

p_palette = ["#00ff00","#00ff00","#ff0000"]

我试过这个:

palette = ["#ff0000","#ff0000","#00ff00",
           "#00ff00","#00ff00","#ff00",
           "#00ff00","#00ff00","#ff00"]

ax = sns.barplot(x=melted['labels'], y=melted['value'], hue = melted['variable'],
                 linewidth=1,
                 palette=palette)

但我得到以下输出:

我想要的是当值为正时组中的前两个条变为绿色,最后一个变为红色。

您似乎想根据一个标准对两列进行着色。添加一个唯一标记该标准的新列似乎是合适的。

此外,seaborn 允许调色板成为一个字典,准确告诉哪个 hue label 得到哪个颜色。添加 barplot(..., order=[...]) 将定义固定顺序。

下面是一些示例代码:

from matplotlib import pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
from io import StringIO

data_str = '''    labels  variable    value
0   -10e5        nat     -38
1     2e5        nat      50
2    10e5        nat      16
3   -10e5        agr     -24
4     2e5        agr      35
5    10e5        agr      26
6   -10e5        art     -11
7     2e5        art      43
8    10e5        art      20
'''
melted = pd.read_csv(StringIO(data_str), delim_whitespace=True, dtype={'labels': str})
melted['legend'] = np.where(melted['value'] < 0, '-', '+')
melted['legend'] = melted['variable'] + melted['legend']
palette = {'nat-': "#ff0000", 'agr-': "#ff0000", 'art-': "#00ff00",
           'nat+': "#00ff00", 'agr+': "#00ff00", 'art+': "#ff0000"}

ax = sns.barplot(x=melted['labels'], y=melted['value'], hue=melted['legend'],
                 linewidth=1, palette=palette)
ax.axhline(0, color='black')
plt.show()

PS:删除图例:ax.legend_.remove()。或者有一个多列的图例:ax.legend(ncol=3).

一种直接使用原始数据框的不同方法是创建两个条形图:一个用于负值,一个用于正值。为了使其正常工作,有必要将 'labels' 列(x=)明确设为分类。还为 'variable' 列添加 pd.Categorical(..., categories=['nat', 'agr', 'art']) 可以修复订单。

这将生成一个带有两次不同颜色标签的图例。根据您的需要,您可以删除它或创建更自定义的图例。 一个想法是在正条下方和负条上方添加标签:

sns.set()
melted = pd.read_csv(StringIO(data_str), delim_whitespace=True, dtype={'labels': str})
palette_pos = {'nat': "#00ff00", 'agr': "#00ff00", 'art': "#ff0000"}
palette_neg = {'nat': "#ff0000", 'agr': "#ff0000", 'art': "#00ff00"}
melted['labels'] = pd.Categorical(melted['labels'])
ax = sns.barplot(data=melted[melted['value'] < 0], x='labels', y='value', hue='variable',
                 linewidth=1, palette=palette_neg)
sns.barplot(data=melted[melted['value'] >= 0], x='labels', y='value', hue='variable',
            linewidth=1, palette=palette_pos, ax=ax)
ax.legend_.remove()
ax.axhline(0, color='black')
ax.set_xlabel('')
ax.set_ylabel('')
for bar_container in ax.containers:
    label = bar_container.get_label()
    for p in bar_container:
        x = p.get_x() + p.get_width() / 2
        h = p.get_height()
        if not np.isnan(h):
            ax.text(x, 0, label + '\n\n' if h < 0 else '\n\n' + label, ha='center', va='center')
plt.show()

还有一个选项涉及sns.catplot(),当涉及大量数据时可能会更清楚:

sns.set()
melted = pd.read_csv(StringIO(data_str), delim_whitespace=True, dtype={'labels': str})
melted['legend'] = np.where(melted['value'] < 0, '-', '+')
melted['legend'] = melted['variable'] + melted['legend']
palette = {'nat-': "#ff0000", 'agr-': "#ff0000", 'art-': "#00ff00",
           'nat+': "#00ff00", 'agr+': "#00ff00", 'art+': "#ff0000"}
g = sns.catplot(kind='bar', data=melted, col='labels', y='value', x='legend',
                 linewidth=1, palette=palette, sharex=False, sharey=True)
for ax in g.axes.flat:
    ax.axhline(0, color='black')
    ax.set_xlabel('')
    ax.set_ylabel('')
plt.show()