NumPy 多维数组迭代是如何工作的? (有和没有 nditer)

Posted

技术标签:

【中文标题】NumPy 多维数组迭代是如何工作的? (有和没有 nditer)【英文标题】:How does NumPy multidimensional array iteration work? (With and without nditer) 【发布时间】:2018-01-16 13:58:01 【问题描述】:

注意:我不确定这是否重复——如果是重复的,请告诉我(并关闭问题)。

如果一个人有一个一维 NumPy 数组vector,那么如果一个人写了一个形式如下的 for 循环:

for element in vector :
    print(element)

结果将打印 NumPy 数组的每个元素。

如果一个人有一个二维 NumPy 数组matrix,那么如果一个人写了一个形式如下的 for 循环:

for vector in matrix :
    print(vector)

结果将打印二维 NumPy 数组的每一行,即它将打印一维 NumPy 数组,并且它不会单独打印数组的每个元素。

但是,如果将 for 循环改为:

import numpy
for element in numpy.nditer(matrix) :
     print(element)

结果打印二维 NumPy 数组的每个元素。

问题:如果有一个 3 维 NumPy 数组 tensor 会发生什么?

a.如果写了一个for循环的形式:

for unknownType in tensor :
     print(unknownType)

这会打印出tensor 的组成二维 NumPy(子)数组吗?

即对于一个 n 维 NumPy 数组nArrayfor unknownType in nArray : 是否迭代了nArray 的组成(n-1)维 NumPy(子)数组?

b.如果写了一个for循环的形式:

for unknownType in numpy.nditer(tensor) :
    print(unknownType)

这会打印tensor 的元素吗?或者它会打印tensor的组成二维NumPy(子)数组的组成一维NumPy(子)数组吗?

即对于 n 维 NumPy 数组 nArrayfor unknownType in nditer(nArray) : 是否会迭代 nArray 的元素?还是迭代nArray 的组成(n-1)维 NumPy(子)数组的组成(n-2)维 NumPy(子)数组?

我不清楚nditer 这个名字,因为我不知道“nd”代表什么(“iter”显然是“iteration”的缩写)。并且大概可以将元素视为“0 维 NumPy 数组”,因此给我的二维 NumPy 数组示例是模棱两可的。

我看过np.nditer documentation,但老实说,我不理解这些示例或它们试图演示的内容——它似乎是由程序员为程序员(我不是)编写的。

【问题讨论】:

“这会打印张量的元素吗?”你为什么不试试看呢? nditer 在 Python 级代码中用处不大。 nditer 教程面向高级程序员,尤其是那些打算使用 cython 的程序员。 np.ndindex 使用nditer 生成索引,用于np.vectorizenp.apply_along_axis @hpaulj 你是说"Iterating Over Arrays" - Putting the Inner Loop in Cython 是的。这就是cython 的例子。但如果您不需要所有广播帮助,则输入 memoryviews 的 cython 会更快。 【参考方案1】:

一)

for x in arr: 迭代数组的第一维。

In [233]: for x in np.arange(24).reshape((2,3,4)):
     ...:     print(x.shape)
     ...:     
(3, 4)
(3, 4)

我认为它是for x in list(arr):...。它将数组分解为子数组列表。

b)

nditer 控制迭代深度很棘手。默认情况下,它在元素级别进行迭代。教程页面显示了一些使用缓冲区和顺序的技巧。但我看到的最好方法是使用ndindex

ndindex 构造一个大小合适的虚拟数组,并进行multi_index 迭代。

例如迭代 3d 数组的第 2 维:

In [237]: arr = np.arange(24).reshape(2,3,4)
In [240]: for idx in np.ndindex(arr.shape[:2]):
     ...:     print(idx, arr[idx], arr[idx].sum())
     ...:      
(0, 0) [0 1 2 3] 6
(0, 1) [4 5 6 7] 22
(0, 2) [ 8  9 10 11] 38
(1, 0) [12 13 14 15] 54
(1, 1) [16 17 18 19] 70
(1, 2) [20 21 22 23] 86

我可以做同样的迭代

for i in range(2):
    for j in range(3):
         arr[i,j]...

arr1 = arr.reshape(-1,4)
for ij in range(6):
    arr1[ij]....

速度将基本相同 - 与同时处理整个 3d 数组的数组函数或采用某种axis 参数的数组函数相比,所有这些函数都很差。

In [241]: arr.sum(axis=2)
Out[241]: 
array([[ 6, 22, 38],
       [54, 70, 86]])

numpy 作为数组的类是np.ndarray。大概nditer 就是这样命名的。 nditer 被编写为一种整合c 级别代码可以在数组上迭代的各种方式,尤其是几个可广播的。 np.nditer 函数提供对c 级别迭代器的访问。但是由于实际的迭代仍然是在 Python 代码中完成的,所以几乎没有速度优势。

【讨论】:

【参考方案2】:

如果您只使用for 循环,则迭代在第一个维度上,如果数组只有一个维度,这将是元素,如果它是 2D 它将是行,如果它是 3D 它将迭代飞机,...

但是nditer 是一个 ND(代表 n 维)迭代器。它将遍历数组中的每个元素。它(大致!)相当于for item in your_array.ravel()(迭代数组的扁平“视图”)。对于一维数组,它遍历元素,对于二维数组,它首先遍历第一行中的元素,然后遍历第二行,依此类推。

请注意,nditer 比这更强大,它可以一次迭代多个数组,您可以缓冲迭代和许多其他内容。


但是对于 NumPy,您通常想使用 for-loop 或 np.nditer很多“矢量化”操作使得手动迭代(在大多数情况下)变得不必要。

【讨论】:

以上是关于NumPy 多维数组迭代是如何工作的? (有和没有 nditer)的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地找到 NumPy 中平滑多维数组的局部最小值?

如何在不使用 NumPy 复制的情况下展平多维数组的轴?

Python之深入解析Numpy的高级操作和使用

NumPy数据分析基础:ndarray数组运算基本操作及切片索引迭代

如何将 128 维面部编码存储在 numpy 多维数组中?

如何理解外行人的坎坷步伐?