NumPy:数组复制与视图
在使用 NumPy 进行数据处理时数组对象不仅可以被读取或修改还经常需要在不同变量或不同数组之间进行“复制”。例如将一个数组赋值给另一个变量、通过切片获取数组的一部分、或显式创建新的数组副本。需要注意的是这些操作在语法上都表现为“复制”但在内存层面的行为却可能完全不同。NumPy 数组由数据缓冲区data buffer与数组结构信息shape、strides、dtype 等两部分构成。不同“复制方式”的本质差异在于这两部分是否被共享。从内存共享的角度来看NumPy 中数组之间的关系可以分为三类1引用reference2视图view3副本copy理解这三种关系是掌握 NumPy 数组行为的重要基础。一、数组引用Reference在 Python 中如果将一个数组赋值给另一个变量b a此时并不会创建新的数组对象而只是创建了一个新的变量名指向同一个数组。示例import numpy as np a np.array([[1, 2, 3], [4, 5, 6]]) b ab[0, 0] 99 print(a)输出[[99 2 3] [ 4 5 6]]说明该行为本质上属于引用别名绑定而非拷贝。变量 a 与 b 指向的是同一个数组对象因此修改任意一方都会影响另一方。可以通过 id() 函数查看对象标识print(id(a))print(id(b))两个变量对应的对象标识是相同的。二、数组视图ViewNumPy 提供了一种重要机制视图view。视图是一个新的数组对象它与原数组共享底层数据缓冲区。即• 数据共享• 数组结构独立shape、strides 通常独立因此• 修改视图的数据会影响原数组的数据• 修改视图的结构通常不影响到原数组需要注意的是虽然视图通常具有独立的结构信息但在某些操作中如直接修改属性仍需谨慎处理结构一致性。这种设计的目的在于• 提高内存利用率• 避免不必要的数据复制• 提升大规模数组计算效率1、创建视图的常用方法view()创建当前数组的数据视图新数组对象与原数组共享底层数据。ndarray.view(dtypeNone, typeNone)参数说明• dtype可选指定新的数据类型• type可选指定返回数组的子类返回值返回一个新的 ndarray 对象共享数据。示例a np.array([[1, 2, 3], [4, 5, 6]]) b a.view()b[0, 0] 100 print(a)输出[[100 2 3] [ 4 5 6]]说明虽然 a 与 b 是不同的数组对象但它们共享同一块数据内存因此修改其中一个数组会影响另一个数组。2、返回视图的常见操作1切片在 NumPy 中基本索引切片通常返回视图。示例a np.array([[1, 2, 3], [4, 5, 6]]) b a[:, 1:3]b[0, 0] 99 print(a)输出[[ 1 99 3] [ 4 5 6]]说明数组 b 是 a 的切片视图因此修改 b 的数据会影响原数组。2数组形状重构时在不改变数据内容的情况下使用 reshape() 重新解释数组的形状。多数情况下返回视图。示例a np.arange(6)b a.reshape(2, 3) b[0, 0] 100print(a)输出[100 1 2 3 4 5]说明reshape() 在大多数情况下返回视图因此 b 与 a 共享数据内存。修改 b 会反映到原数组 a 上。3展平数组时使用 numpy.ravel() 将数组展平为一维数组时优先返回视图。示例a np.array([[1, 2, 3], [4, 5, 6]]) b np.ravel(a)b[0] 100print(a)输出[[100 2 3] [ 4 5 6]]三、数组副本Copy如果希望获得一个完全独立的数组则需要创建数组副本。1、显式创建数组副本NumPy 提供了 ndarray.copy() 和 numpy.copy() 两种复制方法。ndarray.copy()创建数组的完整副本复制底层数据新数组与原数组的数据完全独立。ndarray.copy(orderC)参数说明• order内存布局方式默认为 C返回值返回一个新的独立的 ndarray 数组对象。示例a np.array([[1, 2, 3], [4, 5, 6]]) b a.copy()b[0, 0] 99 print(a)输出[[1 2 3] [4 5 6]]说明数组 b 是 a 的数据副本因此修改 b 不会影响 a。numpy.copy()返回输入数组的副本。numpy.copy(a)参数说明• a输入数组返回值返回一个新的独立的 ndarray 数组对象。示例a np.array([[1, 2, 3], [4, 5, 6]]) b np.copy(a)b[0, 0] 99 print(a)输出[[1 2 3] [4 5 6]]说明函数 numpy.copy() 与 ndarray.copy() 的行为基本一致。2、返回副本的常见操作1高级索引时当使用整数数组或布尔数组进行高级索引时返回副本不共享底层数据。示例a np.array([[1, 2, 3], [4, 5, 6]]) b a[:, [1, 2]]b[0, 0] 99 print(a)输出[[1 2 3] [4 5 6]]说明虽然语法类似切片但因使用整数数组索引返回副本。需要注意的是是否返回视图并不取决于语法形式而取决于索引类型。只要使用整数数组或布尔数组即属于高级索引将返回副本。2展平数组时使用 ndarray.flatten() 将数组展平为一维数组时始终复制数据。示例a np.array([[1, 2, 3], [4, 5, 6]]) b a.flatten()b[0] 100print(a)输出[[1 2 3] [4 5 6]]说明flatten() 会返回数组副本因此修改 b 不会影响原数组 a。四、浅拷贝与深拷贝在 Python 中对象复制通常分为浅拷贝shallow copy和深拷贝deep copy两种方式通常通过 copy 模块实现copy.copy()copy.deepcopy()但对于 NumPy 数组来说情况略有不同。由于 NumPy 数组的数据通常存储在连续的内存块 中因此 ndarray.copy() 通常已经复制了数组数据在大多数情况下它的行为相当于 深拷贝。例如import numpy as np a np.array([1,2,3])b a.copy()此时两个数组的数据完全独立。需要注意的是如果数组的元素类型 dtype 为 object则数组中存储的是 Python 对象引用。在这种情况下ndarray.copy() 只会复制数组结构而不会递归复制对象本身因此仍可能出现浅拷贝行为。五、判断是否共享内存NumPy 提供了两个函数用于判断数组之间是否共享内存。numpy.shares_memory()判断两个数组是否共享同一块内存。numpy.shares_memory(a, b)返回值返回布尔值。说明numpy.may_share_memory() 与其基本一致但基于保守判断判断两个数组是否可能共享内存。示例a np.arange(6).reshape(2, 3)b a[:, 1:] print(np.shares_memory(a, b))输出True说明数组 b 是 a 的切片视图因此两者共享数据内存。如果数组是通过 copy() 创建c a.copy()np.shares_memory(a,c)输出False说明两个数组的数据不共享内存彼此独立。 小结NumPy 中的“复制”涉及数据缓冲区与数组结构两个层面。变量赋值仅创建引用切片与 view 通常生成共享数据的视图而 copy() 等操作才会复制底层数据。理解这些机制有助于避免隐式数据修改并提升数组计算的效率。“点赞有美意赞赏是鼓励”
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2450225.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!