2010-05-02 9 views
5

estoy binning una matriz 2D (x por y) en Python en los contenedores de su valor x (dado en "contenedores"), utilizando np.digitize:enfoque vectorizada a binning con numpy/scipy en Python

elements_to_bins = digitize(vals, bins) 

donde "Vals" es una matriz 2D, es decir:

vals = array([[1, v1], [2, v2], ...]). 

elements_to_bins sólo dice lo bin cada elemento cae en. Lo que quiero hacer es obtener una lista cuya longitud es la cantidad de contenedores en "contenedores", y cada elemento devuelve la dimensión y de "vals" que cae en ese contenedor. Lo hago de esta manera en este momento:

points_by_bins = [] 
for curr_bin in range(min(elements_to_bins), max(elements_to_bins) + 1): 
    curr_indx = where(elements_to_bins == curr_bin)[0] 
    curr_bin_vals = vals[:, curr_indx] 
    points_by_bins.append(curr_bin_vals) 

¿Hay alguna manera más elegante/más simple de hacer esto? Todo lo que necesito es una lista de las listas de valores y que caen en cada contenedor.

gracias.

+0

¡Si una de las respuestas resolvió su problema, márquelo como aceptado (marca de verificación verde)! :) – EOL

Respuesta

3

Si entiendo bien su pregunta: rápida operación gama

vals = array([[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]]) # Example 

(x, y) = vals.T # Shortcut 
bin_limits = range(min(x)+1, max(x)+2) # Other limits could be chosen 
points_by_bin = [ [] for _ in bin_limits ] # Final result 
for (bin_num, y_value) in zip(searchsorted(bin_limits, x, "right"), y): # digitize() finds the correct bin number 
    points_by_bin[bin_num].append(y_value) 

print points_by_bin # [[10, 11], [20, 21, 22]] 

de Numpy searchsorted() se utiliza para una máxima eficiencia. Los valores se agregan uno por uno (dado que el resultado final no es una matriz rectangular, Numpy no puede ayudar mucho, para esto). Esta solución debe ser más rápida que múltiples llamadas where() en un bucle, lo que obliga a Numpy a volver a leer misma matriz muchas veces.

+1

numpy.searchsorted debe preferirse digitalizar por razones de rendimiento: https://github.com/numpy/numpy/issues/2656 – Alleo

+0

@Alleo: Muy buen punto (para la implementación actual de 'digitalizar()'). Actualicé la respuesta. – EOL

0

¿Las claves del contenedor son enteros, sin agrupamiento, como en el ejemplo? Posteriormente, se podría simplemente hacer esto, sin numpy:

from collections import defaultdict 
bins = defaultdict(list) # or [ [] ...] as in EOL 

vals = [[1, 10], [1, 11], [2, 20], [2, 21], [2, 22]] # nparray.tolist() 
for nbin, val in vals: 
    bins[nbin].append(val) 

print "bins:", bins 
# defaultdict(<type 'list'>, {1: [10, 11], 2: [20, 21, 22]}) 
+0

+1: esto se ve bien para mí, excepto tal vez por el hecho de que los contenedores vacíos no contienen listas vacías (que podrían corregirse con un valor predeterminado). Sin embargo, tal vez el cartel original tenga compartimientos más generales en mente? – EOL

1

Esto devolverá una estructura de datos análoga a Reverse_Indices de IDL HISTOGRAMA:

ovec = np.argsort(vals) 
ivec = np.searchsorted(vals, bin_limits, sorter=ovec) 

a continuación la lista de los elementos que entran en #i bin es

ovec[ ivec[i] : ivec[i+1] ] 

(mis pruebas rápidas de tiempo dicen que esto es 5 veces más rápido que el algoritmo de EOL, ya que no se molesta en la creación de listas de diferentes tamaños)

Cuestiones relacionadas