更高效的matplotlib堆叠条形图 - 如何计算底部值

21

我需要帮助使用matlibplot在Python中制作一组堆叠条形图。我的基本代码如下,但是我的问题是如何有效地为第2个元素之外的任何元素生成底部值。我可以正确堆叠示例图(始终从底部到顶部为a、b、c、d)。

import numpy as np
import matplotlib.pyplot as plt

ind = np.arange(3)

a = [3,6,9]
b = [2,7,1]
c = [0,3,1]
d = [4,0,3]

p1 = plt.bar(ind, a, 1, color='#ff3333')
p2 = plt.bar(ind, b, 1, color='#33ff33', bottom=a)
p3 = plt.bar(ind, c, 1, color='#3333ff', bottom=[a[j] +b[j] for j in range(len(a))])
p4 = plt.bar(ind, d, 1, color='#33ffff', bottom=[a[j] +b[j] +c[j] for j in range(len(a))])

plt.show()

我的最终代码可能会有很多条,而不断扩展的函数 bottom = [...] 可能不是最好的解决方案。如果您能解释一下我需要如何推导出这个值,那就太好了。是否有一个 numpy 函数可以做到这点。

非常感谢!!! 附言:我已经搜索过答案,但我不明白我找到的东西。

4个回答

30

我最近也遇到了同样的问题,于是我决定将所有内容包装到一个美观的类中。对于任何感兴趣的人,您可以在此处获取一个堆叠条形图类的实现:

https://github.com/minillinim/stackedBarGraph

它允许按比例缩放的堆叠图形,以及设置条形的宽度和高度(带有缩放内部)。

给定这样的数据集:

    d = np.array([[101.,0.,0.,0.,0.,0.,0.],
                  [92.,3.,0.,4.,5.,6.,0.],
                  [56.,7.,8.,9.,23.,4.,5.],
                  [81.,2.,4.,5.,32.,33.,4.],
                  [0.,45.,2.,3.,45.,67.,8.],
                  [99.,5.,0.,0.,0.,43.,56.]])

    d_heights = [1.,2.,3.,4.,5.,6.]
    d_widths = [.5,1.,3.,2.,1.,2.]
    d_labels = ["fred","julie","sam","peter","rob","baz"]
    d_colors = ['#2166ac',
                '#fee090',
                '#fdbb84',
                '#fc8d59',
                '#e34a33',
                '#b30000',
                '#777777']

它可以制作出像这样的图片:

堆积条形图

使用GPLv3协议,带着爱心。


谢谢 - 我该如何在条形之间添加空格? - Matt
我更新了代码以允许间隙。实际上很简单,如果你从条形图的宽度中减去一个固定的量,那么它们就会有效地缩小。之后只需要调整xlims即可。主函数调用现在有两个新参数,gap和endGaps,下面的两张图片展示了这些参数的使用示例。 - minillinim
喜欢 @minillinim 的包。感觉太容易了。如果您使用诸如“stacked_colors = ['#2166ac','#fee090','#fdbb84']”和“cols = stacked_colors”的数组设置颜色,则很容易向由 pandas DataFrame 创建的图形添加图例:legends = [] i = 0 for column in df.columns: legends.append(mpatches.Patch(color=stacked_colors[i], label=column)) i+=1 plt.legend(handles=legends) - canary_in_the_data_mine

16

将您的数值转换为NumPy数组会使您的生活更轻松:

data = np.array([a, b, c, d])
bottom = np.cumsum(data, axis=0)
colors = ('#ff3333', '#33ff33', '#3333ff', '#33ffff')

plt.bar(ind, data[0], color=colors[0])
for j in xrange(1, data.shape[0]):
    plt.bar(ind, data[1], color=colors[j], bottom=bottom[i-1])

另外,为了消除第一个条的棘手特例:

data = np.array([a, b, c, d])
bottom = np.vstack((np.zeros((data.shape[1],), dtype=data.dtype),
                    np.cumsum(data, axis=0)[:-1]))
colors = ('#ff3333', '#33ff33', '#3333ff', '#33ffff')
for dat, col, bot in zip(data, colors, bottom):
    plt.bar(ind, dat, color=col, bottom=bot)

4
如果你在使用matplotlib,最终所有的东西都会成为一个ndarray。因此,最好让你的生活更愉快一些;) - tacaswell
谢谢,我该如何添加标签呢? 我有一系列的标签/名称,用于我正在堆叠的每个系列,但尽管我已经尝试过,我无法使它们正确显示出来。 我还尝试运行下面这样简单的图例,但它并没有真正起作用: codeplt.legend((pl[0], pm[0],ph[0],pa[0]),('L','M','H','At'),bbox_to_anchor=[1.05, 0.5], loc='center') - 2705114-john

7
[sum(values) for values in zip(a, b, c)]

在Python 2中,您也可以这样做:
map(sum, zip(a, b, c))

但是Python 3需要

list(map(sum, zip(a, b, c)))

您可以将其封装为:

这段代码:

def sumzip(*items):
    return [sum(values) for values in zip(*items)]

然后执行

p1 = plt.bar(ind, a, 1, color='#ff3333')
p2 = plt.bar(ind, b, 1, color='#33ff33', bottom=sumzip(a))
p3 = plt.bar(ind, c, 1, color='#3333ff', bottom=sumzip(a, b))
p4 = plt.bar(ind, d, 1, color='#33ffff', bottom=sumzip(a, b, c))

如果 abcd 是 numpy 数组,你也可以执行 sum([a, b, c]):
a = np.array([3,6,9])
b = np.array([2,7,1])
c = np.array([0,3,1])
d = np.array([4,0,3])

p1 = plt.bar(ind, a, 1, color='#ff3333')
p2 = plt.bar(ind, b, 1, color='#33ff33', bottom=sum([a]))
p3 = plt.bar(ind, c, 1, color='#3333ff', bottom=sum([a, b]))
p4 = plt.bar(ind, d, 1, color='#33ffff', bottom=sum([a, b, c]))

2
我这样解决的:

我这样解决的:

import numpy as np

dates = # somehow get a list of dates
labels = # a list of various labels
colors = # somehow get a list of colors

margin_bottom = np.zeros(dates)

for index, label in enumerate(labels):
    values = # get your values for the label at index-th position from somewhere
    ax.bar(
        dates, values, 
        align='center', label=label, color=colors[index], bottom=margin_bottom
    )
    margin_bottom += values # here you simply add it to the previous margin
    # margin_bottom is a numpy array, adding a list will not change that

这个解决方案与其他一些解决方案类似,但它不需要所有边距始终存储。相反,它从底部向上“构建”堆栈,在每次迭代中添加越来越多的边距。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接