有效地在列特定列表比较中获取唯一条目

时间:2013-10-25 09:26:09

标签: python python-2.7

我有以下两个清单:

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

我的问题是:是否有更快的方式获得完整条目?

4 个答案:

答案 0 :(得分:4)

目前,您正在遍历lst1lst2一次以生成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_dictsalt更快:

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

我认为这是因为在上面的例子中,lst1lst2包含了很多重复的x[0:2] d1d2很小。< / p>

如果您的lst1lst2包含许多唯一键,例如

lst1 = [(i,i,i) for i in range(10**4+100)]
lst2 = [(i,i,i) for i in range(10**4)]

然后altusing_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中配置比较。虽然不确定速度。