【问题标题】:Filter a list to only leave objects that occur once过滤列表以仅保留出现一次的对象
【发布时间】:2010-11-20 02:31:35
【问题描述】:

我想过滤这个列表,

[0, 1, 1, 2, 2]

只离开

[0]

我正在努力以“pythonic”的方式做到这一点。没有嵌套循环可以吗?

【问题讨论】:

  • @Scott - 阅读标题。
  • @Markus - 是的,这有帮助!我想我一定要瞎了。在其他人认为我也是白痴之前,我将删除我原来的评论! :)

标签: python list filter


【解决方案1】:

您需要两个循环(或等效的一个循环和一个 listcomp,如下所示),但不需要嵌套循环:

import collections
d = collections.defaultdict(int)
for x in L: d[x] += 1
L[:] = [x for x in L if d[x] == 1]

此解决方案假定列表项是可散列的,也就是说,它们可用作字典、集合成员等的索引。

OP 表示他们关心对象 IDENTITY 而不是 VALUE(例如,两个子列表都值 [1,2,3,它们相等但可能不相同,不会被视为重复)。如果情况确实如此,那么这段代码是可用的,只需在两次出现时将d[x] 替换为d[id(x)],它将适用于列表 L 中的任何类型的对象。

可变对象(列表、字典、集合...)通常不可散列,因此不能以此类方式使用。默认情况下,用户定义的对象是可散列的(使用hash(x) == id(x)),除非它们的类定义了比较特殊方法(__eq____cmp__,...),在这种情况下,当且仅当它们的类还定义了一个__hash__ 方法。

如果列表 L 的项目不可散列,但 具有可比性(因此可排序),并且您不关心它们在列表中的顺序,您可以及时执行任务@ 987654329@,首先对列表进行排序,然后应用itertools.groupby(几乎但不完全按照另一个答案建议的方式)。

当您确实关心列表的原始顺序时,其他逐渐降低性能和增加通用性的方法可以处理不可散列的可排序对象(制作排序副本并在第二个循环中借助 bisect -- 也是 O(N log N) 但慢一点),并且对象的唯一适用属性是它们在相等性方面具有可比性(无法避免在最大一般情况下可怕的 O(N**2) 性能)。

如果 OP 可以澄清哪种情况适用于他的具体问题,我将很乐意提供帮助(特别是,如果他的对象是可散列的,那么我上面已经给出的代码就足够了;-)。

【讨论】:

  • 不,我不需要散列,我认为这只是我想要删除的对象的重复。 (我还在用 C 语言思考,但我上面想说的是指向对象的指针是相同的,所以不需要散列——这在 Python 领域有效吗?)
  • 你为什么写“L[:] = list(set(L))”而不是更明显的(对我来说)“L = list(set(L))”?当我在解释器中尝试它们时,它们似乎做同样的事情。我缺少一些细微差别吗?谢谢!
  • 第二种解决方案似乎没有做正确的事情:它删除了重复项,但问题是删除了所有重复的项目。
  • @samtregar,有时只重新绑定名称和重新绑定内容一样有效,有时却不行(因为除了原始名称之外,还有对原始列表对象的其他未完成引用——例如,函数参数的情况),为什么要冒险?
  • 爱 collections.defaultdict。我需要编写一个机器人,用 defaultdict 回答所有 python 问题。
【解决方案2】:
[x for x in the_list if the_list.count(x)==1]

虽然这仍然是幕后的嵌套循环。

【讨论】:

  • 我认为我更喜欢 Alex 的解决方案,因为它只遍历列表两次,您的解决方案是 n^2。
  • 哇。我真的需要阅读有关列表理解的内容!我仍在试图弄清楚上面到底发生了什么。但是非常感谢 sepp 和 Alex。
  • @boyfarrell:您可以将其解读为“遍历 the_list 中的所有 x 并选择 the_list.count(x)==1 所在的那些,即仅出现一次的那些”
  • 是的,我的方法归结为完全相同,只是我事先进行了一次传递以计算每个对象出现的次数(因此总体方法是 O(N))而不是计数通过per item(这使得这种方法总体上 O(N**2))。
  • Alex 的解决方案可能更快,但我认为这更优雅。 ;-)
【解决方案3】:

这是另一种面向字典的方式:

l = [0, 1, 1, 2, 2]
d = {}
for i in l: d[i] = i in d

[k for k in d if not d[k]]  # unordered, loop over the dictionary
[k for k in l if not d[k]]  # ordered, loop over the original list

