我有一个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
但是我想知道是否还有更Python化的方式。在numpy >=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
答案 0 :(得分:0)
我更喜欢 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, True, False
mat = np.where(mat < -20, -20, mat)
mat = np.where(mat > +20, 20, mat)
mat /= 20.0
答案 1 :(得分:0)
在尝试使用更好的/替代的迭代器之前,您应该尝试不执行迭代任务(即使用已编译的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
中实现的想法)中最有用的方法。它没有任何速度优势。