NEP 27 — 零阶数组#
- 作者:
亚历山大·贝洛波尔斯基(萨沙),转录为马特·皮库斯 < matti 。皮库斯@gmail 。 com >
- 地位:
最终的
- 类型:
信息性
- 创建:
2006-06-10
- 解决:
https://mail.python.org/pipermail/numpy-discussion/2018-October/078824.html
笔记
NumPy 具有零阶数组和标量。此设计文档改编自2006 年 wiki 条目,描述了零秩数组是什么以及它们存在的原因。它于 2018 年 10 月 13 日转录为 NEP,并更新了链接。该 Pull 请求引发了关于 NumPy 中对零秩数组和标量的持续需求的热烈讨论。
这里的一些信息已经过时了,例如现在已经实现了 0 维数组的索引并且不会出错。
零秩数组#
零秩数组是 shape=() 的数组。例如:
>>> x = array(1)
>>> x.shape
()
零秩数组和数组标量#
数组标量在许多方面与零秩数组相似:
>>> int_(1).shape
()
他们甚至打印相同的内容:
>>> print int_(1)
1
>>> print array(1)
1
然而,有一些重要的区别:
数组标量是不可变的
对于不同的数据类型,数组标量有不同的 python 类型
数组标量的动机#
除了本机 Python 类型之外,NumPy 的设计决策还提供 0 维数组和数组标量,这违背了 Python 的基本设计原则之一,即应该只有一种明显的方法来做到这一点。在本节中,我们将尝试解释为什么需要三种不同的方式来表示数字。
有几个 numpy 讨论线程:
2002 年邮件列表主题中的Rank-0 数组。
2005 年邮件列表主题中关于零维数组与 Python 标量的思考]
已经多次建议 NumPy 在所有情况下仅使用 0 级数组来表示标量。将 0 级数组转换为标量的优缺点总结如下:
优点:
在某些情况下,当 Python 需要一个整数时(最引人注目的是对序列进行切片和索引时:ceval.c 中的 _PyEval_SliceIndex),它不会在引发错误之前首先尝试将其转换为整数。因此,使用由数组对象转换为整数的 0 维数组会很方便。
拥有两种几乎但不完全相同的类型并且它们的单独存在只能通过 Python 和 NumPy 开发的历史来解释,因此不存在用户混淆的风险。
执行显式类型检查 或.尽管显式类型检查通常被认为是不好的做法,但使用它们有几个有效的理由。
(isinstance(x, float)
type(x) == types.FloatType)
不会在 pickle 文件中创建对 Numeric 的依赖(尽管这也可以通过数组的 pickle 代码中的特殊情况来完成)
缺点:
编写通用代码很困难,因为标量不具有与数组相同的方法和属性。 (例如
.type
或.shape
)。 Python 标量也有不同的数值行为。这导致了令人不愉快的特殊情况检查。从根本上来说,它让用户相信多维同构数组在某种程度上类似于 Python 列表(除了对象数组之外,它们不是)。
NumPy 实现的解决方案具有上述所有优点,但没有任何缺点。
为所有 21 种类型创建 Python 标量类型,并继承现有的三种类型。为这些 Python 标量类型定义等效方法和属性。
零秩数组的需要#
一旦使用零秩数组来表示标量的想法被拒绝,很自然地考虑是否可以完全消除零秩数组。然而,在一些重要的用例中,零秩数组不能被数组标量替换。另请参阅2006 年 2 月的0 级数组案例。
输出参数:
>>> y = int_(5) >>> add(5,5,x) array(10) >>> x array(10) >>> add(5,5,y) Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: return arrays must be of ArrayType
共享数据:
>>> x = array([1,2]) >>> y = x[1:2] >>> y.shape = () >>> y array(2) >>> x[1] = 20 >>> y array(20)
零秩数组的索引#
从 NumPy 版本 0.9.3 开始,零秩数组不支持任何索引:
>>> x[...]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
IndexError: 0-d arrays can't be indexed.
另一方面,有几种情况对于零秩数组有意义。
省略号和空元组#
Alexander于 2006 年 1 月开始了关于 scipy-dev 的讨论,提出了以下建议:
......允许可能是合理的
a[...]
。这样省略号可以被解释为任意数量的:
s ,包括零。对标量有意义的另一个下标操作是a[...,newaxis]
or Even ,其中 代表任意数量的以逗号分隔的 newaxis 标记。这将允许人们在适用于任何 numpy 类型的通用代码中使用省略号。a[{newaxis, }* ..., {newaxis,}*]
{newaxis,}*
[...]
Francesc Altet 支持零秩数组
的想法,并建议[()]
也予以支持。
弗朗西斯科的建议是:
In [65]: type(numpy.array(0)[...])
Out[65]: <type 'numpy.ndarray'>
In [66]: type(numpy.array(0)[()]) # Indexing a la numarray
Out[66]: <type 'int32_arrtype'>
In [67]: type(numpy.array(0).item()) # already works
Out[67]: <type 'int'>
人们一致认为,对于零秩数组x
, 和 都x[...]
应该x[()]
有效,但问题仍然是结果的类型应该是什么 - 零秩 ndarray 还是x.dtype
?
- (亚历山大)
首先,无论做出什么选择
x[...]
,x[()]
它们都应该是相同的,因为...
只是“尽可能多:必要时”的语法糖,在零等级的情况下会导致。其次,零阶数组和 numpy 标量类型在 numpy 中可以互换,但 numpy 标量可以在某些 Python 结构中使用,而 ndarray 则不能。例如:... = (:,)*0 = ()
>>> (1,)[array(0)] Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: tuple indices must be integers >>> (1,)[int32(0)] 1
[...]
由于大多数(如果不是全部)numpy 函数在返回时会自动将零秩数组转换为标量,因此和操作没有理由
[()]
不同。
请参阅 SVN 变更集 1864(已成为 git commit 9024ff0)以了解numpy 标量的实现x[...]
和返回。x[()]
请参阅 SVN 变更集 1866 (已成为 git commit 743d922 )以了解和的实现x[...] = v
x[()] = v
使用 newaxis 提高排名#
每个发表评论的人都喜欢这个功能,因此从 SVN 变更集 1871(后来成为 git commit b32744e)开始,可以将任意数量的省略号和 newaxis 标记放置为零秩数组的下标参数。例如:
>>> x = array(1)
>>> x[newaxis,...,newaxis,...]
array([[1]])
目前尚不清楚为什么应允许使用多个省略号,但这是我们试图保留的高阶数组的行为。
重构#
目前,零秩数组上的所有索引都是在一个特殊的代码分支中实现的,该代码分支过go总是引发索引错误。这可确保更改不会影响任何现有用法(依赖于异常的用法除外)。另一方面,这些更改的部分动机是使 ndarray 的行为更加统一,这应该可以 完全消除检查。if (nd
== 0)
if (nd == 0)
版权所有#
原始文档出现在 scipy.org wiki 上,没有版权声明,其历史记录将其归因于 sasha。