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

Проверь себя · 1/3разбор после ответа
Дан список пар pairs = [('views', 5), ('clicks', 6), ('purchases', 9)]. Что вернёт выражение {k: v for k, v in pairs}?

Зачем аналитику Counter и defaultdict

Подсчёт частот и группировка — две самых частых задачи при обработке данных. «Какие категории самые популярные?», «Сколько раз встречается каждый keyword?», «Сгруппируй покупки по пользователям». Вместо громоздкого dict + if/else — Counter и defaultdict решают за 2 строки.

На собесе эти структуры спрашивают в задачах: «найди топ-3 самых частых слова», «сгруппируй анаграммы», «первый неповторяющийся символ». Без Counter / defaultdict вы будете писать в 3 раза больше кода. Middle-уровень без них не пройти.

В шпаргалке — все рабочие паттерны:

  • Counter для подсчёта частот (most_common(3))
  • defaultdict(list) для группировки
  • defaultdict(int) для счётчиков без KeyError
  • defaultdict(set) для уникальных значений на ключ
  • Математика: сложение / вычитание Counter-ов
  • Связь с value_counts() в pandas
  • Типичные ошибки: dict вместо defaultdict, чтение создаёт ключ

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)
Закрепи Python для аналитика
200+ задач по pandas, numpy и работе с данными — с разборами
Тренировать Python в Telegram

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 — словарь-подобный.