了解Numpy多维数组索引

时间:2017-01-15 17:46:45

标签: indexing numpy-broadcasting

请有人解释这三个索引操作之间的区别:

y = np.arange(35).reshape(5,7)

# Operation 1
y[np.array([0,2,4]),1:3]
# Operation 2
y[np.array([0,2,4]), np.array([[1,2]])]
# Operation 3
y[np.array([0,2,4]), np.array([[1],[2]])]

我得不到的是:

  • 为什么在操作1正常工作时操作2不起作用?
  • 为什么操作3正在工作,但是返回我期望的转置(即操作1的结果)?

根据numpy参考:

  

如果索引数组的形状不同,则尝试进行   将它们播放到相同的形状。如果他们不能广播到   相同的形状,提出异常。

好的,这意味着我做不到:

y[np.array([0,2,4]), np.array([1,2])]

但是numpy参考文献也谈到了操作1:

  

实际上,切片被转换为索引数组np.array([[1,2]])   (形状(1,2))与索引数组一起广播以产生a   结果形状阵列(3,2)。

为什么我不能这样做:

y[np.array([0,2,4]), np.array([[1,2]])]

我收到错误:

  

IndexError:形状不匹配:索引数组无法与形状一起广播(3,)(1,2)

1 个答案:

答案 0 :(得分:1)

In [1]: import numpy as np; y = np.arange(35).reshape(5,7)

操作1

In [2]: y[np.array([0,2,4]), 1:3]
Out[2]: 
array([[ 1,  2],
       [15, 16],
       [29, 30]])

这里我们混合了高级索引(使用数组)和基本索引(使用切片),只有一个高级索引。根据{{​​3}}

  

[a]单个高级索引可以替换切片和结果   数组将是相同的[...]

这是正确的,如下面的代码所示:

In [3]: y[::2, 1:3]
Out[3]: 
array([[ 1,  2],
       [15, 16],
       [29, 30]])

Out[2]Out[3]之间的唯一区别是前者是y中的数据副本(高级索引始终生成副本),而后者是共享与y相同的内存(基本索引仅生成视图)。

因此,在操作1中,我们通过np.array([0,2,4])选择了行,并通过1:3选择了列。

操作2

In [4]: y[np.array([0,2,4]), np.array([[1,2]])]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-bf9ee1361144> in <module>()
----> 1 y[np.array([0,2,4]), np.array([[1,2]])]

IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (1,2) 

这失败了,并且理解为什么我们首先必须意识到这个例子中索引的本质与操作1根本不同。现在我们只有高级索引(并且只有一个高级索引)。这意味着索引数组必须具有相同的形状或至少与reference兼容的形状。让我们来看看形状。

In [5]: np.array([0,2,4]).shape
Out[5]: (3,)
In [6]: np.array([[1,2]]).shape
Out[6]: (1, 2)

这意味着广播机制将尝试将这两个数组合并:

np.array([0,2,4])  (1d array):     3
np.array([[1,2]])  (2d array): 1 x 2
Result             (2d array): 1 x F

最后一行末尾的F表示形状不兼容。这就是IndexError操作2的原因。

操作3

In [7]: y[np.array([0,2,4]), np.array([[1],[2]])]
Out[7]: 
array([[ 1, 15, 29],
       [ 2, 16, 30]])

同样,我们只有高级索引。让我们看看这些形状现在是否兼容:

In [8]: np.array([0,2,4]).shape
Out[8]: (3,)
In [9]: np.array([[1],[2]]).shape
Out[9]: (2, 1)

这意味着广播将如下工作:

np.array([0,2,4])     (1d array):     3
np.array([[1],[2]])   (2d array): 2 x 1
Result                (2d array): 2 x 3

所以现在广播工作!由于我们的索引数组被广播到2x3数组,因此这也将是结果的形状。因此,它也解释了结果的形状与操作1的形状不同。

要获得形状3x2的结果,如操作1,我们可以

In [10]: y[np.array([[0],[2],[4]]), np.array([1, 2])]
Out[10]: 
array([[ 1,  2],
       [15, 16],
       [29, 30]])

现在广播机制的工作原理如下:

np.array([[0],[2],[4]])  (2d array): 3 x 1
np.array([1, 2])         (1d array):     2
Result                   (2d array): 3 x 2

给出3x2阵列。而不是np.array([1, 2])

In [11]: y[np.array([[0],[2],[4]]), np.array([[1, 2]])]
Out[11]: 
array([[ 1,  2],
       [15, 16],
       [29, 30]])

会因为

而起作用
np.array([[0],[2],[4]])  (2d array): 3 x 1
np.array([[1, 2]])       (2d array): 1 x 2
Result                   (2d array): 3 x 2