在 Numpy 的使用过程中,我们常常需要从原数组中提取部分信息或将原数组拼凑成一个高维的数组,这些操作根本上来说都是希望基于原数组得到一个维度不同的数组。 下面对一些常用方法进行总结。
比如我们希望从一个二维数组中抽出某些列,可以这样:
a = np.arange(12).reshape(4,3)
a
array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])
b = np.array([True, False, True, False])
a[b,:]
array([[0, 1, 2], [6, 7, 8]])
利用 ndarray 的 broadcasting 机制,我们可以直接写成
a[b]
array([[0, 1, 2], [6, 7, 8]])
b 也可以不用与第一维的长度匹配,缺失部分默认为 False
b = np.array([True, False, True])
a[b,:]
array([[0, 1, 2], [6, 7, 8]])
需要注意的是 b 必須也是一个 ndarray,否则 numpy 不会将其当作一个 mask 数组,而是当作 index 数组,比如
b = [True, False, True, False]
a[b], a[b,:]
(array([[3, 4, 5], [0, 1, 2], [3, 4, 5], [0, 1, 2]]), array([[3, 4, 5], [0, 1, 2], [3, 4, 5], [0, 1, 2]]))
上例中 numpy 将 True 和 False 直接当作 1 和 0 来处理。其实用 index 数组同样可达到 mask 数组的效果
b = [0, 2]
a[b,:]
array([[0, 1, 2], [6, 7, 8]])
总结一下,只有布尔型的 ndarray 才能当作 mask 数组提取数值。下面列举一些高维的例子:
a = np.arange(16).reshape((2,2,4))
a
array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]])
b = np.array([[True, False], [True, False]])
a[b,:]
array([[ 0, 1, 2, 3], [ 8, 9, 10, 11]])
这等价于在前两维指定坐标
a[[0,1], [0, 0], :]
array([[ 0, 1, 2, 3], [ 8, 9, 10, 11]])
a[b.nonzero()]
array([[ 0, 1, 2, 3], [ 8, 9, 10, 11]])
有两种常用方法可以用于增加数组维度,一是使用 index 数组(必须是 ndarray 类型),二是借助 newaxis
,这里只讨论第二种方法,因为其简单直观。
比如我们希望将两个二维数组在一个新的维度上拼接成一个三维数组,我们可以先利用 newaxis
构建出这个维度,然后再使用 concatenate
,vstack
, hstack
等方法。
a = np.array([[1,2],[3,4]])
b = np.array([[5,6],[7,8]])
a_ = a[..., np.newaxis]
b_ = b[..., np.newaxis]
c = np.concatenate((a_, b_), 2)
c
array([[[1, 5], [2, 6]], [[3, 7], [4, 8]]])
注意: concatenate
vstack
hstack
等方法并不能修改数组本身的维度。
假设我们想要在第一维之前插入新维度,可以更简单地写成:
c = np.array([a, b])
c
array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
当然如果觉得 newaxis
麻烦,可以先用上面的方法增加新维度,然后再将其从第一维移至想要的位置:
np.rollaxis(c,0,3)
array([[[1, 5], [2, 6]], [[3, 7], [4, 8]]])
注意: np.rollaxis
这个函数设计得极其糟糕,它的第二个参数为需要调整位置的轴,第三个参数为目标位置。如果当前位置等于目标位置,函数不作任何操作,否则当前轴将被调整至紧邻目标位置的前一位置处,np.rollaxis(x,n,n+1)
都是没有实际效果的写法。