Есть ли эквивалент эквивалента памяти MATLAB в numpy?

Я ищу быстрое решение для 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, которая может делать такое накопление? Или любые другие рекомендации?

Ответы

Ответ 1

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

np.bincount(accmap, weights=a)

Ответ 2

Позднее вечеринке, но...

Как говорит @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 повторов.

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

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

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

Ответ 4

Как насчет следующего:

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

Ответ 5

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

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