获取两个列表之间的差异

我在Python中有两个列表:

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

我想创建第三个列表,其中包含第一个列表中不在第二个列表中的项目:

temp3 = ['Three', 'Four']

有没有没有周期和检查的快速方法?

1345771 次浏览

试试这个:

temp3 = set(temp1) - set(temp2)

要获取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]))

你可以使用列表理解:

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

我会折腾一下,因为目前的解决方案都不会产生元组:

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))

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

现有的解决方案都提供以下一种或另一种:

  • 快于O(n*m)的性能。
  • 保留输入列表的顺序。

但到目前为止,还没有解决方案两者兼而有之。如果你想要两者兼而有之,试试这个:

s = set(temp2)temp3 = [x for x in temp1 if x not in s]

性能测试

import timeitinit = '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' answer4.2770634955  # This answer30.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' answer3.63890368748 # this answer (3 times faster!)37.7445402279 # matt b's answer

这可能比Mark的列表理解更快:

list(itertools.filterfalse(set(temp2).__contains__, temp1))

两个列表(例如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文档参考

这是另一种解决方案:

def diff(a, b):xa = [i for i in set(a) if i not in b]xb = [i for i in set(b) if i not in a]return xa + xb

如果对扩散列表的元素进行排序和设置,则可以使用朴素方法。

list1=[1,2,3,4,5]list2=[1,2,3]
print list1[len(list2):]

或者使用本机集合方法:

subset=set(list1).difference(list2)
print subset
import timeitinit = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'print "Naive solution: ", timeit.timeit('temp1[len(temp2):]', init, number = 100000)print "Native set solution: ", timeit.timeit('set(temp1).difference(temp2)', init, number = 100000)

朴素解:0.0787101593292

本机集解决方案:0.998837615564

arulr解决方案的单行版本

def diff(listA, listB):return set(listA) - set(listB) | set(listB) -set(listA)

如果您遇到TypeError: unhashable type: 'list',则需要将列表或集合转换为元组,例如。

set(map(tuple, list_of_lists1)).symmetric_difference(set(map(tuple, list_of_lists2)))

另见如何比较Python中的列表/集合列表?

如果你想要递归差异,我为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!+world12End

类型变更

>>> 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}}

区别二:

>>> 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}}}

如果你真的在研究性能,那么使用numpy!

这是github上的完整笔记本,其中包含list、numpy和pandas之间的比较。

https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451

在此处输入图片描述

如果你想要更像一个变更集的东西…可以使用计数器

from collections import Counter
def diff(a, b):""" more verbose than needs to be, for clarity """ca, cb = Counter(a), Counter(b)to_add = cb - cato_remove = ca - cbchanges = Counter(to_add)changes.subtract(to_remove)return changes
lista = ['one', 'three', 'four', 'four', 'one']listb = ['one', 'two', 'three']
In [127]: diff(lista, listb)Out[127]: Counter({'two': 1, 'one': -1, 'four': -2})# in order to go from lista to list b, you need to add a "two", remove a "one", and remove two "four"s
In [128]: diff(listb, lista)Out[128]: Counter({'four': 2, 'one': 1, 'two': -1})# in order to go from listb to lista, you must add two "four"s, add a "one", and remove a "two"

我想要一个需要两个列表并且可以做bashdiff所做的事情的东西。由于当你搜索“python diff两个列表”时首先弹出这个问题并且不是很具体,我会发布我想出的。

使用difflib中的#0,您可以像diff一样比较两个列表。其他答案都不会告诉您差异发生的位置,但这个答案会告诉您。有些答案只在一个方向上给出差异。有些重新排序元素。有些不处理重复。但这个解决方案为您提供了两个列表之间的真正差异:

a = 'A quick fox jumps the lazy dog'.split()b = 'A quick brown mouse jumps over the dog'.split()
from difflib import SequenceMatcher
for tag, i, j, k, l in SequenceMatcher(None, a, b).get_opcodes():if tag == 'equal': print('both have', a[i:j])if tag in ('delete', 'replace'): print('  1st has', a[i:j])if tag in ('insert', 'replace'): print('  2nd has', b[k:l])

此输出:

both have ['A', 'quick']1st has ['fox']2nd has ['brown', 'mouse']both have ['jumps']2nd has ['over']both have ['the']1st has ['lazy']both have ['dog']

当然,如果您的应用程序做出与其他答案相同的假设,您将从中受益最多。但如果您正在寻找真正的diff功能,那么这是唯一的出路。

例如,没有其他答案可以处理:

a = [1,2,3,4,5]b = [5,4,3,2,1]

但这一个做到了:

  2nd has [5, 4, 3, 2]both have [1]1st has [2, 3, 4, 5]

可以使用python XOR运算符完成。

  • 这将删除每个列表中的重复项
  • 这将显示的差异,坦普1从坦普2和坦普2从坦普1。

set(temp1) ^ set(temp2)

以下是最简单情况的Counter答案。

这比上面的双向差异更短,因为它只做问题所问的事情:生成第一个列表中的内容列表,而不是第二个列表。

from collections import Counter
lst1 = ['One', 'Two', 'Three', 'Four']lst2 = ['One', 'Two']
c1 = Counter(lst1)c2 = Counter(lst2)diff = list((c1 - c2).elements())

或者,根据您的易读性偏好,它可以成为一个不错的一行:

diff = list((Counter(lst1) - Counter(lst2)).elements())

输出:

['Three', 'Four']

请注意,如果您只是迭代它,您可以删除list(...)调用。

因为此解决方案使用计数器,所以它可以正确处理数量而不是许多基于集合的答案。例如,在此输入上:

lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']lst2 = ['One', 'Two']

输出是:

['Two', 'Two', 'Three', 'Three', 'Four']

我们可以计算交集减去列表的并集:

temp1 = ['One', 'Two', 'Three', 'Four']temp2 = ['One', 'Two', 'Five']
set(temp1+temp2)-(set(temp1)&set(temp2))
Out: set(['Four', 'Five', 'Three'])

这可以用一行来解决。问题给出两个列表(Temp1和Temp2)在第三个列表(Temp3)中返回它们的差异。

temp3 = list(set(temp1).difference(set(temp2)))

我在这个游戏中有点太晚了,但是你可以用这个来比较上面提到的一些代码的性能,两个最快的竞争者是,

list(set(x).symmetric_difference(set(y)))list(set(x) ^ set(y))

我为编码的初级水平道歉。

import timeimport randomfrom itertools import filterfalse
# 1 - performance (time taken)# 2 - correctness (answer - 1,4,5,6)# set performanceperformance = 1numberoftests = 7
def answer(x,y,z):if z == 0:start = time.clock()lists = (str(list(set(x)-set(y))+list(set(y)-set(y))))times = ("1 = " + str(time.clock() - start))return (lists,times)
elif z == 1:start = time.clock()lists = (str(list(set(x).symmetric_difference(set(y)))))times = ("2 = " + str(time.clock() - start))return (lists,times)
elif z == 2:start = time.clock()lists = (str(list(set(x) ^ set(y))))times = ("3 = " + str(time.clock() - start))return (lists,times)
elif z == 3:start = time.clock()lists = (filterfalse(set(y).__contains__, x))times = ("4 = " + str(time.clock() - start))return (lists,times)
elif z == 4:start = time.clock()lists = (tuple(set(x) - set(y)))times = ("5 = " + str(time.clock() - start))return (lists,times)
elif z == 5:start = time.clock()lists = ([tt for tt in x if tt not in y])times = ("6 = " + str(time.clock() - start))return (lists,times)
else:start = time.clock()Xarray = [iDa for iDa in x if iDa not in y]Yarray = [iDb for iDb in y if iDb not in x]lists = (str(Xarray + Yarray))times = ("7 = " + str(time.clock() - start))return (lists,times)
n = numberoftests
if performance == 2:a = [1,2,3,4,5]b = [3,2,6]for c in range(0,n):d = answer(a,b,c)print(d[0])
elif performance == 1:for tests in range(0,10):print("Test Number" + str(tests + 1))a = random.sample(range(1, 900000), 9999)b = random.sample(range(1, 900000), 9999)for c in range(0,n):#if c not in (1,4,5,6):d = answer(a,b,c)print(d[1])

最简单的方法,

使用set()。差异(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)))

代码

使用#0的不寻常方法:

import pathlib

temp1 = ["One", "Two", "Three", "Four"]temp2 = ["One", "Two"]
p = pathlib.Path(*temp1)r = p.relative_to(*temp2)list(r.parts)# ['Three', 'Four']

这假设两个列表都包含具有等效开头的字符串。有关更多详细信息,请参阅文档。请注意,与集合操作相比,它不是特别快。


使用#0的直接实现:

import itertools as it

[x for x, y in it.zip_longest(temp1, temp2) if x != y]# ['Three', 'Four']

这里有一个简单的方法来区分两个列表(无论内容是什么),你可以得到如下所示的结果:

>>> from sets import Set>>>>>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']>>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]>>>>>> Set(l1).symmetric_difference(Set(l2))Set([False, 'xvdbd', None, 12])

希望这会有帮助。

假设我们有两个列表

list1 = [1, 3, 5, 7, 9]list2 = [1, 2, 3, 4, 5]

从上面的两个列表中我们可以看到,项目1、3、5存在于列表2中,项目7、9没有。另一方面,项目1、3、5存在于列表1中,项目2、4没有。

返回包含项目7、9和2、4的新列表的最佳解决方案是什么?

以上所有答案都找到了解决方案,现在什么是最佳的?

def difference(list1, list2):new_list = []for i in list1:if i not in list2:new_list.append(i)
for j in list2:if j not in list1:new_list.append(j)return new_list

