Back to Blog
Python

Python Counter Usage for Efficient Counting

Learn how to use Python's collections.Counter for counting items, updating counts, arithmetic operations, and choosing it over a plain dict when it makes sense.

collectionscountingdictionariesdata structurespython
Python Counter class counting items in a list, with a bar chart representing frequencies.

When you need to count occurrences of items in a Python iterable, the collections.Counter class is the standard tool. This article covers creating counters, updating them, performing arithmetic, and deciding when Counter is more useful than a plain dict.

Creating a Counter from an Iterable

The simplest way to create a Counter is to pass an iterable to the constructor. Each element becomes a key, and its count is the number of times it appears.

from collections import Counter words = ["apple", "banana", "apple", "orange", "banana", "apple"] counter = Counter(words) print(counter) # Counter({'apple': 3, 'banana': 2, 'orange': 1})

The Counter behaves like a dictionary: keys are the distinct items, and values are the counts. Missing keys return 0 instead of raising a KeyError, which is often convenient.

You can also create a Counter from a dictionary of counts, or from keyword arguments:

Counter({"a": 2, "b": 1}) Counter(a=2, b=1)

Both produce the same result. This is useful when you already have counts stored elsewhere.

Accessing and Updating Counts

Accessing a count uses the same syntax as a dict, but a missing key returns 0:

counter["apple"] # 3 counter["pear"] # 0

To increase a count, assign a new value or use update(). The update() method adds counts from another iterable or mapping, rather than replacing them.

counter.update(["apple", "banana"]) print(counter["apple"]) # 4 print(counter["banana"]) # 3

If you need to set a count to a specific value, direct assignment works, but it overwrites any existing count. For subtracting, use subtract(); unlike update(), subtract() can produce zero or negative counts.

Common Counter Operations: most_common, update, subtract

The most_common() method returns a list of (element, count) pairs sorted by count descending. It is one of the most frequently used Counter features.

for item, count in counter.most_common(2): print(item, count)

Without an argument, it returns all items sorted. With n, it returns only the top n. Internally, most_common uses heapq.nlargest, so for small n it is efficient even on large counters.

The update() method, as shown, adds counts. The subtract() method subtracts counts and permits negative results.

counter.subtract(["apple", "apple"]) print(counter["apple"]) # 2

These operations are in-place and return None, so chain them carefully.

Arithmetic and Comparison Between Counters

Counters support addition, subtraction, intersection, and union with +, -, &, |. These operations are element-wise and keep only positive counts.

c1 = Counter(a=3, b=1) c2 = Counter(a=1, b=2, c=1) print(c1 + c2) # Counter({'a': 4, 'b': 3, 'c': 1}) print(c1 - c2) # Counter({'a': 2}) print(c1 & c2) # Counter({'a': 1, 'b': 1}) print(c1 | c2) # Counter({'a': 3, 'b': 2, 'c': 1})

Subtraction removes keys that go to zero or negative. Intersection takes the minimum count for each key, and union takes the maximum. These operations are convenient for set-like comparisons, but they create new Counter objects.

Counter vs. Manual Dictionary Counting

Before Counter, developers often wrote loops with a plain dict:

d = {} for item in items: d[item] = d.get(item, 0) + 1

Counter does the same with less code and provides extra methods. A plain dict gives you full control over the counting logic. For example, if you need to count only items that satisfy a condition, a generator expression with Counter is still concise:

Counter(x for x in items if x.startswith("a"))

If you need a custom increment, update() accepts an iterable of keys but increments by 1 for each element. For weighted counts, pass a mapping to update():

counter.update({"apple": 3, "banana": 2})

This adds 3 to apple and 2 to banana. For more complex logic, a manual loop may be clearer.

Performance and Memory Considerations

Counter is a subclass of dict, so lookups and updates have O(1) average time complexity. The memory overhead is essentially the same as the equivalent dict.

The main performance consideration is most_common(). Without an argument, it sorts all entries, which is O(d log d), where d is the number of distinct items. With a small requested count k, it uses a heap and is typically O(d log k), avoiding a full sort.

Counter is generally efficient for large inputs. In CPython, update() uses an internal C helper for iterables, so bulk counting can be faster than a manual Python loop. If you are only counting a few items and discarding the counter, a simple dict may have slightly less overhead.

One subtle point: a Counter normally stores integer counts, but it can hold non-integer values if you initialize or update it with a mapping. Arithmetic operations work with those values; be aware that helpers like elements() expect integer counts and most_common() sorts by numeric value.

Edge Cases and Compatibility

Counter has a few behaviors that can surprise developers. First, elements() returns an iterator that repeats each element as many times as its count, but it ignores items with counts less than 1.

c = Counter(a=2, b=0, c=-1) list(c.elements()) # ['a', 'a']

Second, subtract() allows negative counts, but some Counter operations ignore non-positive values. For example, elements() ignores them, and most_common() without an argument still includes them in its sorted output, which can be surprising.

Third, because Counter is a dict subclass, normal dict behavior applies. In Python 3.7 and later, iteration follows insertion order. Equality compares keys and counts just like dict equality.

Finally, Counter is part of the collections module in the Python standard library, so it is available in current Python 3 installations.

When choosing between Counter and a plain dict, consider whether you need the convenience methods. If you only need to count and then iterate, a dict is sufficient. If you need to find the most common items, combine counts, or handle missing keys gracefully, Counter is the right tool.

Python Counter Usage: Practical Examples and Trade-Offs | RYUSLOG DEV