Suma la matriz por número en número

Suponiendo que tengo una matriz numpy como: [1,2,3,4,5,6] y otra matriz: [0,0,1,2,2,1] Quiero sumr los elementos de la primera matriz por grupo (la segunda matriz) y obtener los resultados de n-grupos en orden de número de grupo (en este caso, el resultado sería [3, 9, 9]). ¿Cómo hago esto en numpy?

Hay más de una manera de hacer esto, pero aquí hay una manera:

import numpy as np data = np.arange(1, 7) groups = np.array([0,0,1,2,2,1]) unique_groups = np.unique(groups) sums = [] for group in unique_groups: sums.append(data[groups == group].sum()) 

Puedes vectorizar las cosas para que no haya ningún bucle for, pero recomiendo que no. Se vuelve ilegible, y requerirá un par de arreglos temporales 2D, lo que podría requerir grandes cantidades de memoria si tiene muchos datos.

Edición: Aquí hay una forma en que puedes vectorizar por completo. Tenga en cuenta que esto puede (y probablemente será) más lento que la versión anterior. (Y puede que haya una mejor manera de vectorizar esto, pero es tarde y estoy cansado, así que esto es lo primero que me viene a la cabeza …)

Sin embargo, tenga en cuenta que este es un mal ejemplo … Realmente está mejor (tanto en términos de velocidad como de legibilidad) con el bucle anterior …

 import numpy as np data = np.arange(1, 7) groups = np.array([0,0,1,2,2,1]) unique_groups = np.unique(groups) # Forgive the bad naming here... # I can't think of more descriptive variable names at the moment... x, y = np.meshgrid(groups, unique_groups) data_stack = np.tile(data, (unique_groups.size, 1)) data_in_group = np.zeros_like(data_stack) data_in_group[x==y] = data_stack[x==y] sums = data_in_group.sum(axis=1) 

La función bincount se realizó exactamente para este propósito y estoy seguro de que será mucho más rápido que los otros métodos para todos los tamaños de entradas:

 data = [1,2,3,4,5,6] ids = [0,0,1,2,2,1] np.bincount(ids, weights=data) #returns [3,9,9] as a float64 array 

El elemento i-th de la salida es la sum de todos los elementos de data correspondientes a “id” i .

Espero que ayude.

Este es un método vectorizado para hacer esta sum basada en la implementación de numpy.unique. Según mis tiempos, es hasta 500 veces más rápido que el método de bucle y hasta 100 veces más rápido que el método de histogtwig.

 def sum_by_group(values, groups): order = np.argsort(groups) groups = groups[order] values = values[order] values.cumsum(out=values) index = np.ones(len(groups), 'bool') index[:-1] = groups[1:] != groups[:-1] values = values[index] groups = groups[index] values[1:] = values[1:] - values[:-1] return values, groups 

Si los grupos están indexados por enteros consecutivos, puede abusar de la función numpy.histogram() para obtener el resultado:

 data = numpy.arange(1, 7) groups = numpy.array([0,0,1,2,2,1]) sums = numpy.histogram(groups, bins=numpy.arange(groups.min(), groups.max()+2), weights=data)[0] # array([3, 9, 9]) 

Esto evitará cualquier bucle de Python.

Probé scripts de todos y mis consideraciones son:

Joe: Sólo funcionará si tienes pocos grupos.

kevpie: demasiado lento debido a los bucles (esta no es una forma pythonica)

Bi_Rico y Sven: funcionan bien, pero solo funcionarán para Int32 (si la sum supera 2 ^ 32/2, fallará)

Alex: es el más rápido, bueno para la sum.

Pero si quieres más flexibilidad y la posibilidad de agrupar por otras estadísticas utiliza SciPy :

 from scipy import ndimage data = np.arange(10000000) groups = np.arange(1000).repeat(10000) ndimage.sum(data, groups, range(1000)) 

Esto es bueno porque tiene muchas estadísticas para agrupar (sum, media, varianza, …).

Estás todo mal! La mejor manera de hacerlo es:

 a = [1,2,3,4,5,6] ix = [0,0,1,2,2,1] accum = np.zeros(np.max(ix)+1) np.add.at(accum, ix, a) print accum > array([ 3., 9., 9.]) 

Una implementación de python puro:

 l = [1,2,3,4,5,6] g = [0,0,1,2,2,1] from itertools import izip from operator import itemgetter from collections import defaultdict def group_sum(l, g): groups = defaultdict(int) for li, gi in izip(l, g): groups[gi] += li return map(itemgetter(1), sorted(groups.iteritems())) print group_sum(l, g) [3, 9, 9] 

Noté la etiqueta numpy pero en caso de que no te numpy usar pandas , esta tarea se convierte en una sola frase:

 import pandas as pd import numpy as np data = np.arange(1, 7) groups = np.array([0, 0, 1, 2, 2, 1]) df = pd.DataFrame({'data': data, 'groups': groups}) 

Entonces df se ve así:

  data groups 0 1 0 1 2 0 2 3 1 3 4 2 4 5 2 5 6 1 

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

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

lo que le da la salida deseada

  data groups 0 3 1 9 2 9 

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.