在堆叠的 Seaborn 条形图中选择合适的颜色

时间:2021-02-19 08:39:31

标签: pandas seaborn multi-index

我想使用 Seaborn 和 MiltiIndex DataFrame 创建堆叠条形图

header = pd.MultiIndex.from_product([['#'],
                                     ['TE', 'SS', 'M', 'MR']])
dat = ([[100, 20, 21, 35], [100, 12, 5, 15]])
df = pd.DataFrame(dat, index=['JC', 'TTo'], columns=header)
df = df.stack()
df = df.sort_values('#', ascending=False).sort_index(level=0, sort_remaining=False)

enter image description here

我用于绘图的代码是:

fontP = FontProperties()
fontP.set_size('medium')
colors = {'TE': 'green', 'SS': 'blue', 'M': 'yellow', 'MR': 'red'}
kwargs = {'alpha':0.5}

plt.figure(figsize=(12, 9))
sns.barplot(x=df2.index.get_level_values(0).unique(),
            y=df2.loc[pd.IndexSlice[:, df2.index[0]], '#'],
            color=colors[df2.index[0][1]], **kwargs)

sns.barplot(x=df2.index.get_level_values(0).unique(),
                          y=df2.loc[pd.IndexSlice[:, df2.index[1]], '#'],
                          color=colors[df2.index[1][1]], **kwargs)
sns.barplot(x=df2.index.get_level_values(0).unique(),
                          y=df2.loc[pd.IndexSlice[:, df2.index[2]], '#'],
                          color=colors[df2.index[2][1]], **kwargs)
bottom_plot = sns.barplot(x=df2.index.get_level_values(0).unique(),
                          y=df2.loc[pd.IndexSlice[:, df2.index[3]], '#'],
                          color=colors[df2.index[3][1]], **kwargs)

bar1 = plt.Rectangle((0, 0), 1, 1, fc='green', edgecolor="None")
bar2 = plt.Rectangle((0, 0), 0, 0, fc='yellow', edgecolor="None")
bar3 = plt.Rectangle((0, 0), 2, 2, fc='red', edgecolor="None")
bar4 = plt.Rectangle((0, 0), 3, 3, fc='blue', edgecolor="None")
l = plt.legend([bar1, bar2, bar3, bar4], [
    "TE", "M",
    'MR', 'SS'
],
               bbox_to_anchor=(0.95, 1),
               loc='upper left',
               prop=fontP)
l.draw_frame(False)

sns.despine()
bottom_plot.set_ylabel("#")

axes = plt.gca()
axes.yaxis.grid()

我得到:

enter image description here

我的问题是第二个栏 ('TTo') 中颜色的顺序,我希望根据 1 级索引值 (['TE', 'SS', 'M', 'MR']) 以便正确排序。再往下是具有最高值的那个及其对应的颜色,在前一个具有次高值的下一个及其颜色,依此类推,如第一个条形所示 ('JC)。

也许在 Seaborn 中有一种比我正在使用的方法更简单的方法......

1 个答案:

答案 0 :(得分:1)

我不知道如何用 seaborn 创建这样的情节。这是一种通过循环遍历行并在每一步添加一个 matplotlib 栏来创建它的方法:

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

sns.set()
header = pd.MultiIndex.from_product([['#'],
                                     ['TE', 'SS', 'M', 'MR']])
dat = ([[100, 20, 21, 35], [100, 12, 5, 15]])
df = pd.DataFrame(dat, index=['JC', 'TTo'], columns=header)
df = df.stack()
df = df.sort_values('#', ascending=False).sort_index(level=0, sort_remaining=False)

colors = {'TE': 'green', 'SS': 'blue', 'M': 'yellow', 'MR': 'red'}

prev_index0 = None
for (index0, index1), quantity in df.itertuples():
    if index0 != prev_index0:
        bottom = 0
    plt.bar(index0, quantity, fc=colors[index1], ec='none', bottom=bottom, label=index1)
    bottom += quantity
    prev_index0 = index0
legend_handles = [plt.Rectangle((0, 0), 0, 0, color=colors[c], label=c) for c in colors]
plt.legend(handles=legend_handles)
plt.show()

resulting plot

要在不堆叠的情况下从后到前绘制条形图,可以简化代码:

colors = {'TE': 'forestgreen', 'SS': 'cornflowerblue', 'M': 'gold', 'MR': 'crimson'}

for (index0, index1), quantity in df.itertuples():
    plt.bar(index0, quantity, fc=colors[index1], ec='none', label=index1)
legend_handles = [plt.Rectangle((0, 0), 0, 0, color=colors[c], label=c, ec='black') for c in colors]
plt.legend(handles=legend_handles, bbox_to_anchor=(1.02, 1.02), loc='upper left')
plt.tight_layout()

non-stacked bars