Python 在N-dim稀疏Numpy数组中,向与行/列索引相邻的所有单元格添加数字的快速方法?

Python 在N-dim稀疏Numpy数组中,向与行/列索引相邻的所有单元格添加数字的快速方法?,python,arrays,numpy,indexing,slice,Python,Arrays,Numpy,Indexing,Slice,是否有一种有效的方法可以将相同的数字(int/float)添加到Numpy数组中与任何给定[i,j]或[i,j,k]索引相邻的所有单元格中,而不会产生低效的循环?只是似乎找不到正确的slice命令 import numpy as np arr = np.zeros((5,5)) arr[0,0] = 2 arr[2,2] = 1 arr 返回 Out [1]: array([[ 2., 0., 0., 0., 0.], [ 0., 0., 0.,

是否有一种有效的方法可以将相同的数字(int/float)添加到Numpy数组中与任何给定[i,j]或[i,j,k]索引相邻的所有单元格中,而不会产生低效的循环?只是似乎找不到正确的slice命令

import numpy as np
arr = np.zeros((5,5))
arr[0,0] = 2
arr[2,2] = 1
arr
返回

Out [1]: array([[ 2.,  0.,  0.,  0.,  0.],
                [ 0.,  0.,  0.,  0.,  0.],
                [ 0.,  0.,  1.,  0.,  0.],
                [ 0.,  0.,  0.,  0.,  0.],
                [ 0.,  0.,  0.,  0.,  0.]])
希望实现下面所示的效果(手动编码以显示所需的输出),但不直接知道单元格内容或索引号,除非它们不是零,也不必循环数组中的每个元素

# Add 2 around [0,0]
arr[0:2, 0:2] += 2
arr[0,0] -= 2

# Add 1 around [2,2]
arr[1:4,1:4] += 1
arr[2,2] -= 1
arr

Out [2]: array([[ 2.,  2.,  0.,  0.,  0.],
                [ 2.,  3.,  1.,  1.,  0.],
                [ 0.,  1.,  1.,  1.,  0.],
                [ 0.,  1.,  1.,  1.,  0.],
                [ 0.,  0.,  0.,  0.,  0.]])
使用0填充阵列应该更容易:

In [87]: arr1 = np.zeros((arr.shape[0]+2, arr.shape[1]+2),arr.dtype)
In [88]: arr1[1:-1,1:-1]=arr
适应

现在来试试你的想法,首先是迭代

查找非零:

In [124]: idx = np.argwhere(arr1)
In [125]: idx
Out[125]: 
array([[1, 1],
       [3, 3]], dtype=int32)
对它们进行迭代:

In [126]: N1 = np.zeros_like(arr1)
In [127]: for i in idx:
     ...:     slc = (slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))
     ...:     N1[slc] += arr1[tuple(i)]
     ...: 
In [128]: N1[1:-1,1:-1]
Out[128]: 
array([[ 2.,  2.,  0.,  0.,  0.],
       [ 2.,  3.,  1.,  1.,  0.],
       [ 0.,  1.,  1.,  1.,  0.],
       [ 0.,  1.,  1.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
相同的迭代,但使用
np.add.at

In [132]: N1 = np.zeros_like(arr1)
In [133]: for i in idx:
     ...:     slc = (slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))
     ...:     np.add.at(N1, slc, arr1[tuple(i)])
下一步是将那些
slc
合并成一个整体,这样
np.add.at
就可以完成一次。实现这一点的一个步骤是使用
np.ix
展开切片:

In [143]: for i in idx:
     ...:     slc = (slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))
     ...:     ii = np.ix_(np.r_[slc[0]],np.r_[slc[1]])
     ...:     np.add.at(N1,ii, arr1[tuple(i)])
     ...:     print(ii)
     ...:     
     ...: 
(array([[0],
       [1],
       [2]]), array([[0, 1, 2]]))
(array([[2],
       [3],
       [4]]), array([[2, 3, 4]]))
(待续)

我在想我可以把这些连接起来,使之成为
[0,1,2,2,3,4]
等等,但那是行不通的。我需要生成
mgrid
数组,并连接这些数组

In [159]: slc=[np.mgrid[(slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))] for i in idx]
In [160]: slc = tuple(np.hstack(slc))
In [161]: N2 = np.zeros_like(arr1)
In [162]: np.add.at(N2, slc,1)
In [163]: N2[1:-1,1:-1]
Out[163]: 
array([[ 1.,  1.,  0.,  0.,  0.],
       [ 1.,  2.,  1.,  1.,  0.],
       [ 0.,  1.,  1.,  1.,  0.],
       [ 0.,  1.,  1.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.]])
这很接近。我在每个点上加1,而不是相应的
arr
值。我需要将这些点广播到正确的形状

这仍然在非零点上有一个迭代,但我可以想象用广播加法生成idx数组

我需要补充以下几点:

In [182]: slc1 = np.mgrid[slice(-1,2), slice(-1,2)]
In [183]: slc1
Out[183]: 
array([[[-1, -1, -1],
        [ 0,  0,  0],
        [ 1,  1,  1]],

       [[-1,  0,  1],
        [-1,  0,  1],
        [-1,  0,  1]]])
In [177]: idx
Out[177]: 
array([[1, 1],
       [3, 3]], dtype=int32)
以这样的方式产生:

In [179]: slc=[np.mgrid[(slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))] for i in idx]
In [180]: slc=np.hstack(slc)  # np.concatenate(slc, axis=1)
In [181]: slc
Out[181]: 
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2],
        [2, 2, 2],
        [3, 3, 3],
        [4, 4, 4]],

       [[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2],
        [2, 3, 4],
        [2, 3, 4],
        [2, 3, 4]]])
