Python 中的“三个点”在对看起来像数字的内容进行索引时意味着什么?

问题描述 投票:0回答:4

下面的

x[...]
是什么意思?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
    x[...] = 2 * x
python numpy iterator
4个回答
104
投票

虽然建议的重复 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
函数之一。


74
投票

省略号

...
表示
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])

这使得一次只操作一个维度变得很容易。

有一件事 - 在任何给定的索引表达式中只能有一个省略号,否则您的表达式对于每个应放入多少个

:
会不明确。


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,...]
无效。


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 如何知道如何分配所需的

:

© www.soinside.com 2019 - 2024. All rights reserved.