什么是numpy数组的视图(view)和浅拷贝(shallow copy)之间的区别?

7
在NumPy中,我了解到对数组进行切片会给你一个"视图(view)",这对我来说似乎与浅复制(shallow copy)完全相同。它们有什么不同呢?

请阅读此文章 ViewsVsCopies - gyx-hh
1
还有来自numpy文档的内容:https://docs.scipy.org/doc/numpy/user/quickstart.html#copies-and-views。了解`numpy`数组是如何存储的(例如形状和类型等属性以及数据缓冲区),以及与列表(包含指针的数据缓冲区)相比的区别,这对于理解有所帮助。 - hpaulj
2个回答

12
与Python列表对象不同,NumPy数组仅引用一个数据缓冲区,该缓冲区存储数组所有维度的元素值,且除此数据缓冲区外没有其他元素对象层次结构。
浅拷贝列表将包含对第一级元素引用的副本,并与原始列表共享所引用的元素对象。不太清楚NumPy数组的浅拷贝应该包含什么。它应该(A)与原始数组共享数据缓冲区,还是(B)拥有自己的副本(这实际上使它成为深拷贝)?
NumPy数组的视图是指A意义上的浅拷贝,即它引用与原始数组相同的数据缓冲区,因此对原始数据的更改会影响视图数据,反之亦然。
库函数copy.copy()应该创建其参数的浅拷贝,但是当应用于NumPy数组时,它会在意义B上创建浅拷贝,即新数组获得其自己的数据缓冲区的副本,因此对一个数组所做的更改不会影响另一个数组。
以下是一些展示如何复制/查看NumPy数组的代码:
import numpy as np
import copy

x = np.array([10, 11, 12, 13])

# Create views of x (shallow copies sharing data) in 2 different ways
x_view1 = x.view()
x_view2 = x[:]          # Creates a view using a slice

# Create full copies of x (not sharing data) in 2 different ways
x_copy1 = x.copy()
x_copy2 = copy.copy(x)  # Calls x.__copy__() which creates a full copy of x

# Change some array elements to see what happens
x[0]       = 555        # Affects x, x_view1, and x_view2
x_view1[1] = 666        # Affects x, x_view1, and x_view2
x_view2[2] = 777        # Affects x, x_view1, and x_view2
x_copy1[0] = 888        # Affects only x_copy1
x_copy2[0] = 999        # Affects only x_copy2

print(x)                # [555 666 777  13]
print(x_view1)          # [555 666 777  13]
print(x_view2)          # [555 666 777  13]
print(x_copy1)          # [888  11  12  13]
print(x_copy2)          # [999  11  12  13]

上面的示例创建了整个原始数组索引范围的视图,并具有与原始数组相同的属性,这并不是很有趣(可以用简单的别名替换,例如 x_alias = x)。视图强大的地方在于它们可以是原始数组中所选部分的视图,并具有不同的属性。下面几行代码展示了这一点,扩展了上面的示例:
x_view3 = x[::2].reshape(2,1) # Creates a reshaped view of every 2nd element of x
print(x_view3)          # [[555]
                        #  [777]]
x_view3[1] = 333        # Affects 2nd element of x_view3 and 3rd element of x
print(x)                # [555 666 333  13]
print(x_view3)          # [[555]
                        #  [333]]

3
要复制 x,只需使用 x.copy()。不需要导入 copy - hpaulj
2
不,使用numpy数组时,view是一个新对象,但具有共享数据缓冲区。它可能具有不同的dtype和/或shape。这与x_alias = x不同,其中两个变量引用相同的数组对象。数组copy是一个具有自己数据缓冲区的新数组对象。顶层和底层复制之间的区别适用于列表(浅复制 vs 深复制)。 - hpaulj
@hpaulj,好的。我使用copy.copy()是因为它是一种通用的浅拷贝方式,在Python 2和3中都适用(至少在2中,并不总是有一个复制方法可以复制你想要复制的对象)。我的观点关于x_alias并不是它与视图相同,而是在给定的示例中,人们可能会使用它来代替视图。无论如何,你关于数据缓冲区的观点是正确的——当我写这篇文章时,我确实考虑到了列表。 :-) - Ovaflo
@hpaulj,我编辑了我的答案以回应您的评论。 - Ovaflo

1

基本上有两种主要类型,一种是浅拷贝,另一种是深拷贝。

浅拷贝: 在浅拷贝中,如果您更改一个变量的值,它将反映在另一个变量中。 例如:

import numpy as np
a = np.array([1,2,3,4])
b=a
b[0]=353
print(b)
#>>> array([353,2,3,4])
print(a)
#>>> array([353,2,3,4])

深拷贝: 如果进行深拷贝,当您更改一个变量的值时,它不会反映在其他变量中。 例如:
import numpy as np
a = np.array([1,2,3,4])
c=np.copy(a)
c[0]=111
print(c)
#>>> array([111,2,3,4])
print(a)
#>>> array([1,2,3,4])

b=a 根本不会创建任何副本 - juanpa.arrivillaga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接