Python Counter: Frequency Counting, Most Common Elements, and Arithmetic
Counter is a dict subclass in the collections module. It is designed for one specific job โ counting how often things appear โ and it does that job with a very clean interface. Where you would normally write a loop, initialise counts, and handle missing keys, Counter does it in one call.
Creating a Counter
from collections import Counter
# From a listscores = Counter([85, 90, 85, 72, 90, 85, 60])print(scores)# Counter({85: 3, 90: 2, 72: 1, 60: 1})
# From a string (counts characters)letters = Counter("mississippi")print(letters)# Counter({'s': 4, 'i': 4, 'p': 2, 'm': 1})
# From keyword argumentsinventory = Counter(apples=5, oranges=3, bananas=8)print(inventory)# Counter({'bananas': 8, 'apples': 5, 'oranges': 3})
# Empty counter, then updatevotes = Counter()votes.update(["Alice", "Bob", "Alice", "Alice", "Bob"])print(votes)# Counter({'Alice': 3, 'Bob': 2})most_common() โ Top N Elements
from collections import Counter
text = "to be or not to be that is the question"word_freq = Counter(text.split())
# Top 3 most frequent wordsprint(word_freq.most_common(3))# [('to', 2), ('be', 2), ('or', 1)] โ ties broken by order of first appearance
# All elements sorted by frequency (most common first)print(word_freq.most_common())most_common(n) uses a partial sort internally โ O(k log n) where k is the total number of unique elements. Faster than sorting everything when you only need the top few.
Accessing Counts
from collections import Counter
c = Counter("abracadabra")
# Access like a dict โ missing keys return 0, not KeyErrorprint(c["a"]) # 5print(c["z"]) # 0 โ no error
# Total count of all elementsprint(sum(c.values())) # 11
# Unique elementsprint(list(c.keys())) # ['a', 'b', 'r', 'c', 'd']
# Expand back to a list (each element repeated by count)print(list(c.elements()))# ['a', 'a', 'a', 'a', 'a', 'b', 'b', 'r', 'r', 'c', 'd']Counter Arithmetic
Counter supports +, -, &, and | operations:
from collections import Counter
inventory_a = Counter(apples=5, oranges=3, bananas=2)inventory_b = Counter(apples=2, oranges=4, grapes=1)
# Addition โ combine countsprint(inventory_a + inventory_b)# Counter({'oranges': 7, 'apples': 7, 'bananas': 2, 'grapes': 1})
# Subtraction โ remove counts (drops zero/negative)print(inventory_a - inventory_b)# Counter({'apples': 3, 'bananas': 2}) โ oranges would be -1, dropped
# Intersection โ minimum of each countprint(inventory_a & inventory_b)# Counter({'oranges': 3, 'apples': 2}) โ keys in both, smallest count wins
# Union โ maximum of each countprint(inventory_a | inventory_b)# Counter({'oranges': 4, 'apples': 5, 'bananas': 2, 'grapes': 1})Practical Patterns
Word frequency analysis:
from collections import Counterimport re
def word_frequency(text): """Count word frequency, case-insensitive, ignoring punctuation.""" words = re.findall(r'\b[a-z]+\b', text.lower()) return Counter(words)
sample = "Python is great. Python is also fun. Python!"freq = word_frequency(sample)print(freq.most_common(3))# [('python', 3), ('is', 2), ('great', 1)]Check if one string is an anagram of another:
from collections import Counter
def is_anagram(s1, s2): """Return True if s1 and s2 are anagrams (same characters, different order).""" return Counter(s1.lower()) == Counter(s2.lower())
print(is_anagram("listen", "silent")) # Trueprint(is_anagram("hello", "world")) # FalseFind characters that appear more than once:
from collections import Counter
def find_duplicates(s): return [char for char, count in Counter(s).items() if count > 1]
print(find_duplicates("programming"))# ['r', 'g', 'm']Counter vs dict.get() vs defaultdict
All three can count, but Counter is the most expressive:
words = ["cat", "dog", "cat", "bird", "dog", "cat"]
# dict.get approachcounts = {}for w in words: counts[w] = counts.get(w, 0) + 1
# defaultdict approachfrom collections import defaultdictcounts2 = defaultdict(int)for w in words: counts2[w] += 1
# Counter approach โ one linefrom collections import Countercounts3 = Counter(words)
# All three produce the same result, but Counter gives you most_common(),# elements(), and arithmetic for freeUse Counter when you need counting plus any of its extra features. Use defaultdict(int) when you need a counting dict that you plan to use purely as a regular dict afterward.