此系列文章参照NumPy
官方用户指南1.2.1
版本进行翻译、解读(直译说不清楚的可能会加入自己的一些理解)。
NumPy
简介
NumPy
概述
NumPy
是Python科学计算的基础包。它提供了多维数组对象及其各种派生对象(如掩码数组和矩阵),它还提供了用于数组快速操作的各种API,包括数学、逻辑、形状操作、排序、选择、输入输出、离散傅立叶变换(DFT
)、基本线性代数,基本统计运算和随机模拟等。
ndarray
对象是NumPy
包的核心。ndarray
对象封装了相同数据类型的 n
维数组,出于性能方面的考虑,ndarray
许多操作都是编译之后执行的。
NumPy
数组和标准Python序列之间有几个重要的区别:
NumPy
数组在创建时具有固定的大小,而Python列表可以动态增长。更改ndarray
对象的大小将创建一个新数组并删除原来的数组。NumPy
数组的元素都需要具有相同的数据类型,因此在内存中的大小相同。 例外情况:数组元素为Python对象时(包含NumPy
对象),允许作为元素的数组大小不同。NumPy
数组更便于对大量数据进行高级数学和其他类型的操作。NumPy
数组与Python内置序列相比执行效率更高,且使用的代码更少。- 越来越多Python科学计算和数学软件包使用
NumPy
数组, 虽然这些工具通常都支持Python内置序列作为参数,但它们在处理之前会还是会将输入的数组转换为NumPy
数组,而且输出也通常为NumPy
数组。换言之,为了高效地使用当今Python科学/数学工具(大部分的科学计算工具),只知道如何使用Python内置序列是不够的,还需要知道如何使用NumPy
数组。
序列的大小和速度在科学计算中尤为重要。例如,我们需要将1维序列中的每个元素与相同长度的另一个序列中的相应元素相乘。假设数据存储在Python 列表 a
和 b
中,通过迭代每个元素实现的概要代码如下:
c = []
for i in range(len(a)):
c.append(a[i]*b[i])
上述代码确实符合要求,但是如果a
和b
都包含数百万个数字,使用Python循环的效率将非常低。我们可以通过C语言更快地完成相同任务,概要代码如下:
for (i = 0; i < rows; i++): {
c[i] = a[i]*b[i];
}
基于C语言的示例代码的确比Python代码效率更高,但牺牲了用Python编写代码所带来的好处。
更进一步,如果数据增加了维度,假设数组为二维数组,上面的C语言代码会扩展为:
for (i = 0; i < rows; i++): {
for (j = 0; j < columns; j++): {
c[i][j] = a[i][j]*b[i][j];
}
}
NumPy
提供了两全其美的解决方案:当涉及 ndarray
操作时,默认进行逐元素操作,但是逐元素操作由NumPy
预编译的C代码执行。使用NumPy
实现的概要代码如下:
c = a * b
既具有C代码的运行效率,又具有Python代码的简洁性!NumPy
的用法更为简单。
上述代码展示了NumPy
的两个特性:向量化和广播,它们也是NumPy
的大部分功能的基础。
为什么 NumPy
这么快?
向量化说明代码中没有任何显式的循环、索引等,这当然是预编译的C代码隐式优化的结果。向量化代码有许多优点,其中包括:
- 向量化代码更简洁,更易于阅读
- 更少的代码行通常意味着更少的错误
- 代码更接近于标准的数学符号(通常更容易正确编码数学结构)
- 向量化代码更 Pythonic。如果没有向量化,代码就会充斥着低效且难以阅读的
for
循环。
广播是用于描述隐式逐元素操作行为的术语。通常,NumPy
中的所有操作,不止算术运算,还包括逻辑运算、位运算、函数式编程等都可以通过这种隐式的逐元素方式实现。此外,在上面的示例中,a
并且b
可以是相同形状的多维数组,或者一个标量、一个数组,甚至两者是具有不同形状的两个数组,条件是较小的数组可以“扩展”到更大的形状,由此产生的广播是明确的。
还有谁在使用 NumPy
?
NumPy
完全支持面向对象的方法,再夸奖一次 ndarray
!
ndarray
是一个类,拥有许多方法和属性。
最顶级的NumPy
命名空间中的某些函数镜像了ndarray
的许多方法。
PS:因此,对于同一数组操作,既可以使用ndarray
对象调用某些方法,也可以使用NumPy
模块调用某些函数。
程序员在编码时可选用自己喜欢的范式。这种灵活性使NumPy
数组方言和NumPy
ndarray 类成为Python多维数据交换的事实标准语言。
源文档
https://numpy.org/doc/stable/user/whatisnumpy.html