我正在尝试查找有关何时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 进行足够的更改以使其不再是视图。在哪里记录?
答案 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.])
数组是具有dtype
和shape
之类的属性的对象,外加指向数据缓冲区的指针(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()
会创建一个副本,但是该副本的详细信息取决于对象类。 numpy
在copy
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