I think, collections.Counter is the most magic and powerful container in Python. This is smart and beautiful multiset realization. Let’s have a look at what Counter can do.

Basic usage

Basic usage for Counter is to count some element (for example, words) in a sequence and get N most popular.

from collections import Counter

words = 'to be or not to be'.split()

c = Counter(words)
# Counter({'to': 2, 'be': 2, 'or': 1, 'not': 1})

c.most_common()
# [('to', 2), ('be', 2), ('or', 1), ('not', 1)]

c.most_common(3)
# [('to', 2), ('be', 2), ('or', 1)]

Ok, now let’s dive into Counter features.

Init

Counter is a child of dict, and all elements, so you can initialize it from a sequence as in “Basic usage” section or by any way how you initialize dict.

Counter()
Counter('gallahad')
Counter({'a': 4, 'b': 2})
Counter(a=4, b=2)
Counter({1: 2, 3: 4})

You can use any int as value. Yes, zero or negative too.

Manage values

You can get, set and delete values from Counter:

c = Counter(first=2, second=3)
c['junk'] = 4
c  # Counter({'first': 2, 'second': 3, 'junk': 4})
c['junk']
del c['junk']
c  # Counter({'first': 2, 'second': 3})

Default value

If you try to get missing value Counter returns 0 instead of raising KeyError:

c['missing']
# 0

It allows you to work with Counter as with defaultdict(int).

Use in if you want to check that a Counter contains a key:

'missing' in c
# False

'first' in c
# True

Dict

Counter has all dict methods:

list(c.items())
# [('first', 2), ('second', 3)]

list(c.keys())
# ['first', 'second']

list(c.values())
# [2, 3]

Method .update() smarter than in dict and can get anything that you can pass in init. Also, it merges values.

c = Counter({'first': 1})
c.update(Counter({'second': 2}))
c.update({'third': 3})
c.update(fourth=5)
c.update(fourth=-1)
c.update(['fifth'] * 6)
c
# Counter({'first': 1, 'second': 2, 'third': 3, 'fourth': 4, 'fifth': 6})

Arithmetic operations

c1 = Counter(first=1, common=2)
c2 = Counter(common=3, second=4)

c1 + c2
# Counter({'first': 1, 'common': 5, 'second': 4})

c1 - c2
# Counter({'first': 1})

c2 - c1
# Counter({'common': 1, 'second': 4})

As you can see, arithmetic operations drop negative values.

Counter(a=-2) - Counter(a=-1)
# Counter()

Counter(a=-2) + Counter(a=-1)
# Counter()

Set operations

Counter supports set operations:

c1 = Counter(first=1, common=2)
c2 = Counter(common=3, second=4)

c1 & c2
# Counter({'common': 2})

c2 & c1
# Counter({'common': 2})

c1 | c2
# Counter({'first': 1, 'common': 3, 'second': 4})

c2 | c1
# Counter({'common': 3, 'second': 4, 'first': 1})

Union (|)

This operations also drop non-positive values:

Counter(a=-1) | Counter(b=-2)
# Counter()

A little bit more about non-positive values

From source code:

# Outputs guaranteed to only include positive counts.
# To strip negative and zero counts, add-in an empty counter:
c += Counter()

You can use this magic to drop or flip negative values:

+Counter(a=-1)
# Counter()

+Counter(a=1)
# Counter({'a': 1})

-Counter(a=-1)
# Counter({'a': 1})

-Counter(a=1)
# Counter()

Get elements

Some ways to get elements from Counter:

c = Counter(first=1, second=2, third=3)

list(c.items())
# [('first', 1), ('second', 2), ('third', 3)]

# iterator over elements repeating each as many times as its count
list(c.elements())
# ['first', 'second', 'second', 'third', 'third', 'third']

c.most_common()
# [('third', 3), ('second', 2), ('first', 1)]

c.most_common(2)
# [('third', 3), ('second', 2)]

Conclusion

Counter is:

  • Dictionary with a default value,
  • Supports set and arithmetic operations,
  • Can count elements in sequence fast because of C realization of this function,
  • Can return N or all elements sorted by value,
  • Can merge 2 or more Counters,
  • Drops negative values.

That’s beautiful, isn’t it?