NEP 27 — 零秩陣列#
- 作者:
Alexander Belopolsky (sasha),轉錄者 Matt Picus <matti.picus@gmail.com>
- 狀態:
最終
- 類型:
資訊性
- 建立日期:
2006-06-10
- 決議:
https://mail.python.org/pipermail/numpy-discussion/2018-October/078824.html
注意
NumPy 同時具有零秩陣列和純量值。這份設計文件改編自2006 年的 Wiki 條目,描述了什麼是零秩陣列以及它們存在的原因。它於 2018-10-13 被轉錄成 NEP,並且連結已更新。提取請求引發了關於 NumPy 中持續需要零秩陣列和純量值的熱烈討論。
此處的一些資訊已過時,例如,0-D 陣列的索引現在已實作,並且不會產生錯誤。
零秩陣列#
零秩陣列是 shape=() 的陣列。例如
>>> x = array(1)
>>> x.shape
()
零秩陣列和陣列純量值#
陣列純量值在許多方面與零秩陣列相似
>>> int_(1).shape
()
它們甚至列印出來也相同
>>> print int_(1)
1
>>> print array(1)
1
然而,它們之間存在一些重要的差異
陣列純量值是不可變的
陣列純量值對於不同的資料型別具有不同的 Python 型別
陣列純量值的動機#
NumPy 提供 0 維陣列和陣列純量值(除了原生 Python 型別之外)的設計決策,違反了 Python 的基本設計原則之一,即應該只有一種顯而易見的方法來做某事。在本節中,我們將嘗試解釋為什麼有必要使用三種不同的方式來表示數字。
有幾個 numpy-discussion 線程
rank-0 arrays 在 2002 年的郵件列表線程中。
關於零維陣列與 Python 純量值的想法,在 2005 年的郵件列表線程]
有人多次建議 NumPy 在所有情況下都只使用 rank-0 陣列來表示純量。將 rank-0 陣列轉換為純量的優缺點總結如下
優點
在某些情況下,當 Python 期望整數時(最戲劇性的是在切片和索引序列時:ceval.c 中的 _PyEval_SliceIndex),它不會先嘗試將其轉換為整數,然後才引發錯誤。因此,擁有由陣列物件為您轉換的整數 0 維陣列很方便。
使用者不會因為有兩個幾乎但不完全相同的型別而感到困惑,這兩個型別的獨立存在只能用 Python 和 NumPy 開發的歷史來解釋。
對於執行顯式型別檢查的程式碼沒有問題
(isinstance(x, float)
或type(x) == types.FloatType)
。雖然顯式型別檢查通常被認為是不好的做法,但有幾個合理的理由可以使用它們。不會在 pickle 檔案中建立對 Numeric 的依賴(儘管這也可以透過陣列的 pickling 程式碼中的特殊情況來完成)
缺點
很難編寫通用程式碼,因為純量值不具有與陣列相同的方法和屬性。(例如
.type
或.shape
)。此外,Python 純量值也具有不同的數值行為。這導致了令人不快的特殊情況檢查。從根本上來說,它讓使用者相信,多維同質陣列在某種程度上類似於 Python 列表(除了 Object 陣列之外,它們並非如此)。
NumPy 實作了一個旨在擁有上述所有優點且沒有任何缺點的解決方案。
為所有 21 種型別建立 Python 純量型別,並從已存在的三種型別繼承。為這些 Python 純量型別定義等效的方法和屬性。
對零秩陣列的需求#
一旦使用零秩陣列表示純量值的想法被拒絕,很自然地會考慮是否可以完全消除零秩陣列。但是,在某些重要的用例中,零秩陣列無法被陣列純量值取代。另請參閱 2006 年 2 月的 A case for rank-0 arrays。
輸出引數
>>> 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 在 scipy-dev 上發起了 2006 年 1 月的討論,並提出以下提案
… 允許
a[...]
可能是合理的。這樣,省略符號可以解釋為任意數量的:
,包括零。對於純量值有意義的另一個下標運算將是a[...,newaxis]
甚至是a[{newaxis, }* ..., {newaxis,}*]
,其中{newaxis,}*
代表任意數量的逗號分隔的 newaxis 標記。這將允許在通用程式碼中使用省略符號,該程式碼適用於任何 numpy 型別。
Francesc Altet 支持零秩陣列上的 [...]
想法,並且 建議 也應該支援 [()]
。
Francesc 的提案是
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
?
- (Alexander)
首先,無論對
x[...]
和x[()]
做出什麼選擇,它們都應該是相同的,因為...
只是「根據需要盡可能多的 :」的語法糖,在零秩的情況下,這會導致... = (:,)*0 = ()
。其次,在 numpy 中,零秩陣列和 numpy 純量型別是可互換的,但 numpy 純量值可以用在某些 ndarray 無法使用的 Python 結構中。例如>>> (1,)[array(0)] Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: tuple indices must be integers >>> (1,)[int32(0)] 1
由於大多數(如果不是全部)numpy 函數在返回時會自動將零秩陣列轉換為純量值,因此 [...]
和 x[()]
運算沒有理由不同。
請參閱 SVN changeset 1864(已成為 git commit 9024ff0),以了解 x[...]
和 x[()]
返回 numpy 純量值的實作。
請參閱 SVN changeset 1866(已成為 git commit 743d922),以了解 x[...] = v
和 x[()] = v
的實作
使用 newaxis 增加秩#
所有評論的人都喜歡此功能,因此從 SVN changeset 1871(已成為 git commit b32744e)開始,任意數量的省略符號和 newaxis 標記都可以作為零秩陣列的下標引數放置。例如
>>> x = array(1)
>>> x[newaxis,...,newaxis,...]
array([[1]])
尚不清楚為什麼應該允許多個省略符號,但這是我們試圖保留的較高秩陣列的行為。
重構#
目前,零秩陣列上的所有索引都在程式碼的特殊 if (nd == 0)
分支中實作,該分支過去總是會引發索引錯誤。這確保了變更不會影響任何現有的用法(除了依賴異常的用法)。另一方面,這些變更的部分動機是使 ndarray 的行為更加統一,這應該可以完全消除 if (nd == 0)
檢查。
版權#
原始文件出現在 scipy.org wiki 上,沒有版權聲明,並且其 歷史記錄 將其歸於 sasha。