NumPy(发音为“NUM-py”)是一个为Python编程语言提供支持的库,它增加了对大型、多维数组和矩阵的支持,并附带了一系列用于操作这些数组的高级数学函数。它是一个开源软件,拥有众多贡献者,并由NumFOCUS提供财务赞助。NumPy是科学计算Python生态系统的核心组成部分,支撑着SciPy和Matplotlib等库,广泛应用于从数据分析到机器学习和深度学习的各个领域。
NumPy的前身是Numeric,最初由Jim Hugunin创建,并得到了其他几位开发者的贡献。2005年,Travis Oliphant通过将Numarray的功能整合进Numeric并进行了大量修改,创建了NumPy。NumPy 1.0于2006年首次发布。此后,NumPy不断发展,支持了Python 3(从2011年的1.5.0版本开始),并已成为Python数值计算的标准工具。
历史
Python编程语言最初并非为数值计算而设计,但它很早就引起了科学和工程界的关注。1995年,特殊兴趣小组matrix-sig成立,旨在定义一个数组计算包。该小组的成员包括Python的设计者和维护者Guido van Rossum,他扩展了Python的语法,特别是索引语法,以使数组计算更加容易。
Jim Fulton完成了一个矩阵包的实现,随后Jim Hugunin对其进行了扩展以支持多维数组,并将其命名为Numeric(也称为“Numerical Python extensions”或“NumPy”)。Hugunin是麻省理工学院的研究生,他加入CNRI(国家研究计划公司)从事JPython的工作,后来由劳伦斯利弗莫尔国家实验室的Paul Dubois接手维护。其他早期贡献者包括David Ascher、Konrad Hinsen和Travis Oliphant。
Numarray是作为Numeric的更灵活替代品而编写的。它在处理大型数组时速度更快,但在处理小型数组时速度较慢,因此在一段时间内,这两个包因不同的使用场景而并行存在。Numeric的最后一个版本(v24.2)于2005年11月11日发布,而numarray的最后一个版本(v1.5.2)于2006年8月24日发布。当时有人希望将Numeric纳入Python标准库,但Guido van Rossum认为其代码在当时的状态下难以维护,因此未予采纳。
2005年初,Travis Oliphant希望统一社区对单一数组包的使用。他将Numarray的功能移植到Numeric中,并于2006年发布了NumPy 1.0。这个新项目最初是SciPy的一部分,但为了避免为了获取数组对象而安装庞大的SciPy包,它被分离出来并命名为NumPy。2011年,PyPy开始开发NumPy API的实现;截至2023年,该实现尚未完全兼容NumPy。
特性
NumPy主要针对Python的CPython参考实现,而CPython是一个非优化的字节码解释器。因此,为Python编写的数学算法通常比编译型等效代码运行得慢得多。NumPy通过提供多维数组以及能高效操作这些数组的函数和运算符,部分解决了这一速度问题。使用NumPy需要将部分代码(主要是内层循环)重写为数组操作。
在Python中使用NumPy提供的功能可与MATLAB相媲美,因为两者都是解释型语言,并且只要大多数操作针对数组或矩阵而非标量,用户就能编写出运行速度较快的程序。MATLAB拥有大量额外的工具箱(尤其是Simulink),而NumPy则与Python这一更现代、更完整的编程语言紧密集成。此外,还有互补的Python包可用:SciPy提供了更多类似MATLAB的功能,Matplotlib则提供了类似MATLAB的绘图功能。尽管MATLAB可以处理稀疏矩阵运算,但NumPy本身无法做到,需要借助scipy.sparse库。在内部,MATLAB和NumPy都依赖BLAS和LAPACK来实现高效的线性代数计算。
广泛使用的计算机视觉库OpenCV的Python绑定利用NumPy数组来存储和操作数据。由于图像的多通道特性可以简单地表示为三维数组,因此索引、切片或使用其他数组进行掩码操作是访问特定像素的有效方式。NumPy数组作为OpenCV中图像、提取的特征点、滤波器内核等的通用数据结构,简化了编程工作流程并便于调试。重要的是,许多NumPy操作会释放全局解释器锁,从而支持多线程处理。NumPy还提供了C API,使Python代码能够与用低级语言编写的外部库进行互操作。
ndarray数据结构
NumPy的核心功能是其“ndarray”(n维数组)数据结构。这些数组是内存的跨步视图。与Python内置的列表数据结构不同,数组是类型同质的:单个数组中的所有元素必须具有相同的类型。此类数组还可以作为由C/C++、Python以及CPython解释器的Fortran扩展所分配的内存缓冲区的视图,而无需复制数据,这使其与现有数值库具有一定的兼容性。SciPy包利用了此功能,它封装了许多此类库(尤其是BLAS和LAPACK)。NumPy还内置了对内存映射ndarray的支持。
局限性
向数组中插入或追加条目并不像使用Python列表那样简单。np.pad例程用于扩展数组,但它实际上是创建一个具有所需形状和填充值的新数组,将给定数组复制到新数组中,然后返回新数组。同样,np.concatenate([a1, a2])并不会链接两个数组,而是返回一个新数组,其中包含来自两个输入数组的条目。使用np.reshape改变数组的维度,仅在元素总数不变的情况下才可行。这些限制源于NumPy数组必须是连续内存缓冲区的视图这一事实。
无法向量化的算法通常运行缓慢,因为它们必须用“纯Python”实现,而向量化可能会将某些操作的内存复杂度从常数级增加到线性级,因为需要创建与输入大小相同的临时数组。为了规避这些问题,一些团体已经实现了数值代码的运行时编译;与NumPy互操作的开源解决方案包括numexpr和Numba。Cython和Pythran则是可以加速基于NumPy代码的静态编译选项。
应用与影响
NumPy是许多科学和数据驱动领域的基础。它为机器学习框架和库(如TensorFlow和PyTorch)提供了数组操作支持,这些框架和库用于构建神经网络模型和Transformer架构。在人工智能研究中,NumPy数组常用于数据预处理、特征提取以及从头实现算法。该库的高效性和灵活性使其成为学术界和工业界的标准工具,从麻省理工学院和斯坦福大学到谷歌和亚马逊等公司都广泛使用。
NumPy的影响力超越了传统的科学计算领域。它被用于计算机视觉中的图像处理、自然语言处理中的词嵌入处理,以及强化学习中的状态空间管理。它与OpenCV及其他库的集成,简化了机器人技术和自动驾驶系统(如Waymo和特斯拉开发的系统)中的工作流程。截至2023年,NumPy仍然是下载量最大的Python包之一,这反映了它在现代计算生态系统中的核心地位。