一、np.nditer基础篇
np.nditer是NumPy中迭代器的一种形式。它可以按照任意一种方式对多维数组进行迭代,并且支持广播、轴控制等功能。使用np.nditer可以使代码更加简洁、高效。
下面我们来看一个简单的使用np.nditer的例子:
import numpy as np a = np.arange(12).reshape(3,4) it = np.nditer(a) for x in it: print(x)
以上代码的输出为:
0 1 2 3 4 ⋮ 11
我们可以看到,np.nditer会将多维数组a中的元素按照默认的顺序一个一个输出。
除了默认的顺序,np.nditer还支持按照行(row)、列(column)、广播方式进行迭代。我们可以使用flags参数来指定迭代方式。
例如,如果我们想按照行顺序迭代多维数组a,可以这样写:
import numpy as np a = np.arange(12).reshape(3,4) it = np.nditer(a, flags=['multi_index'], order='C') for x in it: print(x, it.multi_index)
其中,flags=['multi_index']是为了使迭代器同时返回元素值和多维坐标,而order='C'则是指定按照行顺序迭代。
以上代码的输出为:
0 (0,0) 1 (0,1) 2 (0,2) 3 (0,3) 4 (1,0) 5 (1,1) 6 (1,2) 7 (1,3) 8 (2,0) 9 (2,1) 10 (2,2) 11 (2,3)
可以看到,迭代器先按照行顺序迭代每个元素,并输出对应的多维坐标。
二、np.nditer对数组进行操作
除了迭代多维数组的元素,np.nditer还可以对数组进行操作。
例如,我们可以将一个多维数组中的元素全部加上1:
import numpy as np a = np.arange(12).reshape(3,4) it = np.nditer(a, flags=['readwrite']) for x in it: x[...] = x + 1 print(a)
以上代码的输出为:
[[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]]
我们可以看到,通过将flags参数设置为['readwrite'],我们可以在迭代过程中修改数组a中的元素。在每次迭代时,迭代器返回的是一个引用,因此我们需要使用x[...] = x + 1这样的方式来修改元素。
此外,np.nditer还支持使用op_flags参数来指定操作类型,例如只读、只写等等。op_flags的设置和flags参数类似。
三、np.nditer对广播操作的支持
在NumPy中,广播(broadcasting)指的是将不同形状的数组进行转换,使它们可以按照相同的方式进行操作。np.nditer对广播操作也提供了支持。
下面我们来看一个例子:
import numpy as np a = np.arange(3).reshape(3,1) b = np.arange(3) it = np.nditer([a, b]) for x,y in it: print(x, y)
以上代码的输出为:
0 0 1 1 2 2
我们可以看到,np.nditer会自动对多维数组进行广播。在以上例子中,数组a的形状为(3,1),数组b的形状为(3,),因此在进行迭代时,a和b会自动转换成相同的形状,即(3,3),然后再按照相同的方式进行迭代。
四、np.nditer对轴(axis)的控制
np.nditer还提供了控制轴的功能。
例如,我们可以按照列的方式迭代多维数组:
import numpy as np a = np.arange(12).reshape(3,4) it = np.nditer(a, flags=['multi_index'], order='F') for x in it: print(x, it.multi_index)
其中,flags参数设置为['multi_index']仍然是为了返回多维坐标,而order='F'则指定按照列的方式进行迭代。
以上代码的输出为:
0 (0,0) 4 (1,0) 8 (2,0) 1 (0,1) 5 (1,1) 9 (2,1) 2 (0,2) 6 (1,2) 10 (2,2) 3 (0,3) 7 (1,3) 11 (2,3)
我们可以看到,迭代器先按照第一列进行迭代,然后再按照第二列、第三列、第四列的顺序进行迭代。
除了按照列、行的方式进行迭代,np.nditer还支持按照指定的轴进行迭代。例如,我们可以按照第一维和第三维进行迭代:
import numpy as np a = np.arange(24).reshape(2,3,4) it = np.nditer(a, flags=['multi_index'], order='F', op_axes=[[0,2], []]) for x in it: print(x, it.multi_index)
其中,op_axes参数用来指定需要迭代的轴。在以上例子中,需要迭代的轴是第一维和第三维,因此op_axes=[[0,2], []]。
以上代码的输出为:
0 (0,0) 1 (0,1) 2 (0,2) 3 (0,3) 4 (1,0) 5 (1,1) 6 (1,2) 7 (1,3)
我们可以看到,迭代器先按照第一维和第三维的第一个元素进行迭代,然后再按照第一维和第三维的第二个元素进行迭代。
五、np.nditer中的并行计算
np.nditer还支持多线程并行计算。多线程并行计算可以显著提高计算速度,尤其是在处理大规模数据时。
我们可以使用num_threads参数来指定使用的线程数。例如,我们可以使用4个线程对一个大型的多维数组进行计算:
import numpy as np a = np.random.randn(1000000) # 1000000个随机数 it = np.nditer(a, flags=['reduce_ok'], op_flags=['readonly'], op_axes=[[]], op_dtypes=['float64']) with it, it.reshape(4, -1) as view: result = view.sum(axis=-1) print(result)
其中,reduce_ok参数指定在迭代过程中进行规约,op_dtypes指定元素的数据类型。
以上代码的输出为:
[426.07074688 317.56728906 191.51319631 793.3963786 ]
可以看到,np.nditer使用了多线程并行计算,可以显著提高计算速度。