我有以下两个清单:
lst1 = [('vr1', '635', '1'), ('vr1', '32', '1'), ('vr1', '784', '0.526'), ('vr1', '431', '1')]
lst2 = [('vr1', '635', '3'), ('vr1', '784', '2.526'), ('vr1', '431', '2')]
目前,我使用以下代码确定lst1
的唯一内容。比较基于每个条目的前两列的内容。
uniq = set([i[0:2] for i in lst1]).difference([j[0:2] for j in lst2])
,并提供:
set([('vr1', '32')])
然后我在lst1
中搜索每个条目,如果它包含在uniq
中以获得完整条目。
uniq_full = [i for i in lst1 if i[0:2] in uniq]
以我想要的方式返回条目。
[('vr1', '32', '1')]
我的问题是:是否有更快的方式获得完整条目?
答案 0 :(得分:4)
目前,您正在遍历lst1
和lst2
一次以生成uniq
。然后,您再次循环lst1
以生成uniq_full
。
相反,您可以遍历lst2
一次以生成要删除的密钥,
然后循环lst1
一次以过滤掉不需要的元素:
lst1 = [('vr1', '635', '1'), ('vr1', '32', '1'), ('vr1', '784', '0.526'), ('vr1', '431', '1')]
lst2 = [('vr1', '635', '3'), ('vr1', '784', '2.526'), ('vr1', '431', '2')]
remove_keys = set([item[:2] for item in lst2])
unique = [item for item in lst1 if item[:2] not in remove_keys]
print(unique)
产量
[('vr1', '32', '1')]
这是一个timeit测试(使用IPython),表明它更快:
def orig(lst1, lst2):
uniq = set([i[0:2] for i in lst1]).difference([j[0:2] for j in lst2])
uniq_full = [i for i in lst1 if i[0:2] in uniq]
return uniq_full
def alt(lst1, lst2):
remove_keys = set([item[:2] for item in lst2])
unique = [item for item in lst1 if item[:2] not in remove_keys]
return unique
In [4]: %timeit orig(lst1, lst2)
100000 loops, best of 3: 2.29 µs per loop
In [5]: %timeit alt(lst1, lst2)
1000000 loops, best of 3: 1.36 µs per loop
关于@ otus的评论(如下):让我们看看在创建remove_keys
时是否使用生成器表达式提高了速度:
def alt2(lst1, lst2):
remove_keys = set(item[:2] for item in lst2)
unique = [item for item in lst1 if item[:2] not in remove_keys]
return unique
In [7]: %timeit alt2(lst1, lst2)
1000000 loops, best of 3: 1.54 µs per loop
以下是~10 ** 4项目列表的基准:
In [8]: lst1 = [('vr1', '635', '1'), ('vr1', '32', '1'), ('vr1', '784', '0.526'), ('vr1', '431', '1')]*10000
In [9]: lst2 = [('vr1', '635', '3'), ('vr1', '784', '2.526'), ('vr1', '431', '2')]*10000
In [10]: %timeit alt(lst1, lst2)
100 loops, best of 3: 9.34 ms per loop
In [11]: %timeit alt2(lst1, lst2)
100 loops, best of 3: 9.49 ms per loop
In [12]: %timeit orig(lst1, lst2)
100 loops, best of 3: 13.5 ms per loop
以下是约10 ** 6项目列表的另一个基准:
In [19]: %timeit alt(lst1, lst2)
1 loops, best of 3: 972 ms per loop
In [20]: %timeit alt2(lst1, lst2)
1 loops, best of 3: 957 ms per loop
因此,对于小型或中型列表,列表推导更快,但对于较大的列表,使用生成器表达式更快。当然,被认为大或小的取决于您的机器。您需要使用timeit进行基准测试,以了解哪种方法最适合您。
通常情况下,当你有足够的内存时,如果你需要遍历整个集合,使用列表理解比生成器更快。当您没有足够的内存或者不需要遍历整个集合时,生成器会更好。
看看@ thg435的解决方案也很有趣。在某些情况下,它更快:
def using_dicts(lst1, lst2):
d1 = {x[0:2]:x for x in lst1}
d2 = {x[0:2]:x for x in lst2}
return [d1[key] for key in set(d1) - set(d2)]
如果您的列表包含许多重复键:
lst1 = [('vr1', '635', '1'), ('vr1', '32', '1'), ('vr1', '784', '0.526'), ('vr1', '431', '1')]*10000
lst2 = [('vr1', '635', '3'), ('vr1', '784', '2.526'), ('vr1', '431', '2')]*10000
然后using_dicts
比alt
更快:
In [31]: %timeit alt(lst1, lst2)
100 loops, best of 3: 8.39 ms per loop
In [32]: %timeit using_dicts(lst1, lst2)
100 loops, best of 3: 7.98 ms per loop
我认为这是因为在上面的例子中,lst1
和lst2
包含了很多重复的x[0:2]
d1
和d2
很小。< / p>
如果您的lst1
和lst2
包含许多唯一键,例如
lst1 = [(i,i,i) for i in range(10**4+100)]
lst2 = [(i,i,i) for i in range(10**4)]
然后alt
比using_dicts
更快:
In [34]: %timeit alt(lst1, lst2)
100 loops, best of 3: 3.12 ms per loop
In [35]: %timeit using_dicts(lst1, lst2)
100 loops, best of 3: 5.93 ms per loop
答案 1 :(得分:3)
您的if i[:2] in uniq
相当于if i[:2] not in [j[0:2] for j in lst2]
。所以你可以简单地将第二个列表变成一个集合并对其进行测试。
second = set(i[:2] for i in lst2)
full = [i for i in lst1 if i[:2] not in second]
答案 2 :(得分:1)
我只是使用dict
而不是设置:
lst1 = [('vr1', '635', '1'), ('vr1', '32', '1'), ('vr1', '784', '0.526'), ('vr1', '431', '1')]
lst2 = [('vr1', '635', '3'), ('vr1', '784', '2.526'), ('vr1', '431', '2')]
d1 = {x[0:2]:x for x in lst1}
d2 = {x[0:2]:x for x in lst2}
for key in set(d1) - set(d2):
print d1[key] # ('vr1', '32', '1')
不知道这是否更快,但看起来更透明。
答案 3 :(得分:0)
您无需使用代理模式进行上次搜索即可实现此目的:
class Wrapper:
def __init__(self, tuple):
self.tuple = tuple
def __eq__(self, other):
return self.tuple[0:2] == other.tuple[0:2]
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return 0
def __str__(self):
return str(self.tuple)
def __repr__(self):
return repr(self.tuple)
lst1 = [Wrapper(('vr1', '635', '1')), Wrapper(('vr1', '32', '1')), Wrapper(('vr1', '784', '0.526')), Wrapper(('vr1', '431', '1'))]
lst2 = [Wrapper(('vr1', '635', '3')), Wrapper(('vr1', '784', '2.526')), Wrapper(('vr1', '431', '2'))]
uniq = set(lst1) - set(lst2)
print uniq
给出:
set([('vr1','32','1')])
thg435's answer要简单得多。我的唯一优势是您可以在Wrapper
中配置比较。虽然不确定速度。