什么是NumPy?是知名的使用Python进行科学计算的基础软件包,是Python生态中数据分析、机器学习、科学计算的主力军。简化向量和矩阵的运算和处理。除了计算,它还包括:一个强大的N维数组对象。完善的广播功能。用于集成C/C++和Fortran代码的工具。强大的线性代数、傅里叶变换和随机数函数今天,NumPy核心开发团队的一篇论文终于在Nature上发表,详细介绍了使用NumPy进行数组编程。这篇评论论文的发表是在NumPy诞生15年后。NumPy官方团队在Twitter上简要总结了本文的核心内容:NumPy为数组编程提供了简洁、易懂、表达力高的API,同时也考虑了维持快速运算的底层机制。NumPy提供的数组编程基础与生态系统中的大量工具相结合,形成了探索性数据分析的完美交互环境。NumPy还包括增强与PyTorch、Dask和JAX等外部库的互操作性的协议。基于这些特点,NumPy为张量计算提供了标准的API,成为Python中不同数组技术之间的核心协调机制。接下来,让我们看看这篇NumPy评论论文的细节。摘要:数组编程为访问、操作和计算向量、矩阵和高维数组中的数据提供了一种强大、紧凑和富有表现力的语法。NumPy是Python语言的主要数组编程库,在物理、化学、天文学、地球科学、生物学、心理学、材料科学、工程、金融和经济效应等领域的研究分析中起着至关重要的作用。例如,在天文学中,NumPy是发现引力波和第一张黑洞图像的软件堆栈的重要组成部分。本文回顾了一些基本的数组概念,以及它们如何形成一个简单而强大的编程范例,该范例可用于组织、探索和分析科学数据。NumPy是构建科学Python生态系统的基础。它的使用如此普遍,以至于一些针对特殊需求受众的项目开发了自己的类似NumPy的界面和数组对象。由于其在Python生态系统中的核心地位,NumPy越来越多地充当阵列计算库之间的互操作层,并与其API一起提供一个灵活的框架来支持未来十年的科学和工业分析。NumPy的演变在NumPy之前,有两个Python数组包。1990年代中期开发的Numeric包在Python中提供数组对象和数组感知函数。Numeric是用C语言编写的,并链接到线性代数的标准快速实现。它最早的应用之一是美国劳伦斯利弗莫尔国家实验室的惯性约束聚变研究。为了处理来自哈勃太空望远镜的大型天文图像,Numeric被重新实现为Numarray,它增加了对结构化数组、灵活索引、内存映射、endian变体、更高效的内存使用和更好的类型转换规则的支持。尽管Numarray与Numeric高度兼容,但这两个包之间的差异足以将开发者社区分成两派。2005年,NumPy的出现完美地统一了这两个包,将Numarray的功能与Numeric的小数组性能及其丰富的CAPI结合在一起。15年后的今天,NumPy支持几乎所有用于科学和数值计算的Python库(包括SciPy、Matplotlib、pandas、scikit-learn和scikit-image)。NumPy是一个社区开发的开源库,它提供多维Python数组对象和用于操作它们的数组感知函数。由于其固有的简单性,NumPy数组已成为Python中数组数据的实际交换格式。NumPy使用CPU对内存数组进行操作。为了利用现代专用存储和硬件,最近扩展了一系列Python数组包。与Numarray–Numeric存在很大分歧不同,这些新库现在很难在社区开发人员之间产生分歧,因为它们都是建立在NumPy之上的。然而,为了让社区能够使用新的探索性技术,NumPy正在过渡到一种核心协调机制,该机制会规划一个定义良好的数组编程API,并在适当的时候将其分发给专门的数组实现。NumPy数组NumPy数组是一种能够高效存储和访问多维数组的数据结构,支持广泛的科学计算。NumPy数组包括用于解释存储数据的指针和元数据,即数据类型(datatype)、形状(shape)和步幅(stride),见下图1a。图1:NumPy数组包含几个基本的数组概念。数据类型描述存储在数组中的元素的性质。数组只有一种数据类型,数组中的每个元素在内存中占用的字节数相同。数据类型包括实数、复数、字符串、时间戳、指针等。数组的形状决定了每个轴上元素的个数,轴的个数就是数组的维数。例如,一个数值向量可以存储为形状为N的一维数组,而彩色视频是一个形状为(T,M,N,3)的四维数组。步幅是考虑计算机内存的必要组成部分,它线性存储元素。步幅描述了在内存中逐列移动所需的字节数。例如,形状为(4,3)的浮点数二维数组在内存中使用每个元素8个字节。要在连续的列之间移动,我们需要在内存中前进8个字节,而要到达下一行,我们需要前进3×8=24个字节。所以数组的步长是(24,8)。NumPy可以按C或Fortran内存顺序存储数组,遍历行或列。这使得用这些语言编写的外部库可以直接访问内存中的NumPy数组数据。用户使用“索引”(访问子数组或单个元素)、“运算符”(各种运算符)和“数组感知函数”与NumPy数组交互。它们为NumPy数组编程提供了简洁且富有表现力的高级API,同时还考虑了保持快速操作的底层机制。索引数组将返回单个元素、子数组或满足特定条件的元素(参见上面的图1b)。数组甚至可以与其他数组一起索引(参见图1c)。返回子数组的索引也可以返回原始数组的“视图”,允许在两个数组之间共享数据。这提供了一种强大的方法来对内存有限的数组数据子集执行操作。为了补充数组语法,NumPy还包括对数组执行向量化计算的函数,包括算术、统计和三角函数(参见图1d)。数组编程所必需的矢量化计算对整个数组进行操作,而不是对其中的单个元素进行操作。这意味着,用C等语言表达需要几十行的操作,在这里只需要一个清晰的Python表达式就可以实现。这导致代码简洁,使用户无需分析细节,而NumPy以接近最佳的方式循环遍历数组元素。当对两个形状相同的数组执行向量化计算(例如加法)时,接下来会发生什么是一目了然的。“广播”机制允许NumPy处理不同维度的数组之间的操作,例如向数组添加标量值。广播还泛化到更复杂的示例,例如缩放数组的每一列或生成坐标网格。在广播中,单个或两个数组可以重叠(无需从内存中复制任何数据),以便操作数具有匹配的形状(参见图1d)。其他数组感知函数(例如加法、平均和最大化)执行逐元素“缩减”,在单个数组的一个、多个或所有轴上累积结果。例如,累积一个具有d轴的n维数组会产生一个维度为n?d的数组(见图1f)。NumPy还包含数组感知函数,可以创建、重塑、连接和填充数组,执行数据排序和计数,以及读写文件。这为生成伪随机数提供了广泛的支持,它还可以使用OpenBLAS或IntelMKL等后端执行加速线性代数。总之,内存数组表示、数学上接近的语法和各种数组感知函数结合在一起形成了一种高效且富有表现力的数组编程语言。科学的Python生态系统Python是一种开源、通用、解释型编程语言,非常适合标准编程任务,例如数据清理、与Web资源交互和解析文本。添加快速数组运算和线性代数可以让科学家们用一种编程语言来完成这一切。尽管NumPy不是Python标准库的一部分,但它也受益于与Python开发人员的良好关系。在过去几年中,Python语言添加了几个新特性和特殊语法,让NumPy拥有更简洁和可读性更高的数组表示法。然而,由于NumPy不是Python标准库的一部分,它能够决定自己的发布策略和开发模型。从历史、发展和应用的角度来看,SciPy和Matplotlib与NumPy密切相关。SciPy为科学计算提供基本算法,包括数学、科学和工程程序。Matplotlib生成出版质量的图表和可视化。NumPy、SciPy和Matplotlib的组合,以及IPython、Jupyter等高级交互环境,为Python中的数组编程提供了坚实的基础。如图2所示,科学Python生态系统建立在上述基础之上,提供范围广泛的专有库,这些库是许多特定领域项目的基础。NumPy是这个数组感知库生态系统的基础,设置文档标准,提供数组测试基础设施,并添加对Fortran等编译器的构建支持。图2:NumPy是科学Python生态系统的基础。许多研究小组设计大型、复杂的科学库,将特定于应用程序的功能添加到Python生态系统。例如,事件视界望远镜(EHT)协作项目开发的eht-imaging库依赖于科学Python生态系统的许多低级组件。EHTCollaboration使用该库捕获了黑洞的第一张图像。在eht-imaging库中,NumPy数组在流程链的每一步存储和操作数值数据。由基于数组的编程及其周边工具生态系统(在IPython或Jupyter内部)创建的交互式环境非常适合探索性数据分析。用户可以流畅地检查、操作和可视化他们的数据,并快速迭代以改进编程语句。然后将这些语句拼接成命令式或函数式程序,或包含计算和叙述的笔记本。探索性研究之外的科学计算通常在文本编辑器或集成开发环境(IDE)(如Spyder)中完成。这种丰富而多产的环境使Python在科研界广受欢迎。为了为探索性研究和快速原型制作提供补充支持,NumPy培养了一种使用经过时间考验的软件工程实践来改善协作和减少错误的文化。这种文化不仅被项目负责人采纳,也传给了初学者。NumPy团队很早就采用了分布式版本控制和代码审查机制来提高代码协调性,并使用持续测试对每个建议的NumPy更改运行大量自动化测试。这种使用最佳实践来生产值得信赖的科学软件的文化已经被建立在NumPy之上的生态系统所采用。例如,在皇家天文学会最近授予Astropy的奖项中:“Astropy项目为数百名初级科学家提供了专业级的软件开发实践,包括使用版本控制、单元测试、代码审查和问题跟踪器”这是现代研究人员的一项重要技能,它经常被正规的大学物理或天文学教育所忽视。”社区成员通过课程和研讨会填补了正规教育中的这一空白。最近数据科学、机器学习和人工智能的快速发展进一步显着增加了Python的科学使用。Python的重要应用程序,例如eht-imaging库,现在几乎存在于自然科学和社会科学的每个学科中。这些工具已经成为许多领域的主要软件环境。NumPy及其生态系统通常在大学课程、新兵训练营和暑期学校教授,它们也是世界各地社区会议和研讨会的焦点。NumPy及其API已变得无处不在。数组爆炸和互操作性NumPy在CPU上提供内存中、多维和统一类型(即单指针和跨步)数组。NumPy可以运行在嵌入式设备和机器上,比如世界上最大的超级计算机,性能接近编译语言。在大多数情况下,NumPy解决了绝大多数数组计算用例。但是现在,科学数据集往往超过了单台机器的存储能力,可以存储在多台机器上,也可以存储在云端。此外,最近对深度学习和人工智能应用程序的加速需求催生了专门的加速器硬件,包括GPU、TPU和FPGA。目前,由于NumPy的内存数据模型,它不能直接使用这样的存储和专用硬件。然而,GPU、TPU和FPGA的分布式数据和并行执行很好地映射到数组编程范式,因此可用的现代硬件架构与利用其计算能力所需的工具之间存在差距。弥合这一差距的社区努力导致了新数组实现的激增。例如,每个深度学习框架都会创建自己的数组。PyTorch、TensorFlow、ApacheMXNet和JAX阵列都能够以分布式方式在CPU和GPU上运行,使用惰性评估进行额外的性能优化。SciPy和PyData/Sparse都提供稀疏数组,通常包含很少的非零值,为了效率只存储在内存中。此外,还有一些项目以NumPy数组作为数据容器,并扩展了相应的功能。Dask通过这种方式让分布式数组成为可能,taggedarrays是通过xarray来实现的。此类库通常模仿NumPyAPI以降低初学者的入门门槛,并为更广泛的社区提供稳定的数组编程接口。这反过来又防止了一些破坏性的分裂,例如Numeric和Numarray之间的区别。但探索使用数组的新方法本质上是实验性的,事实上,一些有前途的库,如Theano和Caffe已经停止开发。每当用户决定尝试一项新技术时,他们必须更改导入语句并确保新库实现了他们当前使用的NumPyAPI的所有部分。理想情况下,用户将能够通过NumPy函数或语义对专用数组进行操作,这样他们只需编写一次代码,就可以在NumPy数组、GPU数组、分布式数组和其他数组之间切换,从中受益。为了支持外部数组对象之间的数组操作,NumPy增加了一个作为核心协调机制的函数,并提供了指定的API,如上图2所示。为了促进这种互操作性,NumPy提供了一个“协议”,允许将专门的数组传递给NumPy函数,如下面的图3所示。反过来,NumPy根据需要将操作分派到原始库。支持超过400个最流行的NumPy函数。该协议使用广泛使用的库实现,例如Dask、CuPy、xarray和PyData/Sparse。由于这些进步,用户现在可以使用Dask将他们的计算从一台机器扩展到多个系统。该协议允许用户通过嵌入在Dask阵列等中的CuPy阵列,在分布式多GPU系统上大规模重新部署NumPy代码。使用NumPy的高级API,用户可以在具有数百万个内核的多系统上利用高度并行的代码执行,而所需的代码更改最少。如下图3所示,NumPy的API和数组协议为生态系统提供了新的数组:这些数组协议现在是NumPy的主要功能,并且它们的重要性预计会增长。NumPy开发人员(其中许多人也是本文的作者)迭代地改进和添加协议设计以提高可用性并简化实现。论文最后对NumPy的现状和未来进行了总结和展望:未来十年,NumPy开发者将面临几大挑战。新设备将会出现,现有的专用硬件将面临摩尔定律带来的收益递减,并且将会有越来越多类型的数据科学从业者。他们中的大多数人会使用NumPy。随着光片显微镜和大型天气巡天望远镜(LSST)等设备和仪器的采用,科学数据的规模将不断扩大。新一代的语言、解释器和编译器,例如Rust、Julia和LLVM,将创建新的概念和数据结构。
