本文是对官方文档的翻译,原文在此What is NumPy? - NumPy v1.14 Manualdocs.scipy.org
以下开始正文。
NumPy是Python的一个用于科学计算的基础包。它提供了多维数组对象,多种衍生的对象(例如隐藏数组和矩阵)和一个用于数组快速运算的混合的程序,包括数学,逻辑,排序,选择,I/O,离散傅立叶变换,基础线性代数,基础统计操作,随机模拟等等。
NumPy包的核心是ndarray对象。它封装了n维同类数组。很多运算是由编译过的代码来执行的,以此来提高效率。NumPy数组和标准Python序列有以下几点重大区别:NumPy数组创建的时候有一个固定的大小,不像Python列表可以动态地增加要素。改变一个ndarray的大小会创建一个新的数组并删除原数组。
NumPy数组中的要素必须是同一个数据类型,从而在内存中是同样的大小。唯一例外是可以由Python(包括NumPy)对象作为要素组成数组,因此允许有要素大小不同的数组的存在。
NumPy数组更有利于大规模数据的高级数学运算。通常来说,这些运算执行更高效,并且代码量比用Python自带的序列来实现更少。
越来越多的科学和数学Python包使用NumPy数组;虽然这些包通常支持Python序列输入,但它们通常在处理前把输入转化为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];
}
这里节省了包括解析Python代码和操作Python对象的消耗,但是也失去了使用Python编程的好处。更进一步讲,编程工作量会随着数据维度的增加而增加。例如,对于2D数组,C代码要扩展成这样:
for (i = 0; i < rows; i++): {
for (j = 0; j < columns; j++): {
c[i][j] = a[i][j]*b[i][j];
}
}
NumPy则同时有这两种方式的最好处:如果涉及到ndarray,逐个要素的运算是“默认的模式”,但同时逐个要素的计算是由预先编译好的C代码高效执行的。用NumPy
c = a * b
用接近C语言的速度完成了我们前面的例子做的相同事情,并且保留了我们期待的Python语言的简洁性。事实上NumPy语法比Python更加简洁!以上这个例子揭示了NumPy的两个特性:矢量化(vectorization)和广播机制(broadcasting)。
矢量化解释了为什么不需要显式地循环,索引等等操作。当然这些操作只是在背后用优化过的编译好的C语言完成了。矢量化的代码有很多好处,包括:矢量化的代码更加简洁和易读
更少的代码通常意味着更少错误
代码更类似标准数学符号(使得编写数学结构更加简单)
矢量化使得代码更具Python风格。如果没有矢量化,代码里会充斥着低效和难读的for循环。
广播机制是指暗含的逐个要素进行的运算;通常而言,在NumPy中,不只是算术运算,包括逻辑,位操作,函数等等所有运算都暗含这种机制。而且,在上面的例子中,a和b可以是两个相同形状的多维数组,或者一个向量和一个数组,甚至两个不同形状的数组,前提是较小的数组可以以一种明确的方式扩展成跟较大的数组一样的形状。更具体的规则,参见这篇文章。Broadcasting - NumPy v1.14 Manualdocs.scipy.org
NumPy的ndarray完全支持面向对象的方式。例如,ndarray是一个类,拥有大量的方法和属性。它的很多方法复制了NumPy最外层的命名空间的函数,给程序员完全的自由去选择自己喜欢或者适合手头任务的方式来写代码。