人工智能之数据分析 numpy
第十章 副本视图
文章目录
- [人工智能之数据分析 numpy](#人工智能之数据分析 numpy)
- 前言
- 一、核心概念
- 二、如何判断是副本还是视图?
- [三、常见操作:返回视图 vs 副本](#三、常见操作:返回视图 vs 副本)
- [✅ 返回 **视图** 的操作(共享数据)](#✅ 返回 视图 的操作(共享数据))
- [✅ 返回 **副本** 的操作(独立数据)](#✅ 返回 副本 的操作(独立数据))
- [四、reshape() 和 ravel() 的细节对比](#四、reshape() 和 ravel() 的细节对比)
- 五、实战陷阱:意外修改原数组
- [❌ 错误示例:以为切片是副本](#❌ 错误示例:以为切片是副本)
- [✅ 正确做法:明确需要副本时用 `.copy()`](#✅ 正确做法:明确需要副本时用
.copy())- 六、何时用视图?何时用副本?
- 七、总结速查表
- 八、最佳实践建议
- 后续
- 资料关注
前言
在 NumPy 中,副本(copy) 和 视图(view) 是理解数组内存管理、性能优化和避免意外修改的关键概念。它们决定了当你对一个数组进行切片、变形或赋值操作时,**是否创建了新的数据副本,还是仅仅创建了一个指向原数据的新"窗口"**。
下面从原理、区别、判断方法到实战示例,详细解析:
一、核心概念
| 概念 | 含义 | 是否共享数据 | 修改影响 |
|---|---|---|---|
| 副本(Copy) | 完全独立的新数组,拥有自己的内存空间 | ❌ 不共享 | 修改副本不影响原数组 |
| 视图(View) | 原数组的一个"窗口"或"别名",不复制数据 | ✅ 共享 | 修改视图会改变原数组 |
💡 关键点 :NumPy 默认尽可能返回 视图(为了节省内存和提升速度),只有在必要时才返回副本。
二、如何判断是副本还是视图?
使用数组的 .base 属性:
- 如果
arr.base is None→ 这是一个副本(或原始数组) - 如果
arr.base is not None→ 这是一个视图,base指向原始数组
python
import numpy as np
a = np.array([1, 2, 3, 4])
b = a.view() # 显式创建视图
c = a.copy() # 显式创建副本
d = a[1:3] # 切片(通常返回视图)
print(b.base is a) # True → 视图
print(c.base is a) # False → 副本(c.base is None)
print(d.base is a) # True → 视图(切片是视图!)
三、常见操作:返回视图 vs 副本
✅ 返回 视图 的操作(共享数据)
| 操作 | 示例 | 说明 |
|---|---|---|
| 切片(slicing) | a[1:4] |
最常见! |
np.view() |
a.view() |
显式创建视图 |
reshape()(当可能时) |
a.reshape(2,2) |
如果不改变数据布局,返回视图 |
转置 T |
a.T |
对高维数组通常是视图 |
python
a = np.array([[1, 2], [3, 4]])
b = a.reshape(4,) # 视图(连续内存)
b[0] = 99
print(a) # [[99, 2], [3, 4]] → 原数组被修改!
⚠️ 注意:
reshape()不一定总是视图!如果无法在不复制数据的情况下 reshape(如非连续数组),NumPy 会自动返回副本。
✅ 返回 副本 的操作(独立数据)
| 操作 | 示例 | 说明 |
|---|---|---|
np.copy() |
a.copy() |
显式深拷贝 |
| 花式索引(fancy indexing) | a[[0, 2, 1]] |
总是副本 |
| 布尔索引 | a[a > 2] |
总是副本 |
flatten() |
a.flatten() |
总是返回副本 |
ravel()(有时) |
a.ravel() |
尽量返回视图,不行则副本(与 flatten 不同) |
python
a = np.array([1, 2, 3, 4])
# 花式索引 → 副本
b = a[[0, 2]]
b[0] = 99
print(a) # [1, 2, 3, 4] → 未变
# flatten → 副本
c = a.flatten()
c[0] = 88
print(a) # 仍为 [1, 2, 3, 4]
四、reshape() 和 ravel() 的细节对比
| 方法 | 是否修改原数组 | 返回类型 | 内存行为 |
|---|---|---|---|
arr.reshape(...) |
❌ | 新数组 | 尽量视图,否则副本 |
arr.resize(...) |
✅ | 无返回(in-place) | 直接修改原数组形状 |
arr.ravel() |
❌ | 一维数组 | 尽量视图 |
arr.flatten() |
❌ | 一维数组 | 总是副本 |
python
a = np.array([[1, 2], [3, 4]])
# ravel() → 视图(因为 a 是连续的)
b = a.ravel()
b[0] = 99
print(a) # [[99, 2], [3, 4]]
# flatten() → 副本
c = a.flatten()
c[0] = 88
print(a) # 仍是 [[99, 2], [3, 4]]
五、实战陷阱:意外修改原数组
❌ 错误示例:以为切片是副本
python
data = np.array([10, 20, 30, 40])
subset = data[1:3] # 这是视图!
subset[0] = 999 # 你以为只改 subset?
print(data) # [10, 999, 30, 40] → 原数组被改了!
✅ 正确做法:明确需要副本时用 .copy()
python
data = np.array([10, 20, 30, 40])
subset = data[1:3].copy() # 显式创建副本
subset[0] = 999
print(data) # [10, 20, 30, 40] → 安全!
六、何时用视图?何时用副本?
| 场景 | 推荐 |
|---|---|
| 只读访问子数据(如分析某段信号) | 用视图(高效) |
| 需要修改子数据但不想影响原数组 | 用 .copy() |
| 函数内部处理数组,不确定是否会被修改 | 默认 .copy() 更安全 |
| 内存受限,且确定不会修改 | 用视图节省内存 |
七、总结速查表
| 操作 | 返回 | 是否共享数据 | 安全修改? |
|---|---|---|---|
a[:] |
视图 | ✅ | ❌(会影响 a) |
a[1:3] |
视图 | ✅ | ❌ |
a[[1,2]] |
副本 | ❌ | ✅ |
a[a>0] |
副本 | ❌ | ✅ |
a.reshape(...) |
视图(尽量) | 可能 | 谨慎 |
a.copy() |
副本 | ❌ | ✅ |
a.view() |
视图 | ✅ | ❌ |
a.flatten() |
副本 | ❌ | ✅ |
a.ravel() |
视图(尽量) | 可能 | 谨慎 |
八、最佳实践建议
- 永远不要假设切片是副本 → 如需独立数据,显式调用
.copy() - 在函数参数中接收数组时,若要修改,先
.copy()避免副作用 - 使用
.base属性调试内存关系 - 处理大型数组时,优先使用视图以节省内存,但要小心写操作
后续
本文主要讲述了numpy数组副本和视图。python过渡项目部分代码已经上传至gitee,后续会逐步更新,主要受时间原因限制,当然自己也可以克隆到本地学习拓展。
资料关注
公众号:咚咚王
gitee:https://gitee.com/wy18585051844/ai_learning
《Python编程:从入门到实践》
《利用Python进行数据分析》
《算法导论中文第三版》
《概率论与数理统计(第四版) (盛骤) 》
《程序员的数学》
《线性代数应该这样学第3版》
《微积分和数学分析引论》
《(西瓜书)周志华-机器学习》
《TensorFlow机器学习实战指南》
《Sklearn与TensorFlow机器学习实用指南》
《模式识别(第四版)》
《深度学习 deep learning》伊恩·古德费洛著 花书
《Python深度学习第二版(中文版)【纯文本】 (登封大数据 (Francois Choliet)) (Z-Library)》
《深入浅出神经网络与深度学习+(迈克尔·尼尔森(Michael+Nielsen)》
《自然语言处理综论 第2版》
《Natural-Language-Processing-with-PyTorch》
《计算机视觉-算法与应用(中文版)》
《Learning OpenCV 4》
《AIGC:智能创作时代》杜雨+&+张孜铭
《AIGC原理与实践:零基础学大语言模型、扩散模型和多模态模型》
《从零构建大语言模型(中文版)》
《实战AI大模型》
《AI 3.0》