2010-02-16 13 views
13

Je souhaite obtenir une liste des données contenues dans une corbeille d'histogramme. J'utilise numpy, et Matplotlib. Je sais comment parcourir les données et vérifier les bords de la corbeille. Cependant, je veux le faire pour un histogramme 2D et le code pour le faire est plutôt moche. Est-ce que numpy a des constructions pour rendre cela plus facile?Comment obtenir des données dans une corbeille d'histogramme

Pour le cas 1D, je peux utiliser searchsorted(). Mais la logique n'est pas tellement meilleure, et je ne veux pas vraiment faire une recherche binaire sur chaque point de données quand je n'ai pas à le faire.

La plus grande partie de la mauvaise logique est due aux régions limites de la corbeille. Toutes les régions ont des limites comme celle-ci: [bord gauche, bord droit]. Sauf le dernier bin, qui a une région comme celle-ci: [bord gauche, bord droit].

Voici quelques exemples de code pour le cas 1D:

import numpy as np 

data = [0, 0.5, 1.5, 1.5, 1.5, 2.5, 2.5, 2.5, 3] 

hist, edges = np.histogram(data, bins=3) 

print 'data =', data 
print 'histogram =', hist 
print 'edges =', edges 

getbin = 2 #0, 1, or 2 

print '---' 
print 'alg 1:' 

#for i in range(len(data)): 
for d in data: 
    if d >= edges[getbin]: 
     if (getbin == len(edges)-2) or d < edges[getbin+1]: 
      print 'found:', d 
     #end if 
    #end if 
#end for 

print '---' 
print 'alg 2:' 

for d in data: 
    val = np.searchsorted(edges, d, side='right')-1 
    if val == getbin or val == len(edges)-1: 
     print 'found:', d 
    #end if 
#end for 

Voici quelques exemples de code pour le cas 2D:

import numpy as np 

xdata = [0, 1.5, 1.5, 2.5, 2.5, 2.5, \ 
     0.5, 0.5, 0.5, 0.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, \ 
     0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 3] 
ydata = [0, 5,5, 5, 5, 5, \ 
     15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, \ 
     25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 30] 

xbins = 3 
ybins = 3 
hist2d, xedges, yedges = np.histogram2d(xdata, ydata, bins=(xbins, ybins)) 

print 'data2d =', zip(xdata, ydata) 
print 'hist2d =' 
print hist2d 
print 'xedges =', xedges 
print 'yedges =', yedges 

getbin2d = 5 #0 through 8 

print 'find data in bin #', getbin2d 

xedge_i = getbin2d % xbins 
yedge_i = int(getbin2d/xbins) #IMPORTANT: this is xbins 

for x, y in zip(xdata, ydata): 
    # x and y left edges 
    if x >= xedges[xedge_i] and y >= yedges[yedge_i]: 
     #x right edge 
     if xedge_i == xbins-1 or x < xedges[xedge_i + 1]: 
      #y right edge 
      if yedge_i == ybins-1 or y < yedges[yedge_i + 1]: 
       print 'found:', x, y 
      #end if 
     #end if 
    #end if 
#end for 

Y at-il un moyen plus propre/plus efficace de le faire? Il semble que tout cela aurait quelque chose pour ça.

+3

Juste par curiosité; pourquoi utilisez-vous des commentaires comme #end si dans votre code? "Chaque pixel compte" En faisant cela, vous ignorez le but de l'indentation. –

+3

2 raisons. Je suis d'abord un développeur C++, puis un développeur python. Le manque d'accolades de Python m'irrite énormément. Quand j'ai des blocs de code compliqués avec beaucoup d'indentation variable, je ne veux pas compter les espaces. Et je fais la majeure partie de mon développement chez Emacs. En mettant des commentaires de fermeture sur les blocs de code, cela me permet d'appuyer sur TAB sur chaque ligne et Emacs n'essaiera pas d'indenter quelque chose à tort. – Ben

Répondre

21

digitize, du cœur NumPy, vous donnera l'indice du bac auquel chaque valeur dans votre histogramme appartient:

import numpy as NP 
A = NP.random.randint(0, 10, 100) 

bins = NP.array([0., 20., 40., 60., 80., 100.]) 

# d is an index array holding the bin id for each point in A 
d = NP.digitize(A, bins)  
+0

C'est presque parfait! S'il y a des devs numpy ici, cette fonction devrait vraiment aller dans la partie "voir aussi" des docs de l'histogramme. Il est dommage que la logique bin de digitalize() ne corresponde pas exactement à la logique bin de histogram(). Donc, cela conduit à un code tout aussi maladroit que le reste de mes exemples ci-dessus. – Ben

+1

n'est-ce pas exactement la même chose que 'bins.searchsorted (A, 'right')'? –

4

que diriez-vous quelque chose comme:

In [1]: data = numpy.array([0, 0.5, 1.5, 1.5, 1.5, 2.5, 2.5, 2.5, 3]) 
In [2]: hist, edges = numpy.histogram(data, bins=3) 
In [3]: for l, r in zip(edges[:-1], edges[1:]): 
    print(data[(data > l) & (data < r)]) 
    ....:  
    ....:  
[ 0.5] 
[ 1.5 1.5 1.5] 
[ 2.5 2.5 2.5] 
In [4]: 

avec un peu de code pour gérer les cas de pointe.

0

pyplot.hist dans matplotlib crée un histogramme (mais le dessine également à l'écran, ce que vous ne voulez peut-être pas). Pour les seules cases, vous pouvez utiliser numpy.histogram, comme indiqué dans une autre réponse.

Here est un exemple comparant pyploy.hist et numpy.histogram.