在 numpy 中,用空元组和省略号对数组进行索引有啥作用?

Posted

技术标签:

【中文标题】在 numpy 中,用空元组和省略号对数组进行索引有啥作用?【英文标题】:In numpy, what does indexing an array with the empty tuple vs. ellipsis do?在 numpy 中,用空元组和省略号对数组进行索引有什么作用? 【发布时间】:2013-01-19 07:14:45 【问题描述】:

我刚刚偶然发现numpy 中的数组可能被一个空元组索引:

In [62]: a = arange(5)

In [63]: a[()]
Out[63]: array([0, 1, 2, 3, 4])

我在numpy wiki ZeroRankArray 上找到了一些文档:

(Sasha) 首先,无论对 x[...] 和 x[()] 做出何种选择,它们都应该是相同的,因为 ... 只是“尽可能多的:”的语法糖,在零秩的情况导致 ... = (:,)*0 = ()。其次,零级数组和 numpy 标量类型在 numpy 中是可以互换的,但是 numpy 标量可以用于一些 ndarrays 不能的 python 构造中。

因此,对于 0 维数组,a[()]a[...] 应该是等效的。它们也适用于高维数组吗?它们似乎是:

In [65]: a = arange(25).reshape(5, 5)

In [66]: a[()] is a[...]
Out[66]: False

In [67]: (a[()] == a[...]).all()
Out[67]: True

In [68]: a = arange(3**7).reshape((3,)*7)

In [69]: (a[()] == a[...]).all()
Out[69]: True

但是,它是不是语法糖。不适用于高维数组,甚至不适用于 0 维数组:

In [76]: a[()] is a
Out[76]: False

In [77]: a[...] is a
Out[77]: True

In [79]: b = array(0)

In [80]: b[()] is b
Out[80]: False

In [81]: b[...] is b
Out[81]: True

然后是一个空的 list 索引的情况,它完全做其他事情,但看起来等同于用一个空的ndarray 进行索引:

In [78]: a[[]]
Out[78]: array([], shape=(0, 3, 3, 3, 3, 3, 3), dtype=int64)

In [86]: a[arange(0)]
Out[86]: array([], shape=(0, 3, 3, 3, 3, 3, 3), dtype=int64)

In [82]: b[[]]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)

IndexError: 0-d arrays can't be indexed.

因此,()... 似乎相似但并不完全相同,而使用 [] 进行索引则完全不同。而a[]b[]SyntaxErrors。使用列表进行索引记录在 index arrays,并且有一个关于使用元组索引的简短通知 at the end of the same document。

剩下的问题是:

a[()]a[...] 之间的区别是设计使然吗?那么设计是什么?

(这个问题让人想起:What does the empty `()` do on a Matlab matrix?)

编辑:

事实上,即使是 标量 也可能被空元组索引:

In [36]: numpy.int64(10)[()]
Out[36]: 10

【问题讨论】:

【参考方案1】:

A[...]的处理是特例,优化为always return A itself:

if (op == Py_Ellipsis) 
    Py_INCREF(self);
    return (PyObject *)self;

任何其他应该等效的东西,例如A[:]A[(Ellipsis,)]A[()]A[(slice(None),) * A.ndim] 将改为返回整个A视图,其baseA

>>> A[()] is A
False
>>> A[()].base is A
True

这似乎是不必要且过早的优化,因为A[(Ellipsis,)]A[()] 将始终给出相同的结果(A 的完整视图)。从https://github.com/numpy/numpy/commit/fa547b80f7035da85f66f9cbabc4ff75969d23cd 看来,它最初似乎是必需的,因为使用 ... 进行索引在 0d 数组上无法正常工作(以前到 https://github.com/numpy/numpy/commit/4156b241aa3670f923428d4e72577a9962cdf042 它会将元素作为标量返回),然后扩展到所有数组以保持一致性;从那时起,索引已固定在 0d 数组上,因此不需要优化,但它设法保留下来(并且可能有一些代码取决于 A[...] is A 是否为真)。

【讨论】:

我相信这个答案可能已经过时了。从 Numpy 1.16.4 开始,A[...] is A 对我来说评估为 False,对于 0-dim 和 multi-dim 数组。 是的,这个特殊情况已经消失了很长时间 - 事实上,我认为取出它的 commit 距离最初发布这个答案仅两个月。【参考方案2】:

虽然在您给出的示例中,空元组和省略号给出了相似的结果,但通常它们用于不同的目的。索引数组时,A[i, j, k] == A[(i, j, k)],特别是A[...] == A[(Ellipsis,)]。在这里,元组只是用作索引元素的容器。当您需要将索引作为变量进行操作时,这可能很有用,例如您可以这样做:

index = (0,) * A.ndim
A[index]

注意,由于元组是索引元素的容器,它不能与其他索引组合,例如A[(), 0] == A[[], 0]A[(), 0] != A[..., 0]

因为数组A 可以使用比A.ndim 更少的索引进行索引,所以使用空元组进行索引是该行为的自然扩展,它在某些情况下可能很有用,例如,上面的代码片段将在以下情况下工作A.ndim == 0.

简而言之,元组作为索引元素的容器,允许为空,而省略号是可能的索引元素之一。

【讨论】:

【参考方案3】:

根据official Numpy documentation,区别很明显:

空(元组)索引是零维的全标量索引 大批。 x[()] 如果x 是零维且是视图,则返回一个标量 否则。另一方面,x[...] 总是返回一个视图

当省略号 (...) 存在但没有大小时(即替换零 :) 结果仍然是一个数组。如果没有高级视图 索引存在,否则为副本。

>>> import numpy as np
>>> # ---------------------------------- #
>>> # when `x` is at least 1 dimensional #
>>> # ---------------------------------- #
>>> x = np.linspace(0, 10, 100)
>>> x.shape
(100,)
>>> x.ndim
1
>>> a = x[()]
>>> b = x[...]
>>> id(x), id(a), id(b)
(4559933568, 4561560080, 4585410192)
>>> id(x.base), id(a.base), id(b.base)
(4560914432, 4560914432, 4560914432)
>>> # ---------------------------- #
>>> # when `z` is zero dimensional #
>>> # ---------------------------- #
>>> z = np.array(3.14)
>>> z.shape
()
>>> z.ndim
0
>>> a = z[()]
>>> b = z[...]
>>> type(a), type(b)
(<class 'numpy.float64'>, <class 'numpy.ndarray'>)
>>> id(z), id(a), id(b)
(4585422896, 4586829384, 4561560080)
>>> id(z.base), id(a.base), id(b.base)
(4557260904, 4557260904, 4585422896)
>>> b.base is z
True

【讨论】:

从 Numpy 1.16.4 开始,这似乎是正确的答案,接受的答案已经过时。 A[...] 为任何维度返回 A 的视图,并且未优化以返回相同的对象。 A[...] is A 所有维度的计算结果为 False。 A[()] 仅在 A 为 0 维时返回标量,如果 A 为 0 维,这可能是“索引 A”的规范方法。

以上是关于在 numpy 中,用空元组和省略号对数组进行索引有啥作用?的主要内容,如果未能解决你的问题,请参考以下文章

使用 n-d numpy 数组作为索引对 Panda 的数据帧进行分层索引

python 数组和列表的区别

Python元组和序列区别是啥?

python常用序列listtuples及矩阵库numpy的使用

关于python的元组操作

python学习:元组和嵌套