我想检查一个列表中的任何项是否存在于另一个列表中。我可以使用下面的代码简单地完成它,但我怀疑可能有一个库函数来执行此操作。如果没有,是否有更多的pythonic方法来实现相同的结果。
In [78]: a = [1, 2, 3, 4, 5]
In [79]: b = [8, 7, 6]
In [80]: c = [8, 7, 6, 5]
In [81]: def lists_overlap(a, b):
....: for i in a:
....: if i in b:
....: return True
....: return False
....:
In [82]: lists_overlap(a, b)
Out[82]: False
In [83]: lists_overlap(a, c)
Out[83]: True
In [84]: def lists_overlap2(a, b):
....: return len(set(a).intersection(set(b))) > 0
....:
答案 0 :(得分:244)
简短回答:使用not set(a).isdisjoint(b)
,一般来说最快。
有四种常用方法可以测试两个列表a
和b
是否共享任何项目。第一个选项是将两者都转换为集合并检查它们的交集,如下:
bool(set(a) & set(b))
因为集合是使用Python中的哈希表存储的,所以搜索它们是O(1)
(有关Python中运算符复杂性的更多信息,请参阅here)。从理论上讲,对于O(n+m)
和n
列表中的m
和a
个对象,平均b
。但是1)它必须首先从列表中创建集合,这可能需要不可忽略的时间,2)它假设散列冲突在您的数据中是稀疏的。
第二种方法是使用生成器表达式对列表执行迭代,例如:
any(i in a for i in b)
这允许就地搜索,因此没有为中间变量分配新内存。它也拯救了第一个发现。 但in
运算符在列表上始终为O(n)
(请参阅here)。
另一个提议的选项是混合迭代遍历列表之一,转换集合中的另一个并测试此集合的成员资格,如下所示:
a = set(a); any(i in a for i in b)
第四种方法是利用(冻结)集合的isdisjoint()
方法(参见here),例如:
not set(a).isdisjoint(b)
如果您搜索的元素靠近数组的开头(例如,它已排序),则生成表达式是有利的,因为集合交集方法必须为中间变量分配新内存:
from timeit import timeit
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=list(range(1000))", number=100000)
26.077727576019242
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=list(range(1000))", number=100000)
0.16220548999262974
以下是此示例在列表大小函数中执行时间的图表:
请注意,两个轴都是对数轴。这代表了生成器表达式的最佳情况。可以看出,isdisjoint()
方法对于非常小的列表大小更好,而生成器表达式对于更大的列表大小更好。
另一方面,当搜索从混合和生成器表达式的开头开始时,如果共享元素系统地位于数组的末尾(或者两个列表都不共享任何值),则不相交和设置交集然后,方法比生成器表达式和混合方法更快。
>>> timeit('any(i in a for i in b)', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
13.739536046981812
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(1000));b=[x+998 for x in range(999,0,-1)]", number=1000))
0.08102107048034668
值得注意的是,对于较大的列表大小,生成器表达式会慢一些。这仅重复1000次,而不是前一次的100000次。当没有共享元素时,此设置也很接近,并且是不相交和集合交集方法的最佳情况。
以下是使用随机数进行的两次分析(而不是将设置绑定到某种技术或其他技术):
分享的可能性很高:元素是从[1, 2*len(a)]
中随机抽取的。共享的可能性很低:元素是从[1, 1000*len(a)]
中随机抽取的。
到目前为止,这种分析认为两个列表的大小相同。如果有两个不同大小的列表,例如a
要小得多,isdisjoint()
总是更快:
确保a
列表较小,否则性能会降低。在此实验中,a
列表大小已设置为5
。
总结:
not set(a).isdisjoint(b)
总是最快的。any(i in a for i in b)
在大型列表大小上最快; not set(a).isdisjoint(b)
测试集合交集,总是快于bool(set(a) & set(b))
。a = set(a); any(i in a for i in b)
通常比其他方法慢。在大多数情况下,使用isdisjoint()
方法是最好的方法,因为生成器表达式执行需要更长的时间,因为当没有共享元素时效率非常低。
答案 1 :(得分:24)
def lists_overlap3(a, b):
return bool(set(a) & set(b))
注意:上面假设你想要一个布尔值作为答案。如果您只需要在if
语句中使用的表达式,只需使用if set(a) & set(b):
答案 2 :(得分:10)
def lists_overlap(a, b):
sb = set(b)
return any(el in sb for el in a)
这是渐近最优的(最差情况为O(n + m)),并且由于any
的短路可能比交叉方法更好。
E.g:
lists_overlap([3,4,5], [1,2,3])
一旦到达3 in sb
编辑:另一种变化(感谢Dave Kirby):
def lists_overlap(a, b):
sb = set(b)
return any(itertools.imap(sb.__contains__, a))
这依赖于imap
的迭代器,它是用C实现的,而不是生成器理解。它还使用sb.__contains__
作为映射函数。我不知道这会带来多大的性能差异。它仍然会短路。
答案 3 :(得分:4)
您还可以将any
与列表理解结合使用:
any([item in a for item in b])
答案 4 :(得分:3)
在python 2.6或更高版本中,你可以这样做:
return not frozenset(a).isdisjoint(frozenset(b))
答案 5 :(得分:2)
您可以使用任何内置函数/ w生成器表达式:
def list_overlap(a,b):
return any(i for i in a if i in b)
正如John和Lie所指出的那样,当两个列表共享的每个i bool(i)== False时,这会得到错误的结果。它应该是:
return any(i in b for i in a)
答案 6 :(得分:1)
这个问题很老了,但我注意到,虽然人们在争论集合与列表,但没有人想到将它们结合使用。按照Soravux的例子,
列表的最坏情况:
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
100.91506409645081
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
19.746716022491455
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=[x+9999 for x in range(10000)]", number=100000)
0.092626094818115234
列表的最佳案例:
>>> timeit('bool(set(a) & set(b))', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
154.69790101051331
>>> timeit('any(i in a for i in b)', setup="a=list(range(10000)); b=list(range(10000))", number=100000)
0.082653045654296875
>>> timeit('any(i in a for i in b)', setup="a= set(range(10000)); b=list(range(10000))", number=100000)
0.08434605598449707
因此,比迭代两个列表更快的是通过列表迭代以查看它是否在一个集合中,这是有道理的,因为检查一个数字是否在一个集合中需要恒定的时间,同时通过遍历列表进行检查需要的时间与列表的长度成正比。
因此,我的结论是遍历列表,并检查它是否在一组中。
答案 7 :(得分:1)
如果你不关心重叠元素是什么,你可以简单地检查组合列表的len
与组合列表的列表。如果存在重叠元素,则该集合将更短:
len(set(a+b+c))==len(a+b+c)
将返回True。
答案 8 :(得分:1)
我将使用函数编程风格抛出另一个:
any(map(lambda x: x in a, b))
<强>解释强>
map(lambda x: x in a, b)
返回一个布尔列表,其中b
中找到了a
的元素。然后该列表会传递给any
,如果任何元素为True
,则会返回True
。
答案 9 :(得分:-1)
如果您想查看整个列表是否在列表内,您也可以这样做。
index = 0
for a in list_one:
for _ in list_two:
if a == list_two[index]:
print("match")
index += 1
else:
print("this list did not match at index" + str(index))
index += 1