当前位置: 首页 > news >正文

NumPy 2.x 完全指南【十三】复制和视图

文章目录

  • 1. 引言
  • 2. 视图
  • 3. 复制
  • 4. 判断

1. 引言

在实际应用场景中,一个输入数据往往要经过多轮处理,比如,如图像识别任务中的一张图片的主要流程有:

  • 加载原始图像
  • 预处理:裁剪为模型要求的格式、归一化处理等。
  • 推理:输入到模型中
  • 后处理

在操作 NumPy 数组时,可以通过视图直接访问内部数据缓冲区,而无需复制数据。这能确保良好的性能,但如果用户不了解这一机制,可能会导致一些不必要的问题。因此,了解这两个术语的区别,以及哪些操作返回复制,哪些操作返回视图非常重要。

NumPy 提供了复制(Copy)和视图(View)机制,不仅满足大规模数据处理时的内存效率,同时兼顾了数据操作的灵活性和安全性。

复制与视图的对比:

特性视图(View)复制(Copy)
数据缓冲区共享原数组数据独立的新数据块
内存占用低(仅存储元数据)高(复制全部数据)
修改是否影响原数组
创建速度快(无需复制数据)慢(需复制数据)
典型操作切片、reshape()Tcopy()、高级索引

2. 视图

视图(View):一个新的数组对象,​共享原始数组的数据缓冲区(即底层内存块)。

注意事项:

  • 不复制数据,直接引用原数据的内存。
  • 数据缓冲区保持不变,因此修改视图的数据会影响原始数组。
  • 内存占用低,操作速度快。

创建视图时,仅改变某些元数据(如步幅和数据类型),而不改变数据缓冲区,视图的底层数据与原始数组共享,每个视图是独立的 ndarray 实例,拥有自己的元数据:

示例 1 ,ndarray.view() 方法可以显式创建数组的视图:

# 原始数组
arr = np.array([1, 2, 3, 4], dtype=np.int32)
view = arr.view()  # 创建视图# 修改视图
view[0] = 99
print("原数组:", arr)    # 输出: [99  2  3  4]
print("视图:", view)     # 输出: [99  2  3  4]

当元素可以通过偏移量和步幅在原始数组中进行寻址时,视图就会被创建。因此,基础索引(如切片、整数索引)访问数组时总是会创建视图。

示例 2 ,切片生成视图:

# 原始数组
arr = np.array([1, 2, 3, 4, 5])# 切片操作
view = arr[1:4]  # 生成视图
view[0] = 99     # 修改视图# 原数组被修改
print("原数组:", arr)    # 输出: [1, 99, 3, 4, 5]
print("视图:", view)     # 输出: [99, 3, 4]

示例 3 ,整数索引生成视图:

# 二维数组
arr_2d = np.array([[1, 2], [3, 4]])# 整数索引
view_row = arr_2d[0]  # 生成视图(第一行)
view_row[0] = 99print("原数组:\n", arr_2d)
# 输出:
# [[99  2]
#  [ 3  4]]

示例 4 ,当返回根据整数值索引返回单个元素时,返回的是标量值,而不是视图,修改标量不影响原数组:

# 原始数组
arr = np.array([1, 2, 3, 4, 5])
scalar = arr[0]  # 返回标量(并非数组对象)
scalar = 100     # 修改标量
print(scalar)    # 100 
print(arr)       # 输出: [1 2 3 4 5]

3. 复制

复制(Copy):一般也翻译为拷贝,创建一个与原数组完全独立的新数组对象,包括复制数据缓冲区和元数据。

注意事项:

  • 复制会创建新的数据缓冲区,与原数组完全分离。
  • 修改复制后的数组不会影响原数组。
  • 复制需要时间和内存(尤其是大规模数据时)。

示例 1 ,可以直接使用 ndarray.copy() 方法创建副本:

# 原始数组
arr = np.array([1, 2, 3, 4])
copy = arr.copy()  # 强制创建副本# 修改副本
copy[0] = 99print("原数组:", arr)  # 输出: [1 2 3 4]
print("副本:", copy)  # 输出: [99 2 3 4]

示例 2 ,使用高级索引(后续会介绍)时返回的都是副本:

copy = arr[[0, 1, 2]]  # 使用整数数组索引
copy[0] = 99print("原数组:", arr)   # 输出: [1 2 3 4]

4. 判断

可以通过 ndarray.base 属性判断一个数组是视图还是副本。

核心规则:

  • 视图的 base:指向原始数组(共享数据缓冲区)。
  • 副本的 base:返回 None(数据独立)。

示例:

# 原始数组
x = np.arange(9)
print("原始数组 x:", x)
# 创建视图
y = x.view()# 返回 True 表示 x 是 y 的 原始数组(视图关系)
print(y.base is x)  # 输出: True# 新建一个数组
z = np.arange(6)
# 返回 False 表示 x 不是 z 的 原始数组(非视图关系)
print(z.base is x)  # 输出: False# 返回 None 表示 z 没有原始数组(独立副本)
print(z.base)  # 输出: None

还可以使用 np.shares_memory(arr1, arr2) 检查两个数组是否共享数据,示例:

print(np.shares_memory(x, y)) # True
print(np.shares_memory(y, z)) # False
http://www.xdnf.cn/news/553825.html

相关文章:

  • 使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第十三讲)
  • 口腔牙科小程序源码介绍
  • 多线程下如何保证事务的一致性
  • AliSQL:阿里巴巴开源数据库的技术革新与应用实践
  • idea查看class文件源码
  • OpenAI Codex 加入Agent编程工具新阵营
  • Git客户端安装、操作
  • 进阶知识:自动化框架开发之有参的函数装饰器@wraps()和无参之间的对比
  • 蓝牙OBEX和PBAP协议概述
  • 项目计划缺乏可行性,如何制定实际可行的计划?
  • 利用 SQL Server 作业实现异步任务处理,简化系统架构
  • 集成思想在算法(目标检测)中的体现
  • 芯片分享之AD5542性能介绍
  • docker 安装 Nacos
  • 从复杂度到有序:大模型专家系统的进化之路——深入解析层次化专家模式
  • Linux bash shell的循环命令for、while和until
  • 策略调度平台实现总结
  • MySQL基础关键_014_MySQL 练习题
  • KeepassXC (Win10) 打不开的解决方法
  • Nginx笔记
  • 开疆智能Profinet转RS485网关连接电磁流量计到西门子PLC配置案例
  • STM32--串口函数
  • 随机数种子seed和相关系数ρ
  • vue3 + echarts(5.6.0)实现渐变漏斗图
  • vue2实现【瀑布流布局】
  • 粤港澳编程题
  • 【HTML-2】HTML 标题标签:构建网页结构的基础
  • Tomcat配置详情
  • 解码数据语言:如何优雅的进行数仓字典建设?
  • C++:迭代器