Обтекание срезов в Python/numpy - программирование
Подтвердить что ты не робот

Обтекание срезов в Python/numpy

У меня есть массив numpy, и я хочу получить "окрестности" i-й точки. Обычно массивы, которые я использую, являются двумерными, но следующий пример 1D иллюстрирует то, что я ищу. Если

A = numpy.array([0,10,20,30,40,50,60,70,80,90])

Тогда окрестность элемента (размер 5) элемента 4 равна [20,30,40,50,60], и это легко получить, выполняя A[i-2:i+3].

Однако мне также нужны окрестности для "обертывания" краев массива, так что окрестность элемента 0 равна [80,90,0,10,20], а окрестность элемента 9 равна [70,80,90,0,10]. Кажется, я не могу найти элегантный способ сделать это, поэтому в конечном итоге мне приходится использовать сложную, раздражающую логику каждый раз, когда это появляется (что очень часто для меня). В двумерном случае окрестность точки будет прямоугольным массивом.

Итак, мой вопрос: есть ли опрятный способ выражения этой операции "округление окрестности" в numpy? Я бы предпочел что-то, что возвращает фрагмент, а не копию, но читаемость и скорость являются наиболее важными соображениями.

4b9b3361

Ответ 1

numpy.take в режиме 'wrap' будет использовать ваши индексы по модулю длины массива.

indices = range(i-2,i+3)
neighbourhood = A.take(indices, mode='wrap')

Подробнее см. документацию numpy.take

Ответ 2

вы можете использовать аргумент axis=0 для numpy.take для массива n-d.

A = zip(range(0,101,10),range(0,11)) #create 2-d list
A = numpy.array(A)   #create 2-d array  
indices = range(i-2,i+3)
neightbourhood = A.take(indices,axis=0,mode='wrap')

Тот же axis=0 будет работать для измерений n * m...

Ответ 3

Я знаю, что этот вопрос старый, но следует упомянуть scipy.ndimage.filter.generic_filter.

Он имеет параметр mode='wrap', плюс он обрабатывает приложение соседней функции.

import scipy.ndimage as nd

A = np.array([0,10,20,30,40,50,60,70,80,90])

Скажите, что у вас есть соседняя функция:

def nbf(arr):
    return sum(arr)

Чтобы применить соседнюю функцию к каждому 5, с обернутыми значениями по краям:

C = nd.generic_filter(A, nbf, 5, mode='wrap')

print(C)
[200 150 100 150 200 250 300 350 300 250]

Ответ 4

Примечание. В случаях, когда ваши соседи не требуют обертывания, numpy.take работает медленнее, чем просто взять срез A[i-2:i+3]. Вы можете обернуть функцию соседей с помощью некоторых условных операторов:

def neighbors(a,i,n):
    N = a.shape[0] 
    if i - n < 0 and i + n > 0:
        indices = range(i-n,i+n+1)
        nbrs = a.take(indices, mode='wrap')
    elif i-n < N - 1 and i+n > N - 1:
        indices = range(i-n,i+n+1)
        nbrs = a.take(indices, mode='wrap')
    else:
        nbrs = a[i-n:i+n+1]
    return nbrs

Если вы обнаружите, что принимаете соседей при повторении через массив, например, в центрированной скользящей средней, вы обнаружите, что для этого требуется меньше времени, особенно для более длинных массивов:

введите описание изображения здесь

Вот скользящая средняя функция, которую я использовал:

def moving_average(a,n=1):
    N = a.shape[0] 
    ma = np.empty(N)
    for i in range(N):
        if n*2+1 > N:
            ma[i] = a.mean()
        else: 
            ma[i] = neighbors(a,i,n).mean()
    return ma

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

Ответ 5

Вы можете использовать процедуру np.pad следующим образом:

A = np.array([0,10,20,30,40,50,60,70,80,90])
A = np.pad(A, 2, 'wrap')
print(A)
[80, 90,  0, 10, 20, 30, 40, 50, 60, 70, 80, 90,  0, 10]

Скажите, что у вас есть соседняя функция:

def nbf(arr):
    return sum(arr)

Чтобы применить соседнюю функцию к каждому 5, вам нужно быть осторожным относительно ваших начальных и конечных индексов (в команде диапазона (...)) и относительного фрагмента, который вы берете из A.

B = [nbf(A[i-2:i+3]) for i in range(2,12)]
print(B)
[200, 150, 100, 150, 200, 250, 300, 350, 300, 250]

Ответ 6

numpy.roll может смещать массив таким образом, чтобы весь срез находился в начале массива. Затем возьмите свой срез в начале и numpy.roll еще раз, чтобы вернуть массив обратно в исходное положение.

# modify array at index i and nearest two
# locations on each side of i, wrapping
# around the edges
A = np.array([0,10,20,30,40,50,60,70,80,90])
i = 9
neighbors = 2
A=np.roll(A, -i+neighbors)
A[:5] += 1
A=np.roll(A, i-neighbors)

array([ 1, 11, 20, 30, 40, 50, 60, 71, 81, 91])

numpy.roll не работает хорошо для меня на больших массивах.