数据类型#

另请参阅

数据类型对象

数组类型和类型转换#

NumPy 支持比 Python 更多的数值类型。本节将展示可用的类型,以及如何修改数组的数据类型。

NumPy 数值类型是 numpy.dtype(数据类型)对象的实例,每个实例都具有独特的特性。一旦你使用 import numpy as np 导入 NumPy,你就可以使用 NumPy 顶级 API 中的标量类型(例如 numpy.boolnumpy.float32 等)创建具有指定 dtype 的数组。

这些标量类型可以作为许多 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.boolfloatnumpy.float64,而 complexnumpy.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 还支持存储 Unicode 字符串(通过 numpy.str_ dtype,字符代码为 U)、空终止字节序列(通过 numpy.bytes_,字符代码为 S)以及任意字节序列(通过 numpy.void,字符代码为 V)。

以上所有都是 固定宽度 数据类型。它们由一个宽度参数化,该宽度可以是字节或 Unicode 码点,数组中的单个数据元素必须适合此宽度。这意味着使用此 dtype 存储字节序列或字符串数组需要提前知道或计算最长文本或字节序列的大小。

例如,我们可以创建一个存储单词 "hello""world!" 的数组

>>> np.array(["hello", "world!"])
array(['hello', 'world!'], dtype='<U6')

此处数据类型被检测为最大长度为 6 个码点的 Unicode 字符串,足以存储两个条目而不会被截断。如果我们指定更短或更长的数据类型,字符串将被截断或用零填充以适应指定的宽度。

>>> 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'

每个条目都用两个额外的空字节填充。然而请注意,NumPy 无法区分有意存储的尾随空字节和填充的空字节。

>>> x = [b"hello\0\0", b"world"]
>>> a = np.array(x, dtype="S7")
>>> print(a[0])
b"hello"
>>> a[0] == x[0]
False

如果您需要存储并往返任何尾随空字节,您将需要使用非结构化的 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 类型

描述

numpy.boolnumpy.bool_

不适用

bool (定义于 stdbool.h)

布尔值(真或假),存储为字节。

numpy.int8

numpy.byte

signed char

平台定义的 8 位整数类型。

numpy.uint8

numpy.ubyte

unsigned char

平台定义的 8 位无符号整数类型。

numpy.int16

numpy.short

short

平台定义的 16 位整数类型。

numpy.uint16

numpy.ushort

unsigned short

平台定义的 16 位无符号整数类型。

numpy.int32

numpy.intc

int

平台定义的 32 位整数类型。

numpy.uint32

numpy.uintc

unsigned int

平台定义的 32 位无符号整数类型。

numpy.intp

不适用

ssize_t/Py_ssize_t

平台定义的 size_t 大小整数;例如用于表示大小。

numpy.uintp

不适用

size_t

平台定义的能够存储最大分配大小的整数类型。

不适用

'p'

intptr_t

保证可容纳指针。仅字符代码(Python 和 C)。

不适用

'P'

uintptr_t

保证可容纳指针。仅字符代码(Python 和 C)。

numpy.int32numpy.int64

numpy.long

long

平台定义的至少 32 位的整数类型。

numpy.uint32numpy.uint64

numpy.ulong

unsigned long

平台定义的至少 32 位的无符号整数类型。

不适用

numpy.longlong

long long

平台定义的至少 64 位的整数类型。

不适用

numpy.ulonglong

unsigned long long

平台定义的至少 64 位的无符号整数类型。

numpy.float16

numpy.half

不适用

半精度浮点数:1 位符号位,5 位指数位,10 位尾数位。

numpy.float32

numpy.single

float

平台定义的单精度浮点数:通常为 1 位符号位,8 位指数位,23 位尾数位。

numpy.float64

numpy.double

double

平台定义的双精度浮点数:通常为 1 位符号位,11 位指数位,52 位尾数位。

numpy.float96numpy.float128

numpy.longdouble

long double

平台定义的扩展精度浮点数。

numpy.complex64

numpy.csingle

float complex

复数,由两个单精度浮点数表示(实部和虚部)。

numpy.complex128

numpy.cdouble

double complex

复数,由两个双精度浮点数表示(实部和虚部)。

numpy.complex192numpy.complex256

numpy.clongdouble

long double complex

复数,由两个扩展精度浮点数表示(实部和虚部)。

由于其中许多类型具有平台相关的定义,因此提供了一组固定大小的别名(参见 大小别名)。

数组标量#

NumPy 通常将数组元素作为数组标量(具有相关 dtype 的标量)返回。数组标量与 Python 标量不同,但大多数情况下它们可以互换使用(主要例外是 Python v2.x 之前的版本,其中整数数组标量不能作为列表和元组的索引)。也有一些例外情况,例如当代码需要标量的非常特定的属性,或者它专门检查某个值是否是 Python 标量时。通常,通过使用相应的 Python 类型函数(例如 intfloatcomplexstr)将数组标量显式转换为 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.iinfonumpy.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 doubledouble(64 位)相同。NumPy 将编译器的 long double 作为 numpy.longdouble 提供(复数则为 np.clongdouble)。您可以通过 np.finfo(np.longdouble) 找出您的 NumPy 提供了什么。

NumPy 不提供比 C 的 long double 精度更高的数据类型;特别是,128 位 IEEE 四精度数据类型(FORTRAN 的 REAL*16)不可用。

为了实现高效的内存对齐,numpy.longdouble 通常用零位填充存储,填充到 96 位或 128 位。哪种效率更高取决于硬件和开发环境;通常在 32 位系统上填充到 96 位,而在 64 位系统上通常填充到 128 位。np.longdouble 填充到系统默认值;np.float96np.float128 是为需要特定填充的用户提供的。尽管名称如此,np.float96np.float128 提供的精度仅与 np.longdouble 相同,即在大多数 x86 机器上为 80 位,在标准 Windows 构建中为 64 位。

请注意,即使 numpy.longdouble 比 Python float 提供更高的精度,也很容易丢失额外的精度,因为 Python 通常会强制值通过 float 进行转换。例如,% 格式化运算符要求其参数转换为标准 Python 类型,因此即使请求很多小数位也无法保留扩展精度。使用值 1 + np.finfo(np.longdouble).eps 测试您的代码可能很有用。