1.1.循环分类:
1)普通循环 for ...in
2)np.flatiter/a.flat数组迭代;flat属性是数组中所有元素的迭代器
3)np.nditer数组迭代
1.2.class:
class np.nditer(op, flags=None, op_flags=None, op_dtypes=None, order='K',
casting='safe', op_axes=None, itershape=None, buffersize=0)
用途:高效多维迭代器对象,可对数组迭代,使用Python的标准迭代器接口访问数组元素
参数:
op: 迭代数组array_like
order: {'C','F','A','K'},order ='K'默认内存排序
op_flags: 操作数的标志str列表-默认只读至少指定任1个'readonly', 'readwrite'或'writeonly' 注1
flags: str序列类型用来控制迭代器的行为 ['external_loop','buffered'] 注2
op_dtypes: dtype or tuple of dtype(s) ['complex128']
casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, 控制在制作副本或缓冲时可能会发生什么样的数据转换
op_axes : [int] or None 操作数的轴列表是从迭代器的维度到操作数的维度的映射。
可以为条目放置-1的值,从而使该维度被视为`newaxis`
itershape : tuple of ints, 可选 迭代器所需形状
buffersize : int, 可选 启用缓冲时控制临时缓冲区的大小。 默认值0
说明:迭代器作用于多维数组;迭代匹配的是数组所在内存顺序,不考虑其他特定顺序
1.3.可迭代对象中建立数组
函数:np.fromiter(iterabe, dtype, count=-1) #从可迭代对象中建立,返回1D数组。
实例:
iterator=iter(range(5))
iterator = (x*x for x in range(5))
np.fromiter(iterator,dtype= float) #array([ 0., 1., 4., 9., 16.])
注1:参数op_flags
[ 'readonly'] 指示操作数将只读
[ 'readwrite'] 读写
[ 'writeonly' ] 只写
[ 'no_broadcast' ] 可防止用户广播该操作数
[ 'contig'] 强制操作数数据连续
[ 'aligned' ] 强制将操作数数据对齐
[ 'nbo' ] 强制操作数数据按本机字节的顺序排列
[ 'copy'] 允许根据需要使用临时的只读副本
[ 'updateifcopy'] 允许临时读写副本
[ 'allocate' ] 如果该数组在“操作”参数中为None,则会导致分配该数组
[ 'no_subtype' ] 阻止“allocate”操作数使用子类型
[ 'arraymask'] 表示此操作数是在写入操作数时用于选择元素的掩码 'writemasked' flag set.
迭代器不强制执行此操作,但当从缓冲区写入回数组时,它只复制此掩码所指示的那些元素
[ 'writemasked'] 指示只将所选“arraymask”:操作数为True的元素写入
[ 'overlap_assume_elementwise']
可用于标记仅按照迭代器顺序访问的操作数,以便在存在‘copy_if_overlap’时允许不那么保守的复制
注2:参数flags
['external_loop'] 外部循环 (效率高)给出值是具有多值的一维数组而非零维数组
['external_loop','buffered']
['buffered'] 在需要时启用缓冲
['c_index'] 跟踪 C 顺序索引
['f_index'] 跟踪 Fortran 顺序索引
['multi_index'] 每次迭代可跟踪一种索引类型(元组索引)
['common_dtype'] 使所有操作数转换为公共数据类型,必要时进行复制或缓冲
['copy_if_overlap'] 使迭代器确定读操作数是否与写操作数重叠,根据需要临时复制以避免重叠
['delay_bufalloc'] 延迟缓冲区分配直到reset()调用,允许在操作数值被复制到缓冲区之前初始化它们
['grow_inner'] 当用'buffered'和'external_loop'时允许使数组大小大于缓冲区大小
['ranged'] 允许迭代器被限制为迭代索引值的子范围
['refs_ok'] 启用引用类型迭代,如对象数组
['reduce_ok'] 启用'readwrite'被广播的操作数的迭代,也称为还原操作数
['zerosize_ok'] 允许'itersize'为零
跟踪索引或多索引与使用外部循环不兼容
注3:参数casting
控制在制作副本或缓冲时可能会发生什么样的数据转换
不建议将其设置为“不安全”,因为它会对累积产生不利影响
*‘no’: 表示数据类型根本不应该被转换
*‘equiv‘: 意味着只允许字节顺序更改
*‘safe’: 意味着只允许能够保存值的转换
*‘same_kind: 意思是只允许在一种类型中进行安全的铸造或铸造,比如float64到float32
*‘unsafe’: 意味着任何数据转换都可以完成
2.实例:
实例1:for ...in遍历数组
a = np.arange(6).reshape(2, 3)
print(f'a={a}') # a=[[0 1 2] [3 4 5]]
for row in a:
print(row,end=' ; ') # [0 1 2] ; [3 4 5] ;
for row in a: # 0,1,2,3,4,5,
for cell in row:print(cell,end=',')
实例2:np.flatiter/a.flat
a = np.arange(6).reshape(2, 3)
it = a.flat
type(it) # <class 'numpy.flatiter'>
for v in it:
print(v,end=',') # 0,1,2,3,4,5,
it.copy() # array([0, 1, 2, 3, 4, 5])
it.base is a # True
it = a.flat
it.coords # (0, 0)
next(it) # 0
it.coords # (0, 1)
it = a.flat
it.index # 0
next(it) # 0
it.index # 1
实例3:
实例3.1:
a = np.array([[1,2,3],[4,5,6]])
for x in np.nditer(a):print(x,end=' ') #0 1 2 3 4 5
# 迭代次序:默认保持本机内存顺序,不受特定数组排序(C/F风格顺序)影响
for x in np.nditer(a.T, order='C'):print(x,end=' ') # C顺序1 4 2 5 3 6
for x in np.nditer(a.T.copy(order='C')): print(x,end=' ') # C顺序1 4 2 5 3 6
# 修改数组元素:迭代数组默认不能修改
for x in np.nditer(a, op_flags=['readwrite']):x[...] = 2 * x #数组a=array([[ 2, 4, 6], [ 8, 10, 12]])
#外部循环 (效率高)
a = np.array([[1,2,3],[4,5,6]])
for x in np.nditer(a, flags=['external_loop']):print(x) #[1 2 3 4 5 6]
for x in np.nditer(a, flags=['external_loop'], order='F'):print(x) #[1 4] [2 5] [3 6]
#跟踪索引或多重索引(索引到迭代器来访问当前值)
it = np.nditer(a, flags=['f_index'])
while not it.finished:
print( "%d:%d" % (it.index,it[0]),end=" ") #0:1 2:2 4:3 1:4 3:5 5:6
it.iternext()
it = np.nditer(a, flags=['multi_index'])
while not it.finished:
print( "%d <%s>" % (it[0], it.multi_index))
it.iternext()
'''
1 <(0, 0)>
2 <(0, 1)>
3 <(0, 2)>
4 <(1, 0)>
5 <(1, 1)>
6 <(1, 2)>
'''
it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
while not it.finished:
it[0] = it.multi_index[1] - it.multi_index[0]
it.iternext()
a #array([[ 0, 1, 2],[-1, 0, 1]])
实例3.2: 特定数据类型迭代
将数组视为与存储数据类型不同的数据类型;方法:临时拷贝(消耗大量内存)缓冲模式
如数据类型不准确没启用复制或缓冲模式,迭代器将引发异常
缓冲数组元素(使迭代器提供给内部循环的块变大)
a = np.array([[1,2,3],[4,5,6]])
# 在复制模式下'copy'被指定为每个操作数标志。这是为以操作数方式提供控制。缓冲模式被指定为迭代器标志
# for x in np.nditer(a,op_dtypes=['complex128']):print( np.sqrt(x)) #TypeError: Iterator 要求拷贝或缓冲区
for x in np.nditer(a, op_flags=['readonly','copy'],op_dtypes=['complex128']):print(x,end=" ")#(1+0j) (2+0j) ...
# 将int数组视为32位浮点数组
for x in np.nditer(a, flags=['buffered'], op_dtypes=['float32'],casting='same_kind'):print(x,end=" ")#1.0 2.0 3.0 4.0 5.0 6.0
for x in np.nditer(a, flags=['buffered'], op_flags=['readonly'],op_dtypes=['float64'], casting='same_kind'):x= x / 2.0 #只读模式
# for x in np.nditer(a, flags=['buffered'], op_flags=['readwrite'],op_dtypes=['float64'], casting='same_kind'):x= x / 2.0#读写模式引发异常
实例4:广播阵列迭代
#如两个数组是可广播的,可用组合nditer对象对它们进行并发迭代
a = np.arange(12).reshape(3,4)
b = np.array([1, 2, 3, 4], dtype = int)
# 同时迭代2个可广播的数组:数组a维数3X4,b维数1X4,将数组b广播到a的大小
for x,y in np.nditer([a,b]): #数组 b 被广播到 a 的大小
print ("%d,%d" % (x,y), end=" ; " )#0,1;1,2;2,3;3,4;4,1;5,2;6,3;7,4;8,1;9,2;10,3;11,4 ;
实例5.1:迭代器分配的输出数组:
np函数一常见情况根据输入广播分配输出,可选参数'out'保存结果;nditer对象支持这种机制
# 默认nditer使用标志'allocate'和'writeonly'作为None传入的操作数
def square(a, out=None):
it = np.nditer([a, out],op_flags = [['readonly'],['writeonly', 'allocate', 'no_broadcast']])#np.nditer([a, None])
for x, y in it:y[...] = x*x
return it.operands[1]
square([1,2,3]) #array([1, 4, 9])
b=np.array([1,1,1])
square([1,2,3],b)
b #array([1, 4, 9])
实例5.2:迭代器分配的输出数组,禁止广播
def square(a, out=None):
it = np.nditer([a, out],
flags = ['external_loop', 'buffered'],
op_flags = [['readonly'],['writeonly', 'allocate', 'no_broadcast']])# 'no_broadcast'阻止播放输出
for x, y in it:
y[...] = x*x
return it.operands[1]
square([1,2,3]) # array([1, 4, 9])
b = np.zeros((3,))
square([1,2,3], out=b) # array([ 1., 4., 9.])
b # array([ 1., 4., 9.])
# square(np.arange(6).reshape(2,3), out=b)#错误,禁止广播
实例6:op_axes 参数实现类似np.outer的迭代
任何二进制操作都可像outer一样扩展到数组操作,nditer通过显式映射操作数的轴来完成此操作
可用newaxis 索引实现,但我们将使用nditer op_axes 参数来完成此操作而不需要中间视图
一简单外部产品,将操作数1的维度放在操作数2的维度之前
op_axes 参数:list 操作数映射轴
操作数1是一维,操作数2是二维,迭代器将有3维度,op_axes 将有两个3元素列表
列表1选取操作数1的一个轴,其余迭代器轴为-1,最终结果为[0,-1,-1]
列表2选取操作数2的两个轴,但不应该与操作数1中选取轴重叠它是[-1,0,1]
输出操作数以标准方式映射到迭代器轴上,因此可提供None而不是构建另一个列表
内循环中操作是一个直接的乘法。与外部产品有关的所有事情都由迭代器设置来处理
a = np.arange(3)
b = np.arange(8).reshape(2,4)
it = np.nditer([a, b, None], flags=['external_loop'],op_axes=[[0, -1, -1], [-1, 0, 1], None])
for x, y, z in it:
z[...] = x*y
it.operands[2]
array([[[ 0, 0, 0, 0],
[ 0, 0, 0, 0]],
[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 0, 2, 4, 6],
[ 8, 10, 12, 14]]])
实例7:reduce_ok减少迭代
只要可写操作数的元素少于整个迭代空间,该操作数正在进行缩减。
该nditer对象要求任何缩减操作数都被标记为读写,且只有当'reduce_ok'作为迭代器标志被提供时才允许减少
#数组元素总和
a = np.arange(24).reshape(2,3,4)
b = np.array(0)
for x, y in np.nditer([a, b], flags=['reduce_ok', 'external_loop'],op_flags=[['readonly'], ['readwrite']]): y[...] += x
b #array(23)
for x, y in np.nditer([a, b], flags=['reduce_ok'],op_flags=[['readonly'], ['readwrite']]):y[...] += x
b #array(276)
np.sum(a) #276
# 组合约简和分配操作数:在迭代开始之前,任何缩减操作数都必须初始化为其初始值
a = np.arange(24).reshape(2,3,4)
it = np.nditer([a, None], flags=['reduce_ok'],op_flags=[['readonly'], ['readwrite', 'allocate']], op_axes=[None, [0,1,-1]])
it.operands[1][...] = 0
for x, y in it:y[...] += x
it.operands[1] # array([[ 6, 22, 38],[54, 70, 86]])
np.sum(a, axis=2) # array([[ 6, 22, 38], [54, 70, 86]])
要进行缓冲缩减需进行另一次调整。通常迭代器构造包括将数据缓冲区1从可读数组复制到缓冲区
任何约简操作数都是可读的,所以它可能被读入缓冲区
不幸的是,该缓冲操作完成后操作数的初始化将不会反映在迭代开始的缓冲区中,且会产生垃圾结果
delay_bufalloc允许迭代器分配的约简操作数与缓冲一起存在
迭代器会将其缓冲区保持未初始化状态,直到它接收到一个复位为止,之后它将准备好进行常规迭代
a = np.arange(24).reshape(2,3,4)
it = np.nditer([a, None], flags=['reduce_ok','buffered', 'delay_bufalloc'],op_flags=[['readonly'], ['readwrite', 'allocate']],op_axes=[None, [0,1,-1]])
it.operands[1][...] = 0
it.reset()
for x, y in it: y[...] += x
it.operands[1] # array([[ 6, 22, 38],[54, 70, 86]])
实例8:函数
a=np.array([[1,2,3],[4,5,6]])
b=np.array([[2,2,2],[3,3,3]])
c=np.zeros(a.shape)
def add(x, y, out=None):#使用Python迭代器协议:
it = np.nditer([x, y, out], [], [['readonly'], ['readonly'], ['writeonly','allocate']])
with it:
for (a, b, c) in it:
np.add(a, b, out=c)
return it.operands[2]
def add(x, y, out=None):#C-style pattern
it = np.nditer([x, y, out], [],[['readonly'], ['readonly'], ['writeonly','allocate']])
with it:
while not it.finished:
np.add(it[0], it[1], out=it[2])
it.iternext()
return it.operands[2]
#测试:
add(a,b) # array([[3, 4, 5], [7, 8, 9]])
add(a,b,c)
c # array([[3., 4., 5.], [7., 8., 9.]])
def outer_it(x, y, out=None):#outer product function外部乘积函数
mulop = np.multiply
it = np.nditer([x, y, out], ['external_loop'],[['readonly'], ['readonly'], ['writeonly', 'allocate']],
op_axes=[list(range(x.ndim)) + [-1] * y.ndim,[-1] * x.ndim + list(range(y.ndim)),None])
with it:
for (a, b, c) in it:mulop(a, b, out=c)
return it.operands[2]
#测试:
a = np.arange(2)+1
b = np.arange(3)+1
outer_it(a,b)#array([[1, 2, 3], [2, 4, 6]])
def lambda_func(lamdaexpr, *args, **kwargs):#"lambda" ufunc
'''lambda_func(lambdaexpr, op1, ..., opn, out=None, order='K', casting='safe', buffersize=0)'''
nargs = len(args)
op = (kwargs.get('out',None),) + args
it = np.nditer(op, ['buffered','external_loop'],[['writeonly','allocate','no_broadcast']] + [['readonly','nbo','aligned']]*nargs,
order=kwargs.get('order','K'),casting=kwargs.get('casting','safe'), buffersize=kwargs.get('buffersize',0))
while not it.finished:
it[0] = lamdaexpr(*it[1:])
it.iternext()
return it.operands[0]
#测试:
a = np.arange(5)
b = np.ones(5)
lambda_func(lambda i,j:i*i + j/2, a, b)# array([ 0.5, 1.5, 4.5, 9.5, 16.5])
实例9:综合实例
import numpy as np
a = np.arange(6).reshape(2,3)# [[0 1 2][3 4 5]]
a.copy(order='C') # [[0 1 2][3 4 5]]
a.copy(order='F') # [[0 1 2][3 4 5]]
#迭代函数定义
def np_view(a,order='K',op_flags=['readonly'],flags=None):
if flags!=None:
t=np.nditer(a,order=order,op_flags=op_flags,flags=flags)
else:
t = np.nditer(a, order=order, op_flags=op_flags)
b='write' in op_flags[0]
for v in t:
if b: v[...] = 2*v
print (v, end=", " )
print ('\n')
# 控制遍历顺序
np_view(a) # 0, 1, 2, 3, 4, 5,
np_view(a.T) # 0, 1, 2, 3, 4, 5, # a 和 a.T 的遍历顺序是一样
# 迭代次序
np_view(a,'A') # 0, 1, 2, 3, 4, 5,
np_view(a,'C') # 0, 1, 2, 3, 4, 5, # C order行序优先
np_view(a,'F') # 0, 3, 1, 4, 2, 5, # Fortran order列序优先
# 修改数组中元素的值
np_view(a,op_flags=['readwrite'])#0, 2, 4, 6, 8, 10,
# 外部循环
# 默认保持本机内存顺序,迭代器提供单个一维块,强制Fortran顺序提供三个两元素块。
np_view(a,flags=['external_loop'], order = 'C')# [ 0 2 4 6 8 10],
np_view(a,flags=['external_loop'], order = 'F')# [0 6], [2 8], [ 4 10],