【问题标题】:Understanding Numpy Multi-dimensional Array Indexing了解 Numpy 多维数组索引
【发布时间】:2017-05-30 13:50:36
【问题描述】:

请,谁能解释这三种索引操作之间的区别:

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]])]

我没有得到的是:

  • 为什么操作 2 不工作而操作 1 工作正常?
  • 为什么操作 3 有效,但返回了我期望的转置(即操作 1 的结果)?

根据numpy的参考:

如果索引数组的形状不同,则尝试 将它们广播到相同的形状。如果它们不能被广播到 相同的形状,引发异常。

好的,所以这意味着我不能这样做:

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

但 numpy 参考也提到了操作 1:

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

那为什么我不能这样做:

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

我得到错误:

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

【问题讨论】:

    标签: python numpy indexing array-broadcasting


    【解决方案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]])
    

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

    [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 根本不同。现在我们只有高级索引(并且不止一个高级索引)。这意味着索引数组必须具有相同的形状或至少与broadcasting 兼容的形状。让我们看看形状。

    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 表示形状不兼容。这就是操作2中IndexError的原因。

    操作 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不同的结果的形状。

    要获得操作 1 中形状 3x2 的结果,我们可以这样做

    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
    

    【讨论】:

    • 谢谢,很清楚!所以,广播机制只允许在每个维度上除了一个维度之外的所有维度都不同于“1”时组合 nd 数组,对吧?
    • 感谢您接受我的回答 :) 我不确定我是否正确理解了您的问题。如果您想知道两个 ndarray 是否与广播兼容,只需像我在答案中所做的那样写下它们的形状(就像examples 中的情况一样)。然后开始比较数字。如果两个数字相同或其中一个为 1,则一切正常。如果数字不同且都不是 1,则广播将失败。
    • 我的问题确实不清楚,但你回答了!再次感谢 !由于缺乏声誉,我无法支持您的答案,但再次感谢:)
    猜你喜欢
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-25
    • 2018-01-22
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    相关资源
    最近更新 更多