索引和切片是访问和修改数组元素的核心操作。NumPy提供了丰富的索引方式,包括基本索引、切片、布尔索引和花式索引,灵活运用它们可以高效地操作数组数据。
一维NumPy数组的索引和切片与Python列表非常相似。
import numpy as np
arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print("原数组:", arr)
# 单个索引
print("arr[0] =", arr[0]) # 第一个元素
print("arr[-1] =", arr[-1]) # 最后一个元素
# 切片 [start:stop:step]
print("arr[2:5] =", arr[2:5]) # 索引2到4
print("arr[:5] =", arr[:5]) # 开头到索引4
print("arr[5:] =", arr[5:]) # 索引5到最后
print("arr[::2] =", arr[::2]) # 步长为2
print("arr[::-1] =", arr[::-1]) # 反转数组
输出:
原数组: [0 1 2 3 4 5 6 7 8 9]
arr[0] = 0
arr[-1] = 9
arr[2:5] = [2 3 4]
arr[:5] = [0 1 2 3 4]
arr[5:] = [5 6 7 8 9]
arr[::2] = [0 2 4 6 8]
arr[::-1] = [9 8 7 6 5 4 3 2 1 0]
对于二维数组,索引和切片使用逗号分隔各个维度,格式为 arr[行, 列]。
arr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
print("二维数组:\n", arr2d)
# 访问单个元素
print("arr2d[1, 2] =", arr2d[1, 2]) # 第2行第3列
# 获取整行
print("第一行 arr2d[0] =", arr2d[0]) # 等价于 arr2d[0, :]
print("第二行 arr2d[1, :] =", arr2d[1, :])
# 获取整列
print("第二列 arr2d[:, 1] =", arr2d[:, 1])
# 切片:前两行,后两列
print("前两行,后两列:\n", arr2d[:2, -2:])
# 步长切片
print("每隔一行,每隔一列:\n", arr2d[::2, ::2])
输出:
二维数组:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
arr2d[1, 2] = 7
第一行 arr2d[0] = [1 2 3 4]
第二行 arr2d[1, :] = [5 6 7 8]
第二列 arr2d[:, 1] = [ 2 6 10]
前两行,后两列:
[[3 4]
[7 8]]
每隔一行,每隔一列:
[[ 1 3]
[ 9 11]]
更高维数组的索引同理,每个维度用逗号分隔。可以使用 ...(省略号)代表多个连续的冒号切片。
arr3d = np.arange(24).reshape(2, 3, 4)
print("三维数组 shape:", arr3d.shape)
print("arr3d:\n", arr3d)
# 访问单个元素
print("arr3d[1, 2, 3] =", arr3d[1, 2, 3]) # 第2个块的第三行第四列
# 切片:获取所有块的第一行
print("所有块的第一行:\n", arr3d[:, 0, :])
# 使用省略号:获取所有块的最后一列
print("所有块的最后一列 (arr3d[..., -1]):\n", arr3d[..., -1])
输出:
三维数组 shape: (2, 3, 4)
arr3d:
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
arr3d[1, 2, 3] = 23
所有块的第一行:
[[ 0 1 2 3]
[12 13 14 15]]
所有块的最后一列 (arr3d[..., -1]):
[[ 3 7 11]
[15 19 23]]
NumPy的切片操作返回原数组的视图,意味着修改切片会影响原数组(反之亦然)。这与Python列表的切片不同(列表返回副本)。
arr = np.array([1, 2, 3, 4, 5])
slice_view = arr[1:4] # 视图
print("原数组:", arr)
print("切片视图:", slice_view)
# 修改视图
slice_view[0] = 99
print("修改视图后原数组:", arr) # 原数组也被修改
# 检查是否共享内存
print("共享内存?", np.may_share_memory(arr, slice_view)) # True
输出:
原数组: [1 2 3 4 5]
切片视图: [2 3 4]
修改视图后原数组: [ 1 99 3 4 5]
共享内存? True
copy() 方法:slice_copy = arr[1:4].copy()。
花式索引使用整数数组或列表作为索引,可以选择任意顺序的元素,并返回副本(不是视图)。
arr = np.array([10, 20, 30, 40, 50])
idx = [0, 2, 4]
print("arr[idx] =", arr[idx]) # 选择索引0,2,4的元素
# 二维花式索引
arr2d = np.arange(12).reshape(3, 4)
print("原二维数组:\n", arr2d)
# 选择特定行(第0行和第2行)
rows = arr2d[[0, 2]]
print("选择行0和2:\n", rows)
# 选择特定列(第1列和第3列)
cols = arr2d[:, [1, 3]]
print("选择列1和3:\n", cols)
# 组合使用:选择(0,1)和(2,2)两个点
points = arr2d[[0, 2], [1, 2]] # 分别对应 (0,1) 和 (2,2)
print("选中的点:", points)
输出:
arr[idx] = [10 30 50]
原二维数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
选择行0和2:
[[ 0 1 2 3]
[ 8 9 10 11]]
选择列1和3:
[[ 1 3]
[ 5 7]
[ 9 11]]
选中的点: [1 10]
布尔索引使用布尔数组(条件表达式的结果)选择满足条件的元素,返回副本。
arr = np.array([5, 10, 15, 20, 25])
bool_idx = arr > 15
print("arr > 15:", bool_idx)
print("arr[arr > 15] =", arr[bool_idx]) # 选择大于15的元素
# 结合赋值:将小于10的元素替换为0
arr[arr < 10] = 0
print("修改后:", arr)
# 二维布尔索引
arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("大于5的元素:\n", arr2d[arr2d > 5]) # 返回一维数组
print("偶数行且偶数列:\n", arr2d[arr2d % 2 == 0])
输出:
arr > 15: [False False False True True]
arr[arr > 15] = [20 25]
修改后: [ 0 0 15 20 25]
大于5的元素:
[6 7 8 9]
偶数行且偶数列:
[2 4 6 8]
可以在不同维度混合使用切片、花式索引和布尔索引,但需要注意结果的维度。
arr2d = np.arange(12).reshape(3, 4)
print("原数组:\n", arr2d)
# 切片与花式索引:选择第0行和第2行的第1到第3列
result = arr2d[[0, 2], 1:3]
print("选择指定行的部分列:\n", result)
# 切片与布尔索引:选择所有行,且元素值大于5的列(列条件作用于整个数组,返回布尔掩码)
mask = arr2d > 5
print("元素大于5的掩码:\n", mask)
# 注意:arr2d[mask] 返回所有满足条件的一维数组
print("arr2d[mask] =", arr2d[mask])
输出:
原数组:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
选择指定行的部分列:
[[1 2]
[9 10]]
元素大于5的掩码:
[[False False False False]
[False False True True]
[ True True True True]]
arr2d[mask] = [ 6 7 8 9 10 11]
...) 的使用
对于高维数组,可以使用省略号 ... 代表一个或多个完整的切片(即 :),简化代码。
arr3d = np.arange(24).reshape(2, 3, 4)
# 以下两种写法等价
print("arr3d[0, ...] 相当于 arr3d[0, :, :]:\n", arr3d[0, ...])
print("arr3d[..., -1] 相当于 arr3d[:, :, -1]:\n", arr3d[..., -1])
take() 和 put() 进行索引和赋值
NumPy还提供了 take() 和 put() 方法,与花式索引类似,但可能更适合某些性能场景。
arr = np.array([10, 20, 30, 40])
indices = [1, 3]
print("arr.take(indices) =", arr.take(indices))
arr.put(indices, [99, 100])
print("arr.put后:", arr)
输出:
arr.take(indices) = [20 40]
arr.put后: [ 10 99 30 100]