机器学习系列(四)——Numpy中的矩阵运算与索引

作者: Ice_spring | 来源:发表于2019-06-07 17:50 被阅读0次

    效率对比

    给定向量a=[0,1,2,3,...,9],让其中每一个元素乘2得到[0,2,4,6,...,18]。

    n=100000
    L=[i for i in range(L)]
    2*L
    out:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    可以发现python原生list不支持这种整体操作。当然可以像下面这样来达成目的:

    A=[]
    for e in L:
        A.append(2*e)
    A
    out:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    

    但是,这样做效率其实是很低的,低于生成表达式效率

    #时间效率测试
    %%time
    A=[]
    for e in L:
        A.append(2*e)
    out:CPU times: user 167 ms, sys: 8 ms, total: 175 ms
        Wall time: 174 ms
    
    
    %%time
    A=[2*e for e in L]
    out:CPU times: user 66.3 ms, sys: 20.2 ms, total: 86.5 ms
        Wall time: 85.3 ms
    

    如果使用numpy.array方式,效率将更强:

    %%time
    A=np.array(2*e for e in L)
    out:CPU times: user 4.28 ms, sys: 7.84 ms, total: 12.1 ms
        Wall time: 12 ms
    

    可以看到速度大大提高。而且numpy支持直接的整体操作速度又要更快:

    L=np.array([1,2,3,4])
    2*L
    out:array([2, 4, 6, 8])
    

    numpy将数组看成向量或矩阵,而且加入了非常好的优化,运行速度非常非常快,和python原生list不在一个数量级。

    Universasl Function

    x=np.arange(6).reshape((2,3))
    x
    array([[0, 1, 2],
           [3, 4, 5]])
    ××××××××××××××××××××××××××××××××××××
    x*2#运算是对其中每个元素的操作
    array([[ 0,  2,  4],
           [ 6,  8, 10]])
    x+1#每个元素加1
    array([[1, 2, 3],
           [4, 5, 6]])
    x/2#每个元素除以2
    array([[0. , 0.5, 1. ],
           [1.5, 2. , 2.5]])
    x//2#除以2取整
    array([[0, 0, 1],
           [1, 2, 2]])
    x**2#二次方
    array([[ 0,  1,  4],
           [ 9, 16, 25]])
    x%2#每个元素取余
    array([[0, 1, 0],
           [1, 0, 1]])
    1/x#取倒数
    np.cos(x)
    np.arctan(x)
    np.exp(x)
    np.power(3,x)#等价于3**x
    np.log(x+0.001)#默认以e为底,log2,log10
    

    以上是对一个矩阵的操作,numpy还支持矩阵与矩阵之间的运算:

    A=np.arange(4).reshape(2,2)
    B=np.full((2,2),3)
    A*B#相乘是对应元素相乘,类似有相加是对应元素相加
    out:array([[0, 3],
           [6, 9]])
    

    如果要进行矩阵乘法,用dot方法:

    A.dot(B)
    out:array([[ 3,  3],
           [15, 15]])
    v.dot(A)
    array([4, 7])
    

    在向量与矩阵做乘法时,系统会自动判断向量处理为列向量还是行向量,A.dot(v)一样能运行。
    A的转置:A.T
    A的逆矩阵:np.linalg.inv(A)
    非方阵可以求广义逆矩阵:np.linalg.pinv(B)

    矩阵与向量运算

    向量与矩阵相加默认是向量与矩阵每一行相加,相乘是与每行做乘法。

    A
    out:array([[0, 1],
           [2, 3]])
    v=np.array([1,2])
    v+A
    out:array([[1, 3],
           [3, 5]])
    v*A
    out:array([[0, 2],
           [2, 6]])
    

    上述方式往往不太直观,可以用vstack方法对v处理后再相加,效果一样:

    v1=np.vstack([v]*A.shape[0])
    out:array([[1, 2],
           [1, 2]])
    v1+A
    out:array([[1, 3],
           [3, 5]])
    

    对于堆叠,有一个tile方法可以更方便进行,如对v在行上堆叠两次,列上堆叠一次:

    np.tile(v,(2,1))
    out:array([[1, 2],
           [1, 2]])
    

    聚合运算

    聚合运算是一组值处理为一个值的过程,如求和。

    L=np.random.random(10)
    sum(L)#python原生方法
    out:5.379448046210108
    np.sum(L)#numpy方法
    out:5.379448046210108
    

    两种求和差别在效率,np.sum效率远远超高sum。可用%time检测。
    相应有:

    np.max(L)#这种方式更全,建议使用
    np.min(L)
    #也可以这样调用
    L.max()
    L.min()
    L.sum()
    

    如果是二维矩阵,返回的仍然是一个值,如果要求每行的和或者每行的最大值,则需要指定axis:

    A=np.arange(9).reshape(3,3)
    out:array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
    np.sum(A)
    out:36
    np.sum(A,axis=0)#axis=0,按行运算即沿着行进行运算,求得的是列和
    out:array([ 9, 12, 15])
    np.sum(A,axis=1)
    out:array([ 3, 12, 21])#axis=1,按列运算即沿着列进行运算,求得的是对应的行和
    

    下面这些聚合运算同样可以指定axis
    返回矩阵中所有元素乘积:np.prod(A)
    返回均值:np.mean(A)
    返回中位数:np.median(A)
    返回方差:np.var(A)
    返回标准差:np.std(A)
    返回百分位分位点:np.percentile(L,q=50)50%分位数

    for p in [25,50,75]:
        print(np.percentile(A,q=p))
    25%分位点:2.0
    50%:4.0
    75%:6.0
    

    索引——arg运算

    我们可以轻易求得向量中的最小值,但有时我们希望得到这个最小值的索引。

    x=np.array([2,1,7,4,6,5,9,12,3])
    np.max(x)
    out:12
    np.argmax(x)
    out:7
    

    排序中使用索引

    x=np.arange(16)
    np.random.shuffle(x)#对x乱序排列,会改变x本身
    x
    out:array([10, 13,  0, 14, 12, 15,  8,  9,  1,  7,  2,  5, 11,  6,  4,  3])
    
    np.sort(x)#不改变x本身
    out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
    

    如果希望直接改变x本身,则x.sort()即可。
    如果对二维矩阵进行排序,默认是对每一行排序,默认axis为1

    m=np.random.randint(10,size=(4,4))
    m
    out:array([[2, 2, 1, 2],
           [7, 6, 0, 5],
           [5, 3, 9, 0],
           [3, 7, 1, 0]])
    np.sort(m)
    out:array([[1, 2, 2, 2],
           [0, 5, 6, 7],
           [0, 3, 5, 9],
           [0, 1, 3, 7]])
    
    np.sort(m,axis=0)
    out:array([[2, 2, 0, 0],
           [3, 3, 1, 0],
           [5, 6, 1, 2],
           [7, 7, 9, 5]])
    

    接着讨论对于向量x,排序后返回排序索引:

    np.argsort(x)
    out:array([ 2,  8, 10, 15, 14, 11, 13,  9,  6,  7,  0, 12,  4,  1,  3,  5])
    

    ·快速排序中有个partition,小于标定点的放左边,大于的放右边,也有np.argpartition方法求索引,只是用的比较少。

    np.partition(x,9)
    out:array([ 7,  3,  0,  4,  6,  5,  8,  2,  1,  9, 13, 15, 11, 12, 14, 10])
    

    Fancy Indexing

    指定索引下标后开始索引

    x=np.arange(16)
    out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
    [x[3],x[5],x[8]]
    out:[3, 5, 8]
    ind=[3,5,8]
    x[ind]
    out:array([3, 5, 8])
    

    如果索引指定为二维,则会返回按索引排列的二维矩阵:

    ind=np.array([[0,2],[1,3]])
    x[ind]
    out:array([[0, 2],
           [1, 3]])
    

    同样在二维矩阵中也可以有一些神奇的索引:

    m=x.reshape(4,-1)#不改变原数组
    m
    out:array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11],
           [12, 13, 14, 15]])
    

    现在对一些数据感兴趣,但是他们没有规律:

    row=np.array([0,1,2])#(0,1)(1,2)(2,3)
    col=np.array([1,2,3])
    m[row,col]
    out:array([ 1,  6, 11])
    m[0,col]#第0行的列
    out:array([1, 2, 3])
    

    col=[True,False,True,True]#对0,2,3行感兴趣
    m[1:3,col]#对中间两列的col感兴趣
    out:array([[ 4,  6,  7],
           [ 8, 10, 11]])
    

    numpy.array比较得bool值

    x
    out:array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
    x<3
    out:array([ True,  True,  True, False, False, False, False, False, False,
           False, False, False, False, False, False, False])
    x==3
    out:array([False, False, False,  True, False, False, False, False, 
            False,False, False, False, False, False, False, False])
    x!=3
    out:array([ True,  True,  True, False,  True,  True,  True,  True,  True,
            True,  True,  True,  True,  True,  True,  True])
    

    可以结合加减乘除获得更复杂表达式,也可以在二维矩阵使用:

    2*x==24-4*x
    out:array([False, False, False, False,  True, False, False, False,
            False,False, False, False, False, False, False, False])
    

    假如x是年龄样本,求问有多少个年龄小于3的孩子:

    np.sum(x<=3)#这种方式将true当作1,false当作0
    out:4
    

    数非零的元素个数:np.count_nonzero(x<=3)将true当作1,false当作0
    查看是否有为0的元素:np.any(x==0)
    查看是否有小于0的元素:np.any(x<0)
    相应有:np.all(x>=0)必须全部>=0才会返回True
    所有以上方法对二维矩阵也适用。

    np.sum(m%2==0) #求m中偶数的个数,默认全局,可以指定axis求每行或每列
    out:8
    
    np.sum(m%2==0,axis=0)#按行,即看每列有多少偶数
    out:array([4, 0, 4, 0])
    
    np.all(m>0,axis=1)
    out:array([False,  True,  True,  True])#第0行不满足每个元素都大于0
    

    对以上比较也可以进行组合,如求x中>3,<10的个数:

    np.sum((x>3)&(x<10))#这里必须使用位运算符
    out:6
    

    求年龄为偶数或者大于10的个数

    np.sum((x%2==0)|(x>10))
    out:11
    

    求不等于0的

    np.sum(~(x==0))#等价于np.sum(x!=0)
    out:15
    

    下面看索引的用法,返回x中小于5的数组成的子数组:

    x[x<5]
    out:array([0, 1, 2, 3, 4])
    

    返回年龄是偶数的:

    x[x%2==0]
    out:array([ 0,  2,  4,  6,  8, 10, 12, 14])
    

    返回矩阵m中最后一个元素能被3整除的行:

    m[m[:,3]%3==0,:]
    out:array([[ 0,  1,  2,  3],
           [12, 13, 14, 15]])
    

    这些方法在实际处理数据时有非常大的用处,比如boston房价数剧集,取出室内面积大于50的行,去除距离中心小于100的行等。
    另外有一个pandas库可对数据进行预处理。以后有机会会更新在笔者简书中。

    相关文章

      网友评论

        本文标题:机器学习系列(四)——Numpy中的矩阵运算与索引

        本文链接:https://www.haomeiwen.com/subject/wmgmxctx.html