在 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[]
是SyntaxError
s。使用列表进行索引记录在 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
的视图,其base
是A
:
>>> 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 的数据帧进行分层索引