有关何时python自动创建数组副本的文档

时间:2019-06-30 09:47:12

标签: python arrays numpy view copy

我正在尝试查找有关何时python自动制作numpy数组副本的文档。这与数组视图有关。

如果使用简单索引,则分配将创建数组视图。如果使用高级索引,python将进行复制。我发现其他时候python制作数组副本时却找不到文档。这是一个示例:

a = np.array([1.,2.,3.,4.,5.])
av = a.view()
print(a)
print(av)
a[0] = 100.0
print(av)
a = 0
print(av)

变量 av a 的视图,直到 a 进行足够的更改以使其不再是视图。在哪里记录?

2 个答案:

答案 0 :(得分:1)

In [118]: a = np.array([1.,2.,3.,4.,5.]) 
     ...: av = a.view()                                                                                         
In [119]: a                                                                                                     
Out[119]: array([1., 2., 3., 4., 5.])
In [120]: av                                                                                                    
Out[120]: array([1., 2., 3., 4., 5.])

数组是具有dtypeshape之类的属性的对象,外加指向数据缓冲区的指针(base)。我们可以使用以下内容获得这些属性的摘要:

In [121]: a.__array_interface__                                                                                 
Out[121]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

av是一个新的数组对象,但是具有相同的属性,包括data

In [122]: av.__array_interface__                                                                                
Out[122]: 
{'data': (69293648, False),       # same value as for a
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

将其与copy进行比较:

In [123]: bv = a.copy()                                                                                         
In [124]: bv.__array_interface__                                                                                
Out[124]: 
{'data': (77843920, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

其他视图可能具有不同的值-切片的形状和步幅等不同。但是数据缓冲区将相同(__array_interface__['data']中显示的实际数字可能会有所不同,指向缓冲区中的其他元素

更改a的元素,由于共享数据缓冲区,我们也看到av的变化:

In [125]: a[0] =12                                                                                              
In [126]: av[0]                                                                                                 
Out[126]: 12.0
In [127]: bv[0]                                                                                                 
Out[127]: 1.0

当您为a分配其他内容时,av不会改变。

In [128]: a = 10                                                                                                
In [129]: a.__array_interface__                                                                                 
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-129-3b348559b028> in <module>
----> 1 a.__array_interface__

AttributeError: 'int' object has no attribute '__array_interface__'
In [130]: av.__array_interface__                                                                                
Out[130]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

a是整数,而不是数组。最初为a创建的数据缓冲区仍然存在,用作av的基础。否则原来的a对象就消失了。

我需要限定最后一条语句-由于ipython的输出缓冲,该数组对象仍然存在:

In [131]: Out[119].__array_interface__                                                                          
Out[131]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

In [132]: Out[119][0]                                                                                           
Out[132]: 12.0

我可能应该显示id(a)等,以便澄清

In [133]: id(av)                                                                                                
Out[133]: 139992523130800
In [134]: id(Out[119])                                                                                          
Out[134]: 139992142353312

av的更改会在Out[119]中出现,但不会在a中出现-因为a是一个完全不同的对象。

In [144]: av[1] = 100                                                                                           
In [145]: Out[119]                                                                                              
Out[145]: array([ 12., 100.,   3.,   4.,   5.])
In [146]: a                                                                                                     
Out[146]: 10

因此,在某种程度上,您需要了解Python变量和对象的性质。例如,b=a仅创建另一个名称或对同一对象的引用,而不是副本。 b = a.copy()会创建一个副本,但是该副本的详细信息取决于对象类。 numpycopy view上添加了一个变体,从而节省了时间和内存。但是您必须了解一些有关其数据存储机制的知识才能理解它。

答案 1 :(得分:0)

来自Advanced Indexing的NumPy文档:

  

当选择对象obj是非元组序列对象,ndarray(数据类型为整数或布尔值)或具有至少一个序列对象或ndarray(数据类型为整数或整数)的元组时,将触发高级索引布尔)。高级索引有两种类型:整数和布尔值。

     

高级索引总是返回数据的副本(与返回视图的基本切片相反)。


您可以使用属性base来检查ndarray是另一个的副本还是视图。

a = np.arange(0, 10)

b = a.view()
print(b.base is a) # True

c, d = a[2:], a[::2]
print(c.base is a and d.base is a) # True

b = a.copy()
print(b.base is a) # False

b = a[a >= 5]
print(b.base is a) # False



编辑: 我发现有趣的是,您可以创建视图'b'到数组'a',然后更改原始数组的内存地址(使用重新调整大小的方法)。 a的view'base'属性仍然指向b,但是b的更改不会反映在a上:

a = np.arange(0, 10)
b = a.view()
print(np.shares_memory(a, b)) # True
print(b.base is a) # True

a.resize(50, refcheck=False)
print(np.shares_memory(a, b)) # False
print(b.base is a) # True

b[0] = 20
print(a[0] == b[0]) # False

您可以做的另一种技巧是手动更改视图的“数据”属性。如果这样做,“ base”属性将更改:

a, b = np.arange(0, 10), np.arange(10, 20)
c = a.view()
print(c.base is a) # True

c.data = b.data
print(c.base is a) # False
print(c.base is b) # False
print(c[0]) # 10
print(c.base) # 'Memory at ....'
print(c.base == c.data == b.data) # True