Counter и defaultdict в Python: шпаргалка
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Collections в Python
Модуль collections даёт специальные контейнеры, которые решают частые задачи аналитика быстрее обычного dict.
from collections import Counter, defaultdictCounter
Специализированный dict для подсчёта элементов.
1. Подсчёт частот
from collections import Counter
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
c = Counter(words)
# Counter({'apple': 3, 'banana': 2, 'orange': 1})
c['apple'] # 3
c['mango'] # 0 (не KeyError!)2. Подсчёт букв / символов
Counter("mississippi")
# Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})3. Топ-N самых частых
c.most_common(3)
# [('apple', 3), ('banana', 2), ('orange', 1)]
c.most_common(1)[0]
# ('apple', 3) — самое частое4. Сумма частот
sum(c.values()) # 6 — всего элементов
len(c) # 3 — уникальных5. Вычитание Counter-ов
c1 = Counter(['a', 'b', 'a', 'c'])
c2 = Counter(['a', 'b'])
c1 + c2 # сложение
c1 - c2 # вычитание (отрицательные убираются)
c1 & c2 # пересечение (min)
c1 | c2 # объединение (max)6. Update
c = Counter(['a', 'b'])
c.update(['a', 'c'])
# Counter({'a': 2, 'b': 1, 'c': 1})
c.update({'d': 5})
# добавит d=57. Elements — «развернуть» обратно
c = Counter(a=2, b=3)
list(c.elements())
# ['a', 'a', 'b', 'b', 'b']8. Пример: частоты слов в тексте
text = "hello world hello python world world"
words = text.split()
Counter(words).most_common()
# [('world', 3), ('hello', 2), ('python', 1)]9. Пример: Counter из DataFrame
import pandas as pd
from collections import Counter
df = pd.DataFrame({'category': ['sql', 'python', 'sql', 'sql', 'python']})
# Counter
Counter(df['category'])
# Counter({'sql': 3, 'python': 2})
# или pandas способ:
df['category'].value_counts()
# sql 3
# python 2defaultdict
Dict с default-значением, если ключа нет.
10. Базовое использование
from collections import defaultdict
d = defaultdict(int)
d['a'] += 1 # без ошибки, даже если 'a' ещё нет
d['b'] += 5
d
# defaultdict(<class 'int'>, {'a': 1, 'b': 5})11. defaultdict(list) — группировка
from collections import defaultdict
orders = [
('Alice', 100),
('Bob', 50),
('Alice', 200),
('Bob', 75),
]
by_user = defaultdict(list)
for name, amount in orders:
by_user[name].append(amount)
# defaultdict(list, {
# 'Alice': [100, 200],
# 'Bob': [50, 75]
# })12. defaultdict(set) — уникальные
tags_per_user = defaultdict(set)
for user, tag in user_tags:
tags_per_user[user].add(tag)13. defaultdict(dict) — вложенные структуры
stats = defaultdict(dict)
stats['2026-01']['orders'] = 100
stats['2026-01']['revenue'] = 5000
stats['2026-02']['orders'] = 12014. Lambda-default
# default = 'unknown'
d = defaultdict(lambda: 'unknown')
print(d['missing']) # 'unknown'
# default = список из 3 нулей
d = defaultdict(lambda: [0, 0, 0])
d['key'][0] += 1Counter vs defaultdict
| Задача | Что выбрать |
|---|---|
| Подсчёт частот | Counter |
| Топ-N самых частых | Counter (most_common) |
| Группировка значений | defaultdict(list) |
| Уникальные значения по ключу | defaultdict(set) |
| Default-значение для любого | defaultdict(lambda: X) |
15. Решение задач с собесов
Задача: анаграммы
def group_anagrams(words):
groups = defaultdict(list)
for w in words:
key = ''.join(sorted(w))
groups[key].append(w)
return list(groups.values())
group_anagrams(['eat', 'tea', 'tan', 'ate', 'nat', 'bat'])
# [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]Задача: первый неповторяющийся символ
from collections import Counter
def first_unique(s):
c = Counter(s)
for ch in s:
if c[ch] == 1:
return ch
return None
first_unique("leetcode") # 'l'Задача: топ-K частых
def top_k(nums, k):
return [x for x, _ in Counter(nums).most_common(k)]
top_k([1, 1, 1, 2, 2, 3], 2) # [1, 2]Эквивалент в pandas
import pandas as pd
df = pd.DataFrame({'category': ['a', 'b', 'a', 'a', 'b']})
# Counter
df['category'].value_counts()
# a 3
# b 2
# defaultdict(list) — через groupby
df.groupby('category')['value'].apply(list)Частые ошибки
Ошибка 1. dict вместо defaultdict
# плохо — KeyError
d = {}
d['missing_key'] += 1 # KeyError
# хорошо
d = defaultdict(int)
d['missing_key'] += 1 # 1
# или
d = {}
d['missing_key'] = d.get('missing_key', 0) + 1Ошибка 2. defaultdict сам создаёт ключи при чтении
d = defaultdict(int)
x = d['never_set'] # создаст d['never_set'] = 0
len(d) # 1 (!)
# если не хотите создавать — используйте .get()
x = d.get('never_set', 0)Ошибка 3. Counter математика не в порядке
c1 = Counter({'a': 3, 'b': 1})
c2 = Counter({'a': 5})
c1 - c2 # Counter() — отрицательные (-2, 1) исключаются
c1.subtract(c2) # Counter({'b': 1, 'a': -2}) — сохраняет отрицательныеОшибка 4. Сравнение Counter и dict
Counter(a=1) == {'a': 1} # True
Counter(a=0) == {} # True (нулевые эквивалентны пустым)Связанные темы
FAQ
Когда Counter быстрее dict?
Для подсчёта частот — одинаково по скорости, но Counter короче. Для .most_common() — значительно удобнее.
Можно ли Counter для больших данных?
Да, Counter оптимизирован в C. Для миллиардов записей — лучше ClickHouse / Spark.
defaultdict(None) работает?
None не вызываемый → ошибка. Нужно передать функцию или класс: lambda: None.
Counter vs pandas value_counts()?
Для тех же задач. value_counts возвращает Series (с индексом), Counter — словарь-подобный.
Тренируйте Python — откройте тренажёр с 1500+ вопросами для собесов.