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

fan*_*dom 315 python list

假设我有两个列表,l1并且l2.我想执行l1 - l2,返回所有l1不在的元素l2.

我可以想到一个简单的循环方法来做到这一点,但这将是非常低效的.什么是pythonic和有效的方法呢?

举个例子,如果我有l1 = [1,2,6,8] and l2 = [2,3,5,8],l1 - l2应该回来[1,6]

Don*_*nut 418

Python有一个名为List Comprehensions的语言功能,非常适合使这类事情变得非常简单.以下语句完全符合您的要求并将结果存储在l3:

l3 = [x for x in l1 if x not in l2]
Run Code Online (Sandbox Code Playgroud)

l3将包含[1, 6].

希望这可以帮助!

  • 非常pythonic; 我喜欢!效率如何? (8认同)
  • @fandom:列表理解本身非常有效(尽管生成器理解可能通过不复制内存中的元素而更有效),但是`in`运算符在列表上效率不高.列表中的"in"是O(n),而集合上的"in"是O(1).但是,在您获得数千个或更多元素之前,您不太可能注意到这种差异. (5认同)
  • 你也可以设置`l2s = set(l2)`然后说`l3 = [x代表l1中的x,如果x不是l2s]`.稍微容易些. (4认同)
  • 我相信这是非常有效的,并且它的优点是对您要完成的工作具有极高的可读性和清晰性。我遇到了一个博客文章,您可能会发现与效率有关的有趣内容:http://blog.cdleary.com/2010/04/efficiency-of-list-comprehensions/ (2认同)
  • `l3 = [x for x in l1 if x not in set(l2)]` ?我确信“set(l2)”是否会被多次调用。 (2认同)

Ark*_*kku 131

一种方法是使用集合:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])
Run Code Online (Sandbox Code Playgroud)

  • 这也将从'l1`中删除重复,这可能是不希望的副作用. (53认同)
  • ..并且丢失元素顺序(如果顺序很重要). (34认同)
  • 我只是想补充一点,我对这个与接受的答案进行了计时,它的性能提高了大约 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', 数字=100000) -> 0.04106225999998969`. 因此,如果性能是一个重要因素,那么这个答案可能更合适(而且如果您不关心重复项或顺序) (6认同)
  • @TereusScott如果您的商品是可清洗的,这是最好的答案.在我的情况下,他们不是,所以我不得不使用选定的答案. (3认同)
  • 对不起帮派,但这是最好的答案和最简单的阅读.我需要生成我将存储在列表中的随机密钥.我需要确保它们是独一无二的.所以我有一个范围说1-10000,然后我有一个我生成的数字列表.一个很好的解决方案如下所示:random.sample(set(range(1,10)) - set([2,3]),1) (2认同)

Dan*_*den 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)
Run Code Online (Sandbox Code Playgroud)

结果将是一个可以延迟获取过滤列表的迭代.如果你需要一个真实的列表对象(例如,如果你需要对len()结果做一个),那么你可以轻松地建立一个如下列表:

filtered_list = list(filter_list(full_list, excludes))
Run Code Online (Sandbox Code Playgroud)


Moi*_*dri 30

作为替代方案,您还可以使用filterlambda表达式来获得所需的结果.例如:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

绩效比较

在这里,我将比较这里提到的所有答案的表现.正如所料,Arkku的 set运营速度最快.

PS: set不维护订单并从列表中删除重复的元素.因此,如果您需要任何这些,请不要使用设置差异.

  • 这个答案对人类来说是一项伟大的服务。我正在使用列表理解,但我的操作未能在 25 分钟内完成;然后我切换到设置减法,24秒内完成。奇迹般的进步远远超出了你的时间。 (4认同)

non*_*ot1 28

使用Python集类型.这将是最Pythonic.:)

此外,由于它是原生的,它也应该是最优化的方法.

看到:

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
Run Code Online (Sandbox Code Playgroud)

  • 当使用集合时,应注意输出是有序的,即{1,3,2}变为{1,2,3}而{"A","C","B"}变为{"A", "B","C"}你可能不想拥有它. (5认同)
  • 如果列表 `l1` 包含重复元素,则此方法将不起作用。 (2认同)

