获取两个列表之间的不同项,这些项是唯一的。

1230

我在Python中有两个列表:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']

假设每个列表中的元素都是唯一的,我希望创建第三个列表,其中包含第一个列表中不在第二个列表中的项目:

假设每个列表中的元素都是唯一的,我想要创建一个新的列表,其中包含来自第一个列表的但不在第二个列表中的项目:

temp3 = ['Three', 'Four']

有没有不需要循环和检查的快速方式?


26
元素是否保证唯一?如果你有 temp1 = ['One', 'One', 'One']temp2 = ['One'],你希望得到 ['One', 'One'] 还是 [] - Michael Mrozek
1
@michael-mrozek 他们是独一无二的。 - Max Frai
19
你想保留元素的顺序吗? - Mark Byers
1
这个回答解决了您的问题吗?查找不在列表中的元素 - Gonçalo Peres
33个回答

1769

要获取在temp1中但不在temp2中的元素(假设每个列表中的元素都是唯一的):

In [5]: list(set(temp1) - set(temp2))
Out[5]: ['Four', 'Three']

请注意这是不对称的:

In [5]: set([1, 2]) - set([2, 3])
Out[5]: set([1]) 

你可能期望或希望它等于set([1, 3])。如果你确实想要set([1, 3])作为你的答案,你可以使用set([1, 2]).symmetric_difference(set([2, 3]))


47
列表不支持"-"操作符,但是集合支持,如果你仔细观察上面的例子,就会发现这一点。请注意,我的翻译并没有改变原意。 - Godsmith
79
对称差可以用符号 ^(set1 ^ set2)表示。 - Bastian
8
请注意,由于集合是无序的,因此对差集进行迭代可以按任意顺序返回元素。例如,list(set(temp1) - set(temp2)) == ['Four', 'Three'] 或者 list(set(temp1) - set(temp2)) == ['Three', 'Four'] - Arthur
4
此方法不保留输入列表的顺序。 - user1
9
如果存在重复元素怎么办?例如,a=[1, 1, 1, 1, 2, 2],b=[1, 1, 2, 2] - Shark Deng
显示剩余10条评论

643

现有的解决方案要么具有比O(n*m)更快的性能,要么保留输入列表的顺序。

  • 但是到目前为止,没有一个解决方案同时具备这两个特点。如果你想要同时具有这两个特点,请尝试以下方法:
s = set(temp2)
temp3 = [x for x in temp1 if x not in s]

性能测试

import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print timeit.timeit('list(set(temp1) - set(temp2))', init, number = 100000)
print timeit.timeit('s = set(temp2);[x for x in temp1 if x not in s]', init, number = 100000)
print timeit.timeit('[item for item in temp1 if item not in temp2]', init, number = 100000)

结果:

4.34620224079 # ars' answer
4.2770634955  # This answer
30.7715615392 # matt b's answer

我提供的这种方法不仅可以保持顺序,而且比集合减法略快,因为它不需要构建一个不必要的集合。如果第一个列表比第二个列表长得多并且哈希很昂贵,性能差异将更加明显。以下是第二个演示此点的测试:

init = '''
temp1 = [str(i) for i in range(100000)]
temp2 = [str(i * 2) for i in range(50)]
'''

结果:

11.3836875916 # ars' answer
3.63890368748 # this answer (3 times faster!)
37.7445402279 # matt b's answer

3
对于这个答案的额外支持:我遇到了一个使用情况,保留列表顺序对性能很重要。在处理tarinfo或zipinfo对象时,我使用了集合减法来排除某些tarinfo对象不被从归档中提取。创建新列表很快,但提取期间却非常缓慢。一开始我无法理解原因。结果发现重新排序tarinfo对象列表会导致巨大的性能损失。采用列表推导方法挽救了局面。 - Ray Thompson
我同意@Dejel的观点。
temp1 = ['One', 'Two', 'Three', 'Four'] temp2 = ['One', 'Two', 'Six'] s = set(temp2) temp3 = [x for x in temp1 if x not in s] temp3 ['Three', 'Four']
- earlonrails
1
请问您能否解释一下为什么您的代码比Matt的答案运行时间更短?@MarkByers - haccks
7
因为检查列表成员资格是一项O(n)的操作(需要遍历整个列表),但检查集合成员资格是O(1)。 - Mark Byers
如果你真的需要性能,可以看一下NumPy版本:https://dev59.com/T3A75IYBdhLWcg3wH1RB#31881491 - denfromufa
显示剩余4条评论

259

可以使用Python的异或运算符来完成。

  • 这将在每个列表中删除重复项。
  • 这将显示temp1temp2之间的差异以及temp2temp1之间的差异。

set(temp1) ^ set(temp2)

