我在Python中有两个列表:
temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']
假设每个列表中的元素都是唯一的,我想用第一个列表中的项创建第三个列表,这些项不在第二个列表中:
temp3 = ['Three', 'Four']
有没有没有周期和检查的快速方法?
我在Python中有两个列表:
temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']
假设每个列表中的元素都是唯一的,我想用第一个列表中的项创建第三个列表,这些项不在第二个列表中:
temp3 = ['Three', 'Four']
有没有没有周期和检查的快速方法?
当前回答
我们可以计算交集减去列表并集:
temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two', 'Five']
set(temp1+temp2)-(set(temp1)&set(temp2))
Out: set(['Four', 'Five', 'Three'])
其他回答
如果散差表的元素是排序和集合的,你可以使用朴素方法。
list1=[1,2,3,4,5]
list2=[1,2,3]
print list1[len(list2):]
或者使用本机set方法:
subset=set(list1).difference(list2)
print subset
import timeit
init = '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
现有的解决方案都提供了其中的一个或另一个:
比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
如果您真的关注性能,那么使用numpy!
以下是github上的完整笔记本,并对list、numpy和pandas进行了比较。
https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451
如果您想要更类似于变更集的东西……可以使用Counter
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 - ca
to_remove = ca - cb
changes = 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"
我想要一个能够使用两个列表并能做diff in bash所做的事情的东西。因为当你搜索“python diff two lists”时,这个问题首先会弹出,而且不是很具体,所以我会把我想到的贴出来。
使用difflib中的SequenceMather,你可以像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']
当然,如果您的应用程序做出了与其他答案相同的假设,那么您将从中受益最大。但如果你正在寻找一个真正的差异功能,那么这是唯一的方法。
例如,其他答案都无法处理:
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]