2013-05-31 2 views
15

Я ищу быстрое решение для MATLAB's accumarray в numpy. accumarray накапливает элементы массива, принадлежащие одному и тому же индексу. Пример:Есть ли эквивалент эквивалента памяти MATLAB в numpy?

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 функции в recipe here, которая работает хорошо, но медленно.

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 

Затем я попытался использовать solution here, который должен работать быстрее, но он не работает правильно:

accum_np(accmap, a) 
# array([ 1., 2., 12., 13., 17., 10.]) 

Есть встроенный Numpy функция, которая может сделать накопления, как это? Или любые другие рекомендации?

+0

Мое сообщение в блоге одобрено. Попробуйте версию github. у него есть комплект для тестирования покрытия. – Michael

+0

@ Майкл и я создали пакет под названием ['numpy-groupies'] (http://github.com/ml31415/numpy-groupies), который включает в себя функцию, подобную памяти, которая называется' aggregate'. Подробнее см. Мой ответ ниже. –

ответ

17

Используйте np.bincount с необязательным аргументом weights. В вашем примере вы могли бы сделать:

np.bincount(accmap, weights=a) 
+3

ha! это аккуратно :) –

0

Как о следующих:

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 
0

Не так хорошо, как принято отвечать, но:

[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)

5

Late к партии, но ...

Как говорит @Jamie , для случая суммирования, np.bincount быстрый и простой. Однако в более общем случае для других ufuncs, таких как maximum, вы можете использовать метод np.ufunc.at.

Я собрал a gist [см. Ниже ссылку], которая инкапсулирует это в интерфейс, подобный Matlab. Он также использует преимущества повторных правил индексирования для обеспечения функции и 'first', и в отличие от Matlab, 'mean' разумно оптимизирован (вызов accumarray с @mean в Matlab очень медленный, поскольку он выполняет не встроенную функцию для каждой отдельной группы, глупый).

Будьте предупреждены, что я особо не проверял суть, но, надеюсь, обновит его в будущем с дополнительными функциями и исправлениями.

Обновление май/июнь-2015: я переработан моя реализация - теперь доступна как часть ml31415/numpy-groupies и доступна на PyPi (pip install numpy-groupies).Ориентиры следующим образом (см GitHub репо на срок до современных значений) ...

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 повторов.

  • purepy - не использует ничего, кроме чистого питона, опираясь отчасти на itertools.groupby.
  • нп-grouploop - использует numpy для сортировки значений на основе idx, а затем использует split создавать отдельные массивы, а затем перебирает эти массивы, запустив соответствующую numpy функции для каждого массива.
  • нп-ufuncat - использует numpyufunc.at метод, который работает медленнее, чем это должно быть, - как disuccsed в an issue я создал на GitHub репо Numpy в.
  • нп-optimisied - использует пользовательские numpy индексирования/другие трюки бить выше двух реализаций (для min max prod, которые полагаются на ufunc.at за исключением).
  • панды - pd.DataFrame({'idx':idx, 'vals':vals}).groupby('idx').sum() т.д.

Обратите внимание, что некоторые из no-impl с может быть неоправданным, но я не потрудился, чтобы заставить их работать еще.

Как объяснено на GitHub, accumarray теперь поддерживает nan -prefixed функции (например nansum), а также, sort, rsort и array. Он также работает с многомерным индексированием.