数组切片:获取子数组
正如此前用中括号获取单个数组元素,我们也可以用切片(
slice
)符号
获取子数组,切片符号用冒号(
:
)表示。
NumPy
切片语法和
Python
列
表的标准切片语法相同。为了获取数组
x
的一个切片,可以用以下方
式:
x[start:stop:step]
如果以上
3
个参数都未指定,那么它们会被分别设置默认值
start=0
、
stop=
维度的大小(
size of dimension
)和
step=1
。我们将详细介
绍如何在一维和多维数组中获取子数组。
一维子数组
x = np.arange(10)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x[:5] # 前五个元素
x
array([0, 1, 2, 3, 4])
x[5:] # 索引五之后的元素
x
array([5, 6, 7, 8, 9])
x[4:7] # 中间的子数组
x
array([4, 5, 6])
x[::2] # 每隔一个元素
x
array([0, 2, 4, 6, 8])
x[1::2] # 每隔一个元素,从索引1开始
x
array([1, 3, 5, 7, 9])
x[::-1] # 所有元素,逆序的
x
array([9, 8, 7, 6, 5, 4, 3, 2, 1, 0])
x[5::-2] # 从索引5开始每隔一个元素逆序
x
array([5, 3, 1])
多维子数组
多维切片也采用同样的方式处理,用冒号分隔。例如:
x2[:2, :3] # 两行,三列
array([[12, 7, 5],
[ 0, 1, 5]])
x2[:3, ::2] # 所有行,每隔一列
array([[12, 5],
[ 0, 5],
[ 3, 5]])
最后,子数组维度也可以同时被逆序:
x2[::-1, ::-1]
array([[ 0, 5, 0, 3],
[ 9, 5, 1, 0],
[ 5, 5, 7, 12]])
获取数组的行和列
一种常见的需求是获取数组的单行和单列。你可以将索引与切片组
合起来实现这个功能,用一个冒号(
:
)表示空切片:
在获取行时,出于语法的简介考虑,可以省略空的切片:
非副本视图的子数组
关于数组切片有一点很重要也非常有用,那就是数组切片返回的是
数组数据的视图,而不是数值数据的副本。这一点也是
NumPy
数
组切片和
Python
列表切片的不同之处:在
Python
列表中,切片是
值的副本。例如此前示例中的那个二维数组:
从中抽取一个
2×2
的子数组
现在如果修改这个子数组,将会看到原始数组也被修改了!结果如
下所示:
这种默认的处理方式实际上非常有用:它意味着在处理非常大的数
据集时,可以获取或处理这些数据集的片段,而不用复制底层的数
据缓存。
创建数组的副本
尽管数组视图有一些非常好的特性,但是在有些时候明确地复制数
组里的数据或子数组也是非常有用的。可以很简单地通过
copy()
方法实现:
x2_sub_copy = x2[:2, :2].copy()
print(x2_sub_copy)
[[99 7]
[ 0 1]]
如果修改这个子数组,原始的数组不会被改变:
x2_sub_copy[0, 0] = 42
print(x2_sub_copy)
[[42 7]
[ 0 1]]
print(x2)
[[99 7 5 5]
[ 0 1 5 9]
[ 3 0 5 0]]
数组的变形
另一个有用的操作类型是数组的变形。数组变形最灵活的实现方式是通
过
reshape()
函数来实现。例如,如果你希望将数字
1~9
放入一个
3×3
的矩阵中,可以采用如下方法:
grid = np.arange(1, 10).reshape((3, 3))
print(grid)
[[1 2 3]
[4 5 6]
[7 8 9]]
如果希望该方法可行,那么原始数组的大小必须和变形后数组
的大小一致。如果满足这个条件,
reshape
方法将会用到原始数组的一
个非副本视图。但实际情况是,在非连续的数据缓存的情况下,返回非
副本视图往往不可能实现.
另外一个常见的变形模式是将一个一维数组转变为二维的行或列的矩
阵。你也可以通过
reshape
方法来实现,或者更简单地在一个切片操
作中利用
newaxis
关键字: