【问题标题】:A List of 3D numpy array iteration through all elements with nditer() from numpy 1.15.3使用 nditer() 从 numpy 1.15.3 遍历所有元素的 3D numpy 数组迭代列表
【发布时间】:2019-04-04 15:13:54
【问题描述】:

我有一个 3D Numpy 数组列表,我想迭代这个结构的每个元素并使用 if statemets 进行一些更改。下面的代码执行我想做的事情:

for counter1, entry in enumerate(all_frames_flow):
    for counter2, entry2 in enumerate(entry):
        for counter3, entry3 in enumerate(entry2):
            for counter4, entry4 in enumerate(entry3):
                if entry4 < -20.0:
                    all_frames_flow[counter1][counter2][counter3][counter4]=-20.0
                if entry4 > 20.0:
                    all_frames_flow[counter1][counter2][counter3][counter4]=20.0
                all_frames_flow[counter1][counter2][counter3][counter4]/=20

但我想知道是否有更 Pythonic 的方式。在numpy &gt;=1.15.0,我从文档中尝试了这个新代码,但它失败了,它没有返回我想要的结果,我可以看到大于abs(20) 的值,我想知道为什么会这样:

for counteref, _ in enumerate(backup2):                    
    with np.nditer(backup2[counteref], op_flags=['readwrite'], order = 'K') as it:
            for x in it:
                #print x
                if (x < -20.0):
                    x=-20.0
                if (x > 20.0):
                    x = 20.0
                x/=20.0

【问题讨论】:

  • nditer 使用起来很棘手,而且通常不会更快或(这个难以定义的)pythonic。

标签: python-2.7 numpy for-loop iteration


【解决方案1】:

在尝试更好/替代迭代器之前,您应该尝试在没有迭代的情况下完成任务(即使用已编译的 numpy 代码执行操作)

In [347]: arr = np.random.randint(-40,40,(2,3,4))

例如,有一个clip 方法:

In [348]: arr.clip(-20, 20)
Out[348]: 
array([[[-20, -20,  20,  -6],
        [-15, -17,  -8, -20],
        [  2, -20, -16,  20]],

       [[-20,   3, -20,  17],
        [ 20,  20,  20, -17],
        [ 11, -20,  20,   0]]])

numpy 中将所有内容除以 20 是微不足道的:

In [349]: _/20
Out[349]: 
array([[[-1.  , -1.  ,  1.  , -0.3 ],
        [-0.75, -0.85, -0.4 , -1.  ],
        [ 0.1 , -1.  , -0.8 ,  1.  ]],

       [[-1.  ,  0.15, -1.  ,  0.85],
        [ 1.  ,  1.  ,  1.  , -0.85],
        [ 0.55, -1.  ,  1.  ,  0.  ]]])

更好的是,学习使用布尔掩码来做这种事情:

In [351]: arr
Out[351]: 
array([[[-32, -30,  39,  -6],
        [-15, -17,  -8, -34],
        [  2, -31, -16,  35]],

       [[-39,   3, -37,  17],
        [ 31,  30,  28, -17],
        [ 11, -24,  26,   0]]])

In [354]: mask1 = arr<-20
In [355]: mask2 = arr>20
In [356]: mask1
Out[356]: 
array([[[ True,  True, False, False],
        [False, False, False,  True],
        [False,  True, False, False]],

       [[ True, False,  True, False],
        [False, False, False, False],
        [False,  True, False, False]]])
In [357]: mask2
Out[357]: 
array([[[False, False,  True, False],
        [False, False, False, False],
        [False, False, False,  True]],

       [[False, False, False, False],
        [ True,  True,  True, False],
        [False, False,  True, False]]])
In [358]: arr[mask1]=-20
In [359]: arr[mask2]=20
In [360]: arr
Out[360]: 
array([[[-20, -20,  20,  -6],
        [-15, -17,  -8, -20],
        [  2, -20, -16,  20]],

       [[-20,   3, -20,  17],
        [ 20,  20,  20, -17],
        [ 11, -20,  20,   0]]])

至于你的迭代,重要的是要记住,在任何 Python 迭代中你都不能使用

对于 x in ...: x=-20.0

修改源。 x=... 赋值为 x 变量分配了一个新值,并中断了它与迭代的链接。如果您不明白原因,请尝试使用简单的列表。您必须就地改变变量。如果x 是不可能的简单整数。

在您的第一次迭代中,您正在索引和变异 all_frames_flow,所以它可以工作:

all_frames_flow[counter1][counter2, counter3, counter4]=-20.0

nditer 确实提供了一个可变的迭代变量,所以你可以这样做:

In [364]: with np.nditer(arr, op_flags=['readwrite'], order = 'K') as it:
     ...:    for x in it:
     ...:        #print x
     ...:        if (x < -20.0):
     ...:            x[...]=-20.0      # change x in-place
     ...:        if (x > 20.0):
     ...:            x[...] = 20.0
     ...:            

所有更改值的nditer 示例都应使用此[...]= 表示法。

我不鼓励使用nditer,至少不鼓励使用 Python 代码。在 Python 代码中,它是最有用的一种测试想法的方法,这些想法将在您自己的编译代码中实现(使用cython)。它不提供任何速度优势。

【讨论】:

  • 但是哪个执行速度更快?
  • 有定时执行的工具。
  • 最后一行应该是 x[...]/=20.0 吧?或 x/=20 ?
  • 如果把所有x /= 20分开是对的。 x[...] /= 有效,但不是必需的。
【解决方案2】:

我更喜欢 numpy.ndindex 来节省一些嵌套循环并保持函数结构合理平坦。我觉得 numpy.nditer 对于有更多数组循环的情况更有用:

mat = np.random.rand(2, 3, 4) * 100 - 50
for i in np.ndindex(*mat.shape):
    if mat[i] < -20:
        mat[i] = -20
    if mat[i] > 20:
        mat[i] = 20  
mat /= 20.0

另一种方法是使用 numpy.where 进行条件和操作,这些条件和操作不依赖于特定索引但适用于整个数组:

mat = np.random.rand(2, 3, 4) * 100 - 50
#              Condition, if True, if False
mat = np.where(mat < -20,     -20,     mat)
mat = np.where(mat > +20,      20,     mat)
mat /= 20.0

对于您将数组的值裁剪到定义范围之外的特定情况 numpy.clip(arr, a_min, a_max) 可能是最简单和最快的方法。

【讨论】:

  • ndindex 是少数使用np.nditer 的解释numpy 代码之一。
猜你喜欢
  • 1970-01-01
  • 2013-05-14
  • 1970-01-01
  • 2018-07-21
  • 2016-04-27
  • 2013-08-11
  • 2015-01-30
  • 2016-02-25
  • 2014-07-09
相关资源
最近更新 更多