def sym_diff(list1, list2):return list(set(list1).symmetric_difference(set(list2)))

利用时间我们可以看到结果

t1 = timeit.Timer("difference(list1, list2)", "from __main__ import difference,list1, list2")t2 = timeit.Timer("sym_diff(list1, list2)", "from __main__ import sym_diff,list1, list2")
print('Using two for loops', t1.timeit(number=100000), 'Milliseconds')print('Using two for loops', t2.timeit(number=100000), 'Milliseconds')

返回

[7, 9, 2, 4]Using two for loops 0.11572412995155901 MillisecondsUsing symmetric_difference 0.11285737506113946 Milliseconds
Process finished with exit code 0
def diffList(list1, list2):     # returns the difference between two lists.if len(list1) > len(list2):return (list(set(list1) - set(list2)))else:return (list(set(list2) - set(list1)))

例如,如果list1 = [10, 15, 20, 25, 30, 35, 40]list2 = [25, 40, 35],则返回的列表将是output = [10, 20, 30, 15]

我更喜欢使用转换为集合,然后使用“差异()”函数。完整代码是:

temp1 = ['One', 'Two', 'Three', 'Four'  ]temp2 = ['One', 'Two']set1 = set(temp1)set2 = set(temp2)set3 = set1.difference(set2)temp3 = list(set3)print(temp3)

输出:

>>>print(temp3)['Three', 'Four']

这是最容易理解的,将来如果你处理大数据,如果不需要重复,将其转换为集合将删除重复。希望有帮助;-)

您可以循环浏览第一个列表,对于不在第二个列表中但在第一个列表中的每个项目,将其添加到第三个列表中。例如:

temp3 = []for i in temp1:if i not in temp2:temp3.append(i)print(temp3)

我知道这个问题已经得到了很好的答案,但我希望使用numpy添加以下方法。

temp1 = ['One', 'Two', 'Three', 'Four']temp2 = ['One', 'Two']
list(np.setdiff1d(temp1,temp2))
['Four', 'Three'] #Output

如果您应该从列表一个中删除列表b中存在的所有值。

def list_diff(a, b):r = []
for i in a:if i not in b:r.append(i)return r

list_diff([1,2,2],[1])

结果:[2,2]

def list_diff(a, b):return [x for x in a if x not in b]

这是@SuperNova回答的修改版本

def get_diff(a: list, b: list) -> list:return list(set(a) ^ set(b))

根据@arkolec的回答,这里有一个用于比较列表、元组和集合的实用程序类:

from difflib import SequenceMatcher
class ListDiffer:
def __init__(self, left, right, strict:bool=False):assert isinstance(left, (list, tuple, set)), "left must be list, tuple or set"assert isinstance(right, (list, tuple, set)), "right must be list, tuple or set"self.l = list(left) if isinstance(left, (tuple, set)) else leftself.r = list(right) if isinstance(left, (tuple, set)) else right
if strict:assert isinstance(left, right.__class__), \f'left type ({left.__class__.__name__}) must equal right type ({right.__class__.__name__})'
self.diffs = []self.equal = []
for tag, i, j, k, l in SequenceMatcher(None, self.l, self.r).get_opcodes():if tag in ['delete', 'replace', 'insert']:self.diffs.append((tag, i, j, k, l))elif tag == 'equal':[self.equal.append(v) for v in left[i:j]]                


def has_diffs(self):return len(self.diffs) > 0

def only_left(self):a = self.l[:][a.remove(v) for v in self.equal]return a
def only_right(self):a = self.r[:][a.remove(v) for v in self.equal]return a

def __str__(self, verbose:bool=False):iD = 0sb = []if verbose:sb.append(f"left: {self.l}\n")sb.append(f"right: {self.r}\n")sb.append(f"diffs: ")for tag, i, j, k, l in self.diffs:s = f"({iD})"if iD > 0: sb.append(' | ')if tag in ('delete', 'replace'): s = f'{s} l:{self.l[i:j]}'if tag in ('insert', 'replace'): s = f'{s} r:{self.r[k:l]}'sb.append(s)iD = iD + 1
if verbose:sb.append(f"\nequal: {self.equal}")return ''.join(sb)
def __repr__(self) -> str:return "<ListDiffer> {}".format(self.__str__())

用法:

left = ['a','b','c']right = ['aa','b','c','d']# right = ('aa','b','c','d')ld = ListDiffer(left, right, strict=True)print(f'ld.has_diffs(): {ld.has_diffs()}')print(f'ld: {ld}')print(f'ld.only_left(): {ld.only_left()}')print(f'ld.only_right(): {ld.only_right()}')

输出:

ld.has_diffs(): Trueld: (0) l:['a'] r:['aa'] | (1) r:['d']ld.only_left(): ['a']ld.only_right(): ['aa', 'd']

我不能说性能,但您可以使用ld.only_left()来获得您正在寻找的结果。