2016-11-17 52 views
1

我需要最快可能的解決方案,因爲它會被應用到一個巨大的數據集這樣的問題:Python的比較記號化列表

鑑於這種主列表:

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'],並與第1和m的第四元素匹配 ,結果是那麼[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]] 

提前感謝!使用np.in1d一個循環

回答

3

您可以使用嵌套列表理解是這樣的:

>>> 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]] 

此外,您可以縮短1 if ... else 0使用int(...),你可以轉換的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 
1

一種方法 -

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曾與相同長度的列表,我們可以使用一個完全量化的辦法。

1

你幾乎在那裏。

您要添加1如果xi0如果不是,在mx

所以腳本看起來像它的聲音: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]] 
1

沒有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]] 
0

多重救援!

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