数据类型#

另请参阅

数据类型对象

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

相对于 Python,NumPy 支持更多数值类型。本节显示可用類型以及如何修改数组的数据类型。

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.])

请注意,对于上面的情况,我们可以使用 numpy.float64 而非 Python 浮点对象作为 dtype。NumPy 知道 intnumpy.int_bool 表示 numpy.boolfloatnumpy.float64complexnumpy.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')

这里,数据类型检测为一个 unicode 字符串,最大长度为 6 个代码点,足够储存两个项而无需截断。如果我们指定更短或更长的数据类型,则字符串将被截断或零填充,以使其适合指定的宽度

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

如果我们使用 bytes 数据类型并将要求 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 类型相对应的类型别名。一些 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

不适用

半精度浮点数:符号位、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)
-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

扩展精度#

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 测试你的代码可能非常有帮助。