I need some help making a set of stacked bar charts in python with matlibplot. My basic code is below but my problems is how to generate the value for bottom fo
I solved it like this:
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
It's similar to some other solutions, but it doesn't require all of the margins being stored at all time. Instead it "builds" the stacks from bottom up, adding more and more margin with each iteration.
[sum(values) for values in zip(a, b, c)]
In Python 2 you can also do
map(sum, zip(a, b, c))
but Python 3 would need
list(map(sum, zip(a, b, c)))
which is less nice.
You could encapsulate this:
def sumzip(*items):
return [sum(values) for values in zip(*items)]
and then do
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))
too.
If a
, b
, c
and d
are numpy arrays you can also do 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]))
I have just recently faced the same problem. Afterwards I decided to wrap it all up in a nice class. For anyone interested you get an implementation of a stacked bar graph class here:
https://github.com/minillinim/stackedBarGraph
It allows scaled stacked graphs as well as setting bar widths and set heights (with scaled inners).
Given a data set like this:
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']
It can make images like this:
GPLv3 with love.
Converting your values to numpy arrays will make your life easier:
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])
Alternatively, to get rid of the nasty particular case for the first bar:
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)