web-dev-qa-db-fra.com

Graphique à barres empilées matplotlib plus efficace - Comment calculer les valeurs de fond

J'ai besoin d'aide pour créer un ensemble de graphiques à barres empilées dans python avec matlibplot. Mon code de base est ci-dessous mais mes problèmes sont de savoir comment générer la valeur pour bas pour tout élément au-delà du 2ème efficacement. Je peux faire en sorte que l'exemple de graphique s'empile correctement (toujours a, b, c, d de bas en haut)

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

Mon code final pourrait avoir un très grand nombre de barres et la fonction toujours croissante bottom = [...] ne peut pas être la meilleure solution. Ce serait formidable si vous pouviez également expliquer comment je dois en déduire la valeur. Y a-t-il une fonction numpy.

Merci beaucoup!!! PS J'ai cherché une réponse mais je n'ai pas compris ce que j'ai pu trouver.

19
2705114-john

Je viens de rencontrer récemment le même problème. Ensuite, j'ai décidé de tout terminer dans une classe de Nice. Pour toute personne intéressée, vous obtenez une implémentation d'une classe de graphique à barres empilées ici:

https://github.com/minillinim/stackedBarGraph

Il permet des graphiques empilés à l'échelle ainsi que le réglage des largeurs de barre et des hauteurs définies (avec des échelles intérieures).

Étant donné un ensemble de données comme celui-ci:

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

Il peut créer des images comme celle-ci:

stacked bar graph

GPLv3 avec amour.

29
minillinim

La conversion de vos valeurs en tableaux numpy vous facilitera la vie:

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

Alternativement, pour se débarrasser du cas particulier désagréable pour la première barre:

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)
14
Jaime
[sum(values) for values in Zip(a, b, c)]

Dans Python 2 vous pouvez également faire

map(sum, Zip(a, b, c))

mais Python 3 aurait besoin

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

ce qui est moins sympa.


Vous pouvez résumer ceci:

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

puis faire

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

aussi.


Si a, b, c et d sont des tableaux numpy, vous pouvez également faire 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]))
5
Veedrac

Je l'ai résolu comme ceci:

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

C'est similaire à certaines autres solutions, mais cela ne nécessite pas que toutes les marges soient stockées à tout moment. Au lieu de cela, il "construit" les piles de bas en haut, ajoutant de plus en plus de marge à chaque itération.

2
Zelphir Kaltstahl