Перейти к содержанию

432 уроков, 26 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 11 из 20 Начальный 20 мин 130 XP

Counter: словарь, который считает сам

Counter из collections — словарь, который считает сам: слова строки, буквы слова, повторения в списке. most_common отдаёт самых частых, арифметика счётчиков сравнивает две выборки.

Редакция Питоники

Считать повторения приходится постоянно: слова в тексте, покупки в чеках, голоса в опросе. Ручной счётчик через цикл уже умеет setdefault из урока 10 — но в стандартной библиотеке Python есть готовый инструмент. Counter снимает с программиста весь ручной труд подсчёта: не нужен ни цикл с накопителем, ни проверка, встречался ли элемент раньше.

Первый Counter

список — в счётчик
from collections import Counter

words = ['чай', 'кофе', 'чай', 'чай', 'кофе', 'сок']
count = Counter(words)

print(count)
print(count['чай'])
Вывод
Counter({'чай': 3, 'кофе': 2, 'сок': 1})
3

Counter — словарь-наследник: ключи — элементы, значения — количества. Печатается он словом Counter перед парами «элемент — количество». Доступ обычный: count['чай'] вернул 3. А вот отсутствие элемента — не ошибка: count['пирог'] вернёт 0. Считать нечего — это честный ответ, а не KeyError.

Подсчёт слов за строку

слова и буквы
from collections import Counter

phrase = 'мы пели мы плясали мы пели'
print(Counter(phrase.split()))

print(Counter('абракадабра'))
Вывод
Counter({'мы': 3, 'пели': 2, 'плясали': 1})
Counter({'а': 5, 'б': 2, 'р': 2, 'к': 1, 'д': 1})

split разрезает строку по пробелам в список слов — а список Counter уже умеет считать. Строка и сама по себе последовательность: посчитав её как в уроке 8, получим частоты букв. Так одна строка кода решает классическую задачу частотного анализа.

most_common: самые частые

топ по частоте
from collections import Counter

words = ['чай', 'кофе', 'чай', 'чай', 'кофе', 'сок']
count = Counter(words)

print(count.most_common(2))
print(count.most_common(1)[0][0])
Вывод
[('чай', 3), ('кофе', 2)]
чай
доступ и update
from collections import Counter

words = ['чай', 'кофе', 'чай', 'чай', 'кофе', 'сок']
count = Counter(words)

count['чай'] += 1
count.update(['сок'])
print(count)
print(count.most_common())
Вывод
Counter({'чай': 4, 'кофе': 2, 'сок': 2})
[('чай', 4), ('кофе', 2), ('сок', 2)]

Counter — это словарь, но не совсем

Counter ведёт себя как словарь и при этом умеет то, чего обычный словарь не умеет. Сначала проверим очевидное: к нему применимы in, перебор, len, доступ по ключу. Отличия — в поведении при отсутствующем ключе и в дополнительных методах.

что Counter умеет
from collections import Counter

text = 'мы пели мы плясали мы пели'
words = text.split()

count = Counter(words)
print(count)
print(count['мы'])
print(count['танцевали'])
print(len(count))
Вывод
Counter({'мы': 3, 'пели': 2, 'плясали': 1})
3
0
3

Третья строка — главное отличие: 'танцевали' в счётчике нет, но вместо KeyError вернулся 0. Четвёртая — длина счётчика равна числу уникальных слов, а не числу слов в тексте: в строке их было шесть, уникальных три. Для обычного словаря count['танцевали'] уронил бы программу.

Кстати, про разницу в шесть и три. Это не мелочь: если вы считаете уникальные элементы через len(count), то получаете число различных слов, а не длину текста. Задачи «сколько всего слов» и «сколько разных слов» — разные задачи, и Counter отвечает на обе, если знать, какую спрашивать.

Арифметика счётчиков

Вот здесь Counter раскрывается по-настоящему. Счётчики можно складывать, вычитать и сравнивать как множества. Для обычного словаря это было бы несколько строк с циклами, а здесь — одна операция.

сложение, вычитание, пересечение
from collections import Counter

a = Counter('абракадабра')
b = Counter('абра')

print(a + b)
print(a - b)
print(a & b)
Вывод
Counter({'а': 7, 'б': 3, 'р': 3, 'к': 1, 'д': 1})
Counter({'а': 3, 'б': 1, 'р': 1, 'к': 1, 'д': 1})
Counter({'а': 2, 'б': 1, 'р': 1})

Три операции, три ответа. Сложение складывает количества по каждому ключу. Вычитание вычитает из a количества b и оставляет только положительные результаты — поэтому а встречается 5 раз, б 2, а абра вычитает по одному разу каждого: 5 минус 2 даёт 3, 2 минус 1 даёт 1. Пересечение & оставляет только общие ключи и берёт по минимуму.

Практическое применение: a - b — это «буквы, которые встречаются в первом тексте чаще, чем во втором». Для сравнения двух текстов или подсчёта слов, характерных для одной выборки, такие готовые операции экономят кучу кода. Тот же приём работает и с most_common: посмотрите, какие слова вышли за пределы общей части.

update и накопление

Счётчик можно не только создать сразу, но и наполнять по частям. Метод update принимает строку, список или другой счётчик и прибавляет количества. Это удобно, когда данные приходят порциями: файлы, страницы API, чанки из очереди.

наполняем счётчик по частям
from collections import Counter

count = Counter()
count.update(['чай', 'чай'])
count.update('коф')
print(count)
print(count.most_common(1))
Вывод
Counter({'чай': 2, 'к': 1, 'о': 1, 'ф': 1})
[('чай', 2)]

Строка 'коф' посчиталась по буквам — это не опечатка, а прямое следствие того, что Counter работает с любой последовательностью. Строка — тоже последовательность. Чтобы этого не произошло, передавайте text.split(): список слов, а не строка букв.

Словарь, список и Counter: когда что брать

Стоит проговорить, когда подсчёт через Counter уместен, а когда лучше обойтись без него. Counter — это словарь, у которого values всегда числа и которые всегда означают «сколько раз встретилось». Если вам нужно просто хранить числа под ключами, Counter избыточен: он ничего не добавляет, кроме лишнего импорта.

На практике я беру Counter в трёх случаях. Первый — подсчёт повторов: слова в тексте, голоса в опросе, покупки в заказе. Второй — когда нужно сравнивать две выборки: a - b сразу показывает разницу, и писать цикл с двумя счётчиками не нужно. Третий — когда нужен топ: most_common(10) даёт десять самых частых элементов одной строкой. Во всех остальных случаях обычный словарь с get будет честнее и понятнее.

top-3 и разворот счётчика
from collections import Counter

count = Counter(['чай', 'кофе', 'чай'])

print(count.most_common())
print(count.most_common(1))
print(count.most_common(1)[0][0])
print(list(count.elements()))
Вывод
[('чай', 2), ('кофе', 1)]
[('чай', 2)]
чай
['чай', 'чай', 'кофе']

Четыре способа достать самое частое. most_common() без аргумента отдаёт все пары по убыванию частоты, most_common(1) — только первую, а двойное индексирование [0][0] вытаскивает из кортежа сам элемент. Последний вызов elements() разворачивает счётчик обратно в список: каждый элемент повторяется столько раз, сколько его посчитали. Это, кстати, удобный способ получить исходную последовательность заново — сортировкой по ключу.

Что дальше

Counter закрывает задачу подсчёта повторов целиком: одна строка вместо цикла с накопителем. Дальше — словарь, который сам создаёт значения по заданному типу: defaultdict в уроке 12. А если хочется собрать частотный словарь целого текста с нуля — загляните в урок 20, там финальный проект раздела.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

from collections import Counter

print(Counter('мама'))
from collections import Counter

c = Counter(['а', 'б', 'а'])
print(c.most_common(1)[0][0])
from collections import Counter

c = Counter(['сок', 'сок'])
print(c['чай'])
print(sum(c.values()))
Проверь себя
0 / 8

1. Что вернёт count['пирог'], если Counter создан без слова 'пирог'?

2. Что делает most_common(2)?

3. Как посчитать частоты слов в строке phrase?

4. Что делает count.update(['сок'])?

5. Чем Counter отличается от обычного dict?

6. Что вернёт a - b, если a = Counter('абракадабра'), а b = Counter('абра')?

7. Что делает count.update('коф') для счётчика, созданного из списка слов?

8. Сколько уникальных слов в счётчике Counter('мы пели мы плясали мы пели'.split())?

Карточки терминов
Запомнено: 0 / 8
Практика

Дана строка phrase = 'чай кофе чай сок кофе чай'. Посчитай слова через Counter, выведи счётчик целиком и самое частое слово.

practice.py
Вопросы и ответы по уроку

Как посчитать количество слов в строке на Python?

Разрезать строку по пробелам и отдать список в Counter: Counter(phrase.split()) вернёт словарь «слово — сколько раз встретилось». Для уникальных слов удобен len этого счётчика, для самого частого — most_common(1).

Урок 10: setdefault

Что делает Counter в Python?

Считает элементы последовательности: Counter(['чай', 'чай']) вернёт Counter({'чай': 2}). Это словарь-наследник из стандартного модуля collections, поэтому доступ по ключу, перебор и in работают как у обычного dict.

Что возвращает most_common()?

Список пар «элемент — количество», упорядоченный по убыванию частоты. С аргументом n — только n первых пар, без аргумента — все. most_common(1)[0][0] даёт самый частый элемент.

Падает ли Counter на отсутствующем ключе?

Нет: count['пирог'] вернёт 0, ничего не вставляя в словарь. Это удобно для подсчётов, но опечатки в ключах Counter тоже молча посчитает нулём — проверяйте написание.

Можно ли складывать два счётчика Counter?

Да, и это одно из самых полезных свойств. Оператор + складывает количества по общим ключам, оператор - вычитает и оставляет только положительные результаты, оператор & находит общие ключи по минимуму. Так сравнивают два текста или две выборки: разность показывает, какие слова характерны именно для первого.

Как посчитать количество разных слов, а не общее число слов?

Через len(counter): он считает уникальные элементы. Общее число вхождений даёт sum(counter.values()). Для строки 'мы пели мы плясали мы пели' это 3 и 6 соответственно — две разные величины, которые часто путают.

Почему count.update('коф') посчитает буквы, а не слово?

Потому что update работает с любой последовательностью, а строка — это последовательность букв. Чтобы счётчик считал слова, передавайте text.split(): список слов. Это частая невнимательность при разборе строк.

Понравился урок? Сошлитесь на него

«Counter снимает с программиста весь ручной труд подсчёта: не нужен ни цикл с накопителем, ни проверка, встречался ли элемент раньше.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.