1
好发现!我总是忽略了文档的这一部分:https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types。 - user3521099
4
这是针对两侧差异而言的最佳选择。 - EuberDeveloper
1
绝对是最好的回答,直接回答了 OP 的问题“获取两个列表之间的差异”。其他回答都过于复杂,包含了边缘情况,并且没有数据类型转换。 - Rich Lysakowski PhD
2
这个解决方案在时间方面是否比其他任何解决方案表现更好? - Gangula
3
@Gangula 要查看这两种方法之间的区别,请向 temp2 添加一个在 temp1 中不存在的值,然后再试一次。 - urig
显示剩余4条评论

136
你可以使用列表推导式:

你可以使用列表推导式:

temp3 = [item for item in temp1 if item not in temp2]

23
在之前将temp2转换为一个集合会使这个过程更有效率。 - user355252
9
如果Ockonal在意重复的话,这就是真的。如果不在意,那就不是真的(原问题没有说明)。 - matt b
4
评论说(列表|元组)中没有重复项。 - user395760
2
我点赞了你的回答,因为一开始我认为你对重复项是正确的。但是,无论在temp2中是否有重复项,'item not in temp2'和'item not in set(temp2)'始终会返回相同的结果。 - arekolek
8
支持不要求列表项必须是可哈希的。 - Brent

30

试试这个:

temp3 = set(temp1) - set(temp2)

28

如果您想递归地获得差异,我已经为Python编写了一个包: https://github.com/seperman/deepdiff

安装

从PyPi安装:

pip install deepdiff

用法示例

导入

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

同一个对象返回空

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> print(DeepDiff(t1, t2))
{}

物品的类型已更改

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                 'newvalue': '2',
                                 'oldtype': <class 'int'>,
                                 'oldvalue': 2}}}

某个项的值已经改变

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

添加和/或移除了项目

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

字符串差异

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': 'world'}}}

字符串差异2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                '+++ \n'
                                                '@@ -1,5 +1,4 @@\n'
                                                '-world!\n'
                                                '-Goodbye!\n'
                                                '+world\n'
                                                ' 1\n'
                                                ' 2\n'
                                                ' End',
                                        'newvalue': 'world\n1\n2\nEnd',
                                        'oldvalue': 'world!\n'
                                                    'Goodbye!\n'
                                                    '1\n'
                                                    '2\n'
                                                    'End'}}}

>>> 
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
--- 
+++ 
@@ -1,5 +1,4 @@
-world!
-Goodbye!
+world
 1
 2
 End

类型转换

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}
列表差异
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{'iterable_item_removed': {"root[4]['b'][2]": 3, "root[4]['b'][3]": 4}}

列表差异2:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
  'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                      "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}

忽略顺序或重复项的列表差异:(与上面相同的字典)

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}

包含字典的列表:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
  'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}

集合:

>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}

命名元组:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}

自定义对象:

>>> class ClassA(object):
...     a = 1
...     def __init__(self, b):
...         self.b = b
... 
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>> 
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

新增了对象属性:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

27
以下是一个简单的函数,用于查找两个列表(例如list1和list2)之间的差异。
def diff(list1, list2):
    c = set(list1).union(set(list2))  # or c = set(list1) | set(list2)
    d = set(list1).intersection(set(list2))  # or d = set(list1) & set(list2)
    return list(c - d)

或者

def diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))  # or return list(set(list1) ^ set(list2))

通过使用上述函数,可以使用 diff(temp2, temp1)diff(temp1, temp2) 找到差异。两者都将给出结果 ['Four', 'Three']。您不必担心列表的顺序或应首先给出哪个列表。

Python文档参考


7
为什么不使用 set(list1).symmetric_difference(set(list2))? - swietyy

24

最简单的方法是使用 set().difference(set())

list_a = [1,2,3]
list_b = [2,3]
print set(list_a).difference(set(list_b))

答案是 set([1])

可以打印成列表形式,

print list(set(list_a).difference(set(list_b)))

2
删除重复项并不保留顺序。 - Albert Rothman

17

我也来发表一下意见,因为目前的解决方案都没有提供一个元组:

temp3 = tuple(set(temp1) - set(temp2))

或者说:

#edited using @Mark Byers idea. If you accept this one as answer, just accept his instead.
temp3 = tuple(x for x in temp1 if x not in set(temp2))

像其他在这个方向上没有元组产生的答案一样,它保留顺序


16

我更新了链接中的笔记本和截图。令人惊讶的是,即使在内部切换到哈希表时,pandas 仍然比 numpy 慢。部分原因可能是由于向 int64 的上转换。 - denfromufa
1
Mark Byers Answer运行测试中,numpy花费了所有答案(ars、SuperNova、Mark Byers、Matt b)中最长的时间。 - Gangula

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接