我有一个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]
。我似乎无法找到一种优雅的方法来做到这一点,所以每次出现时我都不得不使用一些复杂,烦人的逻辑(这对我来说很常见)。在2D情况下,点的邻域将是矩形阵列。
所以我的问题是,是否有一种简洁的方法来表达这种“环绕邻居”操作在numpy?我更喜欢返回切片而不是副本的东西,但可读性和速度是最重要的考虑因素。
答案 0 :(得分:27)
numpy.take
模式下的 'wrap'
将使用您的索引模数组的长度。
indices = range(i-2,i+3)
neighbourhood = A.take(indices, mode='wrap')
有关详细信息,请参阅文档numpy.take
答案 1 :(得分:5)
您可以将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维......
答案 2 :(得分: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]
答案 3 :(得分:3)
注意:对于您的邻居不需要换行的情况,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
我确信这些功能可以进一步改进。我愿意接受建议。
答案 4 :(得分:1)
您可以像这样使用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]
答案 5 :(得分:1)
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对我来说在大型阵列上表现不佳。
答案 6 :(得分:0)
如果您不方便将np.take
与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)]