这样就可以了(但我可以直接做吗?)

将非零值复制到匹配形状:

In [288]: pts = arr1[tuple(idx.T)]
In [289]: pts
Out[289]: array([ 2.,  1.])
In [290]: pts1 = pts.repeat(3)[:,None].repeat(3,axis=1)
In [291]: pts1
Out[291]: 
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
In [292]: np.add.at(N2,tuple(Out[229]), pts1)
pts1=pts.repeat(3)[:,None]
没有最后一次重复也可以工作(因为广播)


因此,这些都可以打包成几个函数,并在一些实际大小的数组上计时

在这段代码中,康威一生中完成的多重偏移量求和可能与您得到的速度一样快:。我的直觉是,除非阵列非常稀疏,否则您的备选方案会变慢。任何一种处理边界的方法都会使事情变得复杂。为了一次添加所有非零点,您必须使用
add.at
,即
+=
的无缓冲版本。否则,
arr[1,1]
将无法获得
2+1
<代码>添加。at比缓冲代码慢,并且不使用切片。对于您的实际用例,稀疏ty的度量是什么?非零百分比?稀疏度(使用3D阵列)通常大于90%零。感谢@hpaulj的链接感谢真正深入的解决方案。尽管仍在努力,但就我所看到的几个例子而言,即使是索引解决方案也非常有效
In [179]: slc=[np.mgrid[(slice(i[0]-1,i[0]+2), slice(i[1]-1,i[1]+2))] for i in idx]
In [180]: slc=np.hstack(slc)  # np.concatenate(slc, axis=1)
In [181]: slc
Out[181]: 
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2],
        [2, 2, 2],
        [3, 3, 3],
        [4, 4, 4]],

       [[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2],
        [2, 3, 4],
        [2, 3, 4],
        [2, 3, 4]]])
In [228]: slc2 = idx.T[:,:,None,None]+slc1[:,None,:,:]
In [229]: np.concatenate(list(slc2.transpose(1,0,2,3)),1)
Out[229]: 
array([[[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2],
        [2, 2, 2],
        [3, 3, 3],
        [4, 4, 4]],

       [[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2],
        [2, 3, 4],
        [2, 3, 4],
        [2, 3, 4]]])
In [288]: pts = arr1[tuple(idx.T)]
In [289]: pts
Out[289]: array([ 2.,  1.])
In [290]: pts1 = pts.repeat(3)[:,None].repeat(3,axis=1)
In [291]: pts1
Out[291]: 
array([[ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 2.,  2.,  2.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
In [292]: np.add.at(N2,tuple(Out[229]), pts1)