【问题标题】:check for identical rows in different numpy arrays检查不同 numpy 数组中的相同行
【发布时间】:2018-12-23 10:51:36
【问题描述】:

如何在逐行真/假数组的结果中获得两个数组之间的逐行比较?

给定数据:

a = np.array([[1,0],[2,0],[3,1],[4,2]])
b = np.array([[1,0],[2,0],[4,2]])

结果步骤 1:

c = np.array([True, True,False,True])

最终结果:

a = a[c]

那么如何获取数组c ????

P.S.:在此示例中,数组 ab 已排序,如果在您的解决方案中对数组进行排序很重要,请同时提供信息

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    这是一个矢量化的解决方案:

    res = (a[:, None] == b).all(-1).any(-1)
    
    print(res)
    
    array([ True,  True, False,  True])
    

    注意a[:, None] == ba 的每一行与b 元素进行比较。然后我们使用all + any 来推断每个子数组是否有任何行都是True

    print(a[:, None] == b)
    
    [[[ True  True]
      [False  True]
      [False False]]
    
     [[False  True]
      [ True  True]
      [False False]]
    
     [[False False]
      [False False]
      [False False]]
    
     [[False False]
      [False False]
      [ True  True]]]
    

    【讨论】:

    • 这看起来不错 a = np.array([[1,0],[2,0],[4,2],[3,1],[3,0]]) b = np.array([[1,0],[2,0],[3,1]]) c = (a[:, None] == b).all(-1).any(-1)结果[真真假真假]
    • 谢谢,这是我见过的最简单的方法!
    【解决方案2】:

    方法#1

    我们可以使用基于 view 的矢量化解决方案 -

    # https://stackoverflow.com/a/45313353/ @Divakar
    def view1D(a, b): # a, b are arrays
        a = np.ascontiguousarray(a)
        b = np.ascontiguousarray(b)
        void_dt = np.dtype((np.void, a.dtype.itemsize * a.shape[1]))
        return a.view(void_dt).ravel(),  b.view(void_dt).ravel()
    
    A,B = view1D(a,b)
    out = np.isin(A,B)
    

    示例运行 -

    In [8]: a
    Out[8]: 
    array([[1, 0],
           [2, 0],
           [3, 1],
           [4, 2]])
    
    In [9]: b
    Out[9]: 
    array([[1, 0],
           [2, 0],
           [4, 2]])
    
    In [10]: A,B = view1D(a,b)
    
    In [11]: np.isin(A,B)
    Out[11]: array([ True,  True, False,  True])
    

    方法 #2

    对于b 中的所有行都在a 中并且行按字典顺序排序的情况,使用相同的views,但使用searchsorted -

    out = np.zeros(len(A), dtype=bool)
    out[np.searchsorted(A,B)] = 1
    

    如果行不一定按字典顺序排序 -

    sidx = A.argsort()
    out[sidx[np.searchsorted(A,B,sorter=sidx)]] = 1
    

    【讨论】:

    • 方法 #1 有效,第二个到目前为止还没有测试(或理解)......谢谢
    【解决方案3】:

    您可以将 numpy 与 apply_along_axis 一起使用(特定轴上的迭代类型,而 axis=0 在每个单元格上迭代,axis=1 在每一行上迭代,axis=2 在矩阵上等等

    import numpy as np
    a = np.array([[1,0],[2,0],[3,1],[4,2]])
    b = np.array([[1,0],[2,0],[4,2]])
    c = np.apply_along_axis(lambda x,y: x in y, 1, a, b)
    

    【讨论】:

    • 这实际上并没有使用 np.isin,有点困惑你为什么提到它,因为我认为它在这里不是特别有用。
    • 似乎无法检查相同的行:a = np.array([[1,0],[2,0],[4,2],[3,1] ,[3,0]]) b = np.array([[1,0],[2,0],[3,1]]) c = np.apply_along_axis(lambda x,y: x in y, 1 , a, b) 结果是[True True False True True]最后一个应该是假的
    【解决方案4】:

    您可以通过以下方式将其作为列表组合进行:

    c = np.array([row in b for row in a])
    

    虽然这种方法会比纯 numpy 方法慢(如果存在的话)。

    【讨论】:

      【解决方案5】:
      a = np.array([[1,0],[2,0],[3,1],[4,2]])
      b = np.array([[1,0],[2,0],[4,2]])
      
      i = 0
      j = 0
      result = []
      

      我们可以利用它们已排序的事实并在 O(n) 时间内完成此操作。使用两个指针,我们只是将后面的指针向前移动:

      while i < len(a) and j < len(b):
          if tuple(a[i])== tuple(b[j]):
              result.append(True)
              i += 1
              j += 1 # get rid of this depending on how you want to handle duplicates
          elif tuple(a[i]) > tuple(b[j]):
              j += 1
          else:
              result.append(False)
              i += 1
      

      如果提前结束,则用 False 填充。

      if len(result) < len(a):
          result.extend([False] * (len(a) - len(result)))
      
      print(result) # [True, True, False, True]
      

      此答案改编自Better way to find matches in two sorted lists than using for loops? (Java)

      【讨论】:

        【解决方案6】:

        您可以使用 scipy 的 cdist,它有几个优点:

        from scipy.spatial.distance import cdist
        
        a = np.array([[1,0],[2,0],[3,1],[4,2]])
        b = np.array([[1,0],[2,0],[4,2]])
        
        c = cdist(a, b)==0
        print(c.any(axis=1))
        
        [ True  True False  True]
        
        print(a[c.any(axis=1)])
        
        [[1 0]
         [2 0]
         [4 2]]
        

        此外,cdist 允许传递函数指针。所以你可以指定你自己的距离函数,做任何你需要的比较:

        c = cdist(a, b, lambda u, v: (u==v).all())
        print(c)
        
        [[1. 0. 0.]
         [0. 1. 0.]
         [0. 0. 0.]
         [0. 0. 1.]]
        

        现在您可以找到匹配的索引。这也将指示是否有多个匹配项。

        # Array with multiple instances
        a2 = np.array([[1,0],[2,0],[3,1],[4,2],[3,1],[4,2]])
        
        c2 = cdist(a2, b, lambda u, v: (u==v).all())
        print(c2)
        
        idx = np.where(c2==1)
        print(idx)
        
        print(idx[0][idx[1]==2])
        
        [[1. 0. 0.]
         [0. 1. 0.]
         [0. 0. 0.]
         [0. 0. 1.]
         [0. 0. 0.]
         [0. 0. 1.]]
        (array([0, 1, 3, 5], dtype=int64), array([0, 1, 2, 2], dtype=int64))
        [3 5]
        

        【讨论】:

        • 有趣的方法。我想知道它在性能方面的表现如何
        【解决方案7】:

        推荐的答案很好,但在处理包含大量行的数组时会遇到困难。另一种选择是:

        baseval = np.max([a.max(), b.max()]) + 1
        a[:,1] = a[:,1] * baseval
        b[:,1] = b[:,1] * baseval
        c = np.isin(np.sum(a, axis=1), np.sum(b, axis=1))
        

        这使用任一数组中包含的最大值加上 1 作为数字基数,并将列视为 baseval^0 和 baseval^1 值。这可确保列的总和对于每对可能的值都是唯一的。如果列的顺序不重要,则可以预先使用np.sort(a,axis=1) 按列对两个输入数组进行排序。

        这可以扩展到具有更多列的数组:

        baseval = np.max([a.max(), b.max()]) + 1
        n_cols = a.shape[1]
        a = a * baseval ** np.array(range(n_cols))
        b = b * baseval ** np.array(range(n_cols))
        c = np.isin(np.sum(a, axis=1), np.sum(b, axis=1))
        

        如果使用 int64,baseval ** (n_cols+1) &gt; 9223372036854775807 可能会发生溢出。这可以通过使用dtype=object 将 numpy 数组设置为使用 python 整数来避免。

        【讨论】:

          猜你喜欢
          • 2023-04-07
          • 1970-01-01
          • 1970-01-01
          • 2020-05-27
          • 2012-05-31
          • 2019-11-11
          • 2021-03-31
          相关资源
          最近更新 更多