下面的 x[...]是什么意思?
x[...]
a = np.arange(6).reshape(2,3) for x in np.nditer(a, op_flags=['readwrite']): x[...] = 2 * x
虽然提议的重复 Python Ellipsis 对象做什么?在一般 python上下文中回答了这个问题,但是我认为,在 nditer循环中使用它需要添加信息。
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不仅仅是一个数字,它还是一个数组。
x[...] = ...
x
x = ...
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是一个标量,因此是不可变的
a[i]
x = 2*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[...]
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的行(第一个 dim)。然后 x是一个1d 数组,可以用 x[:]=...或 x[...]=...进行修改。
a
x[:]=...
x[...]=...
如果我从下一个 部分添加 external_loop标志,x现在是一个1d 数组,而 x[:] =将工作。但是 x[...] =仍然可以工作,而且更加通用。x[...]是使用所有其他 nditer的例子。
external_loop
x[:] =
x[...] =
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,)
比较下面这个简单的行迭代(在2d 数组上) :
这将迭代 a的行(第一个 dim)。然后 x是一个1d 数组,可以用 x[:] = ...或 x[...] = ...进行修改。
阅读和实验这个 nditer页所有的方式,直到最后。就其本身而言,nditer在 python中并不那么有用。它不会加速迭代——直到您将代码移植到 cython.np.ndindex是少数几个使用 nditer的未编译的 numpy函数之一。
cython
np.ndindex
numpy
省略号 ...表示 as many : as needed。
...
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]是相同的。
[:, :, :, 0] and [..., 0]
你只能用它来分析一个特定的维度,比如说你有一批50张128x128 RGB 的图片(50,3,128,128) ,如果你想在每个颜色通道的每张图片上切片,你可以选择 image[:,:,50:70, 20:80] or image[...,50:70,20:80]
image[:,:,50:70, 20:80] or image[...,50:70,20:80]
只是要注意,在 [...,0,...]无效这样的语句中,您不能多次使用它。
[...,0,...]