2010-01-27 20 views
11

¿Cuál es una buena manera de bin valores numéricos en un cierto rango? Por ejemplo, supongamos que tengo una lista de valores y quiero ubicarlos en N bandejas por su rango. En este momento, hago algo como esto:asignando puntos a bandejas

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

donde min_index devuelve el índice del valor mínimo. La idea es que puedas encontrar el bin en el que cae el punto al ver con qué bin tiene la menor diferencia.

Pero creo que esto tiene casos de bordes extraños. Lo que estoy buscando es una buena representación de contenedores, idealmente los que están semicerrados medio abierto (por lo que no hay manera de asignar un punto a dos contenedores), es decir

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

lo que es una buena manera de hacer esto en Python, usando numpy/scipy? Aquí solo me preocupa el binning de valores enteros.

muchas gracias por su ayuda.

+0

como nota al margen: estoy más que dispuesto a usar matplotlib además de scipy/numpy si tiene esta funcionalidad. Supongo que funciones como 'hist' deben hacer algo como esto, excepto que aquí no estoy buscando ningún trazado. – user248237dfsf

Respuesta

21

numpy.histogram() hace exactamente lo que quiere.

La firma de la función es:

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

Estamos interesados ​​en su mayoría a y bins. a es la información de entrada que debe ser binned. bins puede ser un número de contenedores (su num_bins), o puede ser una secuencia de escalares, que denotan bordes del contenedor (medio abiertos).

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 

citar el documentation:

Todos menos el último bin (derecha-la mayoría) es entreabierta. En otras palabras, si bins es:

[1, 2, 3, 4] 

entonces el primer bin es [1, 2) (incluyendo 1, pero con exclusión de 2) y el segundo [2, 3). El último intervalo, sin embargo, es [3, 4], que incluye 4.

Edición: ¿Quieres saber el índice en los contenedores de cada elemento. Para esto, puede usar numpy.digitize(). Si sus contenedores van a ser integrales, también puede usar 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]) 

Dado que el intervalo es abierto en el límite superior, los índices son correctas:

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

gracias por su respuesta, pero creo que el histograma aún es diferente de lo que quiero. No estoy interesado en la frecuencia de ninguno de los contenedores, solo quiero saber en qué bin cae cada punto. Parece que el histograma no devuelve esa información, ¿verdad? – user248237dfsf

+7

Oh, entonces deberías mirar 'numpy.digitize()'. –

1

Esto es bastante sencillo de usar numpy radiodifusión - mi ejemplo a continuación es de cuatro líneas de código (sin contar dos primeras líneas para crear contenedores y puntos de datos, lo que por supuesto ordinariamente ser suministrado.)

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' es un array 1d de índices compuesta de valores enteros de 0 a 4, correspondientes a los cinco contenedores: las asignaciones de los contenedores para cada uno de los 30 puntos originales en la matriz 'datos' anterior.

+0

No puedo entender esta respuesta muy bien, ¿puedes explicarla mejor? –

Cuestiones relacionadas