简体   繁体   English

在 Python / numpy 中环绕切片

[英]wrapping around slices in Python / numpy

I have a numpy array, and I want to get the "neighbourhood" of the i'th point.我有一个 numpy 数组,我想获得第 i 个点的“邻域”。 Usually the arrays I'm using are two-dimensional, but the following 1D example illustrates what I'm looking for.通常我使用的数组是二维的,但下面的一维示例说明了我在寻找什么。 If如果

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

Then the (size 5) neighbourhood of element 4 is [20,30,40,50,60] , and this can easily be obtained by doing A[i-2:i+3] .那么元素 4 的(大小为 5)邻域是[20,30,40,50,60] ,这可以通过执行A[i-2:i+3]轻松获得。

However, I also need the neighbourhoods to "wrap around" the edges of the array, so that the neighbourhood of the element 0 is [80,90,0,10,20] and the neighbourhood of the element 9 is [70,80,90,0,10] .但是,我还需要邻域来“环绕”数组的边缘,以便元素 0 的邻域为[80,90,0,10,20] ,元素 9 的邻域为[70,80,90,0,10] I can't seem to find an elegant way to do this, so I end up having to use some complicated, annoying logic every time this comes up (which is very often for me).我似乎找不到一种优雅的方法来做到这一点,所以每次出现这种情况时,我最终不得不使用一些复杂的、烦人的逻辑(这对我来说很常见)。 In the 2D case the neighbourhood of a point would be a rectangular array.在二维情况下,一个点的邻域将是一个矩形阵列。

So my question is, is there a neat way to expres this "wrap-around neighbourhood" operation in numpy?所以我的问题是,有没有一种巧妙的方法可以在 numpy 中表达这种“环绕邻域”操作? I would prefer something that returns a slice rather than a copy, but readability and speed are the most important considerations.我更喜欢返回切片而不是副本的东西,但可读性和速度是最重要的考虑因素。

numpy.take in 'wrap' mode will use your indices modulo the length of the array. 'wrap'模式下的numpy.take将使用您的索引以数组的长度为模。

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

See documentation for details numpy.take有关详细信息,请参阅文档numpy.take

you can use argument axis=0 of numpy.take for nd array.您可以将numpy.take参数axis=0用于 nd 数组。

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

The same axis=0 will work for n*m dimensions...相同的axis=0将适用于 n*m 维度...

Note: For cases where your neighbors do not require wrapping, numpy.take is slower than simply taking a slice A[i-2:i+3] .注意:对于邻居不需要包装的情况, numpy.take比简单地获取切片A[i-2:i+3]慢。 You may want to wrap your neighbors function with some conditional statements:你可能想用一些条件语句来包装你的邻居函数:

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

If you find yourself taking neighbors while iterating through an array, like in a centered moving average, you'll find that this requires less time, especially for longer arrays:如果您发现自己在遍历数组时使用邻居,例如在居中移动平均线中,您会发现这需要更少的时间,尤其是对于较长的数组:

在此处输入图片说明

Here is the moving average function I used:这是我使用的移动平均函数:

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

I'm sure these function can be improved further.我相信这些功能可以进一步改进。 I'm open to suggestions.我愿意接受建议。

I know this question is old, but should mention scipy.ndimage.filter.generic_filter .我知道这个问题很老,但应该提到scipy.ndimage.filter.generic_filter

It has a mode='wrap' option, plus it handles the application of the neighbor function.它有一个mode='wrap'选项,另外它还处理邻居函数的应用。

import scipy.ndimage as nd

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

Say you have a neighbor function:假设你有一个邻居函数:

def nbf(arr):
    return sum(arr)

To apply the neighbor function to every 5, with wrapped values at the edges:要将邻居函数应用于每 5 个,并在边缘处包装值:

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

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

You can use the np.pad routine like this:您可以像这样使用 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]

Say you have a neighbor function:假设你有一个邻居函数:

def nbf(arr):
    return sum(arr)

To apply the neighbor function to every 5 you need to be careful about your start and end indices (in the range(...) command) and the relative slice you take from A.要将邻居函数应用于每 5 个,您需要注意开始和结束索引(在 range(...) 命令中)以及从 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]

numpy.roll can shift the array such that the entire slice is at the beginning of the array. numpy.roll可以移动数组,使整个切片位于数组的开头。 Then take your slice at the beginning and numpy.roll again to revert the array back to its original position.然后在开始处取切片并再次 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 doesn't perform well for me on large arrays however.然而, numpy.roll 在大型阵列上对我来说表现不佳。

If you don't have the convenience of using np.take with mode='wrap' (eg when using numba ), the following works just the same:如果您没有使用带有mode='wrap' np.take的便利(例如,当使用numba 时),下面的工作原理是一样的:

A = numpy.array([0,10,20,30,40,50,60,70,80,90])
indices = range(i-2, i+3)
neighbourhood = A.take(indices % len(A))

or要么

A = numpy.array([0,10,20,30,40,50,60,70,80,90])
indices = range(i-2, i+3)
neighbourhood = A[indices % len(A)]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM