【问题标题】:Counter with filter or subtract or similar带过滤器或减法或类似的计数器
【发布时间】:2013-09-07 22:27:45
【问题描述】:

我有一个Counter(来自collections)并想过滤掉一组不需要的项目。结果应该是一个新的 Counter (或者如果你喜欢,就地做),只包含与属性不匹配的项目。我尝试在Counter 上使用filter,但结果不再是Counter,而只是list。我还尝试从Counter 中减去不需要的项目的set,但该操作未实现。减去Counter 有效,但我没有第二个Counter,创建它基本上与我尝试执行的任务相同。

Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4 ])
→ Counter({4: 6, 5: 5, 3: 4, 6: 3, 2: 2, 1: 1, 7: 1})

现在我想从这个计数器中删除所有23 值,所以结果应该是

Counter({4: 6, 5: 5, 6: 3, 1: 1, 7: 1})

这是我的方法:

filter(lambda x: x not in (2, 3), c)
→ [1, 4, 5, 6, 7]

但我不想要一个列表。

c - set([ 2, 3 ])
→ TypeError: unsupported operand type(s) for -: 'Counter' and 'set'

我可以使用 sth 来迭代 Counter 中的解压缩元素列表,如下所示:

Counter(x for x in c.elements() if x not in (2, 3))
→ Counter({4: 6, 5: 5, 6: 3, 1: 1, 7: 1})

但是对于大笔交易而言,这显然是不必要的昂贵。

我发现的唯一(不是很好)的解决方案是这样的麻烦:

Counter({ k: v for k, v in c.iteritems() if k not in (2, 3) })

有什么更好、更简单、更易读的东西我忽略了吗?

为什么不简单地为 Counter 实现一个减法运算符,它可以与 set 一起使用?

【问题讨论】:

  • 不会过滤列表并将其发送到 Counter 工作吗?
  • 如果你的意思是像Counter(filter(lambda x: x not in (2, 3), c)) 那么:不。过滤器迭代计数器,这意味着它只迭代键;金额在此过程中被遗忘。然后,每个剩余键的结果将始终为 1。
  • 我的意思是你过滤这个列表[ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4 ]并将它传递给计数器。
  • 我没有那个列表(这里的小例子除外);它可能非常长。考虑像@​​987654344@ 这样的事情。我总是可以使用c.elements() 创建这个列表(正如我在上面的问题中所说),但我想避免这样做,因为它很昂贵。

标签: python collections filter counter subtraction


【解决方案1】:

只需使用del:

>>> c = Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4 ])
>>> c
Counter({4: 6, 5: 5, 3: 4, 6: 3, 2: 2, 1: 1, 7: 1})
>>> del c[2]
>>> del c[3]
>>> c
Counter({4: 6, 5: 5, 6: 3, 1: 1, 7: 1})
>>>

为了好玩,您可以减去另一个具有较大值的 Counter 以删除要删除的键,但最好坚持使用 del

>>> c = Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4 ])
>>> c
Counter({4: 6, 5: 5, 3: 4, 6: 3, 2: 2, 1: 1, 7: 1})
>>> c - Counter({2:sys.maxint, 3:sys.maxint})
Counter({4: 6, 5: 5, 6: 3, 1: 1, 7: 1})

【讨论】:

  • 是的,现在也发现了,谢谢。缺点是它在原地工作,并且对于完整的集合来移除它需要一个循环(而且通常我更喜欢函数式方法 - 在那个世界中引入错误的可能性较小)。 (而且我刚刚发现我只需要集合中的值的 Counter 版本和没有集合的 Counter 版本。我必须复制它然后再做两次,但那是不是问题的一部分,只是我的用例......)
  • @Alfe 好吧,就个人而言,我会坚持使用字典理解 Counter({ k: v for k, v in c.iteritems() if k not in (2, 3) }) 然后
  • 是的。我将暂时搁置这个问题,以引起更多关注。如果没有更好的方法弹出(我对此表示怀疑),最终你的答案值得接受。
【解决方案2】:

您可以使用 pop - 它比使用 del 或字典理解更快。

def alt():
    C = Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4])
    for k in C.keys():
        if k in (2, 3):
            del C[k]

def alt2():
    C = Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4])
    for k in C.keys():
        if k in (2, 3):
            C.pop(k)

def alt3():
    C = Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4])
    Counter({ k: v for k, v in c.iteritems() if k not in (2, 3) })

ipython:

>>> %timeit alt()
100000 loops, best of 3: 9.66 µs per loop

>>> %timeit alt2()
100000 loops, best of 3: 8.64 µs per loop

>>> %timeit alt3()
100000 loops, best of 3: 11.3 µs per loop

【讨论】:

  • 使用timeit 时,您应该只将被测代码放入循环中。您还将测试数据创建放入其中,这将均衡所有结果,具体取决于此步骤的成本。但由于测试数据在所有情况下都是相同的,因此趋势应该保持不变。感谢您的贡献!
【解决方案3】:

试试这个:

from collections import Counter
c=Counter([ 1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5,4 ])
c2=Counter()
for x in c.most_common():
    if x[1]<2 or x[1]>3:
        c2[x[0]]+=x[1]
print(c2)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 2019-08-19
    • 2010-11-17
    • 2014-06-16
    相关资源
    最近更新 更多