2010-01-27 13 views
11

Che cosa è un buon modo per collocare valori numerici in un determinato intervallo? Ad esempio, supponiamo di avere un elenco di valori e voglio dividerli in N bin per il loro intervallo. In questo momento, faccio qualcosa del genere:Assegnazione di punti ai raccoglitori

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

dove min_index restituisce l'indice del valore minimo. L'idea è che puoi trovare il cestino in cui cade il punto, vedendo con quale bin ha la più piccola differenza.

Ma penso che questo abbia strani casi limite. Quello che sto cercando è una buona rappresentazione della cassonetti, idealmente quelli che sono chiusi mezza aperta (in modo che non c'è modo di assegnare un punto a due cassonetti) la metà, vale a dire

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

che cosa è un buon modo per fai questo in Python, usando numpy/scipy? Mi interessa solo qui con i valori interi di binning.

grazie mille per il vostro aiuto.

+0

come nota a margine: sono più che disposto a usare matplotlib oltre a scipy/numpy se ha questa funzionalità. Immagino che funzioni come 'hist' debbano fare qualcosa del genere, eccetto che qui non sto cercando nessun tracciato. – user248237dfsf

risposta

21

numpy.histogram() fa esattamente quello che vuoi.

La firma funzione è:

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

Siamo per lo più interessati a a e bins. a sono i dati di input che devono essere abbinati. bins può essere un numero di contenitori (il tuo num_bins), oppure può essere una sequenza di scalari, che denotano i bordi del cestino (mezzo aperto).

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 

Per citare il documentation:

Tutti tranne l'ultimo bidone (più a destra) è socchiusa. In altre parole, se è bins:

[1, 2, 3, 4] 

allora il primo bidone è [1, 2) (di cui 1, esclusi 2) e la seconda [2, 3). L'ultimo bidone, però, è [3, 4], che include 4.

Modifica: Volete sapere l'indice i bidoni di ogni elemento. Per questo, è possibile utilizzare numpy.digitize(). Se i tuoi contenitori saranno integrali, puoi usare anche 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]) 

Poiché l'intervallo è aperta sul limite superiore, gli indici sono corretti:

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

grazie per la tua risposta - ma penso che l'istogramma sia ancora diverso da quello che voglio. Non mi interessa la frequenza di nessuno dei cestini, voglio solo sapere in che cosa cade ogni punto. Sembra che l'istogramma non restituisca quell'informazione, giusto? – user248237dfsf

+7

Oh, allora dovresti dare un'occhiata a 'numpy.digitize()'. –

1

Ciò è molto semplice in NumPy usando broadcasting - mio esempio qui sotto è quattro linee di codice (senza contare prime due righe per creare bidoni e punti di dati, che naturalmente normalmente essere fornite.)

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' è una matrice 1d di indici composto da valori interi Da 0 a 4, corrispondenti ai cinque scomparti: le assegnazioni bin per ciascuno dei 30 punti originali nella matrice "dati" sopra.

+0

Non riesco a capire bene questa risposta, puoi spiegarlo meglio? –