NumPy 中的视图与副本¶
日期 | 2018-02-11(最后修改),2008-01-31(创建) |
---|
在 [ ]
>>> a = numpy.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> v1 = a[1:2]
>>> v1
array([1])
>>> a[1] = 2
>>> v1
array([2])
>>> v2 = a[1::3]
>>> v2
array([2, 4, 7])
>>> a[7] = 10
>>> v2
array([ 2, 4, 10])
在上面的代码片段中,v1 和 v2 是 a 的视图。如果您更新 a 的元素,那么 v1 和 v2 将反映更改。
Dtype 视图¶
另一种创建数组视图的方法是将另一个 **dtype** 分配给同一个数据区域。例如
在 [ ]
>>> b = numpy.arange(10, dtype='int16')
>>> b
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype=int16)
>>> v3 = b.view('int32')
>>> v3 += 1
>>> b
array([1, 1, 3, 3, 5, 5, 7, 7, 9, 9], dtype=int16)
>>> v4 = b.view('int8')
>>> v4
array([1, 0, 1, 0, 3, 0, 3, 0, 5, 0, 5, 0, 7, 0, 7, 0, 9, 0, 9, 0], dtype=int8)
在 [ ]
>>> a = numpy.arange(10)
>>> c1 = a[[1,3]]
>>> c2 = a[[3,1,1]]
>>> a[:] = 100
>>> c1
array([1, 3])
>>> c2
array([3, 1, 1])
花式索引不返回视图的原因是,一般来说,它无法用切片来表示(如上所述,能够用偏移量、步长和计数来寻址)。
例如,花式索引可以表示为,但无法通过切片来表示。因此,返回的是包含原始数据副本的对象。
但是,花式索引似乎有时会返回视图,不是吗?¶
许多用户被误导,认为当他们使用这种习惯用法时,花式索引返回的是视图而不是副本。
在 [ ]
>>> a = numpy.arange(10)
>>> a[[1,2]] = 100
>>> a
array([ 0, 100, 100, 3, 4, 5, 6, 7, 8, 9])
因此,似乎a<1,2>实际上是一个视图,因为元素 1 和 2 已被更新。但是,如果我们一步一步地尝试,它将无法工作。
在 [ ]
>>> a = numpy.arange(10)
>>> c1 = a[[1,2]]
>>> c1[:] = 100
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> c1
array([100, 100])
这里发生的事情是,在第一个习惯用法()中,Python 解释器将其转换为
在 [ ]
a.__setitem__([1,2], 100)
即,不需要创建视图或副本,因为该方法可以在原地进行评估(即不涉及创建新对象)。
但是,第二个习惯用法()被转换为
在 [ ]
c1 = a.__getitem__([1,2])
c1.__setitem__(slice(None, None, None), 100) # [:] translates into slice(None, None, None)
即,在调用之前,创建一个包含副本(记住,花式索引不返回视图)的某些元素的新对象。
这里的经验法则可以是:在左值索引的上下文中(即索引位于赋值的左侧),不会创建数组的视图或副本(因为没有必要)。但是,对于常规值,上述创建视图的规则适用。
最后的练习¶
最后,让我们提出一个稍微高级的问题。下一个代码段按预期工作
在 [ ]
>>> a = numpy.arange(12).reshape(3,4)
>>> ifancy = [0,2]
>>> islice = slice(0,3,2)
>>> a[islice, :][:, ifancy] = 100
>>> a
array([[100, 1, 100, 3],
[ 4, 5, 6, 7],
[100, 9, 100, 11]])
但下一个代码段不工作
在 [ ]
>>> a = numpy.arange(12).reshape(3,4)
>>> ifancy = [0,2]
>>> islice = slice(0,3,2)
>>> a[ifancy, :][:, islice] = 100 # note that ifancy and islice are interchanged here
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
读者能发现其中的区别吗?“提示:从{{{getitem()}}} 和 {{{setitem()}}} 调用的顺序以及它们在每个示例中的作用来思考。”
章节作者:FrancescAltet、Unknown[16]、WarrenWeckesser、Ben Lewis