【问题标题】:Find unique tuples in a list(ignore order) while preserving the others' original order in python?在列表中查找唯一元组(忽略顺序),同时在 python 中保留其他元组的原始顺序?
【发布时间】:2013-04-12 00:33:38
【问题描述】:

假设我有一个看起来像这样的列表:

a = [(1,2),(3,1),(2,1),(4,5),(9,3),(1,3)]

然后,我想要一些看起来像这样的东西:

b = [(1,2),(3,1),(4,5),(9,3)]

非常感谢!

【问题讨论】:

    标签: python tuples list-comprehension


    【解决方案1】:
    b = []
    seen = set()
    for t in a:
        s = tuple(sorted(t))
        if s not in seen:
            seen.add(s)
            b.append(t)
    

    seen = set()
    b = [t for t in a if tuple(sorted(t)) not in seen and not seen.add(tuple(sorted(t)))]
    

    【讨论】:

    • 第二个是丑陋的(但由于列表比较而很快 - 过早优化是邪恶的)而且它必须执行两次相同的操作
    【解决方案2】:

    修改@Pavel 的解决方案,使用frozenset 使其更高效,因为它避免了排序并且通常在原始速度上更快。这应该是O(nm),而@Pavel 应该是O(n * m log(m)),其中n 是列表的长度,m 是每个元组的长度。

    >>> a = [(1,2),(3,1),(2,1),(4,5),(9,3),(1,3)]
    >>> b = []
    >>> seen = set()
    >>> for t in a:
            s = frozenset(t)
            if s not in seen:
                seen.add(s)
                b.append(t)
    
    
    >>> b
    [(1, 2), (3, 1), (4, 5), (9, 3)]
    

    这是差异的证明:

    from timeit import timeit
    
    
    def dosorted(a):
        b = []
        seen = set()
        for t in a:
            s = tuple(sorted(t))
            if s not in seen:
                seen.add(s)
                b.append(t)
        return b
    
    def dofrozenset(a):
        b = []
        seen = set()
        for t in a:
            s = frozenset(t)
            if s not in seen:
                seen.add(s)
                b.append(t)
        return b
    
    import random
    a = [(1,2),(3,1),(2,1),(4,5),(9,3),(1,3)]
    b = [tuple(random.randrange(3) for x in range(10)) for x in range(10)]
    c = [tuple(random.randrange(3) for x in range(20)) for x in range(20)]
    
    setup = '''
    from __main__ import a, b, c, dosorted, dofrozenset'''
    
    print timeit(setup=setup, stmt='dosorted(a)')
    print timeit(setup=setup, stmt='dosorted(b)')
    print timeit(setup=setup, stmt='dosorted(c)')
    print timeit(setup=setup, stmt='dofrozenset(a)')
    print timeit(setup=setup, stmt='dofrozenset(b)')
    print timeit(setup=setup, stmt='dofrozenset(c)')
    

    9.23814695723 # dosorted(a)
    26.8939069072 # dosorted(b)
    86.6305864991 # dosorted(c)
    
    5.99305211975 # dofrozenset(a)
    10.708619182  # dofrozenset(b)
    25.5252673175 # dofrozenset(c)
    

    您可以添加更多调整以使这些更快,例如使用列表推导式,但这很快就会变得丑陋。可以与最后一种结合使用的另一种常见技术是:

    seen_add, b_append = seen.add, b.append # speeds up name lookup
    

    从那时起可以直接调用这些,但请记住,过早的优化是邪恶的。

    【讨论】:

      【解决方案3】:

      只要把你的清单,做成一个集合,然后再把它变成一个清单

      >>>a = [(1, 2), (3, 1), (2, 1), (4, 5), (9, 3), (1, 3)]
      >>>sorted_tuples = [tuple(sorted(tuple_)) for tuple_ in a]
      >>>list(set(sorted_tuples))
      [(1, 2), (4, 5), (3, 9), (1, 3)]
      

      【讨论】:

      • 嗯,这不会不按顺序捕获重复项,也不会保留原始列表的顺序。
      • 你试过我的矢量了吗?格雷格?它不起作用。我想同样对待 (2,1) 和 (1,2) 并删除其中一个。
      • 是的,它不适用于矢量,但它似乎尊重顺序
      • hmm .. 但是这不会尊重第一项......嗯
      【解决方案4】:

      在 Python 3.7+(即字典维护插入顺序的版本)中,您可以简单地将列表转换为以元组的 freezesets 为键的字典并获取其值:

      a = [(1,2), (3,1), (2,1), (4,5), (9,3), (1,3)]
      d = {}
      
      for x in a:
          d.setdefault(frozenset(x), x)
      
      print(list(d.values()))  # [(1, 2), (3, 1), (4, 5), (9, 3)]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-13
        相关资源
        最近更新 更多