
[英]Create index list for np.split from the list that already has number for each section
[英]Why does np.split create a copy when passing into an existing array?
我是 Python 的新手,正在尝试了解视图和副本的行为,如果这是一个明显的问题,我深表歉意,在下面的示例中,我使用np.split()
拆分数组x
。 当我将 np.split 传递到一个新的 object( x1
,一个 3 1D arrays 的列表或x2, x3, x4
,三个独立的一维数组)时,对象是x
的视图,如预期的那样:
import numpy as np
x = np.arange(1, 10) # create an array of length 9
x1 = np.split(x, (3,6)) # split array into 1 new object (list of arrays)
print(x1[0].base) # each array in list is a view
x2, x3, x4 = np.split(x, (3, 6)) # split array into 3 new objects
print(x2.base) # these objects are views
但是,如果我创建一个空的 (3,3) 数组x5
并将 np.split 传递到该数组的每一行(我知道这是一件愚蠢的事情,我只是想弄清楚拆分是如何工作的),一个副本被建造:
x5 = np.empty((3,3), dtype = np.int32) # create an uninitialised array
x5[0], x5[1], x5[2] = np.split(x, (3, 6)) # split x into each row of x5
print(x5.base) # this object is a COPY
我想也许是对 x5 的切片导致了复制,但如果我对x2, x3, x4
进行切片,它们仍然是视图:
x2[:], x3[:], x4[:] = np.split(x, (3, 6)) # split array into 3 existing objects using indexing
print(x2.base) # these objects are views
我还没有在视图和副本或 np.split 的任何解释中找到对此的解释 - 我错过了什么?
您看到的行为仅与split
松散相关。 numpy
arrays 后面只有一个“矩形块”数据,它们不能引用多个独立分配。 该块可能会以奇怪的方式(通过跨步、屏蔽、转置等)查看,因此各个条目不一定是连续的,但它们都由一个且只有一个连续的批量分配支持。
该分配对于阵列或某个其他阵列的视图可以是唯一的,但给定阵列 object 在拥有存储和查看存储之间不会改变,它是其中之一。 数组绑定的名称可以重新分配,但所有名称都可以重新分配( x = 1
不会使x
总是那个特定的int
,甚至总是一些int
; x = ANYTHING
稍后会将它重新绑定到一个全新的 object任意类型,忽略它曾经是什么)。
知道这一点就很清楚,不可能将数组的一部分重新分配为其他数组的视图。 因此,解释您的各种观察结果:
x2, x3, x4 = np.split(x, (3, 6)) # split array into 3 new objects
print(x2.base) # these objects are views
如您所述,这是预期的行为。 np.split
返回一个list
,其中包含三个新的 arrays 视图类型,每个都由x
的一部分支持。
x5 = np.empty((3,3), dtype = np.int32) # create an uninitialised array
x5[0], x5[1], x5[2] = np.split(x, (3, 6)) # split x into each row of x5
print(x5.base) # this object is a COPY
此处的第 1 行创建了一个拥有类型的新数组。 即使可以用不同的缓冲区替换底层存储缓冲区(拥有一个新缓冲区,或者查看其他数组的缓冲区),我也不会发誓这是不可能的(我还没有充分探索numpy
可以肯定地说),要使它成为拥有和观看的混合体是绝对不可能的。 考虑更简单的代码:
x5[0] = x[:3]
如果该代码有效,并且没有将数据从x
的视图复制到x5[0]
的视图,那么不知何故, x5[0]
需要是x
的视图,而x5
的数据的 rest将被拥有。 缓冲区中支持x5[0]
的三个元素会发生什么变化? 您可能会想“但我要一次更换所有这些”,但实际上不是。 x5[0], x5[1], x5[2] = np.split(x, (3, 6))
实际上等同于__unnamedtemp = [x[:3], x[3:6], x[6:]]
,然后是x5[0] = __unnamedtemp[0]
,然后是x5[1] = __unnamedtemp[1]
,然后是x5[2] = __unnamedtemp[2]
。 解包工作必须一点一点地完成(解包是 Python 的一般特征, numpy
无法 hook),所以即使理论上最终结果可以离开x5
查看x
,实际上它不能,即使numpy
想要,因为中间阶段是非法的 state。
通过对比,
x2[:], x3[:], x4[:] = np.split(x, (3, 6)) # split array into 3 existing objects using indexing
print(x2.base)
“有效”只是因为x2
、 x3
和x4
已经是x
的视图。 但是副本仍在制作中; x2
已经是x[:3]
的视图,你只是告诉numpy
将x[:3]
的内容复制到x2[:]
。 在引擎盖下,一旦x2.__setitem__
以完整切片和另一个numpy
视图的参数调用,并且numpy
具有足够的信息和控制权,它可能会注意到原始 memory 地址是相同的并避免复制,但我如果它只是盲目地将视图中每个地址的数据复制到自身,也不会感到惊讶。
如果您没有通过x4
重用x2
,您将能够看到它没有产生新的视图:
x2, x3, x4 = np.arange(1, 4), np.arange(1, 4), np.arange(1, 4) # Three owning arrays
x2[:], x3[:], x4[:] = np.split(x, (3, 6)) # Makes three views, then copies from views to owned buffers
print(x2.base) # Does not have a base, because it's still not a view
这里的简短版本是:
这就是它对 Python 中所有类型的工作方式numpy
案例唯一不同寻常的是您可以创建视图; 内置的 Python 序列(保留memoryview
的怪异内存numpy
)没有视图的概念,因此等号右侧的切片将是浅拷贝,但分配给等号右侧的切片左侧仍将复制(无论右侧是视图还是副本)。
在 ipython session 中,创建x
并拆分
In [23]: x=np.arange(1,10);x
Out[23]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
In [24]: x2,x3,x4 = np.split(x,(3,6))
考察一:
In [25]: x2
Out[25]: array([1, 2, 3])
In [26]: x2.base
Out[26]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
我发现以下显示的数组信息很有帮助。 “数据”字段“指向”底层数据缓冲区。 它不能在代码中使用,但数字是值实际存储位置的有用标识符。
In [28]: x.__array_interface__
Out[28]:
{'data': (2389644026224, False),
'strides': None,
'descr': [('', '<i4')],
'typestr': '<i4',
'shape': (9,),
'version': 3}
x2
具有相同的“数据”值:
In [29]: x2.__array_interface__['data']
Out[29]: (2389644026224, False)
x3
和x4
的值略有不同,指向缓冲区中更远的字节(例如, x3
为2389644026236
,更远的为 3*4)。
x5
是一个新数组,有自己的数据缓冲区:
In [30]: x5 = np.empty((3,3),int);x5
Out[30]:
array([[4128860, 6029375, 3801155],
[5570652, 6619251, 7536754],
[7340124, 7667809, 108]])
In [31]: x5.__array_interface__['data']
Out[31]: (2389645593712, False)
为x5
赋值会复制这些值,但不会更改其数据缓冲区位置:
In [32]: x5[:] = x2,x3,x4
In [33]: x5
Out[33]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
In [34]: x5.__array_interface__['data']
Out[34]: (2389645593712, False)
x
和x5
仍然是它们自己的“基地”:
In [35]: x.base, x5.base
Out[35]: (None, None)
我们可以从x
索引另外 3 个值并将它们分配给x2
视图:
In [38]: x[-2:-5:-1]
Out[38]: array([8, 7, 6])
In [39]: x2[:]=x[-2:-5:-1] # not x2=x[...]
x2
将被更改,但它的基数也会更改:
In [40]: x2.base
Out[40]: array([8, 7, 6, 4, 5, 6, 7, 8, 9])
In [41]: x
Out[41]: array([8, 7, 6, 4, 5, 6, 7, 8, 9])
[39] 中的赋值与将 3 个值复制到x5
一样“昂贵”(时间方面)。 还在抄袭。 但不同之处在于值也被复制到哪里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.