当组合索引操作时,NumPy数组中的视图和副本会引起混淆。

3
>>> a = np.arange(12).reshape(3,4)

>>> a[0:3:2, :][:, [0,2]] = 100   ### the first time
>>> a  
array([[100, 1, 100, 3],
[ 4, 5, 6, 7],
[100, 9, 100, 11]])

>>> a[:, [0, 2]][0:3:2, :] = 0    ### second time
>>> a   
array([[100, 1, 100, 3],
       [ 4, 5, 6, 7],
       [100, 9, 100, 11]])

我对Python中的视图和副本感到非常困惑。上面的代码显示,在第一次将数组a中给定的行和列更改为100时,原始数组a被更改了。

然而,第二次原始数组并没有改变。这是为什么呢?


使用一组[]进行索引通常更好:a[0:3:2, [0,2]] - hpaulj
1个回答

5
使用[:, [0,2]]进行查找会返回一个副本,因为这是高级索引。但是,当你对一个切片进行赋值(例如array[whatever] = sth)时,它不会创建副本,而是将指定的项赋给切片,即使它是高级索引。
因此,第一个示例有效,因为第一个切片返回一个视图,然后使用视图的切片进行赋值。
然而,第二个示例“失败”,因为你将一个副本的切片进行了赋值(因为高级索引是在常规索引之前完成的)。
区别主要在于另一种方法负责设置(__setitem__)到切片中,而不是获取(__getitem__)这些切片。要解开你的陈述:
a[0:3:2, :][:, [0,2]] = 100 

a.__getitem__((slice(0, 3, 2), slice(None))).__setitem__((slice(None), [0, 2]), 100)
|-------------- returns a view ------------|

第二个将是:

而第二个则是:

a[:, [0,2]][0:3:2, :] = 0

a.__getitem__((slice(None), [0, 2])).__setitem__((slice(0, 3, 2), slice(None)), 0)
|--------- returns a copy ---------|

我查看了您提供的高级索引链接,但我仍然不确定 '[0:3:2, :]' 和 '[:, [0,2]]' 之间的区别在哪里。对我来说,两者都看起来像元组,为什么一个是高级索引而另一个是常规索引呢? - Bratt Swan
真的吗?你得到的取决于你如何索引它?这太疯狂了!有没有一种方法可以强制它仅返回视图/副本?谢谢 - Confounded
广泛的文档?语言应该是直观的,而不需要阅读大量文档。我认为这是糟糕的设计。此外,默认行为应该总是复制,如果需要查看,则应明确请求。在我看来这是糟糕的设计。 - Confounded
在strided属性上。我仍然不清楚为什么使用高级索引创建的对象不能包含指向基本数组的指针。这个strided属性如何防止我们存储它们? - Confounded
1
@Confounded Strided 意味着一个数组由基础内存地址和步长(每个维度的步数)表示。这意味着您可以通过使用“基础内存地址+(dimension1 * stride1 + dimension2 * stride2 ...)”来访问元素。使用基本切片,您可以简单地返回一个具有移动基础内存地址和更改步幅的数组。使用高级索引,您可以从数组中获取任意元素,因此不能使用修改后的步幅,因为可能存在重叠或不连续的步骤。因此,您可以实现高级索引的唯一(一致)方法是:创建一个新数组+复制。 - MSeifert
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接