复制numpy数组时显然会出现不一致的行为

问题描述 投票:0回答:1

考虑以下最小的例子。当复制不同嵌套深度的列表元素时,有人可以解释numpy的明显不一致的逻辑吗?

import numpy as np

L = [[[[1, 1], 2, 3]]]
A1 = np.array(L)

A2 = A1.copy()

A1[0][0][2] = 'xx'
A1[0][0][0][0] = 'yy'

print "\nA1 after changes:\n{}".format(A1)
print "\nA2 only partially changed:\n{}".format(A2)

结果:

A1 after changes:
[[[['yy', 1] 2 'xx']]]

A2 only partially changed:
[[[['yy', 1] 2 3]]]

然后:

>>> print A1[0][0][2] == A2[0][0][2]
False
>>> print A1[0][0][0][0] == A2[0][0][0][0]
True

我很难向自己解释为什么没有替换3,但更深层次的1是。

  1. A2 = np.array(A, copy=True)A2 = np.empty_like(A); np.copyto(A4, A)的行为与上面的代码相同
  2. A2 = A[:]的行为与A2 = A相同:两者在变化后都是相同的
  3. import copy; A2 = copy.deepcopy(A)是我发现创建独立副本的唯一解决方案。
python arrays numpy deep-copy
1个回答
1
投票

查看您的数组,并首先了解其结构:

In [139]: A1
Out[139]: array([[[[1, 1], 2, 3]]], dtype=object)

In [140]: A1.shape
Out[140]: (1, 1, 3)

这是一个dtype=object阵列;这是元素是对象指针,而不是数字。它也是3d,有3个元素。

In [142]: A1[0,0]  
Out[142]: array([[1, 1], 2, 3], dtype=object)

由于它是一个阵列,A1[0,0]A1[0][0]更好。功能相同,但更清晰。 A1[0,0,:]甚至更好。无论如何,在这个级别我们仍然有一个形状为(3,)的阵列,即1d有3个元素。

In [143]: A1[0,0,0]
Out[143]: [1, 1]

In [144]: A1[0,0,2]
Out[144]: 3

现在我们得到一个列表和数字,A1的各个元素。列表是可变的,数字不是。

我们可以将第3个元素(一个数字)更改为一个字符串:

In [148]: A1[0,0,2]='xy'

要更改第一个元素的元素,列表,我必须使用混合索引,而不是4级数组索引。

In [149]: A1[0,0,0,0]
...
IndexError: too many indices for array

In [150]: A1[0,0,0][0]='yy'

In [151]: A1
Out[151]: array([[[['yy', 1], 2, 'xy']]], dtype=object)

A1仍然是一个3d对象数组;我们只是改变了一些元素。 'xy'变化与'yy'变化不同。一个更改了数组,另一个更改了数组的列表元素。

A2=A1.copy()创建了一个包含A1元素(数据缓冲区)副本的新数组。所以A2指向与A1相同的对象。

'xy'改变了A1中的指针,但没有改变A2副本。

'yy'更改修改了A1指向的列表。 A2有一个指向同一列表的指针,因此它看到了变化。

请注意,L,原始嵌套列表看到相同的更改:

In [152]: L
Out[152]: [[[['yy', 1], 2, 3]]]

A3 = A[:]生产viewA1A3A1具有相同的数据缓冲区,因此它可以看到所有的变化。

A4 = A也会看到相同的变化,但A4是对A1的新参考,而不是视图或副本。

之前提出的duplicate answer涉及列表的引用,副本和深层副本。这是相关的,因为L是一个列表,而A1是一个对象数组,它在很多方面是一个列表的数组包装器。但A1也是numpy阵列,它有viewcopy之间的区别。

这不是numpy数组的好用,甚至不是对象dtype版本。这是一个有益的例子,但实际上太混乱了。如果你需要在一个数组上做一个deepcopy,你可能使用了错误的数组。

© www.soinside.com 2019 - 2024. All rights reserved.