NumPy是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效的多。
图2-1 主流基于Python的机器学习库
NumPy包括:
·一个强大的N维数组对象Array;
·比较成熟的(广播)函数库;
·用于整合C/C++和Fortran代码的工具包;
·实用的线性代数、傅里叶变换和随机数生成函数。
NumPy提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库,专为进行严格的数字处理。
1.安装方法
pip install --user numpy
2.用法示例
首先需要创建数组才能对其进行其他操作。
可以通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组(下例中的变量c):
>>> a = np.array([1, 2, 3, 4]) >>> b = np.array((5, 6, 7, 8)) >>> c = np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]]) >>> b array([5, 6, 7, 8]) >>> c array([[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10]]) >>> c.dtype dtype('int32')
数组的大小可以通过其shape属性获得:
>>> a.shape (4,) >>> c.shape (3, 4)
数组元素的存取方法和Python的标准方法相同:
>>> a = np.arange(10) >>> a[5] # 用整数作为下标可以获取数组中的某个元素 5 >>> a[3:5] # 用范围作为下标获取数组的一个切片,包括a[3]不包括a[5] array([3, 4]) >>> a[:5] # 省略开始下标,表示从a[0]开始 array([0, 1, 2, 3, 4]) >>> a[:-1] # 下标可以使用负数,表示从数组后往前数 array([0, 1, 2, 3, 4, 5, 6, 7, 8]) >>> a[2:4] = 100,101 # 下标还可以用来修改元素的值 >>> a array([ 0, 1, 100, 101, 4, 5, 6, 7, 8, 9]) >>> a[1:-1:2] # 范围中的第三个参数表示步长,2表示隔一个元素取一个元素 array([ 1, 101, 5, 7]) >>> a[::-1] # 省略范围的开始下标和结束下标,步长为-1,整个数组头尾颠倒 array([ 9, 8, 7, 6, 5, 4, 101, 100, 1, 0]) >>> a[5:1:-2] # 步长为负数时,开始下标必须大于结束下标 array([ 5, 101])
和Python的列表序列不同,通过下标范围获取的新的数组是原始数组的一个视图。它与原始数组共享同一块数据空间:
>>> b = a[3:7] # 通过下标范围产生一个新的数组b,b和a共享同一块数据空间 >>> b array([101, 4, 5, 6]) >>> b[2] = -10 # 将b的第2个元素修改为-10 >>> b array([101, 4, -10, 6]) >>> a # a的第5个元素也被修改为10 array([ 0, 1, 100, 101, 4, -10, 6, 7, 8, 9])
除了使用下标范围存取元素之外,NumPy还提供了两种存取元素的高级方法。
NumPy和MatLab不一样,对于多维数组的运算,缺省情况下并不使用矩阵运算,如果你希望对数组进行矩阵运算的话,可以调用相应的函数。
NumPy库提供了matrix类,使用matrix类创建的是矩阵对象,它们的加减乘除运算缺省采用矩阵方式计算,因此用法和MatLab十分类似。但是由于NumPy中同时存在ndarray和matrix对象,用户很容易将两者弄混。这有违Python的“显式优于隐式”的原则,因此并不推荐在较复杂的程序中使用matrix。下面是使用matrix的一个例子:
>>> a = np.matrix([[1,2,3],[5,5,6],[7,9,9]]) >>> a*a**-1 matrix([[ 1.00000000e+00, 1.66533454e-16, -8.32667268e-17], [ -2.77555756e-16, 1.00000000e+00, -2.77555756e-17], [ 1.66533454e-16, 5.55111512e-17, 1.00000000e+00]])
因为a是用matrix创建的矩阵对象,因此乘法和幂运算符都变成了矩阵运算,于是上面计算的是矩阵a和其逆矩阵的乘积,结果是一个单位矩阵。
矩阵的乘积可以使用dot函数进行计算。对于二维数组,它计算的是矩阵乘积,对于一维数组,它计算的是点积。当需要将一维数组当作列矢量或者行矢量进行矩阵运算时,推荐先使用reshape函数将一维数组转换为二维数组:
>>> a = array([1, 2, 3]) >>> a.reshape((-1,1)) array([[1], [2], [3]]) >>> a.reshape((1,-1)) array([[1, 2, 3]])
除了dot计算乘积之外,NumPy还提供了inner和outer等多种计算乘积的函数。这些函数计算乘积的方式不同,尤其是当处理多维数组的时候,更容易搞混。下面分别介绍这几个函数。
·dot:对于两个一维的数组,计算的是这两个数组对应下标元素的乘积和(数学上称之为“内积”);对于二维数组,计算的是两个数组的矩阵乘积;对于多维数组,它的通用计算公式如下,即结果数组中的每个元素都是——数组a的最后一维上的所有元素与数组b的倒数第二位上的所有元素的乘积和。
dot(a, b)[i,j,k,m] = sum(a[i,j,:] * b[k,:,m])
下面以两个三维数组的乘积演示一下dot乘积的计算结果。
首先创建两个三维数组,这两个数组的最后两维满足矩阵乘积的条件:
>>> a = np.arange(12).reshape(2,3,2) >>> b = np.arange(12,24).reshape(2,2,3) >>> c = np.dot(a,b)
dot乘积的结果c可以看作是数组a,b的多个子矩阵的乘积:
>>> np.alltrue( c[0,:,0,:] == np.dot(a[0],b[0]) ) True >>> np.alltrue( c[1,:,0,:] == np.dot(a[1],b[0]) ) True >>> np.alltrue( c[0,:,1,:] == np.dot(a[0],b[1]) ) True >>> np.alltrue( c[1,:,1,:] == np.dot(a[1],b[1]) ) True
·inner:和dot乘积一样,对于两个一维数组,计算的是这两个数组对应下标元素的乘积和;对于多维数组,它计算的结果数组中的每个元素都是——数组a和b的最后一维的内积,因此数组a和b的最后一维的长度必须相同。
inner(a, b)[i,j,k,m] = sum(a[i,j,:]*b[k,m,:])
下面是inner乘积的演示:
>>> a = np.arange(12).reshape(2,3,2) >>> b = np.arange(12,24).reshape(2,3,2) >>> c = np.inner(a,b) >>> c.shape (2, 3, 2, 3) >>> c[0,0,0,0] == np.inner(a[0,0],b[0,0]) True >>> c[0,1,1,0] == np.inner(a[0,1],b[1,0]) True >>> c[1,2,1,2] == np.inner(a[1,2],b[1,2]) True
outer:只按照一维数组进行计算,如果传入参数是多维数组,则先将此数组展平为一维数组,之后再进行运算。outer乘积计算的列向量和行向量的矩阵乘积:
>>> np.outer([1,2,3],[4,5,6,7]) array([[ 4, 5, 6, 7], [ 8, 10, 12, 14], [12, 15, 18, 21]])
矩阵中更高级的一些运算可以在NumPy的线性代数子库linalg中找到。例如inv函数计算逆矩阵,solve函数可以求解多元一次方程组。下面是solve函数的一个例子:
>>> a = np.random.rand(10,10) >>> b = np.random.rand(10) >>> x = np.linalg.solve(a,b) >>> np.sum(np.abs(np.dot(a,x) - b)) 3.1433189384699745e-15
solve函数有两个参数a和b。a是一个N×N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。