Ответ 1
Используйте np.bincount
с необязательным аргументом weights
. В вашем примере вы бы сделали:
np.bincount(accmap, weights=a)
Я ищу быстрое решение для MATLAB accumarray
в numpy. accumarray
накапливает элементы массива, принадлежащие одному и тому же индексу. Пример:
a = np.arange(1,11)
# array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
accmap = np.array([0,1,0,0,0,1,1,2,2,1])
Результат должен быть
array([13, 25, 17])
Что я сделал до сих пор:
Я пробовал функцию accum
в здесь рецепт, который отлично работает, но медленный.
accmap = np.repeat(np.arange(1000), 20)
a = np.random.randn(accmap.size)
%timeit accum(accmap, a, np.sum)
# 1 loops, best of 3: 293 ms per loop
Затем я попытался использовать решение здесь, которое должно работать быстрее, но оно работает неправильно:
accum_np(accmap, a)
# array([ 1., 2., 12., 13., 17., 10.])
Есть ли встроенная функция numpy, которая может делать такое накопление? Или любые другие рекомендации?
Используйте np.bincount
с необязательным аргументом weights
. В вашем примере вы бы сделали:
np.bincount(accmap, weights=a)
Позднее вечеринке, но...
Как говорит @Jamie, для случая суммирования np.bincount
выполняется быстро и просто. Однако в более общем случае для других ufuncs
, таких как maximum
, вы можете использовать метод np.ufunc.at
.
Я собрал gist [см. ниже ссылку], которая инкапсулирует это в Matlab-like интерфейс. Он также использует преимущества повторных правил индексирования для обеспечения функции 'last'
и 'first'
, и в отличие от Matlab, 'mean'
разумно оптимизирован (вызов accumarray
с @mean
в Matlab очень медленный, потому что он запускает не -builtin для каждой отдельной группы, которая глупа).
Будь предупрежден, что я особо не проверял суть, но, надеюсь, обновит его в будущем с дополнительными функциями и исправлениями.
Обновление май/июнь-2015: Я переработал свою реализацию - теперь он доступен как часть ml31415/numpy-groupies и доступно на PyPi (pip install numpy-groupies
). Контрольные показатели следующие (см. Github repo для актуальных значений)...
function pure-py np-grouploop np-ufuncat np-optimised pandas ratio
std 1737.8ms 171.8ms no-impl 7.0ms no-impl 247.1: 24.4: - : 1.0 : -
all 1280.8ms 62.2ms 41.8ms 6.6ms 550.7ms 193.5: 9.4 : 6.3 : 1.0 : 83.2
min 1358.7ms 59.6ms 42.6ms 42.7ms 24.5ms 55.4: 2.4 : 1.7 : 1.7 : 1.0
max 1538.3ms 55.9ms 38.8ms 37.5ms 18.8ms 81.9: 3.0 : 2.1 : 2.0 : 1.0
sum 1532.8ms 62.6ms 40.6ms 1.9ms 20.4ms 808.5: 33.0: 21.4: 1.0 : 10.7
var 1756.8ms 146.2ms no-impl 6.3ms no-impl 279.1: 23.2: - : 1.0 : -
prod 1448.8ms 55.2ms 39.9ms 38.7ms 20.2ms 71.7: 2.7 : 2.0 : 1.9 : 1.0
any 1399.5ms 69.1ms 41.1ms 5.7ms 558.8ms 246.2: 12.2: 7.2 : 1.0 : 98.3
mean 1321.3ms 88.3ms no-impl 4.0ms 20.9ms 327.6: 21.9: - : 1.0 : 5.2
Python 2.7.9, Numpy 1.9.2, Win7 Core i7.
Здесь мы используем индексы 100,000
, равномерно выбранные из [0, 1000)
. В частности, около 25% значений 0
(для использования с операциями bool), остальные равномерно распределяются по [-50,25)
. Сроки показаны на 10 повторов.
itertools.groupby
.numpy
для сортировки значений на основе idx
, затем использует split
для создания отдельных массивов, а затем перебирает эти массивы, запуская соответствующую функцию numpy
для каждого массива.numpy
ufunc.at
, который медленнее, чем он должен быть - как disuccsed в проблема Я создал на numpy github repo.numpy
индексирование/другие трюки, чтобы обойти эти две реализации (кроме min max prod
, которые полагаются на ufunc.at
).pd.DataFrame({'idx':idx, 'vals':vals}).groupby('idx').sum()
и т.д.Обратите внимание, что некоторые из no-impl
могут быть необоснованными, но я не потрудился заставить их работать.
Как поясняется в github, accumarray
теперь поддерживает nan
-предоставляемые функции (например, nansum
), а также sort
, rsort
и array
. Он также работает с многомерным индексированием.
Я написал реализацию аккумулирования с помощью scipy.weave
и загрузил его в github: https://github.com/ml31415/numpy-groupies
Как насчет следующего:
import numpy
def accumarray(a, accmap):
ordered_indices = numpy.argsort(accmap)
ordered_accmap = accmap[ordered_indices]
_, sum_indices = numpy.unique(ordered_accmap, return_index=True)
cumulative_sum = numpy.cumsum(a[ordered_indices])[sum_indices-1]
result = numpy.empty(len(sum_indices), dtype=a.dtype)
result[:-1] = cumulative_sum[1:]
result[-1] = cumulative_sum[0]
result[1:] = result[1:] - cumulative_sum[1:]
return result
Не так хорошо, как принятый ответ, но:
[np.sum([a[x] for x in y]) for y in [list(np.where(accmap==z)) for z in np.unique(accmap).tolist()]]
Это занимает 108us per loop
(100000 циклов, лучше всего 3)
Принятый ответ (np.bincount(accmap, weights=a
) принимает 2.05us per loop
(100000 циклов, лучше всего 3)