从另一个列表中删除一个列表中出现的所有元素


365

假设我有两个列表,l1l2。我想表演l1 - l2,返回l1not中的所有元素l2

我可以想到一个幼稚的循环方法来执行此操作,但这实际上效率很低。什么是Python高效的方法?

例如,如果我有l1 = [1,2,6,8] and l2 = [2,3,5,8]l1 - l2应返回[1,6]


12
只是一个提示:PEP8指出,小写字母“L”不应该被使用,因为它看起来太像1
spelchekr

2
我同意。我阅读了整个问题,并想知道为什么人们会继续使用11和12。只有当我阅读@spelchekr的评论时,它才有意义。
19:15抢劫


@吉姆 数据框和列表不是同一回事。
减少活动,

Answers:


491

Python具有称为List Comprehensions的语言功能,非常适合使这种事情变得非常容易。以下语句完全满足您的要求,并将结果存储在l3

l3 = [x for x in l1 if x not in l2]

l3将包含[1, 6]


8
非常pythonic 我喜欢!有效率吗?
fandom

2
我相信这是非常有效的,并且它具有使您非常清楚地了解您要完成的工作的好处。我遇到了一个博客文章,您可能会发现与效率有关的有趣内容:blog.cdleary.com/2010/04/efficiency-of-list-comprehensions
Donut

6
@fandom:列表理解本身是非常有效的(尽管生成器理解可能通过不复制内存中的元素来提高效率),但是in运算符在列表上的效率不高。 in列表上的是O(n),而in集合上的是O(1)。但是,除非您使用了成千上万个元素或更多,否则您不太可能注意到差异。
丹尼尔·普里登

1
l3 = [x for x in l1 if x not in set(l2)]?我确定是否set(l2)会被多次调用。
Danosaure 2010年

5
您也可以先设定l2s = set(l2)然后说l3 = [x for x in l1 if x not in l2s]。稍微容易些。
spelchekr,2015年

149

一种方法是使用集合:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

58
这也会从中删除重复项l1,这可能是不希望的副作用。
kindall 2010年

37
..并失去元素顺序(如果顺序很重要)。
Danosaure

3
我只想补充一点,我确定了这个时间与接受的答案的时间,它的性能提高了大约3倍timeit.timeit('a = [1,2,3,4]; b = [1,3]; c = [i for i in a if a not in b]', number=100000) -> 0.12061533199999985 timeit.timeit('a = {1,2,3,4}; b = {1,3}; c = a - b', number=100000) -> 0.04106225999998969。因此,如果性能是一个重要因素,那么这个答案可能会更合适(并且如果您不关心重复或订购)
wfgeo19年

37

或者,您也可以filter其与lambda表达式配合使用以获取所需的结果。例如:

>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])

#     v  `filter` returns the a iterator object. Here I'm type-casting 
#     v  it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]

性能比较

在这里,我正在比较此处提到的所有答案的效果。不出所料,Arkku的 set运营速度最快。

  • Arkku的设置差异 -第一次(每个循环0.124 微秒

    mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
    10000000 loops, best of 3: 0.124 usec per loop
  • 带有set查找的Daniel Pryden的列表理解 -第二(每个循环0.302 微秒

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.302 usec per loop
  • 普通列表上的甜甜圈列表理解 -第三(每个循环0.552微秒)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.552 usec per loop
  • Moinuddin Quadri的使用filter -第四(每个循环0.972微秒)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
    1000000 loops, best of 3: 0.972 usec per loop
  • Akshay Hazari's组合使用reduce+filter -第五(每个循环3.97usec)

    mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
    100000 loops, best of 3: 3.97 usec per loop

PS: set不维持顺序,并从列表中删除重复的元素。因此,如果您需要使用任何设置差异,请不要使用。


32

在此处扩展Donut的答案和其他答案,通过使用生成器理解而不是列表理解,以及通过使用set数据结构,您可以获得甚至更好的结果(因为in运算符在列表中为O(n)但O(1)在一组上)。

所以这是一个适合您的函数:

def filter_list(full_list, excludes):
    s = set(excludes)
    return (x for x in full_list if x not in s)

结果将是可迭代的,将延迟获取已过滤列表。如果您需要一个真实的列表对象(例如,如果需要对len()结果进行操作),则可以轻松构建一个列表,如下所示:

filtered_list = list(filter_list(full_list, excludes))

29

使用Python设置类型。那将是最Python的。:)

另外,由于它是本机的,因此它也应该是最优化的方法。

看到:

http://docs.python.org/library/stdtypes.html#set

http://docs.python.org/library/sets.htm(适用于较旧的python)

# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2

5
使用集时,应注意的输出是有序的,即{1,3,2}变成{1,2,3},而{“ A”,“ C”,“ B”}变成{“ A”, “ B”,“ C”},您可能不想拥有它。
Pablo Reyes

2
如果列表l1包含重复的元素,则此方法将不起作用。
jdhao

10

使用 Set Comprehensions {x in l2中的x}或set(l2)进行设置,然后使用List Comprehensions获取列表

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]

基准测试代码:

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

基准测试结果:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x    

1
l2set = set( l2 )而不是l2set = { x for x in l2 }
cz

1
不错的灵魂!但是必须记住,它仅适用于可哈希对象。
Eerik Sven Puudist,

7

替代解决方案:

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

2
使用这种方法有什么好处吗?看起来更复杂,更难阅读,没有太多好处。
skrrgwasme,2015年

这似乎很复杂。Reduce非常灵活,可以用于许多目的。它被称为折叠。减少实际上是折叠。假设您想在其中添加更复杂的内容,则可以在此功能中使用,但是列表理解是选择的最佳答案,它只会为您提供相同类型的输出,即列表并且可能具有相同的长度,而您可以折叠也改变输出类型。en.wikipedia.org/wiki/Fold_%28higher-order_function%29。此解决方案的复杂度为n * m或更小。其他人可能会更好,也可能不会更好。
阿克斯哈扎里

1
减少(函数,列表,初​​始累加器(可以是任何类型))
Akshay Hazari
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.