lbs*_*eek 12

使用 Set Comprehensions {x for x in l2} 或 set(l2) 获取集合,然后使用List Comprehensions获取列表

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]
Run Code Online (Sandbox Code Playgroud)

基准测试代码:

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))
Run Code Online (Sandbox Code Playgroud)

基准测试结果:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x    
Run Code Online (Sandbox Code Playgroud)

  • `l2set = set( l2 )` 而不是 `l2set = { x for x in l2 }` (2认同)

Aks*_*ari 8

替代解决方案:

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

  • 使用这种方法有什么好处吗?看起来它更复杂,更难以阅读而没有太大的好处. (2认同)

Moi*_*dri 8

使用set.difference()

您可以使用set.difference()该集合中不存在于其他集合中的元素来获取新集合。ie将返回包含 中存在但不存在于 中的set(A).difference(B)项目的集合。例如:AB

>>> set([1,2,6,8]).difference([2,3,5,8])
{1, 6}
Run Code Online (Sandbox Code Playgroud)

这是Arkku 的答案中提到的一种获得set差异的函数方法(使用算术减法运算符来计算集合差异) -

由于集合是无序的,因此您将失去初始列表中元素的顺序。(如果你想保持元素的顺序,请继续阅读下一节)

使用列表理解set基于查找

如果您想保持初始 list 的顺序,那么Donut 的基于列表理解的答案就可以解决问题。但是,通过在内部使用来检查元素是否存在于其他列表中,您可以从接受的答案中获得更好的性能。例如:set

l1, l2 = [1,2,6,8], [2,3,5,8]
s2 = set(l2)  # Type-cast `l2` to `set`

l3 = [x for x in l1 if x not in s2]
                             #   ^ Doing membership checking on `set` s2
Run Code Online (Sandbox Code Playgroud)

如果您有兴趣了解为什么成员资格检查比set列表更快list,请阅读以下内容:是什么让集合比列表更快?


使用filter()lambda表达式

这是使用lambda 表达式filter()另一种替代方法。在这里添加它仅供参考,但性能效率不高:

>>> 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]
Run Code Online (Sandbox Code Playgroud)


Seb*_*ser 5

filterfalse 使用lambda 表达式

当使用类似filterfilterfalse类似的函数时itertools,通常可以通过避免lambda- 表达式并使用已经存在的函数来节省性能。list和的实例set定义了__contains__用于遏制检查的方法。-运算in符在后台调用此方法,因此x in l2可以将 using 替换为l2.__contains__(x)。通常这种替换并不是真的更漂亮,但在这种特定情况下,与使用 - 表达式lambda结合使用时,它使我们能够获得更好的性能filterfalse

>>> from itertools import filterfalse
>>> l1 = [1, 2, 6, 8]
>>> l2 = [2, 3, 5, 8]
>>> list(filterfalse(l2.__contains__, l1))
[1, 6]
Run Code Online (Sandbox Code Playgroud)

filterfalsefalse创建一个迭代器,生成用作 的参数时返回的所有元素l2.__contains__

Sets 的实现速度更快,__contains__所以更好的是:

>>> from itertools import filterfalse
>>> l1 = [1, 2, 6, 8]
>>> l2 = set([2, 3, 5, 8])
>>> list(filterfalse(l2.__contains__, l1))
[1, 6]
Run Code Online (Sandbox Code Playgroud)

表现

使用列表:

$  python3 -m timeit -s "from itertools import filterfalse; l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(filterfalse(l2.__contains__, l1))"
500000 loops, best of 5: 522 nsec per loop
Run Code Online (Sandbox Code Playgroud)

使用集:

$ python3 -m timeit -s "from itertools import filterfalse; l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "list(filterfalse(l2.__contains__, l1))"
1000000 loops, best of 5: 359 nsec per loop
Run Code Online (Sandbox Code Playgroud)