首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python NumPy数组视图与深浅拷贝

Python NumPy数组视图与深浅拷贝

作者头像
sergiojune
发布2024-11-15 12:24:53
发布2024-11-15 12:24:53
5620
举报
文章被收录于专栏:日常学python日常学python

在数据科学和机器学习中,NumPy是Python中处理多维数组和大规模数据计算的重要工具。数组操作中,一个重要但易混淆的概念是视图(view)与拷贝(copy)。在NumPy中,数组的操作并不总是直接复制数据,而是可以通过视图共享数据,以节省内存和提高操作效率。然而,浅拷贝和深拷贝的机制使得数据的引用关系变得更加复杂。

NumPy中的视图(View)与拷贝(Copy)

在NumPy中,当从数组中提取子数组或对数组进行切片操作时,有可能创建的是一个视图,而不是拷贝。视图是原始数组的“窗口”,数据依然存储在原始数组的内存中,因此视图与原始数组共享同一块内存,修改视图的数据会影响原始数组的数据。拷贝则是对数据的完整复制,修改副本不会影响原始数组。

视图与拷贝的判断方法

在NumPy中,可以通过base属性来判断一个数组是否是另一个数组的视图。如果数组a的视图是b,则b.base会指向a,表明b的数据来自于a

代码语言:javascript
复制
import numpy as np

# 创建一个原始数组
a = np.array([1, 2, 3, 4, 5])

# 创建视图
b = a[1:4]
print("数组b:", b)
print("b是否为a的视图:", b.base is a)  # 输出True,表明b是a的视图

# 创建拷贝
c = a[1:4].copy()
print("数组c:", c)
print("c是否为a的视图:", c.base is a)  # 输出False,表明c是a的拷贝

在这个示例中,ba的视图,ca的拷贝。可以通过base属性来验证是否共享内存。

视图与浅拷贝的操作实例

在数据分析中,视图和浅拷贝的主要应用场景包括数据切片、形状变换和数据类型转换。NumPy在这些操作中会尽量创建视图以节省内存,除非视图无法满足需求时才会创建副本。

数据切片与视图

对NumPy数组进行切片操作时,生成的通常是视图。例如:

代码语言:javascript
复制
# 创建二维数组
array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# 进行切片操作,创建视图
view = array[:2, :2]
print("视图view:\n", view)

# 修改视图中的数据
view[0, 0] = 99
print("修改后原始数组:\n", array)

输出:

代码语言:javascript
复制
视图view:
 [[1 2]
  [4 5]]
修改后原始数组:
 [[99  2  3]
  [ 4  5  6]
  [ 7  8  9]]

在这个示例中,viewarray的视图,因此修改view中的数据会影响到原始数组array

形状变换与视图

在NumPy中,reshape方法通常会返回视图,特别是在数组是连续内存布局的情况下。然而,如果变换形状后的数组不是连续的内存布局,NumPy将返回一个拷贝。

代码语言:javascript
复制
# 创建连续存储的一维数组
array = np.array([1, 2, 3, 4, 5, 6])

# 进行形状变换,创建二维视图
reshaped_view = array.reshape(2, 3)
print("reshape生成的视图:\n", reshaped_view)

# 修改视图
reshaped_view[0, 0] = 99
print("修改后的原始数组:", array)

输出:

代码语言:javascript
复制
reshape生成的视图:
 [[1 2 3]
  [4 5 6]]
修改后的原始数组:
 [99  2  3  4  5  6]

在这里,reshape生成了一个视图,因此对reshaped_view的修改影响了原始数组array

数据类型转换与视图

使用astype进行数据类型转换时,NumPy通常会创建一个新的数组,即深拷贝,因而转换后的数组与原数组不会共享内存。

代码语言:javascript
复制
# 创建整数数组
array = np.array([1, 2, 3, 4, 5])

# 转换为浮点数类型,创建新数组
float_array = array.astype(float)
print("浮点数数组:", float_array)

# 修改新数组
float_array[0] = 99.9
print("修改新数组后原始数组:", array)

输出:

代码语言:javascript
复制
浮点数数组: [1. 2. 3. 4. 5.]
修改新数组后原始数组: [1 2 3 4 5]

由于astype生成了浮点类型的新数组float_array,它不与原数组共享内存,修改后的数据不会影响原数组。

深拷贝的使用场景与操作

深拷贝是对数据的完全复制,不共享原始数据的存储空间,因此深拷贝适用于希望避免修改副本影响原始数据的场景。NumPy中的copy方法可以显式生成深拷贝。

深拷贝实例

代码语言:javascript
复制
# 创建二维数组
original = np.array([[1, 2, 3], [4, 5, 6]])

# 生成深拷贝
deep_copy = original.copy()
print("深拷贝后的数组:\n", deep_copy)

# 修改深拷贝
deep_copy[0, 0] = 99
print("修改深拷贝后原始数组:\n", original)

输出:

代码语言:javascript
复制
深拷贝后的数组:
 [[1 2 3]
  [4 5 6]]
修改深拷贝后原始数组:
 [[1 2 3]
  [4 5 6]]

在这里,deep_copyoriginal的完全复制,两者不共享内存,因此修改deep_copy不会影响原始数组。

视图与拷贝的性能对比

在数据处理中,视图比拷贝更节省内存和时间,因为视图仅共享数据,而不需要创建新的数组。以下代码对比了视图和拷贝的创建时间。

代码语言:javascript
复制
import time

# 创建大数组
large_array = np.ones((10000, 10000))

# 视图创建时间
start = time.time()
view = large_array[:5000, :5000]
print("视图创建时间:", time.time() - start)

# 拷贝创建时间
start = time.time()
copy = large_array[:5000, :5000].copy()
print("拷贝创建时间:", time.time() - start)

通常,视图的创建速度更快,因为它不涉及内存的重新分配和数据的复制。

总结

在NumPy中,视图和拷贝是数组操作中的两个重要概念。视图通过共享原始数组的数据来实现内存效率,在切片和形状变换中具有广泛的应用;深拷贝则在不希望共享数据的情况下提供了完全的复制。通过掌握视图和拷贝的区别和适用场景,可以在数据处理中更高效地管理内存、提高代码的执行速度,并减少无意的数据修改。

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-11-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 日常学python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NumPy中的视图(View)与拷贝(Copy)
    • 视图与拷贝的判断方法
  • 视图与浅拷贝的操作实例
    • 数据切片与视图
    • 形状变换与视图
    • 数据类型转换与视图
  • 深拷贝的使用场景与操作
    • 深拷贝实例
  • 视图与拷贝的性能对比
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档