【问题标题】:2-D Matrix: Finding and deleting columns that are subsets of other columns二维矩阵:查找和删除作为其他列子集的列
【发布时间】:2016-10-02 21:26:44
【问题描述】:

我有一个问题,我想识别和删除逻辑矩阵中作为其他列子集的列。即 [1, 0, 1] 是 [1, 1, 1] 的子集;但 [1, 1, 0] 和 [0, 1, 1] 都不是彼此的子集。我写了一段快速的代码来识别作为子集的列,它使用一对嵌套的 for 循环进行 (n^2-n)/2 检查。

import numpy as np
A = np.array([[1, 0, 0, 0, 0, 1],
              [0, 1, 1, 1, 1, 0],
              [1, 0, 1, 0, 1, 1],
              [1, 1, 0, 1, 0, 1],
              [1, 1, 0, 1, 0, 0],
              [1, 0, 0, 0, 0, 0],
              [0, 0, 1, 1, 1, 0],
              [0, 0, 1, 0, 1, 0]])
rows,cols = A.shape
columns = [True]*cols
for i in range(cols):
    for j in range(i+1,cols):
        diff = A[:,i]-A[:,j]
        if all(diff >= 0):
            print "%d is a subset of %d" % (j, i)
            columns[j] = False
        elif all(diff <= 0):
            print "%d is a subset of %d" % (i, j)
            columns[i] = False
B = A[:,columns]

解决办法应该是

>>> print B
[[1 0 0]
 [0 1 1]
 [1 1 0]
 [1 0 1]
 [1 0 1]
 [1 0 0]
 [0 1 1]
 [0 1 0]]

不过,对于大型矩阵,我确信有一种方法可以更快地做到这一点。一个想法是在我去的时候消除子集列,所以我不检查已经知道是子集的列。另一个想法是将其向量化,因此没有 O(n^2) 操作。谢谢。

【问题讨论】:

    标签: python numpy matrix scipy vectorization


    【解决方案1】:

    由于我实际处理的 A 矩阵是 5000x5000 且密度约为 4% 的稀疏矩阵,因此我决定尝试结合 Python 的“集合”对象的稀疏矩阵方法。总的来说,它比我原来的解决方案快得多,但我觉得我从矩阵A 到集合列表D 的过程并没有那么快。任何关于如何更好地做到这一点的想法都值得赞赏。

    解决方案

    import numpy as np
    
    A = np.array([[1, 0, 0, 0, 0, 1],
                  [0, 1, 1, 1, 1, 0],
                  [1, 0, 1, 0, 1, 1],
                  [1, 1, 0, 1, 0, 1],
                  [1, 1, 0, 1, 0, 0],
                  [1, 0, 0, 0, 0, 0],
                  [0, 0, 1, 1, 1, 0],
                  [0, 0, 1, 0, 1, 0]])
    
    rows,cols = A.shape
    drops = np.zeros(cols).astype(bool)
    
    # sparse nonzero elements
    C = np.nonzero(A)
    
    # create a list of sets containing the indices of non-zero elements of each column
    D = [set() for j in range(cols)]
    for i in range(len(C[0])):
        D[C[1][i]].add(C[0][i])
    
    # find subsets, ignoring columns that are known to already be subsets
    for i in range(cols):
        if drops[i]==True:
            continue
        col1 = D[i]
        for j in range(i+1,cols):
            col2 = D[j]
            if col2.issubset(col1):
                # I tried `if drops[j]==True: continue` here, but that was slower
                print "%d is a subset of %d" % (j, i)
                drops[j] = True
            elif col1.issubset(col2):
                print "%d is a subset of %d" % (i, j)
                drops[i] = True
                break
    
    B = A[:, ~drops]
    print B
    

    【讨论】:

      【解决方案2】:

      这是使用NumPy broadcasting 的另一种方法-

      A[:,~((np.triu(((A[:,:,None] - A[:,None,:])>=0).all(0),1)).any(0))]
      

      下面列出了详细的注释说明-

      # Perform elementwise subtractions keeping the alignment along the columns
      sub = A[:,:,None] - A[:,None,:]
      
      # Look for >=0 subtractions as they indicate non-subset criteria
      mask3D = sub>=0
      
      # Check if all elements along each column satisfy that criteria giving us a 2D
      # mask which represent the relationship between all columns against each other
      # for the non subset criteria
      mask2D = mask3D.all(0)
      
      # Finally get the valid column mask by checking for all columns in the 2D mas
      # that have at least one element in a column san the diagonal elements.
      # Index into input array with it for the final output.
      colmask = ~(np.triu(mask2D,1).any(0))
      out = A[:,colmask]
      

      【讨论】:

        【解决方案3】:

        将子集定义为col1.dot(col1) == col1.dot(col2) 当且仅当col1col2 的子集

        当且仅当col1col2 的子集时,定义col1col2 相同,反之亦然。

        我把工作分成两部分。首先摆脱除一个等效列之外的所有列。然后删除子集。

        解决方案

        import numpy as np
        
        def drop_duplicates(A):
            N = A.T.dot(A)
            D = np.diag(N)[:, None]
            drops = np.tril((N == D) & (N == D.T), -1).any(axis=1)
            return A[:, ~drops], drops
        
        def drop_subsets(A):
            N = A.T.dot(A)
            drops = ((N == np.diag(N)).sum(axis=0) > 1)
            return A[:, ~drops], drops
        
        def drop_strict(A):
            A1, d1 = drop_duplicates(A)
            A2, d2 = drop_subsets(A1)
            d1[~d1] = d2
            return A2, d1
        
        
        A = np.array([[1, 0, 0, 0, 0, 1],
                      [0, 1, 1, 1, 1, 0],
                      [1, 0, 1, 0, 1, 1],
                      [1, 1, 0, 1, 0, 1],
                      [1, 1, 0, 1, 0, 0],
                      [1, 0, 0, 0, 0, 0],
                      [0, 0, 1, 1, 1, 0],
                      [0, 0, 1, 0, 1, 0]])    
        
        B, drops = drop_strict(A)
        

        演示

        print B
        print
        print drops
        
        [[1 0 0]
         [0 1 1]
         [1 1 0]
         [1 0 1]
         [1 0 1]
         [1 0 0]
         [0 1 1]
         [0 1 0]]
        
        [False  True False False  True  True]
        

        说明

        N = A.T.dot(A) 是每个点积组合的矩阵。根据顶部子集的定义,这将派上用场。

        def drop_duplicates(A):
            N = A.T.dot(A)
            D = np.diag(N)[:, None]
            # (N == D)[i, j] being True identifies A[:, i] as a subset
            # of A[:, j] if i < j.  The relationship is reversed if j < i.
            # If A[:, j] is subset of A[:, i] and vice versa, then we have
            # equivalent columns.  Taking the lower triangle ensures we
            # leave one.
            drops = np.tril((N == D) & (N == D.T), -1).any(axis=1)
            return A[:, ~drops], drops
        
        def drop_subsets(A):
            N = A.T.dot(A)
            # without concern for removing equivalent columns, this
            # removes any column that has an off diagonal equal to the diagonal
            drops = ((N == np.diag(N)).sum(axis=0) > 1)
            return A[:, ~drops], drops
        

        【讨论】:

        • 看起来快多了,棒极了!
        • 该死,第二行有一些沉重的东西。我将不得不分析一段时间以确保我理解它。我还需要知道哪些列被剪掉了,我怎样才能用这种方法得到呢?
        • 抱歉没有解释。我现在正在更新。我是用手机发的。我无法验证,我讨厌在手机上打字。
        • 不用担心,不用道歉!我非常感谢您的建议!顺便说一句,在未经验证的情况下从您的手机上发布该代码(它工作得很好)是非常荒谬和令人敬畏的。
        • @piRSquared - 所以这段代码的行为方式与我的原始程序的行为方式之间存在细微差别。如果您有两个相同的列,则此代码会同时删除它们。我想保留一份并删除另一份。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-07
        • 2015-03-19
        • 2015-11-04
        • 1970-01-01
        • 1970-01-01
        • 2015-04-02
        • 1970-01-01
        相关资源
        最近更新 更多