传递列表时的高级切片而不是numpy中的元组

时间:2016-11-14 22:22:32

标签: python numpy

在文档中,它说(强调我的):

  

当选择对象obj为a时,将触发高级索引   非元组序列对象,ndarray(数据类型为integer或bool),   或者具有至少一个序列对象或ndarray(数据类型的元组)的元组   整数或布尔)。高级索引有两种类型:整数   和布尔。

     

<剪断>

     

同时认识到x[[1,2,3]]将触发高级索引,而则会触发   x[[1,2,slice(None)]]将触发基本切片

我知道为什么x[(1, 2, slice(None))]会触发基本切片。但是,当x[[1,2,slice(None)]]满足作为非元组序列的条件时,为什么[1,2,slice(None)]会触发基本切片?

在相关的说明中,为什么会发生以下情况?

>>> a = np.eye(4)
>>> a[(1, 2)]  # basic indexing, as expected
0.0
>>> a[(1, np.array(2))] # basic indexing, as expected
0.0

>>> a[[1, 2]]  # advanced indexing, as expected
array([[ 0.,  1.,  0.,  0.],
   [ 0.,  0.,  1.,  0.]])
>>> a[[1, np.array(2)]]  # basic indexing!!??
0.0

2 个答案:

答案 0 :(得分:8)

该规则有例外。高级索引文档部分没有提及它,但在上面Basic Slicing and Indexing部分的开头附近,您将看到以下文本:

  

为了保持与Numeric中常见用法的向后兼容,如果选择对象是包含切片对象的任何非ndarray序列(例如列表),则也会启动基本切片,省略号object或newaxis对象,但不适用于整数数组或其他嵌入式序列。

a[[1, np.array(2)]]并未完全触发基本索引。它会触发向后兼容逻辑的未记录部分,如源代码中的comment中所述:

    /*
     * Sequences < NPY_MAXDIMS with any slice objects
     * or newaxis, Ellipsis or other arrays or sequences
     * embedded, are considered equivalent to an indexing
     * tuple. (`a[[[1,2], [3,4]]] == a[[1,2], [3,4]]`)
     */

列表中的np.array(2)会将列表视为元组,但结果a[(1, np.array(2))]仍然是高级索引操作。与1不同,它最终会将2a[[1, 2]]应用于单独的轴,结果最终看起来与a[1, 2]相同,但如果您尝试使用3D a,它会生成副本而不是视图。

答案 1 :(得分:1)

使用虚拟课程,我可以确定口译员如何将[...]翻译为__getitem__的调用。

In [1073]: class Foo():
      ...:     def __getitem__(idx):
      ...:         print(idx)
In [1080]: Foo()[1,2,slice(None)]
(1, 2, slice(None, None, None))
In [1081]: Foo()[(1,2,slice(None))]
(1, 2, slice(None, None, None))
In [1082]: Foo()[[1,2,slice(None)]]
[1, 2, slice(None, None, None)]

因此用()包装多个术语没有区别 - 它在两种情况下都会得到一个元组。列表作为列表传递。

因此元组和列表之间的区别(或不是)必须在numpy源代码中编码 - 这是编译的。所以我不能轻易研究它。

使用1d数组

使用列表建立索引会生成高级索引 - 选择特定值:

In [1085]: arr[[1,2,3]]
Out[1085]: array([ 0.73703368,  0.        ,  0.        ])

但是用元组或切片替换其中一个值:

In [1086]: arr[[1,2,(2,3)]]
IndexError: too many indices for array

In [1088]: arr[[1,2,slice(None)]] 
IndexError: too many indices for array

并将列表视为元组 - 它会尝试将值与维度匹配。

因此,在顶层,列表和元组的处理方式相同 - 如果列表不能解释为高级索引列表。

另请注意单项列出

的区别
In [1089]: arr[[1]]
Out[1089]: array([ 0.73703368])
In [1090]: arr[(1,)]
Out[1090]: 0.73703367969998546
In [1091]: arr[1]
Out[1091]: 0.73703367969998546

np.apply_along/over_axis这样的函数生成索引作为列表或数组,然后应用它。它们使用列表或数组,因为它是可变的。有些人在用作索引之前将其包装在tuple中;别人没有打扰。这种差异让我感到困扰,但是这些测试用例表明这种经常包装的元组是可选的。

In [1092]: idx=[1,2,slice(None)]
In [1093]: np.ones((2,3,4))[idx]
Out[1093]: array([ 1.,  1.,  1.,  1.])
In [1094]: np.ones((2,3,4))[tuple(idx)]
Out[1094]: array([ 1.,  1.,  1.,  1.])

如果我将索引构建为对象数组,看起来仍然需要元组包装器:

In [1096]: np.ones((2,3,4))[np.array(idx)]
...
IndexError: arrays used as indices must be of integer (or boolean) type
In [1097]: np.ones((2,3,4))[tuple(np.array(idx))]
Out[1097]: array([ 1.,  1.,  1.,  1.])

===================

来自函数@Eric链接

的评论
    /*
     * Sequences < NPY_MAXDIMS with any slice objects
     * or newaxis, Ellipsis or other arrays or sequences
     * embedded, are considered equivalent to an indexing
     * tuple. (`a[[[1,2], [3,4]]] == a[[1,2], [3,4]]`)
     */

===================

此函数将对象数组和列表包装在元组中以进行索引:

def apply_along_axis(func1d, axis, arr, *args, **kwargs):
     ....
     ind = [0]*(nd-1)
     i = zeros(nd, 'O')
     ....
     res = func1d(arr[tuple(i.tolist())], *args, **kwargs)
     outarr[tuple(ind)] = res