Python NumPy库学习

前言

划水学习一下python科学计算库,提升一些自己的逼格:)

Numpy

Numpy是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效的多。

Numpy包括:

  • 一个强大的N维数组对象Array;
  • 比较成熟的(广播)函数库;
  • 用于整合C/C++和Fortran代码的工具包;
  • 使用的线性代数、傅里叶变换和随机数生成函数

Numpy提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库,专为进行严格的数字处理。

安装方法

pip install --user numpy

用法学习

导入numpy

>>> from numpy as np

通过给array函数传递Python的序列对象创建数组,如果传递的是多层嵌套的序列,将创建多维数组

>>> 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('int64')

数组的大小可以通过其shape属性获得:

>>> a.shape
(4,)
>>> c.shape
(3, 4)

数组元素的存取方法和Python的标准方法相同

>>> a = np.arange(10) # arange函数用于创建等差数组,类似range函数,区别在于arange返回的是一个数据,而range返回的是list。
>>> 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的第二个元素修改为-10
>>> b
array([101,   4, -10,   6])
>>> a # a 的第5个元素也被修改为10
array([  0,   1, 100, 101,   4, -10,   6,   7,   8,   9])

NumPy和Matlab不一样对于多维数组的运算,缺省(默认)情况下并不使用矩阵运算,如果你希望对数组进行矩阵运算的话,可以调用相应的函数。

NumPy库提供了matrix类,使用matrix类创建的是矩阵对象,它们的加减乘除运算缺省(默认)采用矩阵方式计算,因此用法和MatLab十分类似。但是由于NumPy中同时存在ndarray和matrix对象,用户很容易将两者弄混。这有违Python的“显式优于隐式”的原则,因此并不推荐在较复杂的程序中使用matrix。

>>> a = np.matrix([[1, 2, 3],[5, 5, 6],[7, 9, 9]])
>>> a*a**-1
matrix([[  1.00000000e+00,   0.00000000e+00,  -5.55111512e-17],
        [  4.44089210e-16,   1.00000000e+00,   3.33066907e-16],
        [  4.44089210e-16,   0.00000000e+00,   1.00000000e+00]])

因为a是用matrix创建的矩阵对象,因此乘法和幂运算符都变成了矩阵运算,于是上面计算的是矩阵a和其逆矩阵的成绩,结果是一个单位矩阵。

矩阵的乘积可以使用dot函数进行计算。对于二维数组,它计算的是矩阵乘积,对于一维数组,它计算的是点积。当需要将一位数组当做矢量或者行矢量进行矩阵计算时,推荐先使用reshape函数将一位数组转换成二维数组:

>>> a = np.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)
>>> a
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]]])
>>> b
array([[[12, 13, 14],
        [15, 16, 17]],

       [[18, 19, 20],
        [21, 22, 23]]])
>>> c
array([[[[ 15,  16,  17],
         [ 21,  22,  23]],

        [[ 69,  74,  79],
         [ 99, 104, 109]],

        [[123, 132, 141],
         [177, 186, 195]]],


       [[[177, 190, 203],
         [255, 268, 281]],

        [[231, 248, 265],
         [333, 350, 367]],

        [[285, 306, 327],
         [411, 432, 453]]]])

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)
>>> a
array([[[ 0,  1],
        [ 2,  3],
        [ 4,  5]],

       [[ 6,  7],
        [ 8,  9],
        [10, 11]]])
>>> b
array([[[12, 13],
        [14, 15],
        [16, 17]],

       [[18, 19],
        [20, 21],
        [22, 23]]])
>>> c.reshape(2,3,2,3)
array([[[[ 13,  15,  17],
         [ 19,  21,  23]],

        [[ 63,  73,  83],
         [ 93, 103, 113]],

        [[113, 131, 149],
         [167, 185, 203]]],


       [[[163, 189, 215],
         [241, 267, 293]],

        [[213, 247, 281],
         [315, 349, 383]],

        [[263, 305, 347],
         [389, 431, 473]]]])
>>> 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)
>>> a
array([[ 0.24353531,  0.29659437,  0.28101984,  0.52485836,  0.00183735,
         0.30703321,  0.53983274,  0.27464789,  0.73209528,  0.67549256],
       [ 0.01559972,  0.92602761,  0.47485384,  0.61468036,  0.37011691,
         0.85070726,  0.41072878,  0.10564565,  0.05305942,  0.23846902],
       [ 0.94943831,  0.85915453,  0.65884278,  0.64876059,  0.35727861,
         0.84109415,  0.33477114,  0.82056791,  0.03162534,  0.39403621],
       [ 0.24769378,  0.07308019,  0.29308628,  0.33071024,  0.14306124,
         0.86322678,  0.4991317 ,  0.96904689,  0.03166429,  0.46169691],
       [ 0.30099666,  0.51024363,  0.55342961,  0.27470896,  0.53636676,
         0.39594886,  0.29878566,  0.13328393,  0.64219768,  0.30102947],
       [ 0.43426324,  0.08116221,  0.20591229,  0.73077177,  0.23106824,
         0.51030064,  0.26527246,  0.25208973,  0.45159047,  0.96635508],
       [ 0.89780861,  0.34967281,  0.04718447,  0.12239271,  0.21352227,
         0.53960624,  0.30945019,  0.85174851,  0.65440119,  0.29891452],
       [ 0.98515015,  0.50517941,  0.42574849,  0.29224782,  0.11825866,
         0.92779448,  0.64939983,  0.14020983,  0.46421195,  0.3061906 ],
       [ 0.30094076,  0.12430322,  0.97022063,  0.69191872,  0.32091803,
         0.37656987,  0.30484649,  0.3463515 ,  0.43320042,  0.9442171 ],
       [ 0.94531206,  0.59132457,  0.81175354,  0.12371409,  0.43948888,
         0.54814142,  0.03465268,  0.62540882,  0.16018337,  0.35340181]])
>>> b
array([ 0.96002843,  0.73284286,  0.19247122,  0.83485676,  0.7007295 ,
        0.04948528,  0.22721187,  0.83641745,  0.9003207 ,  0.43669935])
>>> x
array([-0.77451899,  0.47860311,  0.98693451, -1.35492025, -0.72791704,
        0.33877835,  1.11544924,  0.0841362 ,  0.34789791,  0.67781082])
>>> np.sum(np.abs(np.dot(a,x) - b))
9.9920072216264089e-16

solve函数有两个参数a和b。a是一个N x N的二维数组,而b是一个长度为N的一维数组,solve函数找到一个长度为N的一维数组x,使得a和x的矩阵乘积正好等于b,数组x就是多元一次方程组的解。