索引与切片
NumPy 的索引和切片是访问与操作数组元素的基础。相比 Python 列表,NumPy 支持更强大的多维索引机制,包括基础索引、切片、布尔索引和花式索引。
一、基础索引
1、一维数组
python
import numpy as np
a = np.array([10, 20, 30, 40, 50])
print(a[0]) # 10 — 第一个元素
print(a[-1]) # 50 — 最后一个元素
print(a[2]) # 30 — 第三个元素2、多维数组
python
b = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(b[0]) # [1 2 3] — 第一行
print(b[0, 2]) # 3 — 第一行第三列
print(b[-1, -1]) # 9 — 最后一行最后一列
print(b[1][2]) # 6 — 等价于 b[1, 2](但效率较低)优先使用 b[i, j] 而非 b[i][j]
b[i, j] 一次索引完成,而 b[i][j] 先取第 i 行再取第 j 列,会创建临时数组。两者结果相同,但前者更高效。
3、高维数组
python
c = np.arange(24).reshape(2, 3, 4) # 形状 (2, 3, 4)
print(c[0]) # 第一个 (3, 4) 子数组
print(c[0, 1]) # 第一个子数组的第二行 → shape (4,)
print(c[0, 1, 2]) # 标量值 6二、切片(Slicing)
切片语法:start:stop:step,与 Python 列表一致。
1、一维切片
python
a = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(a[2:6]) # [2 3 4 5]
print(a[::2]) # [0 2 4 6 8] — 步长为 2
print(a[::-1]) # [9 8 7 6 5 4 3 2 1 0] — 反转
print(a[5:]) # [5 6 7 8 9] — 从第 6 个到末尾
print(a[:3]) # [0 1 2] — 前 3 个2、多维切片
python
b = np.arange(16).reshape(4, 4)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]
# [12 13 14 15]]
print(b[0:2, 0:2])
# [[0 1]
# [4 5]] — 左上角 2×2 子矩阵
print(b[:, 1])
# [ 1 5 9 13] — 第二列
print(b[1:3, :])
# [[ 4 5 6 7]
# [ 8 9 10 11]] — 中间两行
print(b[::2, ::2])
# [[ 0 2]
# [ 8 10]] — 隔行隔列取3、切片是视图
python
a = np.array([1, 2, 3, 4, 5])
s = a[1:4] # 视图,共享内存
s[0] = 99
print(a) # [ 1 99 3 4 5] — 原数组被修改这意味着切片赋值可以直接修改原数组:
python
a = np.array([1, 2, 3, 4, 5])
a[1:4] = [20, 30, 40]
print(a) # [ 1 20 30 40 5]三、布尔索引(Boolean Indexing)
通过布尔数组(掩码)筛选元素,返回的是副本。
1、基本用法
python
a = np.array([10, 20, 30, 40, 50])
mask = a > 25
print(mask) # [False False True True True]
print(a[mask]) # [30 40 50]
# 一行写法
print(a[a > 25]) # [30 40 50]2、多条件组合
python
a = np.array([10, 20, 30, 40, 50])
# 与(&)、或(|)、非(~)
print(a[(a > 15) & (a < 45)]) # [20 30 40]
print(a[(a < 20) | (a > 45)]) # [10 50]
print(a[~(a > 30)]) # [10 20 30]注意运算符优先级
在布尔索引中,必须使用 &、|、~(位运算符),不能用 and、or、not(Python 关键字)。同时每个条件必须加括号:(a > 15) & (a < 45)。
3、二维布尔索引
python
b = np.arange(12).reshape(3, 4)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 筛选所有偶数
print(b[b % 2 == 0]) # [ 0 2 4 6 8 10]
# 按行条件筛选
row_mask = np.array([True, False, True])
print(b[row_mask])
# [[ 0 1 2 3]
# [ 8 9 10 11]]
# np.where: 返回满足条件的元素索引
rows, cols = np.where(b > 5)
print(list(zip(rows, cols))) # [(1,2), (1,3), (2,0), (2,1), (2,2), (2,3)]4、np.where 条件赋值
python
a = np.array([1, 2, 3, 4, 5])
# 三元表达式:满足条件取 x,否则取 y
result = np.where(a > 3, a, 0)
print(result) # [0 0 0 4 5]
# 等价于
result = np.where(a > 3, a * 10, -1)
print(result) # [-1 -1 -1 40 50]四、花式索引(Fancy Indexing)
用整数数组作为索引,返回的是副本而非视图。
1、一维花式索引
python
a = np.array([10, 20, 30, 40, 50])
indices = [0, 2, 4]
print(a[indices]) # [10 30 50]
# 重复索引
print(a[[0, 0, 2, 2]]) # [10 10 30 30]
# 用数组作为索引
idx = np.array([3, 1, 4])
print(a[idx]) # [40 20 50]2、二维花式索引
python
b = np.arange(12).reshape(3, 4)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# 选取特定行
print(b[[0, 2]])
# [[ 0 1 2 3]
# [ 8 9 10 11]]
# 选取特定行列交叉点(配对索引)
row_idx = np.array([0, 1, 2])
col_idx = np.array([1, 2, 3])
print(b[row_idx, col_idx]) # [ 1 6 11] → (0,1), (1,2), (2,3)3、ix_ 网格索引
python
b = np.arange(12).reshape(3, 4)
# np.ix_ 创建开放网格
rows = [0, 2]
cols = [1, 3]
print(b[np.ix_(rows, cols)])
# [[ 1 3]
# [ 9 11]] — 取第 0、2 行与第 1、3 列的交叉子矩阵4、花式索引与切片组合
python
b = np.arange(12).reshape(3, 4)
# 切片 + 花式索引
print(b[1:, [0, 2]])
# [[ 4 6]
# [ 8 10]] — 后两行,第 0 和第 2 列五、索引赋值
所有索引方式都支持赋值:
python
a = np.zeros((3, 3))
# 基础索引赋值
a[0, 0] = 1
# 切片赋值
a[1, :] = [4, 5, 6]
# 布尔索引赋值
a[a > 3] = 99
# 花式索引赋值
a[[0, 2], [0, 2]] = [7, 8]
print(a)
# [[ 7. 0. 0.]
# [99. 99. 99.]
# [ 0. 0. 8.]]六、省略号(...)与 np.newaxis
1、省略号 ...
在高维数组中,... 代表"所有剩余维度":
python
c = np.arange(24).reshape(2, 3, 4)
print(c[0, ...]) # 等价于 c[0, :, :]
print(c[..., 0]) # 等价于 c[:, :, 0]
print(c[0, ..., 2]) # 等价于 c[0, :, 2]2、np.newaxis 增加维度
python
a = np.array([1, 2, 3]) # shape (3,)
print(a[:, np.newaxis]) # shape (3, 1) — 列向量
# [[1]
# [2]
# [3]]
print(a[np.newaxis, :]) # shape (1, 3) — 行向量
# [[1 2 3]]📖 官方文档