Класс Counter() модуля collections в Python
Подсчет количества повторений элементов в последовательности
класс collections.Counter() предназначен для удобных и быстрых подсчетов количества появлений неизменяемых элементов в последовательностях.
>>> from collections import Counter >>> cnt = Counter(['red', 'blue', 'red', 'green', 'blue', 'blue']) >>> dict(cnt) #
Синтаксис:
import collections cnt = collections.Counter([iterable-or-mapping])
Параметры:
- iterable-or-mapping — итерируемая последовательность или словарь.
Возвращаемое значение:
Описание:
Класс Counter() модуля collections — это подкласс словаря dict для подсчета хеш-объектов (неизменяемых, таких как строки, числа, кортежи и т.д.). Это коллекция, в которой элементы хранятся в виде словарных ключей, а их счетчики хранятся в виде значений словаря.
Счетчик может быть любым целочисленным значением, включая ноль или отрицательное число. Класс collections.Counter() похож на мультимножества в других языках программирования.
Элементы считываются из итерируемой последовательности, инициализируются из другого словаря или счетчика Counter() :
>>> from collections import Counter # новый пустой счетчик >>> cnt = Counter() # новый счетчик из последовательности >>> cnt = Counter('gallahad') # новый счетчик из словаря >>> cnt = Counter('red': 4, 'blue': 2>) # новый счетчик из ключевых слов 'args' >>> cnt = Counter(cats=4, dogs=8)
Счетчики collections.Counter() имеют интерфейс словаря, за исключением того, что они возвращают 0 для отсутствующих элементов вместо вызова исключения KeyError :
>>> cnt = Counter(['eggs', 'ham']) >>> cnt['bacon'] # 0
Установка счетчика в ноль не удаляет элементы из счетчика. Используйте инструкцию del , чтобы полностью удалить ключ счетчика:
# запись счетчика с нулевым счетом >>> cnt['sausage'] = 0 # удаление счетчика с нулевым счетом >>> del cnt['sausage']
В качестве подкласса dict() , класс Counter() унаследовал возможность запоминания порядка вставки. Математические операции над объектами Counter() также сохраняют порядок. Результаты упорядочены в соответствии с тем, когда элемент сначала встречается в левом операнде, а затем в порядке, в котором встречается правый операнд.
Расширенные операторы сравнения.
Новое в версии 3.10: Добавлены расширенные операции сравнения.
С версии Python 3.10 счетчики Counter поддерживают расширенные операторы сравнения для отношений равенства, подмножества и надмножества: == , != , < , , >= . Все эти сравнения рассматривают отсутствующие элементы как имеющие нулевое количество, так что Counter(a=1) == Counter(a=1, b=0) возвращает True .
Изменено в версии 3.10: при сравнении на равенство, отсутствующие элементы рассматриваются как имеющие нулевое количество. Раньше Counter(a=3) и Counter(a=3, b=0) считались разными.
Объект Counter , атрибуты и методы:
- Counter.elements() возвращает итератор по элементам.
- Counter.most_common() список наиболее распространенных элементов.
- Counter.subtract() вычитает элементы счетчика и итерируемой последовательности.
- Counter.total() вычисляет сумму значений счетчика.
- Counter.update() складывает элементы счетчика и итерируемой последовательности.
Counter.elements() :
Метод Counter.elements() возвращает итератор по элементам, в котором каждый элемент повторяется столько раз, во сколько установлено его значение. Элементы возвращаются в порядке их появления. Если количество элементов меньше единицы, то метод Counter.elements() просто проигнорирует его.
>>> from collections import Counter >>> cnt = Counter(a=4, b=2, c=0, d=-2) >>> sorted(cnt.elements()) # ['a', 'a', 'a', 'a', 'b', 'b']
Counter.most_common([n]) :
Метод Counter.most_common() возвращает список из n наиболее распространенных элементов и их количество от наиболее распространенных до наименее. Если n опущено или None , метод cnt.most_common() возвращает все элементы в счетчике.
Элементы с равным количеством упорядочены в порядке, в котором они встречаются первыми:
>>> from collections import Counter >>> Counter('abracadabra').most_common(3) # [('a', 5), ('b', 2), ('r', 2)]
Counter.subtract([iterable-or-mapping]) :
Метод Counter.subtract() вычитает элементы текущего счетчика cnt и итерируемой последовательности или другого словаря или другого счетчика Counter() . Подобно методу словаря dict.update() , но вычитает количество (значения ключей), а не заменяет их.
Значения ключей как у счетчика так и у словаря могут быть нулевыми или отрицательными.
>>> from collections import Counter >>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.subtract(d) >>> c # Counter()
Counter.total() :
В Python 3.10 появился метод Counter.total() , который вычисляет сумму значений текущего счетчика.
>>> from collections import Counter >>> c = Counter(a=10, b=5, c=0) >>> c.total() # 15
В более ранних версиях Python, этот метод можно заменить выражением:
>>> from collections import Counter >>> c = Counter(a=10, b=5, c=0) >>> sum(c.values()) # 15
Для объектов collections.Counter() доступны обычные методы словарей, за исключением двух, которые для счетчиков работают по-другому.
Counter.fromkeys(iterable) :
Метод Counter.fromkeys() не реализован для объектов Counter() .
Counter.update([iterable-or-mapping]) :
Метод Counter.update() складывает элементы текущего счетчика cnt и итерируемой последовательности или другого словаря или другого счетчика Counter() . Работает подобно методу словаря dict.update() , но складывает количество (значения ключей), а не заменяет их.
Кроме того, ожидается, что итерация будет последовательностью элементов, а не последовательностью двойных кортежей (key, value) .
>>> from collections import Counter >>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.update(d) >>> c # Counter()
Примеры использования класса Counter() .
Общие шаблоны для работы с объектами Counter :
>>> from collections import Counter # создать счетчик из списка кортежей (elem, cnt) >>> cnt = Counter(dict(list_of_pairs)) # преобразовать в список кортежей (elem, cnt) >>> cnt.items() # сумма всех значений в счетчике >>> sum(cnt.values()) # очистить счетчик >>> cnt.clear() # список уникальных элементов >>> list(cnt) # преобразовать в множество >>> set(cnt) # преобразовать в обычный словарь >>> dict(cnt) # N наименее распространенных элементов >>> cnt.most_common()[:-n-1:-1] # Удалить элементы с нулевыми отрицательными значениями >>> +cnt
Предусмотрено несколько математических операций для объединения объектов Counter() для создания мультимножеств (счетчиков, число которых больше нуля). Сложение и вычитание объединяют счетчики путем сложения или вычитания количества соответствующих элементов. Пересечение и объединение возвращают минимум и максимум соответствующих отсчетов.
Каждая операция принимает счетчики, значения ключей которых могут иметь любые числа, но выходные данные исключают из результата ключи с нулевыми отрицательными значениями.
>>> from collections import Counter >>> c = Counter(a=3, b=1) >>> d = Counter(a=1, b=2) # Сложить два счетчика: c[x] + d[x] >>> c + d # Counter() # Вычитание счетчиков, сохраняются # только положительные значения элементов >>> c - d # Counter() # Пересечение счетчиков: min(c[x], d[x]) >>> c & d # Counter() # Объединение счетчиков: max(c[x], d[x]) >>> c | d # Counter()
Унарное сложение и вычитание являются ссылками для добавления пустого счетчика или вычитания из пустого счетчика.
>>> from collections import Counter >>> c = Counter(a=2, b=-4) >>> +c # Counter() >>> -c # Counter()
Подсчёт одинаковых слов в файле при помощи collections.Counter() .
При помощи класса collections.Counter() можно легко подсчитать количество повторений слов в тексте. Например найдем десять самых распространенных слов в трагедия Уильяма Шекспира «Гамлет».
from collections import Counter from re import findall with open('hamlet.txt') as fp: words = findall(r'\w+', fp.read().lower()) cnt = Counter(words).most_common(10) print(cnt) # [('the', 1143), ('and', 966), ('to', 762), # ('of', 669), ('i', 631), ('you', 554), # ('a', 546), ('my', 514), ('hamlet', 471), # ('in', 451)]
Ограничения типа Counter() и минимальный диапазон:
Счетчики были в первую очередь предназначены для работы с положительными целыми числами для представления счетчиков.
Здесь описаны минимальный диапазон и ограничения типа Counter() , чтобы не исключать случаи использования, требующие хранения других типов или отрицательных значений.
- Сам класс collections.Counter() является подклассом словаря dict без ограничений по ключам и значениям. Значения предназначены для чисел, представляющих счетчики, но вы можете хранить все что угодно в поле значений.
- Метод cnt.most_common() только сортирует значения ключей по убыванию.
- Для операций на месте, таких как cnt[key] += 1 , тип значения должен только поддерживать сложение и вычитание. Таким образом, дроби, числа с плавающей запятой и десятичные числа будут работать, отрицательные значения тоже поддерживаются. То же самое относится и к методам cnt.update() и cnt.subtract() , которые допускают отрицательные и нулевые значения для входных и выходных данных.
- Мультимножественные методы предназначены только для случаев использования с положительными значениями. Входные значения ключей могут быть отрицательными или нулевыми, но в результате операций сохраняются только положительные значения. Нет ограничений по типу, но тип значения должен поддерживать сложение, вычитание и сравнение.
- Метод Counter.elements() работает только с положительными целыми числами и будет игнорировать ноль и отрицательные значения.
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Класс ChainMap() модуля collections
- Класс deque() модуля collections
- Класс Counter() модуля collections
- Класс defaultdict() модуля collections
- Класс namedtuple() модуля collections
- Класс OrderedDict() модуля collections
- Класс UserString() модуля collections
- Класс UserDict модуля collections
- Класс UserList модуля collections
collections.Counter
Данный тип, являясь потомком словаря, предоставляет инструменты для подсчёта количества хешируемых объектов.
На заметку
Тип может напомнить вам bag в Smalltalk или multiset в C++.
В этой коллекции сущности хранятся в виде ключей, а их количество — в виде значений.
Количество может обозначаться любым целым, включая нуль и отрицательные.
Подсчёт может производиться для элементов объекта, поддерживающего итерирование, либо другого отображения.
from collections import Counter
mycounter = Counter()
mycounter.items()
# dict_items([])
mycounter = Counter()
mycounter.items()
# dict_items([('one', 1), ('many', 10)])
mycounter = Counter('abca')
mycounter.items()
# dict_items([('a', 2), ('b', 1), ('c', 1)])
mycounter = Counter(['a', 'b', 'c', 'a'])
mycounter.items()
# dict_items([('a', 2), ('b', 1), ('c', 1)])
# дополнение счётчика
mycounter['a'] += 1
mycounter.items()
# dict_items([('a', 3), ('b', 1), ('c', 1)])
# все повторения элементов с ненулевым положительным количеством
list(mycounter.elements())
# ['a', 'a', 'a', 'b', 'c']
# самые встречаемые 2 элемента
mycounter.most_common(2)
# [('a', 3), ('b', 1)]
# вычитание из счётчика
mycounter.subtract()
mycounter.items()
# dict_items([('a', 2), ('b', 1), ('c', 1)])
# удаление элемента
del mycounter['b']
mycounter.items()
# dict_items([('a', 2), ('c', 1)])
Для счётчиков также доступны операции:
+ | сложение |
— | вычитание (с сохранением только положительных значений) |
& | пересечение ( min(counter1[x], counter2[x]) ) |
| | объединение ( max(counter1[x], counter2[x]) ) |
На заметку
Счётчики были спроектированы в основном для работы с положительными целыми. Однако это не означает, что нельзя в качестве значения поместить объект иного типа.
Счётчик (Counter)
Одна из распространённых задач, для которой начинающие питонисты придумывают собственные решения, – подсчёт элементов последовательности: списка, строки символов и т. д.
Если нужно что-то посчитать, определить количество вхождений или наиболее (наименее) часто встречающихся элементов, используйте объекты класса Counter . Создаются они с помощью конструктора collections.Counter() .
Функция принимает итерируемый аргумент и возвращает словарь, в котором ключами служат индивидуальные элементы, а значениями – количества повторений элемента в переданной последовательности. Посчитаем, сколько раз встречается каждая буква в слове «абракадабра»:
Что будет, если обратиться к словарю по ключу, которого в нем ещё нет?
Верно, исключение KeyError :
После разговора о словарях самое время обсудить класс, умеющий объединять словари в надструктуру – ChainMap . При этом получается не один общий словарь, а их совокупность, в которой каждый словарь остаётся независимой составляющей:
Объект типа deque (читается как «дэк», двусторонняя или двусвязная очередь) является усовершенствованным вариантом списка с оптимизированной вставкой/удалением элементов с обоих концов. Реализация deque оптимизирована так, что операции слева и справа имеют примерно одинаковую производительность O(1) . Добавление новых элементов в конец происходит не сильно медленнее, чем во встроенных списках, но добавление в начало выполняется существенно быстрее.
namedtuple() – функция-фабрика для создания именованных кортежей. Этот тип данных похож на struct в других языках программирования:
>>> cols = ['fname', 'pname', 'lname', 'age'] >>> User = collections.namedtuple('User', cols) >>> user1 = User('Петр', 'Иванович', 'Сидоров', 30) >>> user1 User(fname='Петр', pname='Иванович', lname='Сидоров', age=30) >>> user1.lname Сидоров >>> Point = collections.namedtuple('Point', ['x', 'y']) >>> p = Point(3, 4) >>> p.x**2 + p.y**2 25
Именованные кортежи делают код яснее – вместо индексирования составляющие объекта вызываются по явным именам. Остаётся доступной и численная индексация:
>>> p[0]**2 + p[1]**2 25
Именованные кортежи часто используются для назначения имён полей кортежам, возвращаемым модулями csv или sqlite3 :
EmployeeRecord = collections.namedtuple('EmployeeRecord', 'name, age, title, department, paygrade') import csv for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))): print(emp.name, emp.title) import sqlite3 conn = sqlite3.connect('/companydata') cursor = conn.cursor() cursor.execute('SELECT name, age, title, department, paygrade FROM employees') for emp in map(EmployeeRecord._make, cursor.fetchall()): print(emp.name, emp.title)
Структура namedtuple похожа на словарь. Посредством метода _asdict можно представить те же данные в виде OrderedDict :
>>> p._asdict() OrderedDict([('x', 3), ('y', 4)])
Чтобы вызвать значение через строковый ключ, необязательно преобразовывать namedtuple – подходит стандартная функция getattr() :
>>> getattr(p, 'x') 3
Чтобы преобразовать словарь в именованный кортеж заданного типа, достаточно распаковать его оператором ** :
>>> d = >>> Point(**d) Point(x=0, y=1)
Имена полей namedtuple перечислены в _fields :
>>> user1._fields, p._fields (('fname', 'pname', 'lname', 'age'), ('x', 'y'))
С версии 3.7 можно присвоить полям значения по умолчанию.
Поскольку именованный кортеж является обычным классом Python, в него легко привнести новую функциональность или изменить старую. Например, добавим к Point расчёт гипотенузы и формат вывода данных:
class Point(collections.namedtuple('Point', ['x', 'y'])): __slots__ = () # предотвращает создание словарей экземпляров @property def hypot(self): return (self.x**2 + self.y**2) ** 0.5 def __str__(self): return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) for p in Point(3, 4), Point(14, 5/7): print(p)
Point: x= 3.000 y= 4.000 hypot= 5.000 Point: x=14.000 y= 0.714 hypot=14.018
Если вам пришлась по душе компактность namedtuple в сравнении с обычными классами и ваш проект может работать с версиями Python не меньше 3.7, присмотритесь к модулю dataclasses. Эта встроенная библиотека предоставляет декоратор и функции для автоматического добавления в пользовательские классы сгенерированных специальных методов, таких как __init__() и __repr__() .
Резюме
Подведём итог нашему рассказу об основных составляющих модуля collections:
- Counter – инструмент подсчёта неизменяемых объектов. Используйте, если нужно определить количество вхождений или число наиболее (наименее) часто встречающихся элементов.
- defaultdict – словарь, умеющий при вызове отсутствующего ключа вместо вызова исключения KeyError записывать значение по умолчанию (работает быстрее, чем метод setdefault() ).
- OrderedDict – словарь с памятью порядка добавления элементов, умеющий переупорядочивать элементы лучше, чем dict .
- ChainMap – контейнер комбинаций словарей с поиском, обобщением ключей и элементов.
- namedtuple() – функция-фабрика для создания именованного кортежа. Это один из простейших способов сделать код более ясным: использовать вместо индексов имена.
- deque – двусторонняя очередь – список, оптимизированный для вставки и удаления элементов с обоих концов с методом подсчёта вхождений
- UserDict, UserList, UserString – не заслуживающие развёрнутого описания обертки над стандартными объектами словарей, списков и строк для беспроблемного наследования (прямое наследование встроенным типам dict , list , str чревато ошибками, связанными с игнорированием переопределения методов).
Также у модуля collections имеется наследованный модуль коллекции абстрактных базовых классов сollections.abc . Но это тема отдельного разговора.
А вы уже используете collections в своих проектах?
Как использовать модуль Counter в Python
В этой статье объясняется, как использовать модуль «Counter», который по умолчанию поставляется с языком программирования Python. Все примеры кода в этой статье протестированы на Python версии 3.9.5 в Ubuntu 21.04.
О модуле счетчика
Модуль Counter, как следует из названия, может использоваться для подсчета элементов итерируемого или хешируемого объекта в Python. Counter сохраняет каждый элемент итерируемого объекта (например, объект списка Python) как ключ словаря Python. Поскольку словари в Python допускают только уникальные ключи, повторения нет. Соответствующие значения для ключей этих словарей — это количество или количество раз, когда элемент появляется в итерируемом объекте.
Основное использование и синтаксис
Чтобы понять базовое использование и синтаксис класса Counter, взгляните на пример кода ниже:
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts)
Первый оператор импортирует модуль Counter, чтобы в коде можно было использовать класс Counter. Затем определяется новый объект списка Python с некоторыми данными. Затем создается новый экземпляр объекта Counter путем передачи «list1» в качестве аргумента. Последний оператор печатает вывод объекта «counts».
После выполнения приведенного выше примера кода вы должны получить следующий результат:
Counter()
Обратите внимание, что выходные данные возвращают объект типа Counter, а не словарь Python. Хотя он ведет себя как словарь Python с одним незначительным отличием, которое объясняется ниже.
Объект счетчика ведет себя как объект словаря Python
Словарь в Python — это объект, который хранит элементы в парах «ключ: значение». Ниже приведен пример словаря Python:
dict1 =
Часть перед символом «:» (двоеточие) называется «ключом», а часть после символа двоеточия называется «значением». Вы можете получить доступ к значению любого ключа в словаре Python, используя следующий синтаксис:
dict1 = print (dict1["a"])
Вам просто нужно указать имя ключа в квадратных скобках «[]». Если ключ не существует в словаре, выдается ошибка KeyError.
Выходные данные приведенного выше примера Counter показывают, что при создании нового экземпляра класса Counter возвращается новый объект типа Counter. Этот объект типа Counter представляет собой не что иное, как словарь Python, за исключением того, что он не генерирует ошибку «KeyError», когда значение ключа отсутствует. Вместо этого он присваивает ему значение «0» (ноль). Вы также можете получить доступ к значениям элементов в объекте Counter, указав имена ключей в квадратных скобках, как и в объекте словаря. Взгляните на пример кода ниже:
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts["f"]) dict1 = print (dict1["c"])
После выполнения приведенного выше примера кода вы должны получить следующий результат:
0 Traceback (most recent call last): File "main.py", line 11, in print (dict1["c"]) KeyError: 'c'
Как вы можете видеть в выходных данных, когда вы обращаетесь к ключу, которого нет в объекте Counter, возвращается «0» (ноль). С другой стороны, объект словаря Python выдает ошибку «KeyError» при отсутствии ключа.
Создание объекта счетчика вручную
Может быть случай, когда вы захотите вручную определить объект Counter вместо анализа итерации, такой как список Python. Чтобы создать объект счетчика, вы можете использовать следующий синтаксис:
from collections import Counter counter1 = Counter(a=4, b=3) counter2 = Counter() print (counter1) print (counter2)
Вы можете использовать синтаксис стиля аргумента, показанный в первом операторе, или использовать синтаксис стиля словаря Python, показанный во втором операторе, для создания новых экземпляров объекта Counter. Оба метода имеют одинаковый эффект и дают одинаковый результат.
После выполнения приведенного выше примера кода вы должны получить следующий результат:
Counter() Counter()
Получение наиболее распространенных элементов из объекта счетчика
Вы можете использовать метод most_common для сортировки элементов и их количества в порядке убывания от объекта типа Counter. Взгляните на пример кода ниже:
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts.most_common())
Вывод возвращает список кортежей, а не объект словаря Counter или Python.
Вы также можете получить только несколько самых верхних элементов, указав число в методе most_common в качестве аргумента.
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts.most_common(2))
После выполнения приведенного выше примера кода вы должны получить следующий результат:
[('d', 4), ('b', 3)]
Другие полезные методы счетчика
Вы можете получить доступ ко всем ключам и значениям объекта Counter, используя методы «keys» и «values» соответственно.
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts.keys()) print (counts.values())
После выполнения приведенного выше примера кода вы должны получить следующий результат:
dict_keys(['a', 'b', 'c', 'd', 'e']) dict_values([2, 3, 1, 4, 2])
Эти методы создают итерируемые объекты, чтобы вы могли их перебирать.
Вы можете получить как ключи, так и значения, используя метод «items».
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (counts.items())
После выполнения приведенного выше примера кода вы должны получить следующий результат:
dict_items([('a', 2), ('b', 3), ('c', 1), ('d', 4), ('e', 2)])
Вы также можете просмотреть результат, полученный методом «item».
Вы можете преобразовать объект Counter в словарь Python с помощью функции «dict».
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) print (dict(counts))
После выполнения приведенного выше примера кода вы должны получить следующий результат:
Цикл по ключам и значениям объекта счетчика
Вы можете легко просмотреть пары ключ-значение объекта Counter, используя метод «items», описанный выше. Взгляните на пример кода ниже:
from collections import Counter list1 = ["a", "a", "b", "b", "b", "c", "d", "d", "d", "d", "e", "e"] counts = Counter(list1) for key, value in counts.items(): print (key, value)
В коде к переменной пары ключей можно получить доступ с помощью переменных «ключ» и «значение» соответственно в цикле «for».
После выполнения приведенного выше примера кода вы должны получить следующий результат:
a 2 b 3 c 1 d 4 e 2
Заключение
Встроенный в Python модуль «Счетчик» обеспечивает быстрый и эффективный способ подсчета количества элементов, хранящихся в объекте итеративного типа. Вы можете использовать метод «most_common» для получения самых верхних пар с наибольшим счетчиком, указав желаемое число в качестве аргумента.