36

我有一个 numpy 数组,我想获得第 i 个点的“邻居”。通常我使用的数组是二维的,但下面的一维示例说明了我在寻找什么。如果

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

那么元素 4 的(大小 5)邻域是[20,30,40,50,60],这可以很容易地通过做 来获得A[i-2:i+3]

但是,我还需要邻域“环绕”数组的边缘,以便元素 0[80,90,0,10,20]的邻域为 ,元素 9 的邻域为[70,80,90,0,10]。我似乎找不到一种优雅的方法来做到这一点,所以每次出现这种情况时,我最终都不得不使用一些复杂、烦人的逻辑(这对我来说很常见)。在二维情况下,一个点的邻域将是一个矩形阵列。

所以我的问题是,有没有一种巧妙的方法可以在 numpy 中表达这种“环绕邻域”操作?我更喜欢返回切片而不是副本的东西,但可读性和速度是最重要的考虑因素。

4

7 回答 7

43

numpy.takein 'wrap'mode 将使用您的索引以数组的长度为模。

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

有关详细信息,请参阅文档numpy.take

于 2013-07-19T06:45:26.477 回答
6

您可以使用for nd 数组 的参数axis=0numpy.take

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 尺寸...

于 2013-07-21T07:52:34.087 回答
5

注意:对于您的邻居不需要包装的情况,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

我相信这些功能可以进一步改进。我愿意接受建议。

于 2016-03-25T14:49:13.957 回答
4

我知道这个问题很老,但应该提到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]
于 2014-10-23T19:00:33.773 回答
3

您可以像这样使用 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 个,您需要注意您的开始和结束索引(在 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]
于 2014-10-25T19:55:34.613 回答
3

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 在大型阵列上对我来说表现不佳。

于 2015-06-24T20:57:14.180 回答
0

如果您没有使用np.takewith的便利mode='wrap'(例如使用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))

或者

A = numpy.array([0,10,20,30,40,50,60,70,80,90])
indices = range(i-2, i+3)
neighbourhood = A[indices % len(A)]
于 2020-10-08T22:48:01.347 回答