Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs
Это занятие мы с
вами начнем с рассмотрения функции groupby() модуля itertools, которая
позволяет группировать элементы коллекций по определенным ключам. Синтаксис
этой функции следующий:
itertools.groupby(iterable,
key=None)
Она возвращает
итератор, который, в свою очередь, выдает кортежи формата:
(ключ, группа)
Рассмотрим ее
работу на конкретных примерах. Пусть имеется последовательность из оценок
учащегося:
marks = (2, 2, 2, 3, 5, 4, 4, 3)
Требуется
сгруппировать их по оценкам. Для этого вначале последовательность обязательно
нужно упорядочить по ключам. Это требование использования функции groupby(). В нашем
случае ключи – это сами оценки, поэтому просто отсортируем этот список,
получим:
После этого
список s_marks укажем в функции groupby():
it_res = groupby(s_marks)
pprint.pprint(list(it_res))
После запуска
программы в консоли увидим список из кортежей:
[(2, <itertools._grouper object at
0x000001C33225B580>),
(3, <itertools._grouper object at
0x000001C332791D80>),
(4, <itertools._grouper object at
0x000001C332793490>),
(5,
<itertools._grouper object at 0x000001C332793430>)]
Смотрите, у нас
образовались ключи со значениями оценок и группы, которые можно получить через
соответствующие итераторы. Давайте их полностью развернем, например, так:
for k, g in groupby(s_marks):
print(k, list(g))
Получим:
2
[2, 2, 2]
3
[3, 3]
4
[4, 4]
5 [5]
Действительно, у
нас три двойки, две тройки и четверки, и одна пятерка.
Давайте усложним
пример и сгруппируем слова по первым буквам:
names = ["Антон", "Алексей", "Николай", "Никита", "Андрей",
"Сергей", "Афанасий", "Степан"
]
Вначале не
забывает выполнить сортировку этого списка по ключу. Так как ключ – первая
буква слова, то алгоритм сортировки будет выглядеть следующим образом:
names.sort(key=lambda x: x[0]) # ['Антон', 'Алексей', 'Андрей', 'Афанасий', 'Николай', 'Никита', 'Сергей', 'Степан']
После этого
группируем слова по первой букве:
for k, g in groupby(names, key=lambda x: x[0]):
print(k, list(g))
Получим:
А
['Антон', 'Алексей', 'Андрей', 'Афанасий']
Н
['Николай', 'Никита']
С ['Сергей',
'Степан']
Обратите
внимание, что ключ определяется с помощью лямбда-функции в параметре key функции groupby(). Если ее не
указывать, то ключами будут выступать слова целиком и результат станет совсем иным
– не то, что нам требовалось. Поэтому наличие лямбда-функции здесь строго
обязательно.
Давайте
рассмотрим еще один пример группировки. Пусть у нас имеется список из словарей
с информацией о поездках:
trips = [
{'name': 'Сергей', 'city': 'Москва', 'duration': 10},
{'name': 'Федор', 'city': 'Сочи', 'duration': 7},
{'name': 'Кирилл', 'city': 'Сочи', 'duration': 10},
{'name': 'Алексей', 'city': 'Питер', 'duration': 7},
{'name': 'Елена', 'city': 'Москва', 'duration': 10},
]
Требуется
сгруппировать эти данные по городам пребывания. Сделать это можно следующим
образом:
s_trips = sorted(trips, key=lambda x: x['city'])
for k, g in groupby(s_trips, key=lambda x: x['city']):
print(k, list(g))
Получим
результат:
Москва
[{'name': 'Сергей', 'city': 'Москва', 'duration': 10}, {'name': 'Елена',
'city': 'Москва', 'duration': 10}]
Питер
[{'name': 'Алексей', 'city': 'Питер', 'duration': 7}]
Сочи [{'name':
'Федор', 'city': 'Сочи', 'duration': 7}, {'name': 'Кирилл', 'city': 'Сочи',
'duration': 10}]
В качестве
домашнего задания выполните группировку данных списка trips по времени
пребывания (полю duration).
Функция starmap()
Следующая весьма
полезная функция starmap() модуля itertools работает по
аналогии с уже знакомой нам функцией map() из базового курса по языку Python. Напомню, что
функция map() позволяет к
элементам коллекций применять заданную функцию. Очень частый пример – это ввод
значений с клавиатуры и преобразования их к заданному типу:
digits_it = map(int, input().split()) # итератор для перебора введенных данных
digits = tuple(digits_it) # кортеж из введенных чисел
Но функцию map() можно
применить и для обработки сразу нескольких коллекций. Например:
data_x = [5, 3, 2]
data_y = [4, 8, 7]
sum_it = map(lambda x, y: x + y, data_x, data_y)
sum_data = tuple(sum_it) # (9, 11, 9)
Получим
результаты суммирования соответствующих величин из первого и второго списка.
Функция starmap() в целом
работает по аналогичному принципу, только ей можно передавать всего одну
итерируемую коллекцию. Чтобы повторить последний пример, воспользуемся
следующим списком:
data = [(5, 4), (3, 8), (2, 7)]
и суммирование
элементов кортежей можно реализовать так:
sum_it = starmap(lambda x, y: x + y, data)
sum_data = tuple(sum_it) # (9, 11, 9)
То есть
аргументами лямбда-функции являются распакованные значения кортежей (или любых
других допустимых коллекций).
Конечно, вместо
отдельного описания списка data мы могли бы воспользоваться исходными
списками data_x, data_y:
sum_it = starmap(lambda x, y: x + y, zip(data_x, data_y))
Результат будет
аналогичный.
Если же
попытаться в starmap() указать
итерируемый объект без возможности распаковки его элементов, например:
sum_it = starmap(lambda x: x, data_x)
то возникнет
ошибка. В отличие от функции map(), которая отработает здесь без ошибок.
Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs