【问题标题】:Most pythonic way to remove tuples from a list if first element is a duplicate如果第一个元素是重复的,则从列表中删除元组的大多数 Pythonic 方法
【发布时间】:2015-04-10 14:24:48
【问题描述】:

到目前为止我的代码很丑:

orig = [(1,2),(1,3),(2,3),(3,3)]
previous_elem = []
unique_tuples = []
for tuple in orig:
    if tuple[0] not in previous_elem:
        unique_tuples += [tuple]
    previous_elem += [tuple[0]]
assert unique_tuples == [(1,2),(2,3),(3,3)]

一定有更pythonic的解决方案。

【问题讨论】:

  • 在重复的情况下,是否只希望它返回找到的第一个元组?

标签: python list tuples


【解决方案1】:

如果您不在乎返回哪个元组循环重复,您可以随时将列表转换为字典并返回:

>>> orig = [(1,2),(1,3),(2,3),(3,3)]
>>> list(dict(orig).items())
[(1, 3), (2, 3), (3, 3)]

如果您想返回 first 元组轮次,您可以反转您的列表两次并使用 OrderedDict,如下所示:

>>> from collections import OrderedDict
>>> orig = [(1,2),(1,3),(2,3),(3,3)]
>>> new = list(OrderedDict(orig[::-1]).items())[::-1]
[(1, 2), (2, 3), (3, 3)]

这些不是最有效的解决方案(如果这很重要),但它们确实是很好的惯用单线。


一些基准测试

注意速度上的差异,如果你不在乎返回哪个元组,第一个选项效率更高:

>>> import timeit
>>> setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
'''
>>> print (min(timeit.Timer('(list(dict(orig).items()))', setup=setup).repeat(7, 1000)))
0.0015771419037069459

相比

>>>setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
from collections import OrderedDict
'''
>>> print (min(timeit.Timer('(list(OrderedDict(orig[::-1]).items())[::-1])', 
             setup=setup).repeat(7, 1000)))
0.024554947372323

根据这些速度测试,第一个选项快了近 15 倍。

话虽如此,Saksham's answer 也是 O(n) 并在效率方面粉碎了这些字典方法:

>>> setup = '''
orig = [(1,2),(1,3),(2,3),(3,3)]
newlist = []
seen = set()
def fun():
    for (a, b) in orig:
        if not a in seen:
            newlist.append((a, b))
            seen.add(a)
    return newlist
'''
>>> print (min(timeit.Timer('fun()', setup=setup).repeat(7, 1000)))
0.0004833390384996095

【讨论】:

  • 因为我之后按第二项订购元组(因此只关心重复的第一项),这是我的情况的最佳解决方案。将 8 loc 减少到 1。谢谢!
  • 我还要指出OrderedDict来自collections模块,是内置的,需要导入。
【解决方案2】:

如果您希望包含特定键的第一个始终出现在最终列表中:

list(reversed(collections.OrderedDict( reversed([(1,2),(1,3),(2,3),(3,3)])).items()))

结果:

 [(1, 2), (2, 3), (3, 3)]

【讨论】:

    【解决方案3】:

    如果您不希望存储在额外的数据结构中,时间复杂度O(n^2),正如 cmets 中所指出的:

    orig = [(1,2),(1,3),(2,3),(3,3)]
    newlist = []
    for (a, b) in orig:
        if not any(x == a for x, y in newlist):
            newlist.append((a, b))
    print newlist    # prints [(1, 2), (2, 3), (3, 3)]
    

    一点点记账可以将其减少到线性时间:

    orig = [(1,2),(1,3),(2,3),(3,3)]
    newlist = []
    seen = set()
    for (a, b) in orig:
        if not a in seen:
            newlist.append((a, b))
            seen.add(a)
    print newlist    # prints [(1, 2), (2, 3), (3, 3)]
    

    【讨论】:

    • 您的方法可以工作,是的,但是对每个元素运行线性搜索,这会产生 O(n²) 复杂度。但是,使用 dict 会增加额外的内存消耗(不多),但在理想情况下会增加 O(n)
    • @myaut 同意。字典解决方案已经准备好了。我可能应该在我的答案中添加时空权衡。
    • 如果你愿意,我可以为我的答案添加一个基准。
    • @DonkeyKong 当然,请继续。我还添加了另一个版本,它会做一些记账以降低时间复杂度。
    • @SakshamVarma 为我的答案添加了基准,你的是最快的:)
    猜你喜欢
    • 2013-03-22
    • 2013-02-11
    • 1970-01-01
    • 2019-12-12
    • 2016-07-15
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多