Python的Numpy概要介绍

  • Post author:
  • Post category:python

    Numpy是Python中用于科学计算的基础包。它一个Python类库,其中提供了多维数组对象,及其各种派生对象(如掩码数组、矩阵等),以及基于这些数组对象各种快速操作的方法,包括数学计算、逻辑处理、图形操作、排序、选择、输入/输出(I/O),离散傅里叶变换,基本的线性代数、基本的统计操作,随机模拟等。

  • Numpy包的核心是ndarray对象,它是对同类型数据的多维数组的封装,同时为了执行性能,在编译代码中还包含了很多性能相关的操作。Numpy数组与标准的Python序列有一些重要的差异:
  • Numpy数组在创建时有固定的尺寸,这与Python的列表不同(Python的列表可以动态增长)。改变ndarray数组的尺寸,将会创建一个新的数组,并删除原始数组;
  • 在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为我们提供的编码的好处,此外,也增加了我们的编码量。例如使用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的代码时发生。矢量代码有很多好处,其中包括:

  • 矢量化让代码更加简洁和易读;
  • 很少的代码意味着很少的Bug;
  • 编码更加接近标准的数据符号(这让使用正确编码的数学结构更加容易);

矢量化导致更加Python化的编码风格,没有矢量化,我们的代码会变得混乱和低效,并且对循环代码也很难阅读。

广播这个术语是用于说明隐式的逐个元素操作行为。通常,在Numpy中,所有的操作,不仅是算术操作,包括逻辑处理、位运算、函数等,都使用这种隐式的逐个元素的操作方法,即广播。此外,在上面的示例中,a和b可以是相同形状的多维数组,也可以是一个标量和一个数组,也可以是两个不同形状的数组,前提是较小的数组可以扩展成与较大数组的形状一致,这样就使得广播操作明确,有关广播的更多详细规则,请看numpy.doc.broadcating.

Numpy完全支持面向对象的编程方法,例如ndarray就是一个类,它拥有很多方法和属性,它的很多被映射到Numpy命名空间的最外层的函数中。因此程序员完全可以选择自己喜欢编程方式来完成手头的任务。