2010-01-27 6 views
11

Quel est un bon moyen de placer des valeurs numériques dans une certaine plage? Par exemple, supposons que j'aie une liste de valeurs et que je veuille les classer dans N boîtes par leur portée. En ce moment, je fais quelque chose comme ceci:assigner des points aux cases

from scipy import * 
num_bins = 3 # number of bins to use 
values = # some array of integers... 
min_val = min(values) - 1 
max_val = max(values) + 1 
my_bins = linspace(min_val, max_val, num_bins) 
# assign point to my bins 
for v in values: 
    best_bin = min_index(abs(my_bins - v)) 

où min_index renvoie l'indice de la valeur minimale. L'idée est que vous pouvez trouver la poubelle dans laquelle se trouve le point en voyant à quelle bin elle a la plus petite différence.

Mais je pense que cela a des cas bizarres. Ce que je cherche est une bonne représentation des bacs, idéalement ceux qui sont à moitié fermée à moitié ouverte (pour qu'il n'y ait aucun moyen d'attribuer un point à deux bacs), soit

bin1 = [x1, x2) 
bin2 = [x2, x3) 
bin3 = [x3, x4) 
etc... 

ce qui est une bonne façon de faire cela en Python, en utilisant numpy/scipy? Je ne m'intéresse ici qu'aux valeurs entières de binning.

merci beaucoup pour votre aide.

+0

en note: Je suis plus que disposé à utiliser matplotlib en plus de scipy/numpy s'il a cette fonctionnalité. Je suppose que des fonctions comme 'hist' doivent faire quelque chose comme ça, sauf ici je ne cherche pas de complot. – user248237dfsf

Répondre

21

numpy.histogram() fait exactement ce que vous voulez.

La signature de la fonction est:

numpy.histogram(a, bins=10, range=None, normed=False, weights=None, new=None) 

Nous sommes surtout intéressés a et bins. a sont les données d'entrée qui doivent être mises en cascade. bins peut être un nombre de bacs (votre num_bins), ou il peut s'agir d'une séquence de scalaires, qui dénotent des bords de bacs (à moitié ouverts).

import numpy 
values = numpy.arange(10, dtype=int) 
bins = numpy.arange(-1, 11) 
freq, bins = numpy.histogram(values, bins) 
# freq is now [0 1 1 1 1 1 1 1 1 1 1] 
# bins is unchanged 

Pour citer le documentation:

Tout sauf le dernier bac (le plus de droite) est à moitié ouverte. En d'autres termes, si bins est:

[1, 2, 3, 4] 

alors le premier bac est [1, 2) (y compris 1, mais à l'exclusion 2) et le second [2, 3). Le dernier bac, cependant, est [3, 4], qui comprend 4.

Modifier: Vous voulez connaître l'index dans vos bacs de chaque élément. Pour cela, vous pouvez utiliser numpy.digitize(). Si vos bacs sont intégrés, vous pouvez également utiliser numpy.bincount().

>>> values = numpy.random.randint(0, 20, 10) 
>>> values 
array([17, 14, 9, 7, 6, 9, 19, 4, 2, 19]) 
>>> bins = numpy.linspace(-1, 21, 23) 
>>> bins 
array([ -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 
     10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., 
     21.]) 
>>> pos = numpy.digitize(values, bins) 
>>> pos 
array([19, 16, 11, 9, 8, 11, 21, 6, 4, 21]) 

Depuis l'intervalle est ouvert sur la limite supérieure, les indices sont corrects:

>>> (bins[pos-1] == values).all() 
True 
>>> import sys 
>>> for n in range(len(values)): 
...  sys.stdout.write("%g <= %g < %g\n" 
...    %(bins[pos[n]-1], values[n], bins[pos[n]])) 
17 <= 17 < 18 
14 <= 14 < 15 
9 <= 9 < 10 
7 <= 7 < 8 
6 <= 6 < 7 
9 <= 9 < 10 
19 <= 19 < 20 
4 <= 4 < 5 
2 <= 2 < 3 
19 <= 19 < 20 
+1

merci pour votre réponse - mais je pense que l'histogramme est encore différent de ce que je veux. Je ne suis pas intéressé par la fréquence de l'un des bacs, je veux juste savoir à quel bac tombe chaque point. Il semble que l'histogramme ne renvoie pas cette information, non? – user248237dfsf

+7

Oh, alors vous devriez regarder 'numpy.digitize()'. –

1

Ceci est assez simple en utilisant la diffusion numpy - mon exemple ci-dessous est quatre lignes de code (sans compter deux premières lignes pour créer les bacs et les points de données, ce qui bien sûr ordinairement fournie.)

import numpy as NP 
# just creating 5 bins at random, each bin expressed as (x, y, z) although, this code 
# is not limited by bin number or bin dimension 
bins = NP.random.random_integers(10, 99, 15).reshape(5, 3) 
# creating 30 random data points 
data = NP.random.random_integers(10, 99, 90).reshape(30, 3) 
# for each data point i want the nearest bin, but before i can generate a distance 
# matrix, i need to 'conform' the array dimensions 
# 'broadcasting' is an excellent and concise way to do this 
bins = bins[:, NP.newaxis, :] 
data2 = data[NP.newaxis, :, :] 
# now i can calculate the distance matrix 
dist_matrix = NP.sqrt(NP.sum((data - bins)**2, axis=-1)) 
bin_assignments = NP.argmin(dist_matrix, axis=0) 

« bin_assignments » est un tableau 1D d'indices comprenant des valeurs entières de 0 à 4, correspondant aux cinq cases - les affectations bin pour chacun des 30 points originaux dans la matrice 'data' ci-dessus.

+0

Je ne comprends pas très bien cette réponse, pouvez-vous l'expliquer mieux? –

Questions connexes