数据类型#
另请参阅
数组类型和类型之间转换#
相对于 Python,NumPy 支持更多数值类型。本节显示可用類型以及如何修改数组的数据类型。
NumPy 数值类型是 numpy.dtype
(数据类型)对象的实例,每个实例都有自己独特的特性。在使用 import numpy as np
导入 NumPy 之后,即可使用 numpy 顶级 API 中的标量类型(例如,numpy.bool
、numpy.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 知道 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 还支持存储 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 类型 |
说明 |
---|---|---|---|
不适用 |
|
存储为字节的布尔值(真或假)。 |
|
|
平台定义的具有 8 位的整数类型。 |
||
|
平台定义的具有 8 位、无符号的整数类型。 |
||
|
平台定义的具有 16 位的整数类型。 |
||
|
平台定义的具有 16 位、无符号的整数类型。 |
||
|
平台定义的 32 位无符号整数类型。 |
||
|
平台定义的 32 位无符号整数类型。 |
||
不适用 |
|
平台定义的整数 |
|
不适用 |
|
平台定义的整数类型,能够存储最大分配尺寸。 |
|
不适用 |
|
|
保证持有指针。仅限字符代码(Python 和 C)。 |
不适用 |
|
|
保证持有指针。仅限字符代码(Python 和 C)。 |
|
平台定义的至少为 32 位的整数类型。 |
||
|
平台定义的至少为 32 位的无符号整数类型。 |
||
不适用 |
|
平台定义的至少为 64 位的整数类型。 |
|
不适用 |
|
平台定义的至少为 64 位的无符号整数类型。 |
|
不适用 |
半精度浮点数:符号位、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)
-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
扩展精度#
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
测试你的代码可能非常有帮助。