suma acumulativa de una matriz de numpy por el índice
Pregunta
Suponga que tiene una matriz de valores que necesitarán ser sumadas juntas
d = [1,1,1,1,1]
y una segunda matriz que especifica que necesitan ser sumada juntos elementos
i = [0,0,1,2,2]
El resultado se almacena en una nueva matriz de tamaño max(i)+1
. Así, por ejemplo i=[0,0,0,0,0]
sería equivalente a la suma de todos los elementos de d
y almacenando el resultado en la posición 0
de una nueva matriz de tamaño 1
.
He intentado poner en práctica esta utilizando
c = zeros(max(i)+1)
c[i] += d
Sin embargo, la operación +=
añade cada elemento de una sola vez, dando así el resultado inesperado de
[1,1,1]
en lugar de
[2,1,2]
¿Cómo se podría aplicar correctamente este tipo de sumatoria?
Solución
Esta solución debe ser más eficientes para grandes matrices (que itera sobre los posibles valores de índice en lugar de las entradas individuales de i
):
import numpy as np
i = np.array([0,0,1,2,2])
d = np.array([0,1,2,3,4])
i_max = i.max()
c = np.empty(i_max+1)
for j in range(i_max+1):
c[j] = d[i==j].sum()
print c
[1. 2. 7.]
Otros consejos
Si entiendo bien la pregunta, hay una función rápida para esto (siempre y cuando la matriz de datos es 1d)
>>> i = np.array([0,0,1,2,2])
>>> d = np.array([0,1,2,3,4])
>>> np.bincount(i, weights=d)
array([ 1., 2., 7.])
retornos np.bincount una matriz para todos los números enteros rango (max (i)), incluso si algunos recuentos son cero
El comentario de Juh_ es la solución más eficiente. código aquí está funcionando:
import numpy as np
import scipy.ndimage as ni
i = np.array([0,0,1,2,2])
d = np.array([0,1,2,3,4])
n_indices = i.max() + 1
print ni.sum(d, i, np.arange(n_indices))
def zeros(ilen):
r = []
for i in range(0,ilen):
r.append(0)
i_list = [0,0,1,2,2]
d = [1,1,1,1,1]
result = zeros(max(i_list)+1)
for index in i_list:
result[index]+=d[index]
print result
En el caso general cuando se desea submatrices suma por etiquetas que puede utilizar el siguiente código
import numpy as np
from scipy.sparse import coo_matrix
def labeled_sum1(x, labels):
P = coo_matrix((np.ones(x.shape[0]), (labels, np.arange(len(labels)))))
res = P.dot(x.reshape((x.shape[0], np.prod(x.shape[1:]))))
return res.reshape((res.shape[0],) + x.shape[1:])
def labeled_sum2(x, labels):
res = np.empty((np.max(labels) + 1,) + x.shape[1:], x.dtype)
for i in np.ndindex(x.shape[1:]):
res[(...,)+i] = np.bincount(labels, x[(...,)+i])
return res
El primer método utiliza la multiplicación matriz dispersa. La segunda es la generalización de la respuesta de user333700. Ambos métodos tienen velocidad comparable:
x = np.random.randn(100000, 10, 10)
labels = np.random.randint(0, 1000, 100000)
%time res1 = labeled_sum1(x, labels)
%time res2 = labeled_sum2(x, labels)
np.all(res1 == res2)
Salida:
Wall time: 73.2 ms
Wall time: 68.9 ms
True