不懂 NumPy 算什么 Python 程序员?
发布于 2019-08-08 09:24 1466 次浏览 0 赞 来自 随便聊聊  

大约七八年前,我曾经用 pyOpenGL 画过地球磁层顶的三维模型,这段代码至今仍然还运行在某科研机构里。在那之前,我一直觉得自己是一个合(you)格(xiu)的 Python 程序员,似乎无所不能。但磁层顶模型的显示效果令我沮丧——尽管这个模型只有十几万个顶点,拖拽、缩放却非常卡顿。

最终,我把顶点数量删减到两万左右,以兼顾模型质量和响应速度,才勉强交付了这个任务。从此我开始怀疑 Python 的性能,甚至一度怀疑 Python 是否还是我的首选工具。

幸运的是,后来我遇到了 NumPy 这个神器。NumPy 是 Python 科学计算的基础软件包,提供多维数组对象,多种派生对象(掩码数组、矩阵等)以及用于快速操作数组的函数及 API,它包括数学、逻辑、数组形状变换、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。

NumPy 之后,我才想明白当初磁层顶的三维模型之所以慢,是因为使用了 list(Python 数组)而不是 ndarray(NumPy 数组)存储数据。有了 NumPy,Python 程序员才有可能写出媲美 C 语言运行速度的代码。熟悉 NumPy,才能学会使用 PyOpenGL / PyOpenC / Pandas / Matplotlib 等数据处理及可视化的模块。

事实上,NumPy 的数据组织结构,尤其是数组(numpy.ndarray),几乎已经成为所有数据处理与可视化模块的标准数据结构了(这一点,类似于在机器学习领域 Python 几乎已经成为首选工具语言)。越来越多的基于 Python 的科学和数学软件包使用 NumPy 数组,虽然这些工具通常都支持 Python 的原生数组作为参数,但它们在处理之前会还是会将输入的数组转换为 NumPy 的数组,而且也通常输出为 NumPy 数组。在 Python 的圈子里,NumPy 的重要性和普遍性日趋增强。换句话说,为了高效地使用当今科学/数学基于 Python 的工具(大部分的科学计算工具),你只知道如何使用 Python 的原生数组类型是不够的,还需要知道如何使用 numpy 数组。

在这个 AI 和 ML 霸屏的时代,如果不懂 NumPy,请别说自己是 Python 程序员。

list S ndarray

numpy 的核心是 ndarray 对象(numpy 数组),它封装了 python 原生的同数据类型的 n 维数组(python 数组)。numpy 数组和 python 数组之间有几个重要的区别:

numpy 数组一旦创建,其元素数量就不能再改变了。增删 ndarray 元素的操作,意味着创建一个新数组并删除原来的数组。python 数组的元素则可以动态增减。numpy 数组中的元素都需要具有相同的数据类型,因此在内存中的大小相同。python 数组则无此要求。numpy 数组的方法涵盖了大量数学运算和复杂操作,许多方法在最外层的 numpy 命名空间中都有对应的映射函数。和 python 数组相比,numpy 数组的方法功能更强大,执行效率更高,代码更简洁。然而,以上的差异并没有真正体现出 ndarray 的优势之所在,ndarray 的精髓在于 numpy 的两大特征:矢量化(ectorization)和广播(broadcast)。矢量化可以理解为代码中没有显式的循环、索引等,广播可以理解为隐式地对每个元素实施操作。矢量化和广播理解起来有点抽象,我们还是举个栗子来说明一下吧。

例题:a 和 b 是等长的两个整数数组,求 a 和 b 对应元素之积组成的数组。

1、用 python 数组实现:

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

2、用 numpy 数组实现:

c = a*b

这个栗子是不是体现了矢量化和广播的强大力量呢?请仔细体会!

总结:

矢量化代码更简洁,更易于阅读;更少的代码行通常意味着更少的错误;代码更接近于标准的数学符号;矢量化代码更 pythonic。

dtype AND shape

子曰:找对象先品行,学对象先属性。ndarray 对象有很多属性,详见下表。

基于以下三个原因,我认为,dtype 和 shape 是 ndarray 最重要的两个属性,重要到几乎可以忽略其他的属性。

我们趟过的坑,几乎都是 dtype 挖的;我们的迷茫,几乎都是因为 shape 和我们期望的不一样;我们的工作,很多都是在改变 shape。ndarray.astype() 可以修改元素类型,ndarray.reshape() 可以重新定义数组的结构,这两个方法的重要性和其对应的属性一样。记住这两个属性和对应的两个方法,就算是登堂入室了。想 numpy 支持的元素类型,请点击《数学建模三剑客MSN》(https://blog.csdn.net/xufie/article/details/52449255)。

创建数组

(1) 创建简单数组

numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)numpy.empty(shape, dtype=float, order='C')numpy.zeros(shape, dtype=float, order='C')numpy.ones(shape, dtype=float, order='C')numpy.eye(N, M=None, k=0, dtype=float, order='C')

应用示例:

>>> importnumpyasnp>>>> np.array([1, 2, 3])array([1, 2, 3])>>> np.empty((2, 3))array([[2.12199579e-314, 6.36598737e-314, 1.06099790e-313],[1.48539705e-313, 1.90979621e-313, 2.33419537e-313]])>>> np.zeros(2)array([0., 0.])>>> np.ones(2)array([1., 1.])>>> np.eye(3)array([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])

(2) 创建随机数组

numpy.random.random(size=None)numpy.random.randint(low, high=None, size=None, dtype='l')

应用示例:

>>> np.random.random(3)array([0.29334156, 0.45858765, 0.99297047])>>> np.random.randint(2, size=10)array([1, 0, 0, 0, 1, 1, 0, 0, 1, 0])>>> np.random.randint(5, size=(2, 4))array([[4, 0, 2, 1],[3, 2, 2, 0]])>>> np.random.randint(3,10,(2,4))array([[4, 8, 9, 6],[7, 7, 7, 9]])

(3) 在数值范围内创建数组

numpy.arange(start, stop, step, dtype=None)numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)numpy.logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None)

应用示例:

>>> np.arange(5)array([0, 1, 2, 3, 4])>>> np.arange(0,5,2)array([0, 2, 4])>>> np.linspace(0, 5, 5)array([0.  , 1.25, 2.5 , 3.75, 5.  ])>>> np.linspace(0, 5, 5, endpoint=False)array([0., 1., 2., 3., 4.])>>> np.logspace(1,3,3)array([  10.,  100., 1000.])>>> np.logspace(1, 3, 3, endpoint=False)array([ 10.        ,  46.41588834, 215.443469  ])

(4) 从已有数组创建数组

numpy.asarray(a, dtype=None, order=None)numpy.empty_like(a, dtype=None, order='K', subok=True)numpy.zeros_like(a, dtype=None, order='K', subok=True)numpy.ones_like(a, dtype=None, order='K', subok=True)[source]

应用示例:

>>> np.asarray([1,2,3])array([1, 2, 3])>>> np.empty_like(np.asarray([1,2,3]))array([0, 0, 0])>>> np.zeros_like(np.asarray([1,2,3]))array([0, 0, 0])>>> np.ones_like(np.asarray([1,2,3]))array([1, 1, 1])

转自https://t.cj.sina.com.cn/articles/view/6539468690/185c85f9200100kplx?from=tech&subch=otech

添加回复
回到顶部