Numpy是应用Python进行科学计算时的基础模块。它是一个提供多维数组对象的Python库,除此之外,还包含了多种衍生的对象(比如掩码式数组(masked arrays)或矩阵)以及一系列的为快速计算数组而生的例程,包括数学运算,逻辑运算,形状操作,排序,选择,I/O,离散傅里叶变换,基本线性代数,基本统计运算,随机模拟等等。

Numpy库中最核心的部分是ndarray 对象。它封装了同构数据类型的n维数组,它的功能将通过演示代码的形式呈现。 NumPy数组和标准Python序列之间有几个重要区别:
(1)Numpy数组在创建时就会有一个固定的尺寸,这一点和Python中的list数据类型(可以动态生长)是不同的。当Numpy数组的尺寸发生改变时其实会删除之前的而创建一个新的数组。
(2)在一个Numpy数组中的所有元素数组类型要一致,并在内存中占有相同的大小。这里有一点例外:可以在Python的数组中包含Numpy的对象,这样的话就可以实现不同类型的元素。
(3)在数据量巨大时,使用Numpy进行高级数据运算和其他类型的操作是更为方便的。 通常情况下,这样的操作比使用Python的内置序列更有效,执行代码更少。
(4)越来越多的用于数学和科学计算Python库使用了Numpy,虽然这些第三方库也留了Python内置序列的输入接口,但是实际上在处理这些输入前还是要转成Numpy数组,平切这些库的输出一般是Numpy数组。换句话说,为了更好的使用当今大多数(甚至是绝大多数)用于数学/科学的Python库,仅仅知道Python本身是远远不够的。

在科学计算中序列的大小和速度是尤其重要的点。举个简单的例子:如果要将一个一维的序列a中的每一个数与另一个序列b(长度与a相同)。数据存放在python的list结构中,通过遍历列表可以得到:

c = []
for i in range(len(a)):
    c.append(a[i]*b[i])

比如 a = [1,2,3],b = [1,1,1],那么 c = [1,2,3]
这样就能求出结果,但是如果a和b中包含了数百万个数,那么循环遍历的代价就会很高。好在我们有更高效的方法(为了更加清晰,我们忽略了变量的声明,初始化和内存分配的代码),比如用C/C++:

for (i = 0; i < rows; i++): {
  c[i] = a[i]*b[i];
}

这种方式照比第一个代码,有效节约了所有涉及解释Python代码(append等等)和操作Python对象(len(a))所带来的开销,但是Python编程简单带来的好处就没有了(因为用了C~~)。此外,随着数组维数的增加,C代码遍历的消耗也会变大,比如在二维数组的情况下,代码要变为:

for (i = 0; i < rows; i++): {
  for (j = 0; j < columns; j++): {
    c[i][j] = a[i][j]*b[i][j];
  }
}

Numpy提供给我们的方式包含了二者的优势(Python的简单和C的效率),逐个元素操作只需要ndarray对象提供的“默认模式”,但是逐个元素的操作可以通过预编译的C代码快速执行。 在NumPy中:

c = a * b

在这个例子中,它的速度和C代码接近,但是做到了像Python代码一样灰常简单的风格!事实上,Numpy的语法还要 更简单一些!上面的例子阐述NumPy中两个最基础也是最重要的功能:vectorization 和 broadcasting。

vectorization :代码中没有任何明确的循环,索引等 - 这些事情当然是在C编写的源码中完成的。 矢量化代码有很多优点,其中包括:
(1)简洁易读
(2)代码的行数更少(完成同样功能),这意味着更少的bug
(3)代码更像标准的数学符号
(4)比Python的代码更简单一些,避免了循环。

Broadcasting:是用于描述操作的隐含逐个元素行为的术语; 一般来说,在NumPy的所有操作中,不仅仅是算术运算,还有逻辑运算,位操作,功能性的运算,这些算法在表现形式上都隐藏了逐个元素操作的方式,即broadcast。 此外,在上述示例中,a和b可以是相同形状(shape)的多维数组,或标量和序列,或者甚至是具有不同形状的两个数组,只要较小的数组“可扩展”到较大的形状(shape) 以这样一种方式使得最终的broadcast是明确的。 有关broadcast的详细介绍,请参阅numpy.doc.broadcasting

NumPy完全支持面向对象的编程,再次说道ndarray。例如,ndarray是一个类,拥有众多的方法和属性。 它的许多方法在最外层的NumPy命名空间中镜像函数,使程序员能够完全自由地编写任何偏好的范例和最适合手头任务的代码。