【讨论】:

  • +1 真的很好,它收集的信息不会比您解决任务所需的信息多。不过,您可以摆脱.keys()
  • 是的,.keys() 完全是可选的,但 IMO 的可读性略高。
  • 很好的想法,因为它不像 Alex 那样包含不必要的信息,尽管它本质上是相同的概念,并且它不适用于不可哈希的项目。
  • @mhawke:不,不是。它要慢得多,因为 Python 2 中的 d.keys() 每次调用它时都会生成一个新的列表对象。在 Python 3 中,它每次都会创建一个新的字典视图。使用k in d总是dict.has_key 已被弃用,取而代之的是 key in dict。如果你循环使用l 而不是d,你就可以像 Alex 的版本一样维持秩序。这使得for i in l: d[i] = i in d[k for k in l if not d[k]] 在一起。
  • @MartijnPieters:感谢改进这个古老答案的建议。
【解决方案4】:
>>> l = [0,1,1,2,2]
>>> [x for x in l if l.count(x) is 1]
[0]

【讨论】:

  • 使用is 比使用== 有什么优势吗?我知道 1 是一个足够小的数字,但is 在比较整数时实际上更快吗?
  • 您不应该将is 与数字一起使用,它仅适用于 cpython 优化了一些常用常量(如小 (语言的一部分。
  • 不要在测试值相等时使用is。这个解决方案是一种 O(N**2) 复杂度的方法,因为 list.count() 每次调用它时都会对列表进行全面扫描,并且你调用它 N 次。
【解决方案5】:

本着与 Alex 的解决方案相同的精神,您可以使用 Counter/multiset(内置于 2.7,与 2.5 及更高版本兼容的配方)来做同样的事情:

In [1]: from counter import Counter

In [2]: L = [0, 1, 1, 2, 2]

In [3]: multiset = Counter(L)

In [4]: [x for x in L if multiset[x] == 1]
Out[4]: [0]

【讨论】:

    【解决方案6】:
    l = [0,1,2,1,2]
    def justonce( l ):
        once = set()
        more = set()
        for x in l:
            if x not in more:
                if x in once:
                    more.add(x)
                    once.remove( x )
                else:
                    once.add( x )
        return once
    
    print justonce( l )
    

    【讨论】:

    • 转换回列表会很好。
    • 这不像亚历克斯那样保持秩序。
    【解决方案7】:

    我认为实际的时间安排很有趣:

    亚历克斯的回答:

    python -m timeit -s "l = range(1,1000,2) + range(1,1000,3); import collections" "d = collections.defaultdict(int)" "for x in l: d[x] += 1" "l[:] = [x for x in l if d[x] == 1]"
    1000 loops, best of 3: 370 usec per loop
    

    我的:

    python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "once = set()" "more = set()" "for x in l:" " if x not in more:" "  if x in once:" "   more.add(x)" "   once.remove( x )" "  else:" "   once.add( x )"
    1000 loops, best of 3: 275 usec per loop
    

    sepp2k 的 O(n**2) 版本,以说明为什么复杂性很重要 ;-)

    python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "[x for x in l if l.count(x)==1]"
    100 loops, best of 3: 16 msec per loop
    

    罗伯托的 + 排序:

    python -m timeit -s "l = range(1,1000,2) + range(1,1000,3); import itertools" "[elem[0] for elem in itertools.groupby(sorted(l)) if elem[1].next()== 0]"
    1000 loops, best of 3: 316 usec per loop 
    

    麦克的:

    python -m timeit -s "l = range(1,1000,2) + range(1,1000,3)" "d = {}" "for i in l: d[i] = d.has_key(i)" "[k for k in d.keys() if not d[k]]"
    1000 loops, best of 3: 251 usec per loop
    

    我喜欢最后一个,聪明又快 ;-)

    【讨论】:

      【解决方案8】:
      >>> l = [0,1,1,2,2]
      >>> [x for x in l if l.count(x) == 1]
      [0]
      

      【讨论】:

        【解决方案9】:

        在编码问题上出现了类似的问题。我的解决方案不是最优雅的,但它是我第一个自我召唤的解决方案,所以想分享!

        testlist = [2,4,6,8,10,2,6,10]
        
        def unique_elements(testlist):
            final_list = []
            for x in testlist:
                if testlist.count(x)==1:
                    final_list.append(x)
                else:
                    pass
            print(final_list)
        
        unique_elements(testlist)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-03
          • 2019-12-19
          • 2020-04-07
          • 1970-01-01
          • 2015-10-15
          相关资源
          最近更新 更多