NumPy
基础1本章示例代码中的输入和输出均来自IPython会话。
NumPy
数组对象NumPy
中的ndarray
是一个多维数组对象,该对象由两部分组成:
大部分的数组操作仅仅修改修改元数据部分,而不改变底层的实际数据。
在第1章中,我们已经知道如何使用arange
函数创建数组。实际上,当时创建的数组只是包含一组数字的一维数组,而ndarray
支持更高的维度。
NumPy
数组一般是同质的
(但有一种特殊的数组类型例外,它是异质的),即数组中的所有元素类型必须是一致的。这样有一个好处:如果我们知道数组中的元素均为同一类型,该数组所需的存储空间就很容易确定下来。
与Python中一样,NumPy数组的下标也是从0开始的。数组元素的数据类型用专门的对象表示。
我们再次用arange
函数创建数组,并获取其数据类型:
In [1]: import numpy as np
In [2]: a = np.arange(5)
In [3]: a.dtype
Out[3]: dtype('int32')
数组a的数据类型为int32(在我的机器上是这样),当然如果你使用64位的Python,得到的结果可能是int64。不论是哪种情形,该数组的数据类型都是整数(64位或32位)。
除了数据类型,数组的维度也是重要的属性。
第1章中的例子演示了怎样创建一个向量(即一维的NumPy数组)。向量在数学中很常用,但大部分情况下,我们需要更高维的对象。先来确定一下刚刚所创建向量的维度:
In [4]: a
Out[4]: array([0, 1, 2, 3, 4])
In [5]: a.shape
Out[5]: (5,)
正如你所看到的,这是一个包含5个元素的向量,取值分别为0~4
的整数。数组的shape
属性返回一个元组(tuple),元组中的元素即为NumPy
数组每一个维度上的大小。上面例子中的数组是一维的,因此元组中只有一个元素。
既然我们已经知道如何创建向量,现在可以试着创建多维的NumPy
数组,并查看其维度了。
(1) 创建一个多维数组。
(2) 显示该数组的维度。
In [6]: m = np.array([np.arange(2),np.arange(2)])
In [7]: m
Out[7]:
array([[0, 1],
[0, 1]])
In [8]: m.shape
Out[8]: (2, 2)
我们将arange
函数创建的数组作为列表元素,把这个列表作为参数传给array
函数,从而创建了一个2×2的数组,而且没有出现任何报错信息。
array
函数可以依据给定的对象生成数组。给定的对象应是类数组,如Python中的列表。在上面的例子中,我们传给array
函数的对象是一个NumPy
数组的列表。像这样的类数组对象是array
函数的唯一必要参数,其余的诸多参数均为有默认值的可选参数。
问题1 ndarray对象的维度属性是以下列哪种方式存储的?
(1) 逗号隔开的字符串
(2) Python列表(list)
(3) Python元组(tuple)
有时候,我们需要选取数组中的某个特定元素。首先还是创建一个2×2的多维数组:
In [9]: a = np.array([[1,2],[3,4]])
In [10]: a
Out[10]:
array([[1, 2],
[3, 4]])
在创建这个多维数组时,我们给array
函数传递的对象是一个嵌套的列表。现在来依次选取该数组中的元素。记住,数组的下标是从0开始的。
In [11]: a[0,0]
Out[11]: 1
In [12]: a[0,1]
Out[12]: 2
In [13]: a[1,0]
Out[13]: 3
In [14]: a[1,1]
Out[14]: 4
是的,从数组中选取元素就是这么简单。对于数组a,只需要用a[m,n]选取各数组元素,其
中m和n为元素下标,对应的位置如下表所示。
NumPy
数据类型Python支持的数据类型有整型、浮点型以及复数型,但这些类型不足以满足科学计算的需求,因此NumPy
添加了很多其他的数据类型。在实际应用中,我们需要不同精度的数据类型,它们占用的内存空间也是不同的。在NumPy
中,**大部分数据类型名是以数字结尾的,这个数字表示其在内存中占用的位数。**下面的表格(整理自NumPy用户手册)列出了NumPy
中支持的数据类型。
每一种数据类型均有对应的类型转换函数:
In [15]: np.float64(42)
Out[15]: 42.0
In [17]: np.int8(42.0)
Out[17]: 42
In [18]: np.bool(42)
C:\Users\administrator\AppData\Local\Programs\Python\Python37\Scripts\ipython:1: Deprecatio
nWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence thi
s warning, use `bool` by itself. Doing this will not modify any behavior and is
safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdo
cs/release/1.20.0-notes.html#deprecations
Out[18]: True
In [20]: np.float(True)
C:\Users\administrator\AppData\Local\Programs\Python\Python37\Scripts\ipython:1: Deprecatio
nWarning: `np.float` is a deprecated alias for the builtin `float`. To silence t
his warning, use `float` by itself. Doing this will not modify any behavior and
is safe. If you specifically wanted the numpy scalar type, use `np.float64` here
.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdo
cs/release/1.20.0-notes.html#deprecations
Out[20]: 1.0
注:在1.20版本中np.bool
、 np.float
都是对应系统函数的别名
在NumPy中,许多函数的参数中可以指定数据类型,通常这个参数是可选的:
In [23]: np.arange(7, dtype=np.uint16)
Out[23]: array([0, 1, 2, 3, 4, 5, 6], dtype=uint16)
需要注意的是,复数是不能转换为整数的,这将触发TypeError错误:
In [24]: np.int(42.0+1.j)
C:\Users\administrator\AppData\Local\Programs\Python\Python37\Scripts\ipython:1: Deprecatio
nWarning: `np.int` is a deprecated alias for the builtin `int`. To silence this
warning, use `int` by itself. Doing this will not modify any behavior and is saf
e. When replacing `np.int`, you may wish to use e.g. `np.int64` or `np.int32` to
specify the precision. If you wish to review your current use, check the releas
e note link for additional information.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdo
cs/release/1.20.0-notes.html#deprecations
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-24-4b5b3467cc1e> in <module>
----> 1 np.int(42.0+1.j)
TypeError: can't convert complex to int
同样,复数也不能转换为浮点数。不过,浮点数却可以转换为复数,例如complex(1.0)
。注意,有j
的部分为复数的虚部。
数据类型对象是numpy.dtype
类的实例。如前所述,NumPy
数组是有数据类型的,更确切地说,NumPy
数组中的每一个元素均为相同的数据类型。数据类型对象可以给出单个数组元素在内存中占用的字节数,即dtype
类的itemsize
属性:
In [25]: a.dtype.itemsize
Out[25]: 4
NumPy
可以使用字符编码来表示数据类型,这是为了兼容NumPy
的前身Numeric
。我不推荐使用字符编码,但有时会用到,因此下面还是列出了字符编码的对应表。读者应该优先使用dtype
对象来表示数据类型,而不是这些字符编码。
In [26]: np.arange(7, dtype='f')
Out[26]: array([0., 1., 2., 3., 4., 5., 6.], dtype=float32)
与此类似,还可以创建一个复数数组:
In [28]: np.arange(7, dtype='D')
Out[28]: array([0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j])
我们有很多种自定义数据类型的方法,以浮点型为例。
In [3]: np.dtype(float)
Out[3]: dtype('float64')
In [4]: np.dtype('f')
Out[4]: dtype('float32')
In [5]: np.dtype('d')
Out[5]: dtype('float64')
In [6]: np.dtype('f2')
Out[6]: dtype('float16')
sctypeDict.keys()
中找到:In [7]: np.sctypeDict.keys()
Out[7]: dict_keys(['?', 0, 'byte', 'b', 1, 'ubyte', 'B', 2, 'short', 'h', 3, 'ushort', 'H', 4, 'i', 5, 'uint', 'I', 6, 'intp', 'p', 9, 'uintp', 'P', 10, 'long', 'l', 7, 'L', 8, 'longlong', 'q', 'ulonglong', 'Q', 'half', 'e', 23, 'f', 11, 'double', 'd', 12, 'longdouble', 'g', 13, 'cfloat', 'F', 14, 'cdouble', 'D', 15, 'clongdouble', 'G', 16, 'O', 17, 'S', 18, 'unicode', 'U', 19, 'void', 'V', 20, 'M', 21, 'm', 22, 'bool8', 'b1', 'int64', 'i8', 'uint64', 'u8', 'float16', 'f2', 'float32', 'f4', 'float64', 'f8', 'complex64', 'c8', 'complex128', 'c16', 'object0', 'bytes0', 'str0', 'void0', 'datetime64', 'M8', 'timedelta64', 'm8', 'Bytes0', 'Datetime64', 'Str0', 'Uint64', 'int32', 'i4', 'uint32', 'u4', 'int16', 'i2', 'uint16', 'u2', 'int8', 'i1', 'uint8', 'u1', 'complex_', 'int0', 'uint0', 'single', 'csingle', 'singlecomplex', 'float_', 'intc', 'uintc', 'int_', 'longfloat', 'clongfloat', 'longcomplex', 'bool_', 'bytes_', 'string_', 'str_', 'unicode_', 'object_', 'int', 'float', 'complex', 'bool', 'object', 'str', 'bytes', 'a'])
In [8]: np.sctypeDict.items()
Out[8]: dict_items([('?', <class 'numpy.bool_'>), (0, <class 'numpy.bool_'>), ('byte', <class 'numpy.int8'>), ('b', <class 'numpy.int8'>), (1, <class 'numpy.int8'>), ('ubyte', <class 'numpy.uint8'>), ('B', <class 'numpy.uint8'>), (2, <class 'numpy.uint8'>), ('short', <class 'numpy.int16'>), ('h', <class 'numpy.int16'>), (3, <class 'numpy.int16'>), ('ushort', <class 'numpy.uint16'>), ('H', <class 'numpy.uint16'>), (4, <class 'numpy.uint16'>), ('i', <class 'numpy.intc'>), (5, <class 'numpy.intc'>), ('uint', <class 'numpy.uint32'>), ('I', <class 'numpy.uintc'>), (6, <class 'numpy.uintc'>), ('intp', <class 'numpy.int64'>), ('p', <class 'numpy.int64'>), (9, <class 'numpy.int64'>), ('uintp', <class 'numpy.uint64'>), ('P', <class 'numpy.uint64'>), (10, <class 'numpy.uint64'>), ('long', <class 'numpy.int32'>), ('l', <class 'numpy.int32'>), (7, <class 'numpy.int32'>), ('L', <class 'numpy.uint32'>), (8, <class 'numpy.uint32'>), ('longlong', <class 'numpy.int64'>), ('q', <class 'numpy.int64'>), ('ulonglong', <class 'numpy.uint64'>), ('Q', <class 'numpy.uint64'>), ('half', <class 'numpy.float16'>), ('e', <class 'numpy.float16'>), (23, <class 'numpy.float16'>), ('f', <class 'numpy.float32'>), (11, <class 'numpy.float32'>), ('double', <class 'numpy.float64'>), ('d', <class 'numpy.float64'>), (12, <class 'numpy.float64'>), ('longdouble', <class 'numpy.longdouble'>), ('g', <class 'numpy.longdouble'>), (13, <class 'numpy.longdouble'>), ('cfloat', <class 'numpy.complex128'>), ('F', <class 'numpy.complex64'>), (14, <class 'numpy.complex64'>), ('cdouble', <class 'numpy.complex128'>), ('D', <class 'numpy.complex128'>), (15, <class 'numpy.complex128'>), ('clongdouble', <class 'numpy.clongdouble'>), ('G', <class 'numpy.clongdouble'>), (16, <class 'numpy.clongdouble'>), ('O', <class 'numpy.object_'>), (17, <class 'numpy.object_'>), ('S', <class 'numpy.bytes_'>), (18, <class 'numpy.bytes_'>), ('unicode', <class 'numpy.str_'>), ('U', <class 'numpy.str_'>), (19, <class 'numpy.str_'>), ('void', <class 'numpy.void'>), ('V', <class 'numpy.void'>), (20, <class 'numpy.void'>), ('M', <class 'numpy.datetime64'>), (21, <class 'numpy.datetime64'>), ('m', <class 'numpy.timedelta64'>), (22, <class 'numpy.timedelta64'>), ('bool8', <class 'numpy.bool_'>), ('b1', <class 'numpy.bool_'>), ('int64', <class 'numpy.int64'>), ('i8', <class 'numpy.int64'>), ('uint64', <class 'numpy.uint64'>), ('u8', <class 'numpy.uint64'>), ('float16', <class 'numpy.float16'>), ('f2', <class 'numpy.float16'>), ('float32', <class 'numpy.float32'>), ('f4', <class 'numpy.float32'>), ('float64', <class 'numpy.float64'>), ('f8', <class 'numpy.float64'>), ('complex64', <class 'numpy.complex64'>), ('c8', <class 'numpy.complex64'>), ('complex128', <class 'numpy.complex128'>), ('c16', <class 'numpy.complex128'>), ('object0', <class 'numpy.object_'>), ('bytes0', <class 'numpy.bytes_'>), ('str0', <class 'numpy.str_'>), ('void0', <class 'numpy.void'>), ('datetime64', <class 'numpy.datetime64'>), ('M8', <class 'numpy.datetime64'>), ('timedelta64', <class 'numpy.timedelta64'>), ('m8', <class 'numpy.timedelta64'>), ('Bytes0', <class 'numpy.bytes_'>), ('Datetime64', <class 'numpy.datetime64'>), ('Str0', <class 'numpy.str_'>), ('Uint64', <class 'numpy.uint64'>), ('int32', <class 'numpy.int32'>), ('i4', <class 'numpy.int32'>), ('uint32', <class 'numpy.uint32'>), ('u4', <class 'numpy.uint32'>), ('int16', <class 'numpy.int16'>), ('i2', <class 'numpy.int16'>), ('uint16', <class 'numpy.uint16'>), ('u2', <class 'numpy.uint16'>), ('int8', <class 'numpy.int8'>), ('i1', <class 'numpy.int8'>), ('uint8', <class 'numpy.uint8'>), ('u1', <class 'numpy.uint8'>), ('complex_', <class 'numpy.complex128'>), ('int0', <class 'numpy.int64'>), ('uint0', <class 'numpy.uint64'>), ('single', <class 'numpy.float32'>), ('csingle', <class 'numpy.complex64'>), ('singlecomplex', <class 'numpy.complex64'>), ('float_', <class 'numpy.float64'>), ('intc', <class 'numpy.intc'>), ('uintc', <class 'numpy.uintc'>), ('int_', <class 'numpy.int32'>), ('longfloat', <class 'numpy.longdouble'>), ('clongfloat', <class 'numpy.clongdouble'>), ('longcomplex', <class 'numpy.clongdouble'>), ('bool_', <class 'numpy.bool_'>), ('bytes_', <class 'numpy.bytes_'>), ('string_', <class 'numpy.bytes_'>), ('str_', <class 'numpy.str_'>), ('unicode_', <class 'numpy.str_'>), ('object_', <class 'numpy.object_'>), ('int', <class 'numpy.int32'>), ('float', <class 'numpy.float64'>), ('complex', <class 'numpy.complex128'>), ('bool', <class 'numpy.bool_'>), ('object', <class 'numpy.object_'>), ('str', <class 'numpy.str_'>), ('bytes', <class 'numpy.bytes_'>), ('a', <class 'numpy.bytes_'>)])
dtype
类的属性dtype
类有很多有用的属性。例如,我们可以获取数据类型的字符编码:
In [11]: t = np.dtype('float64')
In [12]: t.char
Out[12]: 'd'
type
属性对应于数组元素的数据类型:
In [13]: t.type
Out[13]: numpy.float64
str
属性可以给出数据类型的字符串表示,该字符串的首个字符表示字节序(endianness),后面如果还有字符的话,将是一个字符编码,接着一个数字表示每个数组元素存储所需的字节数。
这里,字节序是指位长为32或64的字(word)存储的顺序,包括大端序(big-endian)和小端序(little-endian)。大端序是将最高位字节存储在最低的内存地址处,用>表示;与之相反,小端序是将最低位字节存储在最低的内存地址处,用<
表示:
In [14]: t.str
Out[14]: '
自定义数据类型是一种异构数据类型,可以当做用来记录电子表格或数据库中一行数据的结构。作为示例,我们将创建一个存储商店库存信息的数据类型。其中,我们用一个长度为40个字符的字符串来记录商品名称,用一个32位的整数来记录商品的库存数量,最后用一个32位的单精度浮点数来记录商品价格。下面是具体的步骤。
In [15]: t = np.dtype([('name', np.str_, 40), ('numitems',np.int32), ('price',np.fl
...: oat32)])
In [16]: t
Out[16]: dtype([('name', '), ('numitems', '), ('price', ')])
In [21]: t['name']
Out[21]: dtype(')
**在用array
函数创建数组时,如果没有在参数中指定数据类型,将默认为浮点数类型。**而现在,我们想要创建自定义数据类型的数组,就必须在参数中指定数据类型,否则将触发TypeError
错误:
In [24]: itemz = np.array([('Meaning of life DVD', 42, 3.14), ('Butter', 13, 2.72)], dtype=t)
In [25]: itemz[1]
Out[25]: ('Butter', 13, 2.72)
刚才做了些什么我们创建了一种自定义的异构数据类型,该数据类型包括一个用字符串记录的名字、一个用整数记录的数字以及一个用浮点数记录的价格。