¿La forma más eficiente de sumr una enorme matriz NumPy 2D, agrupada por columna de ID?

Tengo una matriz de datos masiva (500k filas) que se parece a:

id value score 1 20 20 1 10 30 1 15 0 2 12 4 2 3 8 2 56 9 3 6 18 ... 

Como puede ver, hay una columna de ID no única a la izquierda, y varias puntuaciones en la tercera columna.

Estoy buscando sumr rápidamente todas las puntuaciones, agrupadas por ID. En SQL, esto se vería como una SELECT sum(score) FROM table GROUP BY id

Con NumPy he intentado iterar a través de cada ID, truncando la tabla por cada ID y luego resumiendo la puntuación de esa tabla.

 table_trunc = table[(table == id).any(1)] score = sum(table_trunc[:,2]) 

Lamentablemente estoy encontrando el primer comando para ser perro lento. ¿Hay alguna forma más eficiente de hacer esto?

puedes usar bincount ():

 import numpy as np ids = [1,1,1,2,2,2,3] data = [20,30,0,4,8,9,18] print np.bincount(ids, weights=data) 

la salida es [0. 50. 21. 18.], lo que significa que la sum de id == 0 es 0, la sum de id == 1 es 50.

Noté la etiqueta numpy pero en caso de que no te numpy usar pandas (o si lees estos datos usando este módulo), esta tarea se convierte en una frase de una sola línea:

 import pandas as pd df = pd.DataFrame({'id': [1,1,1,2,2,2,3], 'score': [20,30,0,4,8,9,18]}) 

Entonces tu dataframe se vería así:

  id score 0 1 20 1 1 30 2 1 0 3 2 4 4 2 8 5 2 9 6 3 18 

Ahora puedes usar las funciones groupby() y sum() :

 df.groupby(['id'], sort=False).sum() 

lo que le da la salida deseada:

  score id 1 50 2 21 3 18 

De forma predeterminada, el dataframe se ordenaría, por lo tanto, uso la marca sort=False que podría mejorar la velocidad de grandes marcos de datos.

Puedes intentar usar operaciones booleanas:

 ids = [1,1,1,2,2,2,3] data = [20,30,0,4,8,9,18] [((ids == i)*data).sum() for i in np.unique(ids)] 

Esto puede ser un poco más efectivo que usar np.any , pero claramente tendrá problemas si tiene una gran cantidad de identificadores únicos para acompañar el gran tamaño de la tabla de datos.

Si está buscando solo una sum , probablemente quiera ir con bincount . Si también necesita otras operaciones de agrupación como product, mean, std, etc., visite https://github.com/ml31415/numpy-groupies . Es la operación de agrupación de python / numpy más rápida, vea la comparación de velocidad allí.

Su operación de sum allí se vería como:

 res = aggregate(id, score) 

El paquete numpy_indexed tiene una funcionalidad vectorizada para realizar esta operación de manera eficiente, además de muchas operaciones relacionadas de este tipo:

 import numpy_indexed as npi npi.group_by(id).sum(score) 

Puedes usar un bucle for y numba

 from numba import njit @njit def wbcnt(b, w, k): bins = np.arange(k) bins = bins * 0 for i in range(len(b)): bins[b[i]] += w[i] return bins 

Usando las variables de @ HYRY

 ids = [1, 1, 1, 2, 2, 2, 3] data = [20, 30, 0, 4, 8, 9, 18] 

Entonces:

 wbcnt(ids, data, 4) array([ 0, 50, 21, 18]) 

Sincronización

 %timeit wbcnt(ids, data, 4) %timeit np.bincount(ids, weights=data) 1000000 loops, best of 3: 1.99 µs per loop 100000 loops, best of 3: 2.57 µs per loop 

Tal vez usando itertools.groupby , puede agrupar en la ID y luego iterar sobre los datos agrupados.

(Los datos deben ordenarse de acuerdo con el grupo por función, en este caso ID)

 >>> data = [(1, 20, 20), (1, 10, 30), (1, 15, 0), (2, 12, 4), (2, 3, 0)] >>> groups = itertools.groupby(data, lambda x: x[0]) >>> for i in groups: for y in i: if isinstance(y, int): print(y) else: for p in y: print('-', p) 

Salida:

 1 - (1, 20, 20) - (1, 10, 30) - (1, 15, 0) 2 - (2, 12, 4) - (2, 3, 0)