NumPy之操控ndarray的形状

系列文章

一次性搞定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必须相同。
例如一个的ndarray可以和另一个的ndarray沿着第一个axis进行堆叠,但却不能和另一个的ndarray沿着第一个axis进行堆叠。

ndarray的拆分

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

举例,对于一个6列的ndarray,如果传入参数3,那么NumPy将会按如下方式,将这个ndarray进行等分:

同样的ndarray,如果传入参数(1,3),那么NumPy会在对应的列后面划一条线(第一列后面、第三列后面),进行分拆:

代码示例如下:

>>> 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的形状)