【问题标题】:How does "Fancy Indexing with Broadcasting and Boolean Masking" work?“带有广播和布尔掩码的精美索引”如何工作?
【发布时间】:2020-05-12 22:57:08
【问题描述】:

我在 Jake Vanderplas 的数据科学手册中看到了这段代码。在这里使用广播和花式索引的概念对我来说并不清楚。请解释一下。

In[5]: X = np.arange(12).reshape((3, 4))
 X
Out[5]: array([[ 0, 1, 2, 3],
 [ 4, 5, 6, 7],
 [ 8, 9, 10, 11]])

In[6]: row = np.array([0, 1, 2])
 col = np.array([2, 1, 3])

In[7]: X[row[:, np.newaxis], col]
Out[7]: array([[ 2, 1, 3],
               [ 6, 5, 7],
              [10, 9, 11]])

它说:“这里,每一行值都与每个列向量匹配,就像我们在广播算术运算中看到的那样。例如:”

In[8]: row[:, np.newaxis] * col
Out[8]: array([[0, 0, 0],
               [2, 1, 3],
               [4, 2, 6]])

【问题讨论】:

  • 我假设您已经阅读了numpy's documentation on broadcasting。你到底有什么不清楚的地方?
  • 它试图说 rowcol 值的相同配对(笛卡尔积)适用于 [7] 索引情况,就像广播乘法一样。
  • @hpaulj 这是一个很好的答案。如果你能给出更详细的解释就好了。

标签: python arrays numpy masking array-broadcasting


【解决方案1】:

我来这里是为了寻找这个问题的答案,hpaulj 的评论帮助了我。我将对其进行扩展。

在下面的sn-p中,

import numpy as np
X = np.arange(12).reshape((3, 4))
row = np.array([0, 1, 2])
col = np.array([2, 1, 3])
Y = X[row.reshape(-1, 1), col]

我们传递给 X索引 正在广播。

以下代码遵循 numpy 广播规则但使用更多内存,完成相同的切片:

# Make the row and column indices 'conformable'
R = np.repeat(row.reshape(-1, 1), 3, axis=1)  # repeat row index across columns
C = np.repeat(col.reshape(1, -1), 3, axis=0)  # repeat column index across rows
Y = X[R, C]  # Y[i, j] = X[R[i, j], C[i, j]]

【讨论】:

    【解决方案2】:

    如果你使用一个整数数组来索引另一个数组 您基本上遍历给定的索引并沿着您要索引的轴选择相应的元素(可能仍然是一个数组)并将它们堆叠在一起。

    arr55 = np.arange(25).reshape((5, 5))
    # array([[ 0,  1,  2,  3,  4],
    #        [ 5,  6,  7,  8,  9],
    #        [10, 11, 12, 13, 14],
    #        [15, 16, 17, 18, 19],
    #        [20, 21, 22, 23, 24]])
    
    arr53 = arr55[:, [3, 3, 4]]  
    # pick the elements at (arr[:, 3], arr[:, 3], arr[:, 4])
    # array([[ 3,  3,  4],
    #        [ 8,  8,  9],
    #        [13, 13, 14],
    #        [18, 18, 19],
    #        [23, 23, 24]])
    

    因此,如果您使用长度为k(或长度l)的行(或列)索引来索引(m, n) 数组,则生成的形状为:

    A_nm[row, :] -> A_km
    A_nm[:, col] -> A_nl
    

    但是,如果您使用两个数组 rowcol 来索引一个数组 您同时遍历两个索引并将相应位置的元素(可能仍然是数组)堆叠在一起。 这里rowcol 的长度必须相同。

    A_nm[row, col] -> A_k
    array([ 3, 13, 24])
    
    arr3 = arr55[[0, 2, 4], [3, 3, 4]]  
    # pick the element at (arr[0, 3], arr[2, 3], arr[4, 4])
    
    

    现在终于回答您的问题了:在索引数组时可以使用广播。有时不希望只有元素

    (arr[0, 3], arr[2, 3], arr[4, 4])
    

    被选中,而是扩展版本:

    (arr[0, [3, 3, 4]], arr[2, [3, 3, 4]], arr[4, [3, 3, 4]])
    # each row value is matched with each column vector
    

    这种匹配/广播与其他算术运算完全相同。 但是这里的例子在某种意义上可能是不好的,不是显示的乘法结果对索引很重要。 这里的重点是组合和生成的形状:

    row * col  
    # performs a element wise multiplication resulting in 3 
    numbers
    row[:, np.newaxis] * col 
    # performs a multiplication where each row value is *matched* with each column vector
    

    示例想要强调rowcol 的这种匹配。

    我们可以看看并尝试不同的可能性:

    n = 3
    m = 4
    X = np.arange(n*m).reshape((n, m))
    row = np.array([0, 1, 2])  # k = 3
    col = np.array([2, 1, 3])  # l = 3
    
    X[row, :]  # A_nm[row, :] -> A_km
    # array([[ 0,  1,  2,  3],
    #        [ 4,  5,  6,  7],
    #        [ 8,  9, 10, 11]])
    
    X[:, col]  # A_nm[:, col] -> A_nl
    # array([[ 2,  1,  3],
    #        [ 6,  5,  7],
    #        [10,  9, 11]])
    
    X[row, col]  # A_nm[row, col] -> A_l == A_k
    # array([ 2,  5, 11]
    
    X[row, :][:, col]  # A_nm[row, :][:, col] -> A_km[:, col] -> A_kl 
    # == X[:, col][row, :]
    # == X[row[:, np.newaxis], col]  # A_nm[row[:, np.newaxis], col] -> A_kl 
    # array([[ 2,  1,  3],
    #        [ 6,  5,  7],
    #        [10,  9, 11]])
    
    X[row, col[:, np.newaxis]]
    # == X[row[:, np.newaxis], col].T
    # array([[ 2,  6, 10],
    #        [ 1,  5,  9],
    #        [ 3,  7, 11]])
    
    

    【讨论】:

    • 永远不要使用倒数第二个例子。它是链式索引,效率低下,如果其中一个操作返回副本而不是视图,可能会产生意想不到的副作用。
    猜你喜欢
    • 2019-10-29
    • 1970-01-01
    • 2023-04-03
    • 2019-04-05
    • 2018-12-13
    • 2020-01-14
    • 2016-11-22
    • 2021-09-10
    • 2011-11-03
    相关资源
    最近更新 更多