Seaborn 条形图,每组条形数量不同,且无空格

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

我正在使用seaborn 绘制不同算法的结果。 我想区分不同的算法及其分类(“组”)。 问题是并非所有算法都在所有组中,因此当我将组用作

hue
时,我会得到很多空白:

import seaborn as sns
group = ['Simple', 'Simple', 'Complex', 'Complex', 'Cool']
alg = ['Alg 1', 'Alg 2', 'Alg 3', 'Alg 4', 'Alg 2']
results = [i+1 for i in range(len(group))]
sns.barplot(group, results, hue=alg)

barplot

如您所见,seaborn 为所有组中所有算法的条形留出空间,从而导致大量空白。 我怎样才能避免这种情况? 我确实想在 x 轴上显示不同的组,并通过颜色/样式区分不同的算法。 算法可能出现在多个组中,但不是所有组中。 但我只想要“简单”和“复杂”中的 2 个小节的空间,以及“酷”中的 1 个小节的空间。 也欢迎任何纯

matplotlib
的解决方案;它不需要是seaborn。不过我想保留seaborn 调色板。

python matplotlib seaborn bar-chart
2个回答
2
投票

似乎没有创建这种类型的分组条形图的标准方法。以下代码创建条形的位置列表、颜色以及标签及其位置的列表。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.patches import Patch

group = ['Simple', 'Simple', 'Complex', 'Complex', 'Cool']
alg = ['Alg 1', 'Alg 2', 'Alg 3', 'Alg 4', 'Alg 2']
colors = plt.cm.tab10.colors
alg_cat = pd.Categorical(alg)
alg_colors = [colors[c] for c in alg_cat.codes]

results = [i + 1 for i in range(len(group))]

dist_groups = 0.4 # distance between successive groups
pos = (np.array([0] + [g1 != g2 for g1, g2 in zip(group[:-1], group[1:])]) * dist_groups + 1).cumsum()
labels = [g1 for g1, g2 in zip(group[:-1], group[1:]) if g1 != g2] + group[-1:]
label_pos = [sum([p for g, p in zip(group, pos) if g == label]) / len([1 for g in group if g == label])
             for label in labels]
plt.bar(pos, results, color=alg_colors)
plt.xticks(label_pos, labels)
handles = [Patch(color=colors[c], label=lab) for c, lab in enumerate(alg_cat.categories)]
plt.legend(handles=handles)
plt.show()

resulting plot


1
投票

虽然人们可以在

matplotlib
numpy
内完全处理这种情况,但我已经通过
pandas
解决了它。原因是您需要找到一种正确进行分类分组的方法,这是
pandas
的主要优点之一。

所以我所做的本质上是从您的数据创建一个 DataFrame,然后按 - 显然 -

group
类别进行分组。在使用索引
i=0,1,2,..
迭代每个枚举类别时,我们创建一组
ax.bar()
图,每个图都限制在区间
[i-0.5, i+0,5]
内。根据要求,颜色取自seaborn 颜色图,最后也用于创建自定义图例。

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

group = ['Simple', 'Simple', 'Complex', 'Complex', 'Cool']
alg = ['Alg 1', 'Alg 2', 'Alg 3', 'Alg 4', 'Alg 2']
results = [i+1 for i in range(len(group))]

df = pd.DataFrame({'group':group, 'alg':alg, 'results':results})

## this next line is only required if you have a specific order in mind;
#  else, the .groupby() method will sort alphabetically!
df['group'] = pd.Categorical(df['group'], ["Simple", "Complex", "Cool"])

## choose the desired seaborn color palette here:
palette = sns.color_palette("Paired", len(alg))
labels, levels = pd.factorize(df['alg'])
df['color'] = [palette[l] for l in labels]

gdf = df.groupby('group')

fig,ax=plt.subplots(figsize=(5,3))  

xt = []
xtl = []

min_width = 1/max([len(item) for (key,item) in gdf])


for i,(key,item) in enumerate(gdf):
    xt.append(i)
    xtl.append(key)

    ## for each enumerated group, we need to create the proper x-scale placement
    #  such that each bar plot is centered around " i "
    #  i.e. two bars near " i = 0 " will be placed at [-0.25, 0.25] with widths of 0.5
    #  so that they won't collide with bars near " i = 1 "
    #  which themselves are placed at [0.75 1.25] 
    rel = np.linspace(0,1,len(item)+1)[:-1]
    rel -= rel.mean() 
    rel +=i

    w = 1/(len(item))
    ## note that the entire interval width (i.e. from i-0.5 to i+0.5) will be filled with the bars,
    #  meaning that the individual bar widths will vary depending on the number of bars.
    #  either adjust the bar width like this to add some whitespace:
    # w *= 0.9 
    ## or alternatively, you could use a fixed width instead:
    # w = 0.4
    ## or, by pre-evaluating the minimal required bar width:
    # w = min_width

    ax.bar(rel,item['results'].values,alpha=1,width=w,color=item['color'])

leg = []
for i,l in enumerate(levels):
    p = mpatches.Patch(color=palette[i], label=l)
    leg.append(p)
ax.legend(handles=leg)

ax.set_xticks(xt)
ax.set_xticklabels(xtl)
ax.grid()
plt.show()

结果(使用

sns.color_palette("Paired")
w=1/len(item)
)如下所示:

nice bar plot

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