NumPy

NumPy是Python语言的一个扩充程式库。支援高阶大规模的多维阵列与矩阵运算，此外也针对阵列运算提供大量的数学函数函式库。NumPy的前身Numeric最早是由Jim Hugunin与其它协作者共同开发，2005年，Travis Oliphant在Numeric中结合了另一个同性质的程式库Numarray的特色，并加入了其它扩充功能而开发了NumPy。NumPy为开放源代码并且由许多协作者共同维护开发。

## 历史

### Matrix-sig

尽管很早就受到了科学界和工业界的关注，但Python最初并不是为数值计算设计的。1995年，特殊兴趣小组（Special Interest Group，SIG）Matrix-sig成立，其目的是设计一个数组计算包。Matrix-sig的成员中有Python的设计者和维护者吉多·范罗苏姆，他扩展了Python的语法（特别是索引语法），使数组计算更容易。

### Numeric

矩阵包Numeric由Jim Fulton实现，并由Jim Hugunin推广， (也称Numerical Python extensions或 NumPy)。Jim Hugunin 是麻省理工学院的研究生，于1997年加入国家研究计划公司从事JPython的研发，劳伦斯利弗莫尔国家实验室的Paul Dubois接任维护者。其他早期贡献者包括David Ascher, Konrad Hinsen和Travis Oliphant。

### Numarray

Numarray是作为Numeric的替代品被开发出来的，现已废弃。相比于Numeric，Numarray处理大数组速度较快，然而处理小数组速度较慢，因此有一段时间这两个包被并行使用于不同情况。Numeric的最后一个版本（v24.2）于2005年11月11日发布；Numarray的最后一个版本（v1.5.2）于2006年8月24日发布。

有人希望将Numeric纳入Python标准库，但吉多·范罗苏姆认为，在当时的情况下，代码难以维护。

### NumPy

2005年初，NumPy的开发者Travis Oliphant希望社区的不同数组包可以统一。他将Numarray的功能移植到Numeric上，并于2006将结果作为NumPy 1.0发布。这个新项目是SciPy的一部分。为了避免在只需数组计算的情况下安装庞大的SciPy包，新包以NumPy的名义被分离出来。2011年，NumPy的1.5.0版本加入了对Python 3的支持。

2011年，PyPy开始开发NumPy API，但它尚未与NumPy完全兼容。

## 特色

NumPy参考CPython（一个使用字节码的直译器），而在这个Python实作直译器上所写的数学算法程式码通常远比编译过的相同程式码要来得慢。为了解决这个难题，NumPy引入了多维阵列以及可以直接有效率地操作多维阵列的函式与运算子。因此在NumPy上只要能被表示为针对阵列或矩阵运算的算法，其执行效率几乎都可以与编译过的等效C语言程式码一样快。

NumPy提供了与MATLAB相似的功能与操作方式，因为两者皆为直译语言，并且都可以让使用者在针对阵列或矩阵运算时提供较纯量运算更快的效能。两者相较之下，MATLAB提供了大量的扩充工具箱（例如Simulink）；而NumPy则是根基于Python这个更现代、完整并且开放源代码的程式语言之上。此外NumPy也可以结合其它的Python扩充函式库。例如SciPy，这个函式库提供了更多与MATLAB相似的功能；以及Matplotlib，这是一个与MATLAB内建绘图功能类似的函式库。而从本质上来说，NumPy与MATLAB同样是利用BLAS与LAPACK来提供高效率的线性代数运算。

### ndarray 数据结构

NumPy的核心功能是ndarray（即n-dimensional array，多维阵列）数据结构。这是一个表示多维度、同质并且固定大小的阵列物件。而由一个与此阵列相关联的资料型态物件来描述其阵列元素的资料格式（例如其字元组顺序、在内存中占用的字元组数量、整数或者浮点数等等）。

### 局限性

