【问题标题】:Set color-palette in Seaborn Grouped Barplot depending on values根据值在 Seaborn 分组条形图中设置调色板
【发布时间】:2021-04-02 02:58:57
【问题描述】:

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

    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)

但我得到以下输出:

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

【问题讨论】:

    标签: python matplotlib seaborn palette


    【解决方案1】:

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

    此外,seaborn 允许调色板成为一本字典,准确地告诉哪个色调标签得到哪种颜色。添加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)

    另一种方法是直接使用原始数据框创建两个条形图:一个用于负值,一个用于正值。为了使其正常工作,有必要将“标签”列(x=)明确设为分类。还为“变量”列添加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()
    

    【讨论】:

    • 第一个输出是正确的,但条似乎被移位了,我的意思是它们没有定期放置在我的图像中。
    • 确实,第一种方法创建了 6 个色调类别,并且该图腾出空间为每个 X-label 放置所有 6 个色调类别。这就是为什么我还添加了第二种方法,尽管它更深入地了解了内部 matplotlib 结构。你有没有期待一些非常不同的东西?
    • 差别不大,但我正在努力使条形图正常化。在第二个选项中,分类顺序与数字顺序不匹配 - 我的实际数据框有点长 - 但我想有一种方法可以重新设置顺序。无论如何,这两个选项比我能到达的更接近。谢谢
    • barplot() 接受参数order=,您可以在其中提供['nat', 'agr', 'art'] 的列表来定义订单。同样,pd.Categorical(..., categories=['nat', 'agr', 'art']) 将修复订单。还有一种方法是使用sns.catplot(...) 为每个 x-label 创建一个子图。
    • 现在完成了!这正是我想去的地方。
    猜你喜欢
    • 2019-05-06
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 2021-08-18
    • 2016-05-15
    • 2022-01-12
    • 2021-02-06
    相关资源
    最近更新 更多