Есть ли в матлабе эквивалентный эквивалент в NumPy?

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

7 ответов

Решение

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

np.bincount(accmap, weights=a)

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

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

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

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

Обновление май / июнь 2015: я переработал свою реализацию - теперь она доступна как часть https://github.com/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 - использует numpyufunc.at метод, который медленнее, чем следовало бы - как обсуждалось в проблеме, которую я создал на репозитории github numpy.
  • np-optimisied - использует кастом numpy индексирование / другие приемы, чтобы превзойти две вышеупомянутые реализации (за исключением min max prod которые полагаются на ufunc.at).
  • панды - pd.DataFrame({'idx':idx, 'vals':vals}).groupby('idx').sum() и т.п.

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

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

Я написал реализацию accmarray с scipy.weave и загрузил его на github: https://github.com/ml31415/numpy-groupies

Вы можете сделать это с пандами DataFrame в одну строку.

In [159]: df = pd.DataFrame({"y":np.arange(1,11),"x":[0,1,0,0,0,1,1,2,2,1]})

In [160]: df
Out[160]: 
   x   y
0  0   1
1  1   2
2  0   3
3  0   4
4  0   5
5  1   6
6  1   7
7  2   8
8  2   9
9  1  10

In [161]: pd.pivot_table(df,values='y',index='x',aggfunc=sum)
Out[161]: 
    y
x    
0  13
1  25
2  17

Вы можете сказать pivot_table использовать определенные столбцы в качестве индексов и значений и получить новый объект DataFrame. Когда вы указываете функцию агрегирования в качестве суммы, результаты будут идентичны точным подсчетам Matlab.

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

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

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

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

Это зависит от того, что именно вы пытаетесь сделать, но numpy unique имеет множество дополнительных выходных данных, которые вы можете использовать для накопления. Если ваш массив имеет несколько одинаковых значений, то unique будет подсчитывать, сколько одинаковых значений существует, установив для параметра return_counts значение true. В некоторых простых приложениях это все, что вам нужно сделать.

numpy.unique(ar, return_index=False, return_inverse=False, return_counts=True, axis=None)

Вы также можете установить для индекса значение true и использовать его для накопления другого массива.

Другие вопросы по тегам