在数组中插入或追加元素并不像Python的list一样简单。np.pad(...)实际上创建了新的具有目标形状和填充值的数组，将给定数组的值复制到新数组中并返回新数组。np.concatenate(a1,a2)并没有直接连接两个数组，而是返回新的数组，该数组填充了两个原数组的值。用np.reshape(...)改变数组的维度只有在数组中元素数量不变的情况下才能实现。造成以上情况的原因是NumPy的数组必须占用连续的内存空间。Blaze包尝试克服这个限制。

未经矢量化的算法通常运行缓慢，因为它们必须用纯Python方法实现；由于需要创建和输入等大的临时数组，部分操作的矢量化可能会增长其空间复杂度（例如由常数增长到线性）。部分小组通过运行时编译（Runtime compilation of numerical code）避免这一问题。可与NumPy集成的开源解决方案包括 numexpr和Numba。Cython和Pythran是静态编译的解决方案。

许多现代大型科学计算应用的要求超出了NumPy数组的能力。例如，NumPy数组通常加载到计算机的内存中，然而内存可能没有足够的容量；此外，NumPy仅在单个CPU上进行操作，而许多线性代数算子可以通过CPU的集群和其它特殊硬件（例如GPU、TPU，部分深度学习应用也依赖于这些特殊硬件）来加速。因此，近期在Python的生态中出现了许多其它工具，例如用于分布式数组的Dask、用于GPU计算的TensorFlow和JAX等。这些库通常实现或模仿NumPy的部分API，因此用户不需大量改动就可以部署原先使用NumPy的程序。近期出现的由Nvidia的CUDA架构加速的CuPy库展示了快速计算的潜力，是NumPy的直接替代品。

## 语法

格点

```numpy
>>> import numpy as np
>>> x = np.array([1, 2, 3])
>>> x
array([1, 2, 3])
>>> y = np.arange(10)  # 類似 Python 的 range, 但是回傳 array
>>> y
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
```

基本运算

```numpy
>>> a = np.array([1, 2, 3, 6])
>>> b = np.linspace(0, 2, 4)  # 建立一個array, 在0與2的範圍之間讓4個點3等分
>>> c = a - b
>>> c
array([ 1.        ,  1.33333333,  1.66666667,  4.        ])
>>> a**2
array([ 1,  4,  9, 36])
```

全域方法

```numpy
>>> a = np.linspace(-np.pi, np.pi, 100) 
>>> b = np.sin(a)
>>> c = np.cos(a)
```

线性代数

```numpy
>>> from numpy.random import rand
>>> from numpy.linalg import solve, inv
>>> a = np.array([[1, 2, 3], [3, 4, 6.7], [5, 9.0, 5]])
>>> a.transpose()
array([[ 1. ,  3. ,  5. ],
       [ 2. ,  4. ,  9. ],
       [ 3. ,  6.7,  5. ]])
>>> inv(a)
array([[-2.27683616,  0.96045198,  0.07909605],
       [ 1.04519774, -0.56497175,  0.1299435 ],
       [ 0.39548023,  0.05649718, -0.11299435]])
>>> b =  np.array([3, 2, 1])
>>> solve(a, b)  # 解方程式 ax = b
array([-4.83050847,  2.13559322,  1.18644068])
>>> c = rand(3, 3)  # 建立一個 3x3 隨機矩陣
>>> c
array([[  3.98732789,   2.47702609,   4.71167924],
       [  9.24410671,   5.5240412 ,  10.6468792 ],
       [ 10.38136661,   8.44968437,  15.17639591]])
>>> np.dot(a, c)  # 矩陣相乘
array([[  53.61964114,   38.8741616 ,   71.53462537],
       [ 118.4935668 ,   86.14012835,  158.40440712],
       [ 155.04043289,  104.3499231 ,  195.26228855]])
```

## 基本资料

- 作者：特拉维斯·奥利芬特
- 开发者：社群专案
- 发行日期：Numeric, 1995年、NumPy, 2006年
- 编程语言：Python, C语言
- 操作系统：跨平台
- 类型：数值分析
- 许可协议：BSD授权条款