2013-09-04 4 views
4

У меня есть Counter (от collections) и хотел бы отфильтровать набор нежелательных предметов. Результатом должен быть новый счетчик (или, если хотите, сделать это на месте), содержащий только элементы, не соответствующие свойству. Я попытался использовать filter на Counter, но тогда результат не является Counter, а просто list. Я также попытался вычесть set нежелательных предметов из этого Counter, но эта операция не реализована. Вычитание 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}) 

Теперь я хочу, чтобы удалить все 2 и 3 значения из этого счетчика, поэтому результат должен быть

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' 

можно использовать н который перебирает распакованной список элементов в 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}) 

но, очевидно, является неоправданно дорогостоящим для больших количествах.

Единственный (не очень хорошо) решение, которое я нашел еще является STH громоздким, как это:

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

Есть ли что-нибудь лучше, легче, более читаемым я с видом?

Почему нет простого оператора вычитания для реализованного Counter, который может использоваться с set?

+0

Не будет фильтровать список и отправить его в Противостоять работу? – sagarchalise

+0

Если вы имеете в виду sth like 'Counter (filter (lambda x: x not in (2, 3), c))' then: no. Фильтр выполняет итерацию по счетчику, что означает, что он выполняет итерацию только по клавишам; суммы забываются в этом процессе. Результат всегда будет иметь значение 1 для каждого оставшегося ключа. – Alfe

+0

Я имею в виду, что вы фильтруете этот список '[1,2,3,4,5,6,7,6,5,4,3,2,3,4,5,6,5,4,3,4,5 , 4] 'и передать его счетчику. – sagarchalise

ответ

3

Просто используйте 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}) 
+0

Да, так или иначе, спасибо, спасибо. Недостатком является то, что он работает только на месте, а для полных наборов для его удаления требуется цикл (и в целом я предпочитаю функциональные подходы - меньшая вероятность появления ошибок в этом мире). (_And_ Я только выяснил, что мне нужна версия счетчика только значений в наборе и счетчик без набора. Мне пришлось бы его скопировать и сделать это дважды, но это не вопрос, просто мой прецедент здесь ...) – Alfe

+3

@Alfe Ну, лично я бы придерживался понимания dict 'Counter ({k: v для k, v в c.iteritems(), если k не в (2, 3)}) 'then – sloth

+0

Да. И я оставлю этот вопрос неприемлемым в течение некоторого времени, чтобы привлечь немного больше внимания. В конце концов ваш ответ заслуживает одобрения, если ничего лучше не всплывает (что я сомневаюсь). – Alfe

0

Вы можете использовать поп - это быстрее, чем использование 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 
+0

При использовании 'timeit' вы должны поместить только код в тег. Вы также внесете в него тестовые данные, которые будут выравнивать все результаты, в зависимости от того, насколько это дорого стоит этот шаг. Но поскольку данные испытаний одинаковы во всех случаях, тенденция должна оставаться неизменной. Спасибо за этот вклад! – Alfe

1

Попробуйте это:

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) 
Смежные вопросы