系列文章
一次性搞定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_
的作用分别类似于vstack
和hstack
的作用,但也允许通过参数控制来决定沿着哪个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.]])]