2013-09-27 2 views
15

Мне нужна помощь в создании набора штабелированных гистограмм в python с matlibplot. Мой базовый код ниже, но мои проблемы в том, как создать значение для внизу для любого элемента за пределами второго эффективно. Я могу получить пример графика правильно стека (всегда а, б, в, г снизу вверх)Более эффективная гистограмма диаграммы matplotlib - как рассчитать нижние значения

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() 

Мой окончательный код может иметь очень большое количество баров и постоянно расширяется функция низ = [.. .] не может быть лучшим решением. Было бы здорово, если бы вы также могли объяснить, как мне нужно получить ценность. Есть функция numpy.

спасибо! PS Я искал ответ, но я не понимал, что я могу найти.

ответ

28

Я недавно столкнулся с той же проблемой. Впоследствии я решил обернуть все в класс. Для тех, кто заинтересован вы получите реализацию столбчатого графа класса здесь:

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'] 

Это может сделать изображения, как это:

stacked bar graph

GPLv3 с любовью.

+0

Спасибо - как я могу получить пробелы между барами? – Matt

+0

Я обновил код, чтобы разрешить пробелы. Это на самом деле довольно просто, если вы вычитаете фиксированную сумму из ширины баров, тогда она эффективно сокращает их. После этого это просто вопрос игры с xlims. В главном вызове функции есть два новых параметра, зазор и конечные галочки. Нижние две фотографии показывают примеры использования. – minillinim

+0

Любовь @ minillinim пакет. Это было слишком легко. Чтобы добавить легенду, если вы устанавливаете цвета с таким массивом, как 'stacked_colors = ['# 2166ac', '# fee090', '# fdbb84']' и 'cols = stacked_colors', тогда легко добавить легенду участок выполнен из панд DataFrame: 'легенды = [] = 0 для столбца в df.columns: legends.append (mpatches.Patch (цвет = stacked_colors [I], метка = столбец)) I + = 1 plt.legend (handles = legendends) ' –

5
[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)) 

тоже.


Если a, b, c и d являются 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])) 
14

Преобразования ваших значений 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 под _anyway_. Можете также сделать вашу жизнь приятной, а) – tacaswell

+0

спасибо, как я могу добавить в нее ярлыки? У меня есть список ярлыков/названий для каждой серии, в которой я складываюсь, но, хотя я и пытался, я не могу заставить их выйти правильно. Я также попытался запустить простую легенду, как показано ниже, но она на самом деле не работала .: 'code'plt.legend ((pl [0], pm [0], ph [0], pa [0]) , ('L', 'M', 'H', 'At'), bbox_to_anchor = [1.05, 0.5], loc = 'center') –

1

Я решил так:

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 

Это похоже на некоторые другие решения, но это не требует всех полей хранятся в любое время. Вместо этого он «строит» стеки снизу вверх, добавляя все больше и больше полей с каждой итерацией.

Смежные вопросы