.本节书摘来自华章出版社《机器学习系统设计:Python语言实现》一书中的第2章,第2.4节,作者 [美] 戴维·朱利安(David Julian),更多章节内容可以访问云栖社区“华章计算机”公众号查看
2.4 NumPy
我们应该知道Python中表示数据的类型层次。类型层次的根是不可变对象,例如整数、浮点数和布尔。基于这些类型,我们有序列类型。序列类型是有序的对象集合,并且由非负整数进行索引。序列类型是可遍历对象,包括字符串、列表和元组。序列类型有一组共同的操作,例如返回一个元素(s[i])或分片(s[i:j])、求长度(len(s))或求和(sum(s))。最后还有映射类型。映射类型是由一组关键字对象索引的对象集合。映射对象是无序的,并且由数字、字符串或其他对象索引。Python内置的映射类型是字典。
NumPy在这些数据对象的基础之上提供了两类对象:N维数组对象(ndarray)和通用函数对象(ufunc)。ufunc对象为ndarray对象提供了基于元素的操作,允许类型映射和数组广度映射(broadcasting)。类型映射是将数据类型改变为其他类型的过程,也可称为类型转换,广度映射描述了不同大小的数组是如何进行算术运算处理的。NumPy还包括如下子包:线性代数(linalg)、随机数生成器(random)、离散傅里叶变换(fft)和单元测试(testing)。
NumPy使用dtype对象来描述数据的各个方面。这包括诸如浮点数、整数等数据的类型,数据类型的字节数(如果数据是结构化的),以及字段的名字和任意子数组(阵列)的阵形。NumPy还包括如下一些新的数据类型:
8位、16位、32位和64位的整型
16位、32位、64位的浮点数
64位和128位的复数类型
ndarray结构化数组类型
我们可以使用np.cast对象在类型之间进行转换。该对象实际上是一个字典,其键值是目标转换类型,其值是相应的转换函数。如下代码所示,我们可以将整数转换为float32:
NumPy数组有多种创建方法,例如由其他Python数据结构转换为数组,使用诸如arange()、ones()和zeros()等内嵌的数组创建对象,或者由.csv或.html文件进行创建。
Indexing和slicingNumPy分别建立于索引和分片技术,并应用于序列。我们应该对序列分片已经很熟悉了,例如对于列表和元组,在Python中使用[i:j:k]语法,其中i是开始索引,j是结束索引,k是步长。NumPy将这种选择元组的概念扩展为N维。
输入如下命令,激活Python控制台:
我们可以看到如下输出:
其输出是一个3维、4维、5维的多维数组。你应该知道,我们可以通过例如[2,3,4]的表示法来访问数组中的每个元素,[2,3,4]会返回59。数组的索引由0开始。
我们可以使用分片技术返回数组的一个分片。
A[1:2:]得到如下数组:
使用(…),我们可以选择保留任意维度的数组元素。例如,a[…,1]等同于a[:,:,1]:
我们还可以使用负数从数组末端开始计数:
通过分片,我们可以创建数组的视图,而原始数组保持不变,视图持有原始数组的引用。也就是说,创建了分片之后,即使将其分配给一个新的变量,但如果我们改变了原始数组,这些变化也会反映在新的数组变量中。下图进行了举例说明:
这里的a和b都引用了同一数组。当我们分配a中的值,也会同时在b中反映。如果不仅仅是引用数组,而是对其进行复制,我们需要使用标准库中copy包的deepcopy()函数。
这里,我们创建了一个独立的数组变量c,此时,数组a中的任何变化都不会反映在数组c中。
2.4.1 构造和变换数组
作为构造数组的一种有效手段,NumPy的多个类都使用了分片功能。例如,numpy.mgrid可以用分片来创建meshgrid对象,这在特定情况下比使用arange()更为便利。其主要目的是建立指定维度的N维坐标系数组矩阵。如下图中的例子所示:
有时,我们需要以其他方式来操作数据,这包括:
连接:通过使用np.r_和np.c_函数,我们可以使用分片结构连接一个或两个数轴(axis),例如:
这里我们使用复数5j作为步长,Python将其解释为填充数轴指定范围内点的数量,这里的范围是-1到1,并包含范围边界值。
newaxis:该对象扩展了数组的维度。
上例扩展了数组a的维度,在第一维度增加了额外的数轴。下例则在第二维度增加了新的数轴:
我们还可使用布尔运算符进行过滤:
对指定数轴进行求和:
这里我们指定数轴为2进行了求和。
2.4.2 数学运算
正如所希望的那样,我们可以对NumPy数组运行如加法、减法、乘法,甚至三角函数等数学运算。我们还可以通过广度映射(broadcasting)对不同阵形的数组进行算术运算。当对两个数组进行运算时,NumPy会从尾部维度开始根据元素来比较阵形,如果二者维度相等或其中之一为1,则认为二者兼容,如果不兼容,则会抛出ValueError异常。
运算实际上是由ufunc对象完成的。该对象会对ndarrays的逐个元素进行操作。这些对象本质上是封装器,为标量函数提供了一致的接口,以使其能够对NumPy数组进行操作。NumPy有超过60个ufunc对象,广泛覆盖了各种运算和类型。在运行运算时,ufunc对象是被自动调用的,例如,当对两个数组使用+运算符运行加法运算时,会自动调用相应的ufunc对象。
此外还有一些额外的数学特征如下所述:
向量(Vectors):我们也可以使用np.vectorize()函数创建标量函数的向量化版本。该函数以Python的scalar函数或方法作为参数,输出该函数的向量化版本。
我们可以观察到如下输出:
多项式函数(Polynomial functions):poly1d类允许我们以一种自然的方式来处理多项式函数。它接受降幂顺序的系数数组为参数。例如,可以按如下方式输入多项式2x2 + 3x + 4:
我们可以看到上例中以人类可读方式打印输出了多项式。我们还可以对多项式进行各种运算,例如对给定点进行求值:
求根:
我们可以使用asarray(p)将多项式系数赋值为一个数组,这样就能在所有接受数组参数的函数中使用了。
正如我们所见,NumPy的这些包为机器学习提供了强大而灵活的框架。