2013-01-16 169 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'] 
相关问题