Вопрос

I have a Counter (from collections) and would like to filter out a set of unwanted items. The result should be a new Counter (or if you like, do it in-place) containing only the items not matching the property. I tried using filter on the Counter but then the result isn't a Counter anymore but a mere list. I also tried subtracting a set of unwanted items from that Counter but that operation isn't implemented. Subtracting a Counter works, but I don't have the second Counter and creating it is essentially the same task I'm trying to perform.

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})

Now I want to remove all 2 and 3 values from this Counter, so the result should be

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

Here are my approaches:

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

But I don't want a list.

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

I can use sth which iterates over the unpacked list of elements in the Counter like this:

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

but that obviously is unnecessarily costly for large amounts.

The only (not very nice) solution I found yet is sth cumbersome like this:

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

Is there anything better, easier, more readable I'm overlooking?

Why isn't there simply a subtraction operator for the Counter implemented which can be used with a set?

Это было полезно?

Решение

Just use 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})
>>>

Just for fun, you could substract another Counter with large values for the keys to remove, but better stick with 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})

Другие советы

Try this:

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)

You can use pop - it's faster than using del or the dictionary comprehension.

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top