NumPy之操控ndarray的形状

作者: 泰克尼客 | 来源:发表于2019-10-29 10:08 被阅读0次

    系列文章

    一次性搞定NumPy入门基础知识
    NumPy之操控ndarray的形状
    NumPy之浅拷贝和深拷贝
    NumPy之索引技巧

    概述

    这里主要总结针对ndarray形状的三种操作:

    • 改变ndarray的形状
    • 堆叠几个ndarray
    • 拆分ndarray

    改变ndarray的形状

    以下列ndarray为例:

    >>> a = np.floor(10*np.random.random((3,4)))
    >>> a
    array([[ 2.,  8.,  0.,  6.],
           [ 4.,  5.,  1.,  1.],
           [ 8.,  9.,  3.,  6.]])
    >>> a.shape
    (3, 4)    
    

    如下三个是改变ndarray形状的三个常用命令,注意这三个命令并不改变原始的ndarray:

    >>> a.ravel()  # 将原始ndarray转化为一维ndarray并返回
    array([ 2.,  8.,  0.,  6.,  4.,  5.,  1.,  1.,  8.,  9.,  3.,  6.])
    
    >>> a.reshape(6,2)  # 指定新的shape,返回不同shape的ndarray
    array([[ 2.,  8.],
           [ 0.,  6.],
           [ 4.,  5.],
           [ 1.,  1.],
           [ 8.,  9.],
           [ 3.,  6.]])
    
    >>> a.T  # 返回转置后的ndarray,类似于线性代数中的转置矩阵
    array([[ 2.,  4.,  8.],
           [ 8.,  5.,  9.],
           [ 0.,  1.,  3.],
           [ 6.,  1.,  6.]])
    >>> a.T.shape
    (4, 3)
    >>> a.shape
    (3, 4)  
    

    注意,上述的几个命令,改变形状时遵循的原则都是“C-style”的。也就是说,最右边的索引,变化的最快。以a.ravel()为例,在改变形状时,a[0][0]之后紧跟的元素是a[0][1]。NumPy的内部存储也是遵循这种顺序,因此,使用ravel()并不会导致拷贝的操作。当然,ravel()reshape()都可以通过参数来进行控制,改变成例如采用"FORTRAN-style"的模式,使得最左边的索引变得最快。

    注意下面两个方法的区别:reshape返回一个新的ndarray,而resize则会直接修改这个ndarray本身。

    >>> a
    array([[ 2.,  8.,  0.,  6.],
           [ 4.,  5.,  1.,  1.],
           [ 8.,  9.,  3.,  6.]])
    >>> a.resize((2,6))
    >>> a
    array([[ 2.,  8.,  0.,  6.,  4.,  5.],
           [ 1.,  1.,  8.,  9.,  3.,  6.]]) 
    

    如果reshape的某一个参数是-1,那么reshape的最终形状将会由函数自动计算出来。

    >>> a.reshape(3,-1)
    array([[ 2.,  8.,  0.,  6.],
           [ 4.,  5.,  1.,  1.],
           [ 8.,  9.,  3.,  6.]]) 
    

    ndarray的堆叠

    几个不同的ndarray可以沿着指定的axis进行堆叠,举例如下:

    >>> a = np.floor(10*np.random.random((2,2)))
    >>> a
    array([[ 8.,  8.],
           [ 0.,  0.]])
    >>> b = np.floor(10*np.random.random((2,2)))
    >>> b
    array([[ 1.,  8.],
           [ 0.,  4.]])
    
    >>> np.vstack((a,b))
    array([[ 8.,  8.],
           [ 0.,  0.],
           [ 1.,  8.],
           [ 0.,  4.]])
    
    >>> np.hstack((a,b))
    array([[ 8.,  8.,  1.,  8.],
           [ 0.,  0.,  0.,  4.]])    
    

    上例以二维ndarray为例,vstack方法沿着垂直的方向堆叠(也就是第一个axis),hstack方法沿着水平方向堆叠(也就是第二个axis)。

    需要注意column_stack方法,对于两个1D的ndarray,它会把它们堆叠成一个2D的ndarray。例如:

    >>> a = np.array([1,2,3])
    >>> b = np.array([4,5,6])
    >>> np.column_stack((a,b))
    array([[1, 4],
           [2, 5],
           [3, 6]])
    

    对于2D的ndarray来说,column_stack的作用等同于hstack,否则hstack会把两个1D的ndarray当作行来进行合并:

    >>> from numpy import newaxis
    >>> a = np.floor(10*np.random.random((2,2)))
    >>> b = np.floor(10*np.random.random((2,2)))
    >>> np.column_stack((a,b))    
    array([[ 8.,  8.,  1.,  8.],
           [ 0.,  0.,  0.,  4.]])
    >>> a = np.array([4.,2.])
    >>> b = np.array([3.,8.])
    >>> np.column_stack((a,b))     
    array([[ 4., 3.],
           [ 2., 8.]])
    >>> np.hstack((a,b))           # 结果不同,这时hstack把两个1D ndarray当作行向量来处理了
    array([ 4., 2., 3., 8.])
    >>> a[:,newaxis]               # 变换axis
    array([[ 4.],
           [ 2.]])
    >>> np.column_stack((a[:,newaxis],b[:,newaxis]))
    array([[ 4.,  3.],
           [ 2.,  8.]])
    >>> np.hstack((a[:,newaxis],b[:,newaxis]))   # 这时候结果一致了
    array([[ 4.,  3.],
           [ 2.,  8.]])  
    

    另一方面,row_stack在任何时候和vstack都是等同的。总结一下,对于两维以上的ndarray,hstack沿着第二个axis合并,vstack沿着第一个axis合并,concatenate则通过参数控制,来决定沿着哪个方向进行合并。

    另外,在一些复杂的场景,可以使用r_c_来创建ndarray,举例如下:

    >>> np.r_[1:4,0,4]
    array([1, 2, 3, 0, 4])
    

    其中的:和arange方法中的意义相同。在这里,r_c_的作用分别类似于vstackhstack的作用,但也允许通过参数控制来决定沿着哪个axis进行堆叠。

    需要注意,沿着某一个axis进行堆叠时,参与堆叠的ndarray的其他axis的length必须相同。
    例如一个3 \times 2的ndarray可以和另一个5 \times 2的ndarray沿着第一个axis进行堆叠,但却不能和另一个3 \times 3的ndarray沿着第一个axis进行堆叠。

    ndarray的拆分

    一个ndarray可以拆分成几个小的ndarray。这里以hsplit为例,该函数可以沿着水平方向拆分ndarray。可以以两种方式给其传参。一种是,指定参数N,说明将原始ndarray拆分成几个小的ndarray,NumPy会根据这个参数,沿着这个方向将原来的ndarray等分成N份(如果列数不能被N整除,将会报错);另一种是,指定在哪些列后面切一刀,NumPy会根据这个指示,将原始的ndarray进行拆分。

    举例,对于一个6列的ndarray,如果传入参数3,那么NumPy将会按如下方式,将这个ndarray进行等分:
    \begin{array}{cc:cc:cc} a_{00} & a_{01} & a_{02} & a_{03} & a_{04} & a_{05} \\ a_{10} & a_{11} & a_{12} & a_{13} & a_{14} & a_{15} \end{array}
    同样的ndarray,如果传入参数(1,3),那么NumPy会在对应的列后面划一条线(第一列后面、第三列后面),进行分拆:
    \begin{array}{c:cc:ccc} a_{00} & a_{01} & a_{02} & a_{03} & a_{04} & a_{05} \\ a_{10} & a_{11} & a_{12} & a_{13} & a_{14} & a_{15} \end{array}

    代码示例如下:

    >>> a = np.floor(10*np.random.random((2,12)))
    >>> a
    array([[ 9.,  5.,  6.,  3.,  6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
           [ 1.,  4.,  9.,  2.,  2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])
    >>> np.hsplit(a,3)   # Split a into 3
    [array([[ 9.,  5.,  6.,  3.],
           [ 1.,  4.,  9.,  2.]]), array([[ 6.,  8.,  0.,  7.],
           [ 2.,  1.,  0.,  6.]]), array([[ 9.,  7.,  2.,  7.],
           [ 2.,  2.,  4.,  0.]])]
    >>> np.hsplit(a,(3,4))   # Split a after the third and the fourth column
    [array([[ 9.,  5.,  6.],
           [ 1.,  4.,  9.]]), array([[ 3.],
           [ 2.]]), array([[ 6.,  8.,  0.,  7.,  9.,  7.,  2.,  7.],
           [ 2.,  1.,  0.,  6.,  2.,  2.,  4.,  0.]])]     
    

    相关文章

      网友评论

        本文标题:NumPy之操控ndarray的形状

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