Python在一个列表中查找不在另一个列表中的元素[重复]


135

我需要比较两个列表,以便创建在一个列表中找到但不在另一个列表中找到的特定元素的新列表。例如:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

我想遍历list_1,并将list_2中所有在list_1中找不到的元素附加到main_list。

结果应为:

main_list=["f", "m"]

如何使用python做到这一点?


2
您是否正在寻找中list_2没有出现的list_1元素或list_2中没有出现在相同索引处的元素list_1
Patrick Haugh

Answers:


95

TL; DR:
解决方案(1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

解决方案(2) 您需要一个排序列表

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




说明:
(1)可以使用与NumPy的setdiff1darray1array2assume_unique= False)。

assume_unique询问用户数组是否已经唯一。
如果为False,则首先确定唯一元素。
如果为True,则函数将假定元素已经是唯一的,并且函数将跳过确定唯一元素的操作。

这产生了独特的值array1不是array2assume_uniqueFalse默认。

如果您担心 唯一元素(基于Chinny84响应),则只需使用(其中assume_unique=False=>默认值):

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) 对于想要对答案进行排序的人,我做了一个自定义函数:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

要获得答案,请运行:

main_list = setdiff_sorted(list_2,list_1)

旁注:
(a)解决方案2(自定义函数setdiff_sorted)返回一个列表(与解决方案1中的数组相比)。

(b)如果不确定这些元素是否唯一,则只需setdiff1d在解决方案A和B中都使用NumPy的默认设置。并发症的例子是什么?见注释(c)。

(c)如果两个列表中的任何一个都不唯一,情况将有所不同。
list_2的不是唯一的:list2 = ["a", "f", "c", "m", "m"]。保持list1原样:list_1 = ["a", "b", "c", "d", "e"]
设置assume_uniqueyields 的默认值["f", "m"](在两种解决方案中)。但是,如果您设置了assume_unique=True,两种解决方案都可以["f", "m", "m"]。为什么?这是因为用户认为元素是唯一的)。因此,最好保持assume_unique为其默认值。请注意,两个答案均已排序。


如果您的清单已经订购,这还将返回一个订购清单。转换为集合然后求差的本机解决方案(如下所示的解决方案)将返回一个无序列表,这可能会使目视检查结果变得更加困难。
Doubledown

1
嗨,@ Doubledown!您的担忧已在编辑后的帖子中得到解决。希望这可以帮助!
jcoderepo

182

您可以使用集:

main_list = list(set(list_2) - set(list_1))

输出:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

根据@JonClements的评论,这是一个更简洁的版本:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']

2
如果我们只关心unique元素,那么这很好,但是如果我们有多个元素,那m's该怎么办呢?
Chinny84 '16

确实如此。我以为海报正在寻找独特的元素。我想这取决于他所说的“具体”。
nrlakin '16

的确,我没有拒绝您的回答,特别是对于一个不清楚的原始问题。
Chinny84 '16

13
您可以这样写list(set(list_2).difference(list_1))来避免显式set转换...
乔恩·克莱门茨

别担心!感谢@leaf提供格式化帮助。
nrlakin '16

59

不知道为什么当您拥有本机方法时,上述说明为何如此复杂:

main_list = list(set(list_2)-set(list_1))

6
保持秩序可能是原因
Keith

57

使用这样的列表理解

main_list = [item for item in list_2 if item not in list_1]

输出:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

编辑:

就像下面的注释中提到的那样,如果列表很大,则以上并不是理想的解决方案。在这种情况下,更好的选择是转换list_1set第一个:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]

3
注意:对于较大的代码list_1,您需要预转换为set/ frozenset,例如set_1 = frozenset(list_1),然后main_list = [item for item in list_2 if item not in set_1]将检查时间从O(n)每个项目减少到(大约)O(1)
ShadowRanger

@ettanany请小心,如果您尝试使用ettanany发布的解决方案。我按原样尝试了ettanany的解决方案,对于更大的列表来说,确实确实非常慢。您可以更新答案以纳入Shadowranger的建议吗?
Doubledown

是否可以获取索引而不是字符串?
JareBear

@JareBear您可以使用enumerate()它:[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ettanany非常感谢你!我会尽快实现的,我已经做到了。但是您的代码看起来更加简洁。
JareBear

5

如果您想要一种单线解决方案(忽略导入),该解决方案仅需要O(max(n, m))长度n和长度输入工作,m而不需要O(n * m)工作,则可以使用以下itertools模块

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

这利用了功能函数在构造上采用回调函数的优势,从而允许它创建一次回调并在每个元素中重用它,而无需将其存储在某个位置(因为filterfalse在内部存储);列表推导和生成器表达式可以做到这一点,但这很丑陋。†

在一行中得到与以下结果相同的结果:

main_list = [x for x in list_2 if x not in list_1]

速度:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

当然,如果比较是按位置进行的,则:

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

应该产生:

main_list = [2, 3, 4]

(因为in list_2中的值与in 中的相同索引相匹配list_1),您绝对应该使用Patrick的答案,该答案不涉及临时lists或sets(即使sets大致相同O(1),它们每张支票的“常数”因数也比简单的等式支票高) )并且涉及O(min(n, m))工作,比其他任何答案都要少,并且如果您的问题对位置敏感,则是唯一正确的答案当匹配元素以不匹配的偏移量出现时解决方案。

†:使用列表理解来做与单行代码相同的方法是滥用嵌套循环以在“最外层”循环中创建和缓存值,例如:

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

这也给Python 3带来了次要的性能优势(因为现在set_1它在理解代码中处于本地范围内,而不是从每次检查的嵌套范围中查找;在Python 2上则没有关系,因为Python 2并未使用闭包列出理解;它们的作用范围与所使用的作用域相同)。


4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

输出:

['f', 'm']

等价列表理解基础的解决方案,这将是缓慢的,如果list_1是大,list_2是不平凡的大小,因为它涉及到len(list_2) O(n)的扫描list_1,使它O(n * m)(其中nm是的长度list_2list_1分别)。如果您转换list_1set/ frozenset,则可以在中进行contains的检查O(1),使总工作量O(n)list_2(技术上为O(max(n, m)),因为您确实O(m)做了set)。
ShadowRanger

1

我会将zip这些列表放在一起,以逐个元素比较它们。

main_list = [b for a, b in zip(list1, list2) if a!= b]

如果OP想要逐个元素地比较(目前尚不清楚,示例可能会以任何一种方式进行比较),则这比其他答案高效得多,因为这是对两个lists 的一次廉价传递,并且list正在构造一个新的,而没有其他临时对象,没有昂贵的遏制检查等
ShadowRanger

1
@ShadowRanger这仅适用于元素方面的差异,这是关键点
福特省长

@fordprefect:是的。我自己的答案涵盖与位置无关的差异。
ShadowRanger 2017年

1

我使用了两种方法,发现一种方法比其他方法有用。这是我的答案:

我的输入数据:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

方法1:np.setdiff1d我喜欢这种方法,因为它保留了位置

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

方法2:尽管给出的答案与方法1相同,但扰乱了顺序

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

方法1完全np.setdiff1d符合我的要求。此答案仅供参考。


0

如果应该考虑发生的次数,则可能需要使用类似以下内容的方法collections.Counter

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

如所承诺的,这也可以将不同的出现次数称为“差异”:

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']

-1

从ser1中删除ser2中存在的项目。

输入项

ser1 = pd.Series([1、2、3、4、5])ser2 = pd.Series([4、5、6、7、8])

ser1 [〜ser1.isin(ser2)]


欢迎使用堆栈溢出。这个问题还有其他八个答案,其中一个已经被原始海报接受。请描述您的答案如何改善已经提出的内容。
CHB
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.