資料型別#
另請參閱
陣列型別與型別之間的轉換#
NumPy 支援比 Python 更多的數值型別。本節將展示哪些型別可用,以及如何修改陣列的資料型別。
NumPy 數值型別是 numpy.dtype
(資料型別) 物件的實例,每個物件都具有獨特的特性。一旦您使用 import numpy as np
匯入 NumPy 後,您可以使用 numpy 頂層 API 中的純量型別建立具有指定 dtype 的陣列,例如 numpy.bool
、numpy.float32
等。
這些純量型別可以作為許多 numpy 函式或方法接受的 dtype 關鍵字引數。例如
>>> z = np.arange(3, dtype=np.uint8)
>>> z
array([0, 1, 2], dtype=uint8)
陣列型別也可以用字元碼來表示,例如
>>> np.array([1, 2, 3], dtype='f')
array([1., 2., 3.], dtype=float32)
>>> np.array([1, 2, 3], dtype='d')
array([1., 2., 3.], dtype=float64)
有關指定和建構資料型別物件的更多資訊,包括如何指定位元組順序等參數,請參閱 指定和建構資料型別。
若要轉換陣列的型別,請使用 .astype() 方法。例如
>>> z.astype(np.float64)
array([0., 1., 2.])
請注意,在上面,我們可以使用 Python float 物件作為 dtype,而不是 numpy.float64
。NumPy 知道 int
指的是 numpy.int_
,bool
代表 numpy.bool
,float
是 numpy.float64
,而 complex
是 numpy.complex128
。其他資料型別沒有 Python 等效項。
若要判斷陣列的型別,請查看 dtype 屬性
>>> z.dtype
dtype('uint8')
dtype 物件也包含有關型別的資訊,例如其位元寬度和位元組順序。資料型別也可以間接地用於查詢型別的屬性,例如它是否為整數
>>> d = np.dtype(np.int64)
>>> d
dtype('int64')
>>> np.issubdtype(d, np.integer)
True
>>> np.issubdtype(d, np.floating)
False
數值資料型別#
共有 5 種基本數值型別,分別代表布林值 (bool
)、整數 (int
)、無號整數 (uint
)、浮點數 (float
) 和 complex
。基本數值型別名稱與數值位元大小結合起來定義了具體的型別。位元大小是記憶體中表示單個值所需的位元數。例如,numpy.float64
是一種 64 位元浮點數資料型別。某些型別,例如 numpy.int_
和 numpy.intp
,具有不同的位元大小,具體取決於平台 (例如 32 位元與 64 位元 CPU 架構)。當與底層程式碼 (例如 C 或 Fortran) 介接時,應將此納入考量,因為在底層程式碼中會處理原始記憶體位址。
字串和位元組的資料型別#
除了數值型別之外,NumPy 也支援透過 numpy.str_
dtype (U
字元碼) 儲存 Unicode 字串,透過 numpy.bytes_
(S
字元碼) 儲存以 null 終止的位元組序列,以及透過 numpy.void
(V
字元碼) 儲存任意位元組序列。
以上所有都是固定寬度資料型別。它們透過寬度 (以位元組或 Unicode 碼位表示) 進行參數化,陣列中的單個資料元素必須容納在該寬度內。這表示使用此 dtype 儲存位元組序列或字串陣列需要預先知道或計算最長文字或位元組序列的大小。
作為範例,我們可以建立一個儲存單字 "hello"
和 "world!"
的陣列
>>> np.array(["hello", "world!"])
array(['hello', 'world!'], dtype='<U6')
這裡的資料型別被偵測為 Unicode 字串,最大長度為 6 個碼位,足以儲存兩個條目而不會截斷。如果我們指定較短或較長的資料型別,則字串會被截斷或以零填充以符合指定的寬度
>>> np.array(["hello", "world!"], dtype="U5")
array(['hello', 'world'], dtype='<U5')
>>> np.array(["hello", "world!"], dtype="U7")
array(['hello', 'world!'], dtype='<U7')
如果我們使用位元組資料型別並要求 NumPy 列印出陣列緩衝區中的位元組,我們可以更清楚地看到零填充
>>> np.array(["hello", "world"], dtype="S7").tobytes()
b'hello\x00\x00world\x00\x00'
每個條目都以兩個額外的 null 位元組填充。但請注意,NumPy 無法區分有意儲存的尾隨 null 和填充 null
>>> x = [b"hello\0\0", b"world"]
>>> a = np.array(x, dtype="S7")
>>> print(a[0])
b"hello"
>>> a[0] == x[0]
False
如果您需要儲存和往返任何尾隨 null 位元組,您將需要使用非結構化的 void 資料型別
>>> a = np.array(x, dtype="V7")
>>> a
array([b'\x68\x65\x6C\x6C\x6F\x00\x00', b'\x77\x6F\x72\x6C\x64\x00\x00'],
dtype='|V7')
>>> a[0] == np.void(x[0])
True
進階型別 (未在上面列出) 在 結構化陣列 章節中探討。
NumPy 資料型別與 C 資料型別之間的關係#
NumPy 同時提供基於位元大小的型別名稱和基於 C 型別名稱的名稱。由於 C 型別的定義取決於平台,因此這表示應優先選用明確的位元大小,以避免在使用 NumPy 的程式中出現平台相關的行為。
為了簡化與 C 程式碼的整合 (在 C 程式碼中,參考平台相關的 C 型別更為自然),NumPy 也提供與平台 C 型別相對應的型別別名。某些 dtype 具有尾隨底線,以避免與內建 Python 型別名稱混淆,例如 numpy.bool_
。
標準 Python API 名稱 |
Python API 「類 C」名稱 |
實際 C 型別 |
描述 |
---|---|---|---|
N/A |
|
布林值 (True 或 False) 儲存為位元組。 |
|
|
平台定義的 8 位元整數型別。 |
||
|
平台定義的 8 位元無號整數型別。 |
||
|
平台定義的 16 位元整數型別。 |
||
|
平台定義的 16 位元無號整數型別。 |
||
|
平台定義的 32 位元整數型別。 |
||
|
平台定義的 32 位元無號整數型別。 |
||
N/A |
|
平台定義的大小為 |
|
N/A |
|
平台定義的整數型別,能夠儲存最大配置大小。 |
|
N/A |
|
|
保證可容納指標。僅限字元碼 (Python 和 C)。 |
N/A |
|
|
保證可容納指標。僅限字元碼 (Python 和 C)。 |
|
平台定義的至少 32 位元整數型別。 |
||
|
平台定義的至少 32 位元無號整數型別。 |
||
N/A |
|
平台定義的至少 64 位元整數型別。 |
|
N/A |
|
平台定義的至少 64 位元無號整數型別。 |
|
N/A |
半精度浮點數:符號位元、5 位元指數、10 位元尾數。 |
||
|
平台定義的單精度浮點數:通常為符號位元、8 位元指數、23 位元尾數。 |
||
|
平台定義的雙精度浮點數:通常為符號位元、11 位元指數、52 位元尾數。 |
||
|
|
平台定義的擴展精度浮點數。 |
|
|
複數,由兩個單精度浮點數 (實部和虛部) 表示。 |
||
|
複數,由兩個雙精度浮點數 (實部和虛部) 表示。 |
||
|
|
複數,由兩個擴展精度浮點數 (實部和虛部) 表示。 |
由於其中許多具有平台相關的定義,因此提供了一組固定大小的別名 (請參閱 固定大小別名)。
陣列純量#
NumPy 通常將陣列的元素作為陣列純量 (具有關聯 dtype 的純量) 傳回。陣列純量與 Python 純量不同,但在大多數情況下,它們可以互換使用 (主要例外是 Python v2.x 之前的版本,其中整數陣列純量不能充當列表和元組的索引)。有些例外情況,例如當程式碼需要純量的非常特定的屬性,或者當程式碼專門檢查值是否為 Python 純量時。一般來說,透過使用對應的 Python 型別函式 (例如,int
、float
、complex
、str
) 將陣列純量明確轉換為 Python 純量,可以輕鬆解決問題。
使用陣列純量的主要優點是它們保留了陣列型別 (Python 可能沒有可用的匹配純量型別,例如 int16
)。因此,使用陣列純量可確保陣列和純量之間的行為相同,無論值是否在陣列內部。NumPy 純量也具有與陣列相同的許多方法。
溢位錯誤#
當值需要的記憶體超過資料型別中可用的記憶體時,NumPy 數值型別的固定大小可能會導致溢位錯誤。例如,numpy.power
對於 64 位元整數正確評估 100 ** 9
,但對於 32 位元整數,則給出 -1486618624 (不正確)。
>>> np.power(100, 9, dtype=np.int64)
1000000000000000000
>>> np.power(100, 9, dtype=np.int32)
np.int32(-1486618624)
NumPy 和 Python 整數型別在整數溢位方面的行為差異很大,並且可能會讓期望 NumPy 整數行為類似於 Python int
的使用者感到困惑。與 NumPy 不同,Python int
的大小是彈性的。這表示 Python 整數可以擴展以容納任何整數,並且不會溢位。
NumPy 提供 numpy.iinfo
和 numpy.finfo
,分別用於驗證 NumPy 整數和浮點數值的最小值或最大值
>>> np.iinfo(int) # Bounds of the default integer on this system.
iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)
>>> np.iinfo(np.int32) # Bounds of a 32-bit integer
iinfo(min=-2147483648, max=2147483647, dtype=int32)
>>> np.iinfo(np.int64) # Bounds of a 64-bit integer
iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)
如果 64 位元整數仍然太小,則結果可能會轉換為浮點數。浮點數提供更大但不精確的可能值範圍。
>>> np.power(100, 100, dtype=np.int64) # Incorrect even with 64-bit int
0
>>> np.power(100, 100, dtype=np.float64)
1e+200
浮點數精度#
NumPy 中的許多函式,尤其是 numpy.linalg
中的函式,都涉及浮點數運算,這可能會由於電腦表示十進制數的方式而引入小的誤差。例如,當執行涉及浮點數的基本算術運算時
>>> 0.3 - 0.2 - 0.1 # This does not equal 0 due to floating-point precision
-2.7755575615628914e-17
為了處理這種情況,建議使用 np.isclose 等函式來比較值,而不是檢查是否完全相等
>>> np.isclose(0.3 - 0.2 - 0.1, 0, rtol=1e-05) # Check for closeness to 0
True
在本範例中,np.isclose 透過應用相對容差來考量浮點數計算中發生的微小誤差,確保小閾值範圍內的結果被視為接近。
有關計算精度的資訊,請參閱 浮點算術。
擴展精度#
Python 的浮點數通常是 64 位元浮點數,幾乎等同於 numpy.float64
。在某些不常見的情況下,使用更高精度的浮點數可能很有用。這在 numpy 中是否可行取決於硬體和開發環境:具體而言,x86 機器提供具有 80 位元精度的硬體浮點數,雖然大多數 C 編譯器將其作為 long double
型別提供,但 MSVC (Windows 建置的標準) 使 long double
與 double
(64 位元) 相同。NumPy 將編譯器的 long double
作為 numpy.longdouble
(以及複數的 np.clongdouble
) 提供。您可以使用 np.finfo(np.longdouble)
找出您的 numpy 提供了什麼。
NumPy 不提供精度高於 C 的 long double
的 dtype;特別是,128 位元 IEEE 四倍精度資料型別 (FORTRAN 的 REAL*16
) 不可用。
為了實現高效的記憶體對齊,numpy.longdouble
通常會填充零位元,填充到 96 或 128 位元。哪個更有效取決於硬體和開發環境;通常在 32 位元系統上,它們會填充到 96 位元,而在 64 位元系統上,它們通常會填充到 128 位元。np.longdouble
會填充到系統預設值;np.float96
和 np.float128
提供給想要特定填充的使用者。儘管名稱如此,np.float96
和 np.float128
僅提供與 np.longdouble
相同的精度,即在大多數 x86 機器上為 80 位元,在標準 Windows 建置中為 64 位元。
請注意,即使 numpy.longdouble
提供比 python float
更高的精度,也很容易失去額外的精度,因為 python 通常會強制值通過 float
。例如,%
格式化運算子要求將其引數轉換為標準 python 型別,因此即使要求許多小數位數,也無法保留擴展精度。使用值 1 + np.finfo(np.longdouble).eps
測試您的程式碼可能很有用。