下面的
x[...]
是什么意思?
a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
x[...] = 2 * x
虽然建议的重复 Python Ellipsis 对象做什么? 在一般
python
上下文中回答了问题,但我认为,它在 nditer
循环中的使用需要添加信息。
https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#modifying-array-values
Python 中的常规赋值只是更改本地或全局变量字典中的引用,而不是就地修改现有变量。这意味着简单地分配给 x 不会将值放入数组的元素中,而是将 x 从数组元素引用切换为对您分配的值的引用。要实际修改数组的元素,x 应使用省略号进行索引。
该部分包含您的代码示例。
所以用我的话来说,
x[...] = ...
就地修改了x
; x = ...
会破坏到 nditer
变量的链接,并且不会更改它。 它类似于 x[:] = ...
,但适用于任何维度(包括 0d)的数组。 在这种情况下,x
不仅仅是一个数字,它是一个数组。
也许最接近此
nditer
迭代(没有 nditer
)的是:
In [667]: for i, x in np.ndenumerate(a):
...: print(i, x)
...: a[i] = 2 * x
...:
(0, 0) 0
(0, 1) 1
...
(1, 2) 5
In [668]: a
Out[668]:
array([[ 0, 2, 4],
[ 6, 8, 10]])
请注意,我必须直接索引并修改
a[i]
。 我无法使用,x = 2*x
。 在本次迭代中 x
是一个标量,因此不可变
In [669]: for i,x in np.ndenumerate(a):
...: x[...] = 2 * x
...
TypeError: 'numpy.int32' object does not support item assignment
但在
nditer
情况下 x
是一个 0d 数组,并且是可变的。
In [671]: for x in np.nditer(a, op_flags=['readwrite']):
...: print(x, type(x), x.shape)
...: x[...] = 2 * x
...:
0 <class 'numpy.ndarray'> ()
4 <class 'numpy.ndarray'> ()
...
而且因为是 0d,所以不能用
x[:]
代替 x[...]
----> 3 x[:] = 2 * x
IndexError: too many indices for array
更简单的数组迭代也可能提供见解:
In [675]: for x in a:
...: print(x, x.shape)
...: x[:] = 2 * x
...:
[ 0 8 16] (3,)
[24 32 40] (3,)
这会迭代
a
的行(第一个暗淡的行)。 x
是一个一维数组,可以使用 x[:]=...
或 x[...]=...
进行修改。
如果我从下一个
section添加
external_loop
标志,x
现在是一个一维数组,并且 x[:] =
可以工作。 但 x[...] =
仍然有效并且更通用。 x[...]
用于所有其他 nditer
示例。
In [677]: for x in np.nditer(a, op_flags=['readwrite'], flags=['external_loop']):
...: print(x, type(x), x.shape)
...: x[...] = 2 * x
[ 0 16 32 48 64 80] <class 'numpy.ndarray'> (6,)
比较这个简单的行迭代(在二维数组上):
In [675]: for x in a:
...: print(x, x.shape)
...: x[:] = 2 * x
...:
[ 0 8 16] (3,)
[24 32 40] (3,)
这会迭代
a
的行(第一个暗淡的行)。 x
是一个一维数组,可以使用 x[:] = ...
或 x[...] = ...
进行修改。
从头到尾阅读并尝试此
nditer
页。 就其本身而言,nditer
在 python
中并没有那么有用。 它不会加速迭代 - 直到您将代码移植到 cython
。np.ndindex
是少数使用 numpy
的非编译 nditer
函数之一。
省略号
...
表示 as many : as needed
。
对于没有时间的人,这里有一个简单的例子:
In [64]: X = np.reshape(np.arange(9), (3,3))
In [67]: Y = np.reshape(np.arange(2*3*4), (2,3,4))
In [70]: X
Out[70]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
In [71]: X[:,0]
Out[71]: array([0, 3, 6])
In [72]: X[...,0]
Out[72]: array([0, 3, 6])
In [73]: Y
Out[73]:
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
In [74]: Y[:,0]
Out[74]:
array([[ 0, 1, 2, 3],
[12, 13, 14, 15]])
In [75]: Y[...,0]
Out[75]:
array([[ 0, 4, 8],
[12, 16, 20]])
In [76]: X[0,...,0]
Out[76]: array(0)
In [77]: Y[0,...,0]
Out[77]: array([0, 4, 8])
这使得一次只操作一个维度变得很容易。
有一件事 - 在任何给定的索引表达式中只能有一个省略号,否则您的表达式对于每个应放入多少个
:
会不明确。
我相信一个很好的相似之处(大多数人可能已经习惯了)就是这样思考:
import numpy as np
random_array = np.random.rand(2, 2, 2, 2)
在这种情况下,
[:, :, :, 0] and [..., 0]
是相同的。
您可以用来仅分析特定维度,假设您有一批 50 128x128 RGB 图像(50, 3, 128, 128),如果您想在每个颜色通道的每个图像中切片一部分,您可以要么
image[:,:,50:70, 20:80] or image[...,50:70,20:80]
请注意,您不能在声明中多次使用它,例如
[...,0,...]
无效。
不需要长解释:-) 在 Numpy 中,省略号(3 个点)仅表示“索引匹配数组形状所需的
:
的数量”。
:
本身是表示0:-1
的快捷方式,即所考虑的维度中的所有元素。
例如,对于形状
(n, p, q, r, s)
,即具有 5 维的数组,省略号可用于替换 1、2、3 或 4 个连续的 :
。这些形式是等效的:
a[:, :, 4, 2]
和 a[..., 4, 2]
a[4, :, :, 2]
和 a[4, ..., 2]
a[4, 2, :, :]
和 a[4, 2, ...]
a[:, :, :, 4]
和 a[..., 4]
a[:, :, :, :]
和 a[...]
以及任何带有切片的变体,例如:
a[1:2, :, :, 2:6]
和 a[1:2, ..., 2:6]
这可用于简化返回数组切片的索引,而不是使用连续的
:
来包含相应的整个维度。这种简化隐藏了数组的实际形状,并可能导致切片错误。
确实索引中只能有一个省略号,否则 Numpy 如何知道如何分配所需的
:
?