机器学习与深度学习的 Python 基础之 NumPy(2)
第二章 数组索引
导读
- 本章重点回答三个问题:
- 如何正确访问与修改数组中元素(含一维/二维多维)?
- 切片得到的是“视图”还是“拷贝”?如何避免隐式联动修改?
- 花式索引的适用场景与规则是什么?
一、基本元素访问
- 一维数组:
- 正向索引从 0 开始,负向索引从 -1 表示最后一个。
- 访问并修改均支持索引位置,例如
arr[3] = 100
。
- 二维数组:
- 使用
arr[row, col]
,先行后列;负索引同样有效(如-1
表示最后一行/列)。 - 数组元素的类型可能触发类型提升(如把整型数组某元素赋为浮点数)。
- 使用
示例:
arr1 = np.arange(1, 10)
arr1[3] = 100 # 就地修改
arr2 = np.array([[1,2,3],[4,5,6]])
val = arr2[0,2] # 第1行第3列
要点:
- 推荐一次性使用
arr[row, col]
而不是arr[row][col]
,后者会产生中间数组,不统一也更低效。
二、切片基础
- 语法:
arr[start:end:step]
- start 省略默认 0;end 省略默认到末尾;step 省略默认 1。
- 既支持正向区间,也支持负索引;支持步长控制。
- 一维切片常用形态:
arr[1:4]
、arr[1:]
、arr[:4]
、arr[::2]
、arr[1:-1:2]
等。
- 二维切片:
arr[row_slice, col_slice]
arr[1]
取第二行(一维视图)。arr[1:3, 1:-1]
取第2-3行与第2-倒数第2列子矩阵。arr[::3, ::2]
跨步采样行列。arr[:,2]
第3列(一维视图),arr[:,1:3]
第2-3列(二维视图)。
示例:
arr2 = np.arange(1, 21).reshape(4, 5)
row = arr2[2, :] # 第3行
block = arr2[1:3, 2:4] # 子矩阵
col = arr2[:, 2] # 第3列(一维)
实用技巧:
- 取出列后可 reshape/转置以满足后续计算接口需求:
col = arr2[:, 2] # shape: (4,)
col_row = col.reshape(1, -1) # shape: (1, 4)
col_col = col_row.T # shape: (4, 1)
三、切片是视图
- NumPy 的“切片”默认返回视图(view),与原数组共享同一底层数据。
- 对切片做原地修改,会反映到原数组。
示例:
arr = np.arange(10)
cut = arr[:3] # 视图
cut[0] = 100
# arr[0] 也会变为 100
如何避免联动修改?
- 显式使用
copy()
获取独立副本:
copy = arr[:3].copy()
copy[0] = 200 # arr 不受影响
四、变量赋值“仅是绑定”
arr2 = arr1
不会复制数据,只是让两个变量指向同一块内存(同一底层数组)。- 修改任意一方,另一方“看见”的也是被修改的数据。
示例:
arr1 = np.arange(10)
arr2 = arr1 # 引用同一底层数据
arr2[0] = 100
# arr1[0] 也会变为 100
避免“误共享”的两种常见方式:
- 使用
arr.copy()
创建深拷贝。 - 或基于切片后再
.copy()
,在管道式处理时更明确。
五、花式索引
- 定义:使用“整数数组或列表作为索引”,一次选择多个位置。
- 本质:返回新数组(副本),不是视图。
- 一维花式索引:
arr = np.arange(0, 90, 10)
picked = arr[[0, 2, 5]] # 下标0、2、5处的元素
- 二维花式索引的“配对取值”:
arr[[r1, r2, ...], [c1, c2, ...]]
会取出(r1,c1),(r2,c2),...
这些坐标对应的元素。
arr2 = np.arange(1, 17).reshape(4, 4)
vals = arr2[[0,1,2],[2,1,0]] # 依次取 (0,2),(1,1),(2,0)
- 花式索引也常用于“定点批量赋值”:
arr2[[0,1,2,3],[3,2,1,0]] = 100 # 反对角线元素全部设为100
注意:
- 花式索引与切片语法“:”不能混在同一维度的同一组整数索引列表里(不同维度可混用,如
arr[:, [1,3]]
)。
六、常见易错点与最佳实践
- 易错点
- 误以为切片是“拷贝”,实为“视图”(共享底层数据)。
arr2 = arr1
没有复制;需要独立数据需显式.copy()
。arr[row][col]
的链式取法不是最佳实践,易产生中间结果与歧义。- 花式索引返回副本,后续修改不会影响原数组;与切片行为不同。
- 最佳实践
- 二维索引推荐一次性写成
arr[row, col]
。 - 需要独立数据时,务必使用
.copy()
。 - 批量位置更新,用花式索引更直观;区段连续选取,用切片更高效。
- 二维索引推荐一次性写成
七、速查表(Cheat Sheet)
- 一维元素:
arr[i]
、arr[-1]
- 一维切片:
arr[s:e:step]
,默认s=0,e=len,step=1
- 二维元素:
arr[r, c]
- 二维切片:
arr[r_s:e, c_s:e]
或arr[:, c]
- 花式索引:
- 一维:
arr[[i1, i2, ...]]
- 二维配对:
arr[[r1, r2, ...],[c1, c2, ...]]
- 一维:
- 视图/拷贝:
- 切片→视图;花式索引→拷贝;
arr2=arr1
→同一底层。 - 深拷贝:
arr.copy()
- 切片→视图;花式索引→拷贝;
八、学习要点(总结)
- 索引统一用中括号:NumPy 支持
arr[1, 2]
多维一次完成;相比 Python 原生列表更简洁。 - 花式索引的核心是“用数组作为索引”,能一次取多个元素/不连续区域;N 维就需要提供 N 个索引数组(或列表)。
- 花式索引与普通切片的根本区别:花式索引返回“副本”,切片返回“视图”。
- 花式索引内不能使用
:
切片语法(同一维度),若需跨维度组合,可写成arr[:, idx_list]
。 - NumPy 的切片是“视图”,如需独立数据用
.copy()
。 - NumPy 在数值索引、切片上极其强大,适合科学计算与向量化;原生 Python 列表仍适用于动态结构变更(增删元素等)。
九、NumPy 与 Python 原生 list / C 数组的对比
虽然 Python 的 list
、C 语言的数组和 NumPy 的 ndarray
都可以存储数据,但它们在设计目标、性能、内存布局和索引能力上有本质区别。理解这些差异,有助于我们选择合适的工具。
1. 索引语法对比
类型 | 访问方式 | 示例 | 说明 |
---|---|---|---|
Python list | 链式索引 | lst[1][2] | 每层是独立对象,效率低 |
C 数组 | 多维语法或指针 | arr[1][2] 或 *(arr + 1*cols + 2) | 连续内存,高效 |
NumPy ndarray | 直接多维索引 | arr[1, 2] | 一步到位,高效且清晰 |
🔺 优势:NumPy 的
arr[i,j]
比list
的lst[i][j]
更高效,语法更简洁。
2. 切片能力对比
类型 | 支持多维切片? | 切片返回 | 说明 |
---|---|---|---|
Python list | ❌ 仅支持一维切片 | ✅ 副本 | lst[1:3] 可,lst[1:3, 2:4] 报错 |
C 数组 | ❌ 不支持切片语法 | ❌ 无切片概念 | 需手动循环复制 |
NumPy ndarray | ✅ 支持多维切片 | ✅ 视图(默认) | arr[1:3, 2:4] 返回子矩阵视图 |
🔺 优势:NumPy 的切片是科学计算的利器,支持多维、跨步、布尔索引等高级操作。
3. 内存与性能对比
类型 | 内存布局 | 数据类型 | 性能 |
---|---|---|---|
list | 指针数组(不连续) | 任意对象(异构) | 慢(缓存不友好) |
C 数组 | 连续内存 | 固定类型(同构) | 快(缓存友好) |
NumPy ndarray | 连续内存(C/F-order) | 固定类型(同构) | 极快(向量化) |
🔺 NumPy 本质是“Python 接口 + C 性能”,既易用又高效。
4. 动态性对比
类型 | 支持动态增删? | 适用场景 |
---|---|---|
list | ✅ 支持 append , pop , insert | 动态结构、通用容器 |
C 数组 | ❌ 固定大小 | 嵌入式、性能敏感 |
NumPy ndarray | ❌ 固定大小(需 .copy() 扩展) | 数值计算、矩阵运算 |
✅ 结论:
- 用 NumPy 做 科学计算、数据分析、机器学习
- 用 list 做 动态结构、流程控制、通用数据容器
5. 花式索引 vs 普通索引
能力 | Python list | NumPy ndarray |
---|---|---|
不连续索引 | [lst[i] for i in [0,2,5]] | arr[[0,2,5]] |
布尔索引 | 手动循环或推导式 | arr[arr > 5] |
广播与向量化 | ❌ 不支持 | ✅ 支持 |
🔺 NumPy 的花式索引是向量化编程的核心,极大提升代码简洁性与性能。
✅ 总结:工具选择建议
需求 | 推荐工具 |
---|---|
数值计算、矩阵运算、图像处理 | ✅ NumPy |
动态添加/删除元素、通用容器 | ✅ Python list |
嵌入式、极致性能、C/C++ 集成 | ✅ C 数组 |
🎯 一句话:
- NumPy 强在“数值+多维+向量化”
- list 强在“动态+通用+灵活”
- 二者互补,而非替代