24_Numpy数组通过切片选择和分配子数组
在Python中,使用冒号表示的[[start:stop:step]切片,您可以选择和检索序列对象的一部分,例如列表,字符串或tapple,或分配另一个值。
也可以通过切片选择部分数组并将其提取或将另一个值分配给NumPy数组ndarray。
将描述以下内容。
- 切片的基础
- 切片一维NumPy数组ndarray
- 切片多维NumPy数组ndarray
- 查看(参考)并复制
- 与花式索引组合(按列表选择)
如果要提取满足条件的行和列,请参考以下文章。
切片的基础
在Python中,使用冒号表示的[[start:stop:step]切片,您可以选择和检索序列对象的一部分,例如列表,字符串或tapple,或分配另一个值。
import numpy as np
l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(l[4:8])
# [4, 5, 6, 7]
print(l[-5:-2])
# [5, 6, 7]
print(l[::-1])
# [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
切片一维NumPy数组ndarray
选择
如果要按切片选择一维NumPy数组numpy.ndarray,则与上面的Python基本切片相同。
a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]
print(a[4:8])
# [4 5 6 7]
print(a[-5:-2])
# [5 6 7]
print(a[::-1])
# [9 8 7 6 5 4 3 2 1 0]
代入
使用切片的赋值行为在Python列表(列表类型)和NumPy数组numpy.ndarray之间是不同的
也就是说,如果右侧是标量值,则将切片中选择的所有元素替换为标量值,如果它是一维数组,则按原样替换它。
a[3:6] = 100
print(a)
# [ 0 1 2 100 100 100 6 7 8 9]
a[3:6] = [100, 200, 300]
print(a)
# [ 0 1 2 100 200 300 6 7 8 9]
分配数组时,请注意,如果要分配的数组中的元素数与切片中选择的元素数不匹配,则会发生错误ValueError。
# a[3:6] = [100, 200, 300, 400]
# ValueError: cannot copy sequence with size 4 to array axis with dimension 3
这同样适用于通过指定步骤的切片对离散值的分配。
a = np.arange(10)
print(a)
# [0 1 2 3 4 5 6 7 8 9]
print(a[2:8:2])
# [2 4 6]
a[2:8:2] = 100
print(a)
# [ 0 1 100 3 100 5 100 7 8 9]
a[2:8:2] = [100, 200, 300]
print(a)
# [ 0 1 100 3 200 5 300 7 8 9]
切片多维NumPy数组ndarray
对于多维NumPy数组ndarray,可以指定每个维的切片,并以逗号分隔。 以下面的二维数组为例。
选择
指定每个维度的切片,以逗号分隔。
print(a[1:, 1:3])
# [[ 5 6]
# [ 9 10]]
行的选择
整个切片[:]选择一行。在这种情况下,可以省略结尾的[,:]。
print(a[1:, :])
# [[ 4 5 6 7]
# [ 8 9 10 11]]
print(a[1:])
# [[ 4 5 6 7]
# [ 8 9 10 11]]
选择一行时,如果通过标量值而不是切片来指定索引,则它将成为一维数组,但是,如果在切片中选择一行,则它将成为与原始数组类似的二维数组。
print(a[1])
# [4 5 6 7]
print(a[1].shape)
# (4,)
print(a[1:2])
# [[4 5 6 7]]
print(a[1:2].shape)
# (1, 4)
当形状很重要(例如矩阵计算)时要小心。
列的选择
列选择也是如此。在这种情况下,不能省略第一个:。
print(a[:, 1:3])
# [[ 1 2]
# [ 5 6]
# [ 9 10]]
与行一样,选择一列时,如果通过标量值而不是切片来指定索引,则它将成为一维数组,但是如果在切片中选择一列,则它将成为与原始数组类似的二维数组。
print(a[:, 1])
# [1 5 9]
print(a[:, 1].shape)
# (3,)
print(a[:, 1:2])
# [[1]
# [5]
# [9]]
print(a[:, 1:2].shape)
# (3, 1)
代入
至于对多维NumPy数组ndarray的分配,与一维的情况一样,广播并分配右侧的值。
分配数组时,请注意,如果要分配的数组中的元素数量与切片选择的区域中的对应元素数量不匹配,则会发生错误ValueError。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(a[1:, 1:3])
# [[ 5 6]
# [ 9 10]]
a[1:, 1:3] = 100
print(a)
# [[ 0 1 2 3]
# [ 4 100 100 7]
# [ 8 100 100 11]]
a[1:, 1:3] = [100, 200]
print(a)
# [[ 0 1 2 3]
# [ 4 100 200 7]
# [ 8 100 200 11]]
a[1:, 1:3] = [[100, 200], [300, 400]]
print(a)
# [[ 0 1 2 3]
# [ 4 100 200 7]
# [ 8 300 400 11]]
这同样适用于通过指定step的切片对离散值的分配。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(a[1:, ::2])
# [[ 4 6]
# [ 8 10]]
a[1:, ::2] = 100
print(a)
# [[ 0 1 2 3]
# [100 5 100 7]
# [100 9 100 11]]
a[1:, ::2] = [100, 200]
print(a)
# [[ 0 1 2 3]
# [100 5 200 7]
# [100 9 200 11]]
a[1:, ::2] = [[100, 200], [300, 400]]
print(a)
# [[ 0 1 2 3]
# [100 5 200 7]
# [300 9 400 11]]
查看(参考)并复制
通过切片提取的子数组是原始数组的视图(参考),并且更改子数组的元素也会更改原始数组的元素。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
a_slice = a[1:, 1:3]
print(a_slice)
# [[ 5 6]
# [ 9 10]]
a_slice[0, 0] = 100
print(a_slice)
# [[100 6]
# [ 9 10]]
print(a)
# [[ 0 1 2 3]
# [ 4 100 6 7]
# [ 8 9 10 11]]
如果要复制通过切片提取的部分数组,请使用copy()方法。更改副本的元素不会更改原始数组的元素。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
a_slice_copy = a[1:, 1:3].copy()
print(a_slice_copy)
# [[ 5 6]
# [ 9 10]]
a_slice_copy[0, 0] = 100
print(a_slice_copy)
# [[100 6]
# [ 9 10]]
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
与花式索引组合(按列表选择)
NumPy具有一种称为花式索引的机制,该机制通过索引列表从numpy.ndarray中选择一个子数组。
通过将其与切片组合,可以选择并获取部分阵列。
a = np.arange(12).reshape((3, 4))
print(a)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
print(a[[0, 2], 1:3])
# [[ 1 2]
# [ 9 10]]
代入也是如此。
a[[0, 2], 1:3] = 100
print(a)
# [[ 0 100 100 3]
# [ 4 5 6 7]
# [ 8 100 100 11]]
a[[0, 2], 1:3] = [100, 200]
print(a)
# [[ 0 100 200 3]
# [ 4 5 6 7]
# [ 8 100 200 11]]
a[[0, 2], 1:3] = [[100, 200], [300, 400]]
print(a)
# [[ 0 100 200 3]
# [ 4 5 6 7]
# [ 8 300 400 11]]
请注意,通过花式索引提取的部分数组是副本,而不是视图。
a_subset = a[[0, 2], 1:3]
print(a_subset)
# [[100 200]
# [300 400]]
a_subset[0, 0] = -1
print(a_subset)
# [[ -1 200]
# [300 400]]
print(a)
# [[ 0 100 200 3]
# [ 4 5 6 7]
# [ 8 300 400 11]]