numpy 如何排序数组切片索引?

Posted

技术标签:

【中文标题】numpy 如何排序数组切片索引?【英文标题】:How does numpy order array slice indices? 【发布时间】:2015-01-21 13:14:02 【问题描述】:

我有一个形状为 (28,8,20) 的 np.array data,我只需要其中的某些条目,所以我要分片:

In [41]: index = np.array([ 5,  6,  7,  8,  9, 10, 11, 17, 18, 19])
In [42]: extract = data[:,:,index]
In [43]: extract.shape
Out[43]: (28, 8, 10)

到目前为止一切都很好,一切都应该如此。但现在我只想查看第一行最后一个索引上的前两个条目:

In [45]: extract[0,:,np.array([0,1])].shape
Out[45]: (2, 8)

等等,应该是 (8,2)。它切换了索引,即使我上次切片时它没有!根据我的理解,下面的行为应该是一样的:

In [46]: extract[0,:,:2].shape
Out[46]: (8, 2)

...但这正是我想要的!不过,只要我有一个 3D 数组,这两种方法似乎都是等效的:

In [47]: extract[:,:,np.array([0,1])].shape
Out[47]: (28, 8, 2)

In [48]: extract[:,:,:2].shape
Out[48]: (28, 8, 2)

如果我想要的不仅仅是前两个条目,而是一个不规则的列表,我该怎么办?我当然可以在操作后转置矩阵,但这似乎非常违反直觉。 我的问题的一个更好的解决方案是这样(尽管可能有一个更优雅的解决方案):

In [64]: extract[0][:,[0,1]].shape
Out[64]: (8, 2)

这将我们带入实际

问题:

我想知道这种行为的原因是什么?谁决定这就是它应该如何工作的人可能比我更了解编程,并认为这在某种程度上是一致的,而我完全错过了。除非我有办法理解它,否则我可能会一直在这个问题上大发雷霆。

【问题讨论】:

The docs 用于高级索引(使用数组作为索引)承认这种行为“可能有点难以理解”。我不敢尝试完全掌握这种索引的复杂性。可以这么说,使用数组而不是切片(即[0, 1] 而不是:2)会触发不同类型的索引行为,并且组合不同类型的索引(单个整数、切片、数组)可以快速带您进入陌生的领域。 【参考方案1】:

这是(高级)部分索引的情况。有2个索引数组,1个切片

如果索引子空间是分开的(通过切片对象),那么广播的索引空间是第一个,然后是x的切片子空间。

http://docs.scipy.org/doc/numpy-1.8.1/reference/arrays.indexing.html#advanced-indexing

高级索引示例说明,当ind_1ind_2 可广播子空间为shape (2,3,4) 时:

但是,x[:,ind_1,:,ind_2] 的形状为 (2,3,4,10,30,50),因为在索引子空间中没有明确的放置位置,因此它被附加到开始。总是可以使用 .transpose() 将子空间移动到任何需要的地方。

换句话说,此索引与x[:, ind_1][[:,ind_2] 不同。这 2 个数组共同操作以定义 (2,3,4) 子空间。

在您的示例中,extract[0,:,np.array([0,1])] 被理解为意味着选择一个(2,) 子空间([0] 和 [0,1] 共同作用,而不是顺序作用),并以某种方式将其与中间维度结合起来。

更详细的例子是extract[[1,0],:,[[0,1],[1,0]]],它产生一个(2,2,8) 数组。这是第一个和最后一个维度的(2,2) 子空间,加上中间的一个。另一方面,X[[1,0]][:,:,[[0,1],[1,0]]] 生成 (2,8,2,2),分别从第一个和最后一个维度中选择。

关键区别在于索引选择是顺序操作还是联合操作。 `[...][...] 语法已经可用于顺序操作。高级索引为您提供了一种联合索引的方法。

【讨论】:

如果我理解正确,那么我的代码中的 0 将被视为一个单项列表,因此两个 lists 将一起广播(提供一个 1x2 数组),因此索引移到前面。 ... 因为没有“明确”的其他地方可以放置它。使用广播的整个想法根本不是切片而是索引,我可以在索引中放置任意数组并获得原始数组内容的各种排列 - 这似乎是有道理的,尽管它看起来更多因此也将[:,:,[0,1]] 的索引放在首位。 这意味着我应该使用单独的索引 ([...][...]) 来明确我的意图——谢谢!【参考方案2】:

你是对的,这很奇怪。我只能在这里冒险猜测。我认为这与a[[0,1],[0,1],[0,1]].shape(2,) 而不是(2,2,2) 以及a[0,1,[0,1,2]] 的真正含义是a[[0,0,0],[1,1,1],[0,1,2]] 的计算结果为array([a[0,1,0],a[0,1,1],a[0,1,2]]) 有关。也就是说,您可以并行遍历每个维度的列表即索引,广播长度为一的列表和标量以匹配最长的。

从概念上讲,这将使您的extract[0,:,[0,1]] 等同于extract[[0,0],[slice(None),slice(None)],[0,1]](但是,如果您手动指定它,则不接受该语法)。遍历索引后,将评估为array([extract[0,slice(None),0],extract[0,slice(None),1])。每个内部提取都评估为形状 (8,) 数组,因此完整结果是形状 (2,8)

所以总结一下,我认为广播的副作用是使所有维度都有一个相同长度的索引列表,这导致:也被广播。这是我的假设,但我还没有研究过 numpy 是如何做到这一点的内部工作原理。也许专家会给出更好的解释。

这个假设不能解释为什么extract[:,:,[0,1]] 不会导致相同的行为。我不得不假设只有前导“:”的情况是特殊情况以避免参与列表索引逻辑。

【讨论】:

以上是关于numpy 如何排序数组切片索引?的主要内容,如果未能解决你的问题,请参考以下文章

科学计算基础软件包NumPy入门讲座:操作数组

如何找到重新排序的 numpy 数组的索引?

科学计算基础软件包NumPy入门讲座:操作数组

NumPy 数组切片索引

Numpy常用方法及应用总汇

如何分配 Numpy 数组的特定部分?