2013-01-16 167 views
6

如果一個元素存在於兩個給定列表中,最簡單和最優雅的方法是什麼?例如,我有兩個列表如下?比較一個元素是否存在於兩個列表中

>>>a, b = ['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w'] 

現在在上面給出的列表中,我想檢查兩個列表中是否存在任何元素。目前我這樣做如下

def elm_exists(list_a, list_b): 
    for elm in list_a: 
     if elm in list_b: 
      return True 
    return False 

有沒有更好的做法呢?

+2

是元素每個列表中是唯一的?如果是這樣,你可以簡單地使用'sets'來做到這一點。 – 2013-01-16 06:39:35

+0

是的,列表中的元素是唯一的。 – Amyth

+1

@Mike:等等...爲什麼你不能用set來做這件事,即使這些元素不唯一?您多次丟失元素存在的信息,但如果您關心的是元素存在,則不需要該信息。 (如果你這樣做,你總是可以使用'Counter'來代替'set'來保留它。) – abarnert

回答

8

ab校驗元件與此:

set(a).intersection(b) 

例如:

In [44]: nk=set(a).intersection(b) 

In [45]: for x in a: 
    ...:  if x in nk: 
    ...:   print x, 'present in b' 
    ...:  else: 
    ...:   print x, 'absent in b' 
    ...:   
a absent in b 
b absent in b 
g present in b 
r absent in b 

也:

In [47]: timeit(set(a) & set(b)) 
1000000 loops, best of 3: 940 ns per loop 

In [48]: timeit(set(a).intersection(b)) 
1000000 loops, best of 3: 854 ns per loop 

In [49]: timeit([x for x in a if x in b]) 
1000000 loops, best of 3: 1 us per loop 

因此使用set(a).intersection(b)

+0

@JonClements,非常優雅! – Amyth

+0

我不確定在列出這個簡短結果的時間上有多少時間。如果你只是將長度減少到3和4而不是4和5,那麼至少在我的計算機上,這足以使'list'比較比'set'版本更快,但我不認爲這實際上意味着執行' O(NM)'算法優於'O(N + M)'! – abarnert

+1

看到我編輯的答案。這取決於你的數據集的大小,以及碰撞的頻率。這應該是顯而易見的......但直到我試圖理解數字。無論如何,在大多數情況下,這兩個版本和我在'set(a)'中的生成器表達式都足夠快,OP的原始問題是關於優雅而不是性能,所以...我認爲'intersection'更優雅,可讀性比'&'更好,而'any ... in ... for'表達式更是如此,但YMMV。 – abarnert

6

這工作:

>>> l1,l2=['a', 'b', 'g', 'r'], ['e', 'g', 'l', 1, 'w'] 
>>> list(set(l1)&set(l2)) 
['g'] 
1
def elm_exists(lista, listb): 
    return bool(set(lista) & set(listb)) 

bool函數將返回True所有非空的容器,並False空的。設定的交叉點(&)將返回兩組共同元素。請注意,這些設置會刪除所有重複項。

或者,您可以在bool函數中使用set(lista).intersection(listb)

+0

交叉點內部的'set(listb)'是多餘的,因爲'.intersection'將任何可迭代的(s)作爲參數 –

+0

另外,這裏不需要'len',因爲'bool(len(x))'是保證與'bool(x)'一樣返回相同的東西,更簡單,更高效。 – abarnert

+0

感謝JonClements和abarnert,已做出更改 – Volatility

0
>>> y = [1,23,3] 
>>> z = [3,432] 
>>> (3 in y) and (3 in z) 
True 
3

你並不需要在兩個list秒值進行轉換,以set S,只有一個。我認爲跳過不必要的轉換使其更具可讀性和優雅性。

所以,要麼:

set(a).intersection(b) 

或者:

s = set(a) 
any(e in s for e in b) 

後者的優點是短路只要它找到一個匹配,更好地表達邏輯,並返回TrueFalse而不是非虛假或錯誤的set,但它是兩行而不是一個,如果那樣會困擾你。我不知道這個優點是否消除了將循環放入生成器表達式而不是C函數內部的代價。

績效結果與list s這個小几乎是毫無意義的,所以讓我們試試這個:

In [373]: a=[random.choice(string.ascii_lowercase) for _ in range(10000)] 
In [374]: b=[random.choice(string.ascii_lowercase) for _ in range(10000)] 
In [375]: %timeit(set(a)) 
10000 loops, best of 3: 180 us per loop 
In [376]: s=set(a) # since all versions need to do this 
In [391]: %timeit(s & set(b)) 
1000000 loops, best of 3: 178 us per loop 
In [392]: %timeit(s.intersection(b)) 
1000000 loops, best of 3: 247 us per loop 
In [393]: %timeit(discard(e in s for e in b)) 
1000000 loops, best of 3: 550 ns per loop 
In [394]: %timeit(any(e in s for e in b)) 
1000000 loops, best of 3: 749 ns per loop 
In [395]: %timeit(any(e in a for e in b)) 
1000000 loops, best of 3: 1.42 us per loop 

爲了把這些數字都在納秒的規模,加上早在set(a)的成本是除了最後需要和比較來自三個Python版本的相同測試(Apple股票CPython 2.7.2,python.org CPython 3.3.0,Homebrew PyPy 1.9.0/2.7。2,所有的64位Mac版本):

    CP272 CP330 PyPy 
s & set(b)  358000 316000 180500 
s.intersection(b) 427000 459000 180900 
discard(genexp) 180550 157341 90094 
any(genexp)  180749 157334 90208 
any(list-genexp) 1420 686 960 

現在我想到了這一點,這是完全合理的。很早就發生碰撞的機率非常高,因此將整個事件轉換爲集合的成本控制了一切。

這意味着我們需要一個新的測試,具有10000個唯一值。讓我們重複這個測試:

In [29]: a, b = list(range(10000)), list(range(10000)) 
In [30]: random.shuffle(a) 
In [31]: random.shuffle(b) 

        CP272 CP330 PyPy 
s & set(b)  1277000 1168000 1141000 
s.intersection(b) 1165000 1117000 2520000 
discard(genexp) 1699000 1271000 770000 
any(genexp)  389800 344543 320807 
any(list-genexp) 62000 10400 1520 

這些更合理。而且他們仍然有道理。如果你比較相同的10000個元素隨機洗牌,你必須走多遠?不足以使set的成本 - 使這兩個列表中的任何一個值得做,更不用說它們兩個!

所以,讓我們試着那裏有沒有一致的情況下:

In [43]: a=list(range(10000, 20000)) 


        CP272  CP330  PyPy 
s & set(b)   751000 770000 733000 
s.intersection(b) 466000 530000 1920000 
discard(genexp)  1246000 985000 749000 
any(genexp)   1269000 966000 893000 
any(list-genexp) 185000000 176000000 5870000 

我不知道PyPy是怎麼做的最後一個如此之快,但除此之外,這裏沒有驚喜。

那麼,哪一個最好?很明顯,如果你期望碰到很多碰撞,你希望儘可能地避免做集合 - 但是如果你期望碰撞少,你至少要做一組。如果你不知道,我認爲最安全的賭注是any(genexp) - 最壞的情況是它比最好的要差3倍,如果碰撞率很高,它會快很多。但你可以看看這些數字並親自查看。或者,當然,更好的辦法是將它們全部對照您期望遇到的實際測試數據。

+0

不錯的工作(和+1),但OP要求「最簡單」和「最優雅」,而不是最快! 作爲一個Numpy用戶,發生在我身上的第一個函數是'np.any(np.in1d(x,y))'。我很好奇,看看比較。但是,您的證據支持一條通用規則......除非數據集非常大,否則轉換數據的成本通常會壓倒測試成本。 – cxrodgers

+0

@cxrodgers:我的答案的開始是關於「可讀性」和「優雅」的,這就是原始版本中的所有內容,我認爲這仍然是答案中最重要的部分。所有其他的東西后來增加了,關於什麼是最快的不可避免的爭論之後;它比重要的部分更重要的唯一原因是你可以保持簡單的簡單,但是你不能保持簡單的性能測試。 – abarnert

0

這是一個其他的解決辦法,

>>> c = [filter(lambda x: x in b, sublist) for sublist in a] 
>>> filter (lambda a: a != '', c) 
['g'] 
相關問題