Накопительное суммирование массива numpy по индексу

Предположим, у вас есть массив значений, которые необходимо суммировать.

d = [1,1,1,1,1]

и второй массив, указывающий, какие элементы необходимо суммировать вместе

i = [0,0,1,2,2]

Результат будет сохранен в новом массиве размером max(i)+1. Так, например, i=[0,0,0,0,0] будет эквивалентно суммированию всех элементов d и сохранению результата в позиции 0 нового массива размером 1.

Я попытался реализовать это, используя

c = zeros(max(i)+1)
c[i] += d

Однако операция += добавляет каждый элемент только один раз, что дает неожиданный результат

[1,1,1]

вместо

[2,1,2]

Как правильно реализовать такое суммирование?


person D R    schedule 31.08.2010    source источник
comment
Это было бы намного понятнее, если бы значения d были уникальными. Например, если d = [0,1,2,3,4] Im guessing for i = [0,0,0,0,0]` вам нужно c = [10], а для i = [0,0,1,2,2] вам нужно c = [1,2,7]?   -  person mtrw    schedule 31.08.2010
comment
Это правильно. Спасибо за разъяснения.   -  person D R    schedule 31.08.2010
comment
В этом случае решение juxstapose с изменением, которое я предлагаю в комментариях, должно помочь.   -  person mtrw    schedule 31.08.2010


Ответы (5)


Это решение должно быть более эффективным для больших массивов (оно перебирает возможные значения индекса вместо отдельных записей 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.]
person pberkes    schedule 02.09.2010

Если я правильно понимаю вопрос, для этого есть быстрая функция (пока массив данных 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.])

np.bincount возвращает массив для всего диапазона целых чисел (max (i)), даже если некоторые счетчики равны нулю

person Josef    schedule 11.09.2010
comment
это лучшее решение для случая, описанного здесь. Для общей суммы помеченного массива вы можете использовать scipy.ndimage.sum. Эти модули также имеют другие полезные функции, такие как максимум, минимум, среднее значение, дисперсия, ... - person Juh_; 05.03.2013

Комментарий Juh_ - самое эффективное решение. Вот рабочий код:

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))
person Noam    schedule 17.06.2014

В общем случае, когда вы хотите суммировать подматрицы по меткам, вы можете использовать следующий код

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

Первый метод использует умножение разреженных матриц. Второй - это обобщение ответа пользователя 333700. Оба метода имеют сопоставимую скорость:

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)

Выход:

Wall time: 73.2 ms
Wall time: 68.9 ms
True
person ybeltukov    schedule 02.06.2015

person    schedule
comment
Близко, но я думаю, что ОП хочет for didx,ridx in enumerate(i_list): result[ridx] += d[didx]. Кроме того, поскольку теги включают [numpy], вы можете использовать numpy.zeros. - person mtrw; 31.08.2010