python复制模块中copy.copy和copy.deepcopy的相等性

时间:2016-01-16 06:11:31

标签: python arrays numpy

我正在创建一个numpy数组列表,然后将其复制到另一个数组以保留原始副本。使用deepcopy()函数完成复制。当我现在比较两个数组时,它在等价中显示为false。但是当我使用copy()函数时,这一切都很好。我理解复制和深度复制功能之间的区别,但是等价不一样吗?

那是:

 grid1=np.empty([3,3],dtype=object)
 for i in xrange(3):
    for j in xrange(3):
        grid1[i][j] = [i,np.random.uniform(-3.5,3.5,(3,3))]


 grid_init=[]
 grid_init=copy.deepcopy(grid1)
 grid1==grid_init      #returns False

 grid_init=[]
 grid_init=copy.copy(grid1)
 grid1==grid_init      #returns True

 grid_init=[]
 grid_init=copy.deepcopy(grid1)
 np.array_equal(grid1,grid_init)      #returns False

一切都不是真的吗?

2 个答案:

答案 0 :(得分:1)

这是我在运行第一个示例时得到的结果:

WARNING:py.warnings:/usr/local/bin/ipython:1: DeprecationWarning: elementwise comparison failed; this will raise the error in the future.

要了解元素比较失败的原因,只需尝试比较单个元素:

grid_init=copy.deepcopy(grid1)
grid_init[0][0] == grid1[0][0]
>>> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这会失败,因为列表中的第二个元素本身就是一个numpy数组,并且两个numpy数组的比较不会返回bool(而是一个数组)。

现在,为什么示例案例表现不同?

似乎是一些解释器优化,如果两个对象是相同的,则避免实际的比较逻辑。这两个是同一个对象,因为复制很浅。

grid_init=copy.copy(grid1)
grid_init[0][0] is grid1[0][0]
> True
grid_init[0][0] == grid1[0][0]
> True

根本原因是您正在使用 dtype = object的numpy数组,其中包含列表。这不是一个好主意,可能导致各种奇怪

相反,您应该只创建2个对齐的数组,一个用于列表中的第一个元素,另一个用于第二个元素。

答案 1 :(得分:0)

我必须运行不同版本的numpy / python,但我得到的错误和/或结果略有不同。仍然存在同样的问题 - 混合数组和列表会产生复杂的结果。

制作2份副本

In [217]: x=copy.copy(grid1)
In [218]: y=copy.deepcopy(grid1)

与浅拷贝相等,通过元素比较给出元素,3x3布尔值:

In [219]: x==grid1
Out[219]: 
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]], dtype=bool)

元素是2个项目列表:

In [220]: grid1[0,0]
Out[220]: 
[0, array([[ 2.08833787, -0.24595155, -3.15694342],
        [-3.05157909,  1.83814619, -0.78387624],
        [ 1.70892355, -0.87361521, -0.83255383]])]

在浅层副本中,列表ID是相同的。 2个数组具有不同的数据缓冲区(x不是视图),但它们都指向相同的列表对象(位于记忆中的其他位置)。

In [221]: id(grid1[0,0])
Out[221]: 2958477004
In [222]: id(x[0,0])
Out[222]: 2958477004

使用相同的id列表相同(它们也满足is测试)。

In [234]: grid1[0,0]==x[0,0]
Out[234]: True

但使用deepcopy的==会生成一个简单的False。这里没有元素比较。我不知道为什么。也许这是numpy正在发展的领域。

In [223]: y==grid1
Out[223]: False

请注意,deepcopy元素ID是不同的:

In [229]: id(y[0,0])
Out[229]: 2957009900

当我尝试将==应用于这些数组的元素时,我收到错误:

In [235]: grid1[0,0]==y[0,0]
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这是SO问题中反复出现的错误,通常是因为人们试图在标量Python上下文中使用布尔数组(来自比较)。

我可以在列表中比较数组:

In [236]: grid1[0,0][1]==y[0,0][1]
Out[236]: 
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]], dtype=bool)

我可以使用更简单的比较重现ValueError - 2个列表,其中包含一个数组。从表面看,它们看起来是一样的,但由于数组具有不同的ID,因此失败。

In [239]: [0,np.arange(3)]==[0,np.arange(3)]
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

这对比较显示了正在发生的事情:

In [242]: [0,np.arange(3)][0]==[0,np.arange(3)][0]
Out[242]: True
In [243]: [0,np.arange(3)][1]==[0,np.arange(3)][1]
Out[243]: array([ True,  True,  True], dtype=bool)

Python比较列表的各个元素,然后尝试执行逻辑运算以组合它们all()。但它无法在all上执行[True, array([True,True,True])]

因此,在我的版本中,y==grid1会返回False,因为按元素比较的元素会返回ValueErrors。这是或者提出错误或警告。他们显然不平等。

总而言之,使用这个数字和数组列表数组,相等测试最终会混合数组操作和列表操作。结果是合乎逻辑的,但很复杂。您必须敏锐地意识到如何比较数组,以及如何比较列表。它们不可互换。

结构化数组

您可以将此数据放入结构化数组中,其中包含dtype

 dt = np.dtype([('f0',int),('f1',float,(3,3))])

In [263]: dt = np.dtype([('f0',int),('f1',float,(3,3))])
In [264]: grid2=np.empty([3,3],dtype=dt)
In [265]: for i in range(3):
        for j in range(3):
                grid2[i][j] = (i,np.random.uniform(-3.5,3.5,(3,3)))
   .....:         
In [266]: grid2
Out[266]: 
array([[ (0, 
    [[2.719807845330254, -0.6379512247418969, -0.02567206509563602],
     [0.9585030371031278, -1.0042751112999135, -2.7805349057485946], 
     [-2.244526250770717, 0.5740647379258945, 0.29076071288760574]]),
....]])]], 
      dtype=[('f0', '<i4'), ('f1', '<f8', (3, 3))])

第一个字段,可以使用(给出3x3数组)

获取整数
In [267]: grid2['f0']
Out[267]: 
array([[0, 0, 0],
       [1, 1, 1],
       [2, 2, 2]])

第二个字段包含3x3数组,当按字段名访问时,它们是一个4d数组:

In [269]: grid2['f1'].shape
Out[269]: (3, 3, 3, 3)

单个元素是记录(或元组),

In [270]: grid2[2,1]
Out[270]: (2, [[1.6236266210555836, -2.7383730706629636, -0.46604477485902374], [-2.781740733659544, 0.7822732671353201, 3.0054266762730473], [3.3135671425199824, -2.7466097112667103, -0.15205961855874406]])

现在两种副本产生相同的东西:

In [271]: x=copy.copy(grid2)
In [272]: y=copy.deepcopy(grid2)
In [273]: x==grid2
Out[273]: 
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]], dtype=bool)
In [274]: y==grid2
Out[274]: 
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]], dtype=bool)

由于grid2是纯ndarray(没有中间列表),我怀疑copy.copycopy.deepcopy最终使用grid2.copy()。在numpy中,我们通常使用数组复制方法,而不必担心copy模块。

P.S。似乎dtype=objectgrid1.copy()copy.copy(grid1)相同 - 一个新数组,但是相同的对象指针(即相同的数据)。