Есть ли в матлабе эквивалентный эквивалент в 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 - использует
numpy
ufunc.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 и использовать его для накопления другого массива.