【问题标题】:Python Compare Tokenized ListsPython 比较标记化列表
【发布时间】:2016-11-17 21:58:36
【问题描述】:

我需要最快的解决方案来解决这个问题,因为它将应用于庞大的数据集:

鉴于此主列表:

m=['abc','bcd','cde','def']

...以及此列表参考列表:

r=[['abc','def'],['bcd','cde'],['abc','def','bcd']]

我想将 r 中的每个列表与主列表 (m) 进行比较,并生成一个新的列表列表。这个新对象将根据 m 中的顺序匹配 1 和不匹配的 0。因此新对象(列表列表)将始终具有与 m 相同长度的列表。 这是我根据上面的 m 和 r 所期望的:

[[1,0,0,1],[0,1,1,0],[1,1,0,1]]

因为 r 的第一个元素是['abc','def'] 并且有匹配 加上 m 的第 1 个和第 4 个元素,结果就是 [1,0,0,1]

到目前为止,这是我的方法(可能太慢并且缺少零):

output=[]
for i in r:
    output.append([1 for x in m if x in i])

导致:

[[1, 1], [1, 1], [1, 1, 1]]

提前致谢!

【问题讨论】:

    标签: python arrays list numpy sparse-matrix


    【解决方案1】:

    您可以像这样使用嵌套列表推导:

    >>> m = ['abc','bcd','cde','def']
    >>> r = [['abc','def'],['bcd','cde'],['abc','def','bcd']]
    >>> [[1 if mx in rx else 0 for mx in m] for rx in r]
    [[1, 0, 0, 1], [0, 1, 1, 0], [1, 1, 0, 1]]
    

    另外,您可以使用int(...) 缩短1 if ... else 0,并且您可以将r 的子列表转换为set,以便单个mx in rx 查找更快。

    >>> [[int(mx in rx) for mx in m] for rx in r]
    [[1, 0, 0, 1], [0, 1, 1, 0], [1, 1, 0, 1]]
    >>> [[int(mx in rx) for mx in m] for rx in map(set, r)]
    [[1, 0, 0, 1], [0, 1, 1, 0], [1, 1, 0, 1]]
    

    虽然int(...)1 if ... else 0 短一点,但它似乎也更慢,所以你可能不应该使用它。在重复查找之前将 r 的子列表转换为 set 应该会加快较长列表的速度,但对于非常短的示例列表,它实际上比简单的方法要慢。

    >>> %timeit [[1 if mx in rx else 0 for mx in m] for rx in r]
    100000 loops, best of 3: 4.74 µs per loop
    >>> %timeit [[int(mx in rx) for mx in m] for rx in r]
    100000 loops, best of 3: 8.07 µs per loop
    >>> %timeit [[1 if mx in rx else 0 for mx in m] for rx in map(set, r)]
    100000 loops, best of 3: 5.82 µs per loop
    

    对于更长的列表,使用set 会变得更快,正如预期的那样:

    >>> m = [random.randint(1, 100) for _ in range(50)]
    >>> r = [[random.randint(1,100) for _ in range(10)] for _ in range(20)]
    >>> %timeit [[1 if mx in rx else 0 for mx in m] for rx in r]
    1000 loops, best of 3: 412 µs per loop
    >>> %timeit [[1 if mx in rx else 0 for mx in m] for rx in map(set, r)]
    10000 loops, best of 3: 208 µs per loop
    

    【讨论】:

      【解决方案2】:

      你快到了。

      如果xi 中,您想添加1,如果不在0 中,则为m 中的每个x 添加。

      所以脚本看起来就像听起来一样:1 if x in i else 0 作为条件,for x in m

      output = [[1 if x in i else 0 for x in m] for i in r]
      print(output)
      

      结果

      [[1, 0, 0, 1], [0, 1, 1, 0], [1, 1, 0, 1]]
      

      【讨论】:

        【解决方案3】:

        一种使用np.in1d 和一个循环的方法 -

        np.array([np.in1d(m,i) for i in r]).astype(int)
        

        使用显式循环它看起来像这样 -

        out = np.empty((len(r),len(m)),dtype=int)
        for i,item in enumerate(r):
            out[i] = np.in1d(m,item)
        

        我们可以使用dtype=bool 来提高内存和性能。

        示例运行 -

        In [18]: m
        Out[18]: ['abc', 'bcd', 'cde', 'def']
        
        In [19]: r
        Out[19]: [['abc', 'def'], ['bcd', 'cde'], ['abc', 'def', 'bcd']]
        
        In [20]: np.array([np.in1d(m,i) for i in r]).astype(int)
        Out[20]: 
        array([[1, 0, 0, 1],
               [0, 1, 1, 0],
               [1, 1, 0, 1]])
        

        如果r 有相同长度的列表,我们可以使用完全向量化的方法。

        【讨论】:

          【解决方案4】:

          没有numpy,您可以使用嵌套列表理解来做到这一点:

          >>> m = ['abc','bcd','cde','def']
          >>> r = [['abc','def'],['bcd','cde'],['abc','def','bcd']]
          
          >>> [[int(mm in rr) for mm in m] for rr in r]
          [[1, 0, 0, 1], [0, 1, 1, 0], [1, 1, 0, 1]]
          

          实际上,您不需要对int 进行类型转换,因为Python 将False 视为0True 视为1。此外,使用bool 值可以提高内存效率。因此,您的表达式将如下所示:

          >>> [[mm in rr for mm in m] for rr in r]
          [[True, False, False, True], [False, True, True, False], [True, True, False, True]]
          

          【讨论】:

            【解决方案5】:

            多处理救援!

            import multiprocessing as mp
            
            def matcher(qIn, qOut):
                m = set(['abc','bcd','cde','def'])
                for i,L in iter(qIn.get, None):
                    answer = [1 if e in m else 0 for e in L]
                    qOut.put((i,answer))
            
            
            def main(L):
                qIn, qOut = [mp.Queue() for _ in range(2)]
                procs = [mp.Process(target=matcher, args=(qIn, qOut)) for _ in range(mp.cpu_count()-1)]
                for p in procs: p.start()
            
                numElems = len(L)
                for t in enumerate(L): qIn.put(t)
                for p in procs: qIn.put(None)
            
                done = 0
                while done < numElems:
                    i,answer = qIn.get()
                    L[i] = answer
                    done += 1
            
                for p in procs: p.terminate()
            
            if __name__ == "__main__":
                L = [['abc','def'],['bcd','cde'],['abc','def','bcd']]
                main(L)
                # now L looks like the required output
            

            【讨论】:

              猜你喜欢
              • 2021-10-22
              • 1970-01-01
              • 2015-04-04
              • 1970-01-01
              • 1970-01-01
              • 2020-10-13
              • 1970-01-01
              • 2011-01-03
              • 1970-01-01
              相关资源
              最近更新 更多