数据类型#

另请参阅

数据类型对象

数组类型和类型间的转换#

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

NumPy 的数值类型是 numpy.dtype (数据类型) 对象的实例,每种对象都有其独特的特性。一旦使用 import numpy as np 导入 NumPy,您就可以使用 NumPy 顶级 API 中的标量类型来创建具有指定 dtype 的数组,例如 numpy.boolnumpy.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)

有关指定和构造数据类型对象的更多信息,包括如何指定字节顺序等参数,请参阅 指定和构造数据类型

要确定数组的类型,请查看 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

要转换数组的类型,请使用 .astype() 方法。例如:

>>> z.astype(np.float64)                 
array([0.,  1.,  2.])

请注意,在上面,我们也可以使用 *Python* 的 float 对象作为 dtype,而不是 numpy.float64。NumPy 知道 int 指的是 numpy.int_bool 指的是 numpy.boolfloat 指的是 numpy.float64,而 complex 指的是 numpy.complex128。其他数据类型没有 Python 等效类型。

有时转换会发生溢出,例如将 numpy.int64 值 300 转换为 numpy.int8。NumPy 遵循 C 语言的类型转换规则,因此该值将溢出并变为 44 (300 - 256)。如果您希望避免此类溢出,可以使用 casting 参数的 same_value 来指定溢出操作失败(另请参阅 溢出错误)。

>>> z.astype(np.float64, casting="same_value")   
array([0.,  1.,  2.])

数值数据类型#

有 5 种基本数值类型,分别代表布尔型(bool)、整数型(int)、无符号整数型(uint)、浮点型(float)和复数型(complex)。基本数值类型名称与数字位数结合定义了一个具体类型。位数是指在内存中表示单个值所需的位数。例如,numpy.float64 是一种 64 位浮点数据类型。某些类型,如 numpy.int_numpy.intp,其位数会因平台而异(例如 32 位 vs 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')

此处,数据类型被检测为 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'

每个条目都用两个额外的空字节填充。但请注意,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 类型更自然),NumPy 还提供了与平台 C 类型对应的类型别名。某些 dtypes 带有尾随下划线,以避免与内置 Python 类型名称混淆,例如 numpy.bool_

标准 Python API 名称

Python API “类 C”名称

实际 C 类型

描述

numpy.boolnumpy.bool_

N/A

bool(定义于 stdbool.h

布尔值(True 或 False),存储为字节。

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

N/A

ssize_t/Py_ssize_t

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

numpy.uintp

N/A

size_t

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

N/A

'p'

intptr_t

保证可以存储指针。仅用于字符代码(Python 和 C)。

N/A

'P'

uintptr_t

保证可以存储无符号指针。仅用于字符代码(Python 和 C)。

numpy.int32numpy.int64

numpy.long

long

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

numpy.uint32numpy.uint64

numpy.ulong

unsigned long

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

N/A

numpy.longlong

long long

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

N/A

numpy.ulonglong

unsigned long long

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

numpy.float16

numpy.half

N/A

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

numpy.float32

numpy.single

float

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

numpy.float64

numpy.double

double

平台定义的双精度浮点数:通常是符号位,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 更高精度的 dtype;特别是,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 是很有用的。