Counter и defaultdict в Python: шпаргалка

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Collections в Python

Модуль collections даёт специальные контейнеры, которые решают частые задачи аналитика быстрее обычного dict.

from collections import Counter, defaultdict

Counter

Специализированный 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=5

7. 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    2

defaultdict

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'] = 120

14. Lambda-default

# default = 'unknown'
d = defaultdict(lambda: 'unknown')
print(d['missing'])  # 'unknown'

# default = список из 3 нулей
d = defaultdict(lambda: [0, 0, 0])
d['key'][0] += 1

Counter 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+ вопросами для собесов.