简体   繁体   English

修改numpy 2d数组中的元素和邻居

[英]Modify element and neighbor in numpy 2d array

I am trying find a more efficient way to modify every element of an array, and its lowest neighbor, by some amount. 我正在尝试找到一种更有效的方法来一定程度地修改数组的每个元素及其最低的邻居。 In the example code below, I modify them by their difference, but the change() function could be anything. 在下面的示例代码中,我通过它们的区别来修改它们,但是change()函数可以是任何东西。

After searching, scipy.ndimage.generic_filter() seemed to be the ideal method to use, since it allows for easy comparison between elements and their neighbors. 搜索后, scipy.ndimage.generic_filter()似乎是理想的使用方法,因为它使元素及其相邻元素之间的比较变得容易。 After getting the offset from ndimage.generic_filter() , I am feeding that to numpy.roll() to modify each element's chosen neighbor. ndimage.generic_filter()获得偏移量后,我将其提供给numpy.roll()来修改每个元素的选定邻居。

The problem is that, with very large arrays and multiple iterations, the inefficiency of looping through np.roll() ndimage.generic_filter() eats into performance. 问题在于,对于非常大的数组和多次迭代, 通过np.roll() ndimage.generic_filter() 循环 的效率低下会影响性能。 With a 10000x10000 array, my execution time of the code below is 5m42s. 对于10000x10000数组,我下面代码的执行时间为5m42s。 Is there a more efficient way of doing this using scipy or numpy? 是否有使用scipy或numpy进行此操作的更有效方法?

import numpy as np
from scipy import ndimage

dic = {0: (-1, -1), 1: (-1, 0), 2: (-1, 1),
       3: (0, -1),  4: (0, 0),  5: (0, 1),
       6: (1, -1),  7: (1, 0),  8: (1, 1)}

def change(a):
    return (a[4] - min(a)) / 2

def movement(a):
    return np.argmin(a)

P = np.random.uniform(10, size=(5,5))

# determine element change and direction
chng = np.zeros((5, 5)) 
ndimage.generic_filter(P, change, size=3, output=chng)
move = np.random.randint(10, size=(5, 5))
ndimage.generic_filter(P, movement, size=3, output=move)
P -= chng

# determine neighbor change
chng2 = np.zeros((5, 5))
for j in range(9):
    if j == 4:
        continue
    p = np.where(move == j, chng, 0)
    p = np.roll(p, dic[j], axis=(0, 1))
    chng2 += p
P += chng2

EDIT: Below is a more efficient solution. 编辑:下面是一个更有效的解决方案。 Many thanks @Paul Panzer. 非常感谢@Paul Panzer。

import numpy as np

P = np.random.uniform(10, size=(1000, 1000))

# determine element change and direction
PP = np.bmat([[P[:, -1:], P, P[:, :1]]])
PP = np.bmat([[PP[-1:]], [PP], [PP[:1]]])
PPP = np.lib.stride_tricks.as_strided(PP, (1000, 1000, 3, 3), 2 * PP.strides)
am1 = np.argmin(PPP, axis=3)
i, j, k = np.ogrid[(*map(slice, PPP.shape[:-1]),)]
am0 = np.argmin(PPP[i, j, k, am1], axis=2)
i, j = np.ogrid[(*map(slice, PPP.shape[:-2]),)]
am1 = am1[i, j, am0]
mn = PPP[i, j, am0, am1]
change = (P - mn) / 2
P -= change

# determine neighbor change
am1 -= 1
am0 -= 1
i, j = np.ogrid[(*map(slice, P.shape),)]
np.add.at(P, ((am0 + i) % P.shape[0], (am1 + j) % P.shape[1]), change)

This might be what you are looking for https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.convolve2d.html 这可能就是您正在寻找的https://docs.scipy.org/doc/scipy/reference/genic/scipy.signal.convolve2d.html

in2 (Cf documentation) would be a matrix corresponding to what you wrote as a dict in2(Cf文档)将是一个与您作为字典写的内容相对应的矩阵

dic = {0: (-1, -1), 1: (-1, 0), 2: (-1, 1),
   3: (0, -1),  4: (0, 0),  5: (0, 1),
   6: (1, -1),  7: (1, 0),  8: (1, 1)}

Hope it helps 希望能帮助到你

You could use np.add.at . 您可以使用np.add.at The following snippet picks up after your P -= chng : 在您的P -= chng之后出现以下代码片段:

>>> P_pp = P.copy()
>>> dic_i, dic_j = np.indices((3, 3)).reshape(2, 9) - 1
>>> i, j = np.ogrid[(*map(slice, P_pp.shape),)]
>>> np.add.at(P_pp, ((dic_i[move] + i) % P_pp.shape[0], (dic_j[move] + j) % P_pp.shape[1]), chng)

Since we worked on a copy of P we can now execute the rest of your code and then: 由于我们处理了P的副本,因此我们现在可以执行其余代码,然后:

# Tada!
>>> np.allclose(P_pp, P)
True

Update : Here is a method to calculate local argmin without using ndimage . 更新 :这是一种无需使用ndimage即可计算局部ndimage One potential advantage is that we get the corresponding minima cheaply once we have the argminima. 一个潜在的优势是,一旦我们有了argminima,便可以廉价地获得相应的最小值。 Note that the argmin is already 2D first component is in am0 , second in am1 . 请注意, am0已经是2D的第一个组件在am0 ,第二个在am1 Each ranges between 0 and 2 , so the center is at 1,1 , The minima are in mn 每个范围在02之间,因此中心在1,1 ,最小值为mn

>>> P = np.random.uniform(10, size=(5,5))
>>> PP = np.bmat([[P[:,-1:], P, P[:, :1]]])
>>> PP = np.bmat([[PP[-1:]], [PP], [PP[:1]]])
>>> PPP = np.lib.stride_tricks.as_strided(PP, (5, 5, 3, 3), 2 * PP.strides)
>>> am1 = np.argmin(PPP, axis=3)
>>> i, j, k = np.ogrid[(*map(slice, PPP.shape[:-1]),)]
>>> am0 = np.argmin(PPP[i, j, k, am1], axis=2)
>>> i, j = np.ogrid[(*map(slice, PPP.shape[:-2]),)]
>>> am1 = am1[i, j, am0]
>>> mn = PPP[i, j, am0, am1]

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

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