Применить функцию вдоль диапазонов в массиве numpy - PullRequest
0 голосов
/ 05 февраля 2019

Скажем, у меня есть следующий массив NumPy:

a = np.arange(20)

А также массив, содержащий индексы следующим образом:

ix = np.array([4,10,15])

Я пытался найти векторизованное решениена следующий вопрос: Как я могу применить функцию вдоль a, которая будет разделена, используя индексы в ix?

Так скажи я, где разделить a с np.split (я использую толькоnp.split здесь, чтобы проиллюстрировать группы, к которым я хотел бы применить функцию здесь:

np.split(a,ix)

[array([0, 1, 2, 3]),
 array([4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14]),
 array([15, 16, 17, 18, 19])]

И скажем, например, я хотел бы взять сумму на каждый кусок, так что давая:

[6, 39, 60, 85]

Как можно векторизовать это, используя numpy?

Ответы [ 3 ]

0 голосов
/ 05 февраля 2019

split создает список массивов, которые могут отличаться по длине.Это фактически делает итеративно

In [12]: alist = []
In [13]: alist.append(a[0:idx[0]])
In [14]: alist.append(a[idx[0]:idx[1]])
In [15]: alist.append(a[idx[1]:idx[2]])
....

Применение sum к каждому элементу списка в отдельности имеет смысл:

In [11]: [np.sum(row) for row in alist]
Out[11]: [6, 39, 60, 85]

Когда у вас есть список массивов, которые отличаются по форме, этохорошая ставка, что вам придется делать итерацию уровня Python на нем.

Быстрая «векторизация» означает выполнение вычислений в скомпилированном коде.Большинство из них построены вокруг многомерных массивов, например, 2d.Если ваш split создал массив одинакового размера, вы можете использовать np.sum с соответствующим параметром оси.

In [23]: a1 = a.reshape(4,5)
In [24]: a1
Out[24]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])
In [25]: np.sum(a1, axis=1)
Out[25]: array([10, 35, 60, 85])

Иногда мы можем разыграть трюки, чтобы привести проблему к nd, например, если вашпервый массив разбиения был дополнен 0. Но само приведение может потребовать итерации.

Как поднято здесь (и его ссылки) Происхождение AttributeError: у объекта нет атрибута 'cos' математические (ufunc) функции, применяемые к массивам объектов dtype, заканчивают делегированием действия соответствующим методам объектов.Но это все еще включает (почти) итерацию уровня Python над объектами.


Некоторые моменты времени:

In [57]: timeit [np.sum(row) for row in alist]
31.7 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [58]: timeit np.sum(list(itertools.zip_longest(*alist, fillvalue=0)),axis=0)
25.2 µs ± 82 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [59]: timeit np.nansum(pd.DataFrame(alist), axis=1)
908 µs ± 28.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [61]: timeit np.frompyfunc(sum,1,1)(alist)
12.9 µs ± 21.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

В этом последнем случае Python sum быстрее, чем np.sum.Но это верно и для понимания списка:

In [63]: timeit [sum(row) for row in alist]
6.86 µs ± 13.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

И с помощью wiz-bang Дивакара fillna, Numpy: исправить массив со строками разной длины, заполнив пустые элементы нулями

In [70]: timeit numpy_fillna(np.array(alist)).sum(axis=1)
44.2 µs ± 208 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Если у вас есть многомерный массив, код Numpy работает быстро.Но если начать со списка, даже со списком массивов, методы списка Python часто работают быстрее.Время, необходимое для создания массива (или Dataframe), никогда не бывает тривиальным.

0 голосов
/ 05 февраля 2019

Решение для панд будет:

import numpy as np
import pandas as pd

a = np.arange(20)

ix = np.array([4, 10, 15])

data = pd.DataFrame(np.split(a, ix))

print(np.nansum(data, axis=1))

Выход

[ 6. 39. 60. 85.]
0 голосов
/ 05 февраля 2019

Я не знаю, является ли это лучшим решением, но вы можете преобразовать список массивов с разными размерами в список массивов фиксированного размера, добавив нули.А затем реализовать функцию типа sum, которая не зависит от нулей.

См. Пример ниже.

a = np.arange(20)
ix = np.array([4,10,15])
b = np.split(a,ix)
print(b)

приводит к

[array([0, 1, 2, 3]),
 array([4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14]),
 array([15, 16, 17, 18, 19])]

Затем используйте itertools для преобразования списка в массив отсюда

import itertools
c = np.array(list(itertools.zip_longest(*b, fillvalue=0))).T
print(c)

, что приводит к

[[ 0  1  2  3  0  0]
 [ 4  5  6  7  8  9]
 [10 11 12 13 14  0]
 [15 16 17 18 19  0]]

, а затем суммирует его, используя

np.sum(c, axis = 1)

, в результате

array([ 6, 39, 60, 85])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...