Operadores de asignación compuestos en la biblioteca Numpy de Python

La "vectorización" de la indexación elegante de la biblioteca numpy de Python a veces da resultados inesperados. Por ejemplo:

import numpy
a = numpy.zeros((1000,4), dtype='uint32')
b = numpy.zeros((1000,4), dtype='uint32')
i = numpy.random.random_integers(0,999,1000)
j = numpy.random.random_integers(0,3,1000)

a[i,j] += 1
for k in xrange(1000):
    b[i[k],j[k]] += 1

Da resultados diferentes en las matrices 'a' y 'b' (es decir, la apariencia de la tupla (i,j) aparece como 1 en 'a' independientemente de las repeticiones, mientras que las repeticiones se cuentan en 'b'). Esto se verifica fácilmente de la siguiente manera:

numpy.sum(a)
883
numpy.sum(b)
1000

También es notable que la versión de indexación elegante es casi dos órdenes de magnitud más rápida que el bucle for. Mi pregunta es: "¿Existe una manera eficiente para que numpy calcule los recuentos repetidos tal como se implementó utilizando el bucle for en el ejemplo proporcionado?"

preguntado el 12 de junio de 12 a las 17:06

la semántica es diferente: el bucle for funciona en secuencia (los valores de los índices repetidos se incrementan varias veces), pero a += 1 es equivalente a a = a + 1 (incrementa una vez para índices repetidos). -

Sé que hay una diferencia en la semántica (no debería haber dicho 'inesperado' en mi publicación original), pero ¿existe una forma eficiente de calcular la semántica del bucle for (es decir, contar las ocurrencias de (i, j) tuplas) usando numPy ? -

La opción perezosa es usar Cython. Puede ser más rápido y más legible que la versión basada en funciones numpy. Aunque lo último obliga a pensar en la operación en términos más generales (podría ser algo bueno) y la solución resultante podría ser más extensible, por ejemplo, stackoverflow.com/questions/4962606/… -

1 Respuestas

Esto debería hacer lo que quieras:

np.bincount(np.ravel_multi_index((i, j), (1000, 4)), minlength=4000).reshape(1000, 4)

Como un desglose, ravel_multi_index convierte los pares de índices especificados por i y j a índices enteros en una matriz C-aplanada; bincount cuenta el número de veces que cada valor 0..4000 aparece en esa lista de índices; y reshape convierte la matriz aplanada en C de nuevo en una matriz 2d.

En términos de rendimiento, lo mido 200 veces más rápido que "b", y 5 veces más rápido que "a"; Su experiencia puede ser diferente.

Dado que necesita escribir los recuentos en una matriz existente a, prueba esto:

u, inv = np.unique(np.ravel_multi_index((i, j), (1000, 4)), return_inverse=True)
a.flat[u] += np.bincount(inv)

Hago este segundo método un poco más lento (2x) que "a", lo cual no es demasiado sorprendente ya que el unique el escenario va a ser lento.

Respondido el 13 de junio de 12 a las 16:06

Consideré usar bincount(). Sin embargo, mi matriz es en realidad mucho más grande que en el ejemplo que proporcioné (> 300 * 106). Y el número de pares de actualizaciones (i,j) es relativamente menor (alrededor de 20*106) con muchas repeticiones. Me temo que el enfoque bincount usará demasiada memoria. - user1451766

@ user1451766 - bincount no usará más memoria de la que necesita para construir a = numpy.zeros(...) en tu publicación original. - ecatmur

En mi caso, 'a' es un archivo memmap(). Ninguno de los enfoques de indexación elegante o bucle for requiere un búfer de memoria intermedio que sea tan grande. - user1451766

Sin embargo, su ravel_multi_index en combinación con unique( ,return_inverse=True) podría hacer el truco! - user1451766

Agregué otro método, ¿es eso lo que buscabas? - ecatmur

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.