Специализированные структуры данных и потоковая обработка

Коллекции стандартной библиотеки: deque, Counter, defaultdict и ChainMap

Содержание курса

Анализ частот и операции над мультимножествами с Counter

Когда нужно подсчитать, сколько раз встречаются элементы в последовательности, стандартным решением является collections.Counter. Это подкласс словаря dict, оптимизированный для подсчёта хешируемых объектов.

from collections import Counter

log_levels = ["INFO", "ERROR", "INFO", "WARNING", "ERROR", "ERROR"]
counts = Counter(log_levels)
print(counts)
print(counts.most_common(2))

Вывод программы:

Counter({'ERROR': 3, 'INFO': 2, 'WARNING': 1})
[('ERROR', 3), ('INFO', 2)]

Метод most_common(k) возвращает наиболее часто встречающихся элементов и их количество в виде пар кортежей, отсортированных по убыванию частоты. Если запросить ключ, которого нет в счётчике, Counter не выбросит KeyError, а вернёт 0.

Помимо подсчёта, Counter ведёт себя как мультимножество. Над ним определены бинарные операции со строгой математической семантикой для мультимножеств:

  • Сложение +: складывает количества повторений для каждого элемента.
  • Вычитание -: вычитает счётчики второго мультимножества из первого.
  • Пересечение &: берёт минимум счётчиков для общих элементов — .
  • Объединение |: берёт максимум счётчиков — .
box_a = Counter(apples=3, oranges=1, bananas=4)
box_b = Counter(apples=1, oranges=5, kiwi=2)

print("box_a + box_b:", box_a + box_b)
print("box_a - box_b:", box_a - box_b)
print("box_a & box_b:", box_a & box_b)
print("box_a | box_b:", box_a | box_b)

Вывод программы:

box_a + box_b: Counter({'oranges': 6, 'apples': 4, 'bananas': 4, 'kiwi': 2})
box_a - box_b: Counter({'bananas': 4, 'apples': 2})
box_a & box_b: Counter({'apples': 1, 'oranges': 1})
box_a | box_b: Counter({'oranges': 5, 'bananas': 4, 'apples': 3, 'kiwi': 2})

Важнейшее правило операторов +, -, &, |: они возвращают новый Counter и автоматически отбрасывают все неположительные результаты (значения ). Например, при вычитании box_a - box_b для 'oranges' получается , поэтому ключ 'oranges' вообще не попадает в итоговый счётчик.

Если же вам требуется сохранить отрицательные балансы (например, для учёта дефицита на складе), используют метод subtract():

stock = Counter(apples=3, oranges=1)
order = Counter(apples=1, oranges=5)

stock.subtract(order)
print("Баланс после subtract():", stock)

Вывод программы:

Баланс после subtract(): Counter({'apples': 2, 'oranges': -4})

Метод subtract() мутирует существующий объект на месте и, в отличие от оператора -, честно сохраняет нулевые и отрицательные значения.

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения и доступны преподавателям курса. Чтобы попросить преподавателя подключиться, отправьте обращение из диалога.

Проверка решения остаётся отдельным действием.