NumPy是Python中科学计算的基本软件包。它是一个Python库,提供多维数组对象,各种派生对象(例如蒙版数组和矩阵)以及各种例程,用于对数组进行快速操作,包括数学,逻辑,形状处理,排序,选择,I / O ,离散傅立叶变换,基本线性代数,基本统计运算,随机模拟等等。
NumPy包的核心是ndarray对象。这封装了均匀数据类型的n维数组,为了提高性能,许多操作都在编译后的代码中执行。NumPy数组和标准Python序列之间有几个重要的区别:
NumPy数组在创建时具有固定的大小,这与Python列表(可以动态增长)不同。更改ndarray的大小将创建一个新数组并删除原始数组。
NumPy数组中的所有元素都必须具有相同的数据类型,因此在内存中的大小将相同。例外:一个对象可以具有(Python,包括NumPy)对象的数组,从而允许数组具有不同大小的元素。
NumPy数组有助于对大量数据进行高级数学运算和其他类型的运算。通常,与使用Python的内置序列相比,此类操作可以更高效地执行,并且代码更少。
越来越多的基于Python的科学和数学软件包都使用NumPy数组。尽管这些通常支持Python序列输入,但它们会在处理之前将此类输入转换为NumPy数组,并且通常会输出NumPy数组。换句话说,为了有效地使用很多(也许甚至是大多数)当今基于科学/数学的基于Python的软件,仅仅知道如何使用Python的内置序列类型是不够的-人们还需要知道如何使用NumPy数组。
关于序列大小和速度的要点在科学计算中特别重要。举一个简单的例子,考虑将一维序列中的每个元素与相同长度的另一序列中的对应元素相乘的情况。如果数据被存储在两个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编码中获得的好处。此外,所需的编码工作随着我们数据的维度而增加。例如,在二维数组的情况下,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成语甚至更简单!最后一个示例说明了NumPy的两个功能,这是其强大功能的基础:矢量化和广播。
向量化描述了代码中没有任何显式的循环,索引等操作-当然,这些事情发生在优化的预编译C代码中的“幕后”。向量化代码具有许多优点,其中包括:
向量化的代码更简洁,更易于阅读
更少的代码行通常意味着更少的错误
该代码更类似于标准数学符号(通常更容易正确地编码数学结构)
向量化产生更多的“ Pythonic”代码。没有向量化,我们的代码将效率低下且难以读取for
循环。
广播是一个术语,用于描述操作的隐式逐元素行为。一般而言,在NumPy中,所有运算(不仅是算术运算,而且是逻辑,按位,函数等)都以这种隐式逐元素的方式运行,即它们广播。而且,在上面的示例中,a
并且b
可以是相同形状的多维数组,或者是标量和数组,甚至可以是形状不同的两个数组,条件是较小的数组可以“扩展”到较大的形状。最终广播是明确的。有关广播的详细“规则”,请参见
numpy.doc.broadcasting
。
NumPy完全支持面向对象的方法,再次从ndarray开始。例如,ndarray是一个类,具有许多方法和属性。它的许多方法都由最外层的NumPy命名空间中的函数反映,从而允许程序员以他们喜欢的任何范式进行编码。这种灵活性使NumPy数组方言和NumPy ndarray类成为了Python中使用的多维数据交换的事实上的语言。