NumPy,或 Numerical Python,是一个库,可以轻松地在 Python 中对线性序列和矩阵执行统计和集合运算。它比 Python 列表快几个数量级,我在关于 Python 数据类型的笔记中介绍了这一点。NumPy 在数据分析和科学计算中非常常用。
我将介绍 NumPy 的安装,然后介绍 NumPy 数组的创建、读取和排序。NumPy 数组也称为 ndarray,是 n 维数组 的缩写。
安装 NumPy
使用 pip
安装 NumPy 包非常简单,就像安装任何其他包一样安装即可。
pip install numpy
安装 NumPy 包后,只需将其导入到您的 Python 文件中即可。
import numpy as np
将 numpy
导入为 np
是一种标准约定,但您可以使用您想要的任何其他别名来代替使用 np
。
为什么要使用 NumPy?因为它比 Python 列表快几个数量级。
在处理大量值时,NumPy 比普通的 Python 列表快几个数量级。为了准确了解它有多快,我将首先测量在普通 Python 列表上执行 min()
和 max()
操作所需的时间。
我将首先创建一个包含 999,999,999 个项目的 Python 列表。
>>> my_list = range(1, 1000000000)
>>> len(my_list)
999999999
现在我将测量找到此列表中最小值的时间。
>>> start = time.time()
>>> min(my_list)
1
>>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000))
Time elapsed in milliseconds: 27007.00879096985
这大约花费了 27,007 毫秒,即大约 27 秒。这太长了。现在我将尝试找到查找最大值的时间。
>>> start = time.time()
>>> max(my_list)
999999999
>>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000))
Time elapsed in milliseconds: 28111.071348190308
这大约花费了 28,111 毫秒,即大约 28 秒。
现在我将尝试找到使用 NumPy 查找最小值和最大值的时间。
>>> my_list = np.arange(1, 1000000000)
>>> len(my_list)
999999999
>>> start = time.time()
>>> my_list.min()
1
>>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000))
Time elapsed in milliseconds: 1151.1778831481934
>>>
>>> start = time.time()
>>> my_list.max()
999999999
>>> print('Time elapsed in milliseconds: ' + str((time.time() - start) * 1000))
Time elapsed in milliseconds: 1114.8970127105713
查找最小值花费了大约 1151 毫秒,查找最大值花费了 1114 毫秒。这些大约是 1 秒。
如您所见,使用 NumPy 将查找大约十亿个值的列表的最小值和最大值的时间从大约 28 秒减少到 1 秒。这就是 NumPy 的强大之处。
使用 Python 列表创建 ndarray
在 NumPy 中有几种创建 ndarray 的方法。
您可以使用元素列表来创建 ndarray。
>>> my_ndarray = np.array([1, 2, 3, 4, 5])
>>> print(my_ndarray)
[1 2 3 4 5]
定义上述 ndarray 后,我将检查一些内容。首先,上面定义的变量的类型是 numpy.ndarray
。这是所有 NumPy ndarray 的类型。
>>> type(my_ndarray)
<class 'numpy.ndarray'>
这里需要注意的另一件事是 shape。ndarray 的 shape 是 ndarray 每个维度的长度。如您所见,my_ndarray
的 shape 是 (5,)
。这意味着 my_ndarray
包含一个维度,其中包含 5 个元素。
>>> np.shape(my_ndarray)
(5,)
数组中的维度数称为其 rank。因此,上面的 ndarray 的 rank 为 1。
我将定义另一个 ndarray my_ndarray2
作为多维 ndarray。那么它的 shape 将是什么?请参见下文。
>>> my_ndarray2 = np.array([(1, 2, 3), (4, 5, 6)])
>>> np.shape(my_ndarray2)
(2, 3)
这是一个 rank 为 2 的 ndarray。另一个要检查的属性是 dtype
,即数据类型。检查我们的 ndarray 的 dtype
,我们得到以下结果
>>> my_ndarray.dtype
dtype('int64')
int64
表示我们的 ndarray 由 64 位整数组成。NumPy 无法创建混合类型的 ndarray,并且必须仅包含一种类型的元素。如果您定义一个包含混合元素类型的 ndarray,NumPy 会自动将所有元素强制转换为可以包含所有元素的最高可用元素类型。
例如,创建 int
和 float
的混合将创建一个 float64
ndarray。
>>> my_ndarray2 = np.array([1, 2.0, 3])
>>> print(my_ndarray2)
[1. 2. 3.]
>>> my_ndarray2.dtype
dtype('float64')
此外,将其中一个元素设置为 string
将创建 dtype
等于 <U21
的字符串 ndarray,这意味着我们的 ndarray 包含 unicode 字符串。
>>> my_ndarray2 = np.array([1, '2', 3])
>>> print(my_ndarray2)
['1' '2' '3']
>>> my_ndarray2.dtype
dtype('<U21')
size
属性将显示 ndarray 中存在的元素总数。
>>> my_ndarray = np.array([1, 2, 3, 4, 5])
>>> my_ndarray.size
5
使用 NumPy 方法创建 ndarray
如果您不想直接使用列表创建 ndarray,可以使用几种 NumPy 方法来创建 ndarray。
您可以使用 np.zeros()
创建一个充满零的 ndarray。它将 shape 作为参数,shape 是包含行数和列数的列表。它还可以接受可选的 dtype
参数,该参数是 ndarray 的数据类型。
>>> my_ndarray = np.zeros([2,3], dtype=int)
>>> print(my_ndarray)
[[0 0 0]
[0 0 0]]
您可以使用 np.ones()
创建一个充满一的 ndarray。
>>> my_ndarray = np.ones([2,3], dtype=int)
>>> print(my_ndarray)
[[1 1 1]
[1 1 1]]
您可以使用 np.full()
用特定值填充 ndarray。
>>> my_ndarray = np.full([2,3], 10, dtype=int)
>>> print(my_ndarray)
[[10 10 10]
[10 10 10]]
您可以使用 np.eye()
创建一个单位矩阵/ndarray,它是一个沿主对角线全为 1 的方阵。方阵是行数和列数相同的矩阵。
>>> my_ndarray = np.eye(3, dtype=int)
>>> print(my_ndarray)
[[1 0 0]
[0 1 0]
[0 0 1]]
您可以使用 np.diag()
创建一个矩阵,该矩阵沿对角线具有指定的值,其余部分为零。
>>> my_ndarray = np.diag([10, 20, 30, 40, 50])
>>> print(my_ndarray)
[[10 0 0 0 0]
[ 0 20 0 0 0]
[ 0 0 30 0 0]
[ 0 0 0 40 0]
[ 0 0 0 0 50]]
您可以使用 np.arange()
创建具有特定值范围的 ndarray。它通过指定整数的起始和结束(不包括)范围以及步长来使用。
>>> my_ndarray = np.arange(1, 20, 3)
>>> print(my_ndarray)
[ 1 4 7 10 13 16 19]
读取 ndarray
可以使用索引、切片或布尔索引来读取 ndarray 的值。
使用索引读取 ndarray
在索引中,您可以使用 ndarray 元素的整数索引来读取值,就像读取 Python 列表一样。与 Python 列表一样,索引从零开始。
例如,在如下定义的 ndarray 中
>>> my_ndarray = np.arange(1, 20, 3)
第四个值将是 my_ndarray[3]
,即 10
。最后一个值将是 my_ndarray[-1]
,即 19
。
>>> my_ndarray = np.arange(1, 20, 3)
>>> print(my_ndarray[0])
1
>>> print(my_ndarray[3])
10
>>> print(my_ndarray[-1])
19
>>> print(my_ndarray[5])
16
>>> print(my_ndarray[6])
19
使用切片读取 ndarray
您还可以使用切片来读取 ndarray 的块。切片的工作原理是使用冒号 (:
) 运算符指定起始索引和结束索引。然后,Python 将获取该起始索引和结束索引之间的 ndarray 切片。
>>> print(my_ndarray[:])
[ 1 4 7 10 13 16 19]
>>> print(my_ndarray[2:4])
[ 7 10]
>>> print(my_ndarray[5:6])
[16]
>>> print(my_ndarray[6:7])
[19]
>>> print(my_ndarray[:-1])
[ 1 4 7 10 13 16]
>>> print(my_ndarray[-1:])
[19]
切片创建 ndarray 的引用或视图。这意味着修改切片中的值也会更改原始 ndarray 的值。
例如
>>> my_ndarray[-1:] = 100
>>> print(my_ndarray)
[ 1 4 7 10 13 16 100]
对于 rank 大于 1 的 ndarray 的切片,可以使用 [行起始索引:行结束索引, 列起始索引:列结束索引]
语法。
>>> my_ndarray2 = np.array([(1, 2, 3), (4, 5, 6)])
>>> print(my_ndarray2)
[[1 2 3]
[4 5 6]]
>>> print(my_ndarray2[0:2,1:3])
[[2 3]
[5 6]]
使用布尔索引读取 ndarray
读取 ndarray 的另一种方法是使用布尔索引。在此方法中,您在方括号内指定一个过滤条件,并返回与该条件匹配的 ndarray 部分。
例如,要获取 ndarray 中所有大于 5 的值,您可以将布尔索引操作指定为 my_ndarray[my_ndarray > 5]
。此操作将返回一个包含所有大于 5 的值的 ndarray。
>>> my_ndarray = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> my_ndarray2 = my_ndarray[my_ndarray > 5]
>>> print(my_ndarray2)
[ 6 7 8 9 10]
例如,要获取 ndarray 中的所有偶数值,您可以按如下方式使用布尔索引操作
>>> my_ndarray2 = my_ndarray[my_ndarray % 2 == 0]
>>> print(my_ndarray2)
[ 2 4 6 8 10]
要获取所有奇数值,您可以使用以下方法
>>> my_ndarray2 = my_ndarray[my_ndarray % 2 == 1]
>>> print(my_ndarray2)
[1 3 5 7 9]
ndarray 的向量和标量算术
NumPy ndarray 允许向量和标量算术运算。在向量算术中,在两个 ndarray 之间执行元素级算术运算。在标量算术中,在 ndarray 和常数标量值之间执行算术运算。
考虑以下两个 ndarray。
>>> my_ndarray = np.array([1, 2, 3, 4, 5])
>>> my_ndarray2 = np.array([6, 7, 8, 9, 10])
如果将以上两个 ndarray 相加,将生成一个新的 ndarray,其中两个 ndarray 的每个元素都将被相加。例如,结果 ndarray 的第一个元素将是相加在一起的原始 ndarray 的第一个元素的结果,依此类推。
>>> print(my_ndarray2 + my_ndarray)
[ 7 9 11 13 15]
在这里,7
是 1
和 6
的和,它们是我相加在一起的 ndarray 的前两个元素。同样,15
是 5
和 10
的和,它们是最后一个元素。
考虑以下算术运算
>>> print(my_ndarray2 - my_ndarray)
[5 5 5 5 5]
>>>
>>> print(my_ndarray2 * my_ndarray)
[ 6 14 24 36 50]
>>>
>>> print(my_ndarray2 / my_ndarray)
[6. 3.5 2.66666667 2.25 2. ]
将标量值添加到 ndarray 具有类似的效果 - 标量值将添加到 ndarray 的所有元素。这称为 广播。
>>> print(my_ndarray + 10)
[11 12 13 14 15]
>>>
>>> print(my_ndarray - 10)
[-9 -8 -7 -6 -5]
>>>
>>> print(my_ndarray * 10)
[10 20 30 40 50]
>>>
>>> print(my_ndarray / 10)
[0.1 0.2 0.3 0.4 0.5]
排序 ndarray
有两种方法可以对 ndarray 进行排序 - 原地排序或异地排序。原地排序会排序并修改原始 ndarray,而异地排序将返回排序后的 ndarray,但不会修改原始 ndarray。我将尝试两个示例。
>>> my_ndarray = np.array([3, 1, 2, 5, 4])
>>> my_ndarray.sort()
>>> print(my_ndarray)
[1 2 3 4 5]
如您所见,sort()
方法会原地排序 ndarray 并修改原始数组。
还有另一种名为 np.sort()
的方法可以异地排序数组。
>>> my_ndarray = np.array([3, 1, 2, 5, 4])
>>> print(np.sort(my_ndarray))
[1 2 3 4 5]
>>> print(my_ndarray)
[3 1 2 5 4]
如您所见,np.sort()
方法返回排序后的 ndarray,但不会修改它。
结论
我已经介绍了关于 NumPy 和 ndarray 的很多内容。我谈到了创建 ndarray、读取 ndarray 的不同方法、基本的向量和标量算术以及排序。NumPy 还有很多内容值得探索,包括集合运算(如 union()
和 intersection()
)、统计运算(如 min()
和 max()
)等。
我希望上面演示的示例对您有所帮助。祝您探索 NumPy 愉快。
本文最初发表在作者的个人博客上,并已获得许可进行改编。
评论已关闭。