【问题标题】:Efficient way to find indices of topmost True values in 2d boolean array (Python)在二维布尔数组(Python)中查找最高真值索引的有效方法
【发布时间】:2021-11-02 01:10:50
【问题描述】:

假设我有一个形状为 (nrows,ncols) 的二维布尔数组。我正在尝试有效地提取数组中每一列的最高 True 值的索引。如果该列具有所有 False 值,则不返回该列的索引。下面是一个形状为 (4,6) 的布尔数组示例,其中粗体 Trues 的索引将是所需的输出。

假假假假假假真假

  假 假   假 假

True False True False False True

真 假 真 真 假 假

索引的期望输出(行,列):[(1,0),(2,2),(1,3),(2,5)]

我尝试使用 numpy.where 以及天际线算法的实现,但是这两个选项都很慢。有没有更有效的方法来解决这个问题?

提前感谢您的帮助。

【问题讨论】:

  • 不要认为这里有比从上到下遍历每列直到第一个 True 更好的方法
  • 定义“高效”。你必须走遍整个阵列,所以你不能比O(n^2)做得更好。您只能尽量避免较大的常量开销。
  • @Thomas 我认为您的意思是 O(N) 的数组大小。
  • @juanpa.arrivillage 当然。我认为问题大小n 是行数+列数。

标签: python boolean


【解决方案1】:

您可以使用np.argmax 检测第一个True 值。

准备示例数组。

import numpy as np
a = np.array(
[[0,0,0,0,0,0],
 [1,0,0,1,0,0],
 [1,0,1,0,0,1],
 [1,0,1,1,0,0]]).astype('bool')
a

输出

array([[False, False, False, False, False, False],
       [ True, False, False,  True, False, False],
       [ True, False,  True, False, False,  True],
       [ True, False,  True,  True, False, False]])

堆叠一行False 来处理没有True 的列。在具有np.argmax 的每一列中找到第一个True,并为行索引附加一个范围。您必须通过-1 调整列索引,因为我们在数组中添加了一行。然后选择True的索引大于0的列​​

b = np.vstack([np.zeros_like(a[0]),a])
t = b.argmax(axis=0)
np.vstack([t - 1, np.arange(len(a[0]))]).T[t > 0]

输出

array([[1, 0],
       [2, 2],
       [1, 3],
       [2, 5]])

@HenryYik 的答案翻译成 numpy 给出了一个单行解决方案

np.vstack([a.argmax(axis=0), np.arange(len(a[0]))]).T[a.sum(0) > 0]

输出

array([[1, 0],
       [2, 2],
       [1, 3],
       [2, 5]])

【讨论】:

    【解决方案2】:

    我建议你试试这个:

    def get_topmost(ar: np.ndarray):
        return [(row.index(True), i) for i, row in enumerate(ar.T.tolist()) if True in row]
    

    示例:(应该按原样工作)

    >>> test = np.array([
        [False, False, False, False, False, False],
        [True,  False, False, True,  False, False],
        [True,  False, True,  False, False, True],
        [True,  False, True,  True,  False, False],
    ])
    
    >>> print(get_topmost(test))
    [(1, 0), (2, 2), (1, 3), (2, 5)]
    

    【讨论】:

      【解决方案3】:

      如果你愿意使用pandas,你可以构造一个df,只删除带有False的列,然后idxmax

      arr = [[False, False, False, False, False, False],
             [True, False, False, True, False, False],
             [True, False, True, False, False, True],
             [True, False, True, True, False, False]]
      
      df = pd.DataFrame(arr, columns=range(len(arr[0])))
      
      s = df.loc[:, df.sum()>0].idxmax()
      print (s)
      

      结果:

      0    1
      2    2
      3    1
      5    2
      dtype: int64
      

      哪个是列值与行值。您可以将其转换回您想要的形式:

      print (list(zip(s, s.index)))
      
      [(1, 0), (2, 2), (1, 3), (2, 5)]
      

      【讨论】:

      • 我可以将您的 sum 想法复制到我的解决方案中,以展示它在 numpy 中的工作原理吗?
      • 当然继续 :)
      猜你喜欢
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 2023-01-11
      • 2019-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-11
      相关资源
      最近更新 更多