Конвейеры данных, построенные на генераторах позволяют скомпоновать код для обработки больших наборов данных или потоков данных без максимального использования памяти компьютера. Представим, что есть большой CSV-файл big-data.csv
в несколько тысяч строк с данными посещения сайта, которые нужно обработать.
Для обработки такого файла необходимо проделать следующие действия:
Такую обработку больших данных можно сделать с помощью Python пакета, такого как Pandas
, но также можно достичь этой функциональности при помощи нескольких генераторов.
Начнем обработку больших данных с чтения каждой строки из CSV-файла с помощью выражения генератора. Генераторы ленивы, следовательно не загружают файл целиком в оперативную память, а читают его построчно.
file_name = "big-data.csv" lines = (line for line in open(file_name))
Важно: Здесь чтение CSV-файла при помощи выражения генератора показано в качестве примера и понимания конвейеров. Для работы с CSV-файлами существует специальный оптимизированный модуля csv
, который включен в стандартную библиотеку Python.
Затем будем использовать другое выражение-генератор совместно с предыдущим, чтобы разбить каждую строку на список. Создадим генератор list_line
, который выполняет итерацию по строкам генератора lines
, который в свою очередь будет построчно читать большой файл.
list_line = (s.rstrip().split(",") for s in lines)
По сути - один генератор вкладывается в другой. Это общий шаблон, который используют при проектировании конвейеров генераторов.
Примечание: Код использует метод строки str.rstrip()
в выражении генератора list_line
, чтобы убедиться в отсутствии завершающих символов новой строки, которые могут присутствовать в CSV-файлах.
Затем извлечем имена столбцов из big-data.csv
. Так как имена столбцов обычно являются первой строкой CSV-файла, то их можно получить при помощи вызова встроенной функции next()
:
cols = next(list_line)
Вызов функции next()
сохранит список названий колонок в переменную cols
и переместит итератор на следующую строку генератора list_line
, с которой уже начинаются данные.
Чтобы иметь возможность фильтровать и выполнять операции с полученными данными, создадим словари, где ключами будут имена столбцов из CSV-файла:
log_dicts = (dict(zip(cols, data)) for data in list_line)
Выражение-генератора log_dicts
перебирает списки, созданные генератором list_line
и использует функции zip()
и dict()
для создания словаря, где ключами будут имена столбцов cols
, а значения - соответствующие данные.
Для обработки данных нужно использовать четвертый генератор, который например, будет фильтровать данные по столбцу user_agent
, в котором есть вхождение строки "YandexBot"
и вытаскивать соответствующие значения столбца ip_address
:
finding = ( log_dict["ip_address"] for log_dict in log_dicts if "YandexBot" in log_dict["user_agent"] ) list_ip = list(finding)
В этом фрагменте кода выражение-генератор finding
перебирает результаты генератора log_dicts
и возвращает значение ключа ip_address
для любого словаря log_dict
, в котором значение "YandexBot"
встречается в значении словаря с ключом user_agent
.
Необходимо понимать и помнить, что код выше не перебирает все сразу в выражении генератора finding
. На самом деле ничего не будет исполняться, пока не будет задействован цикл for
или функция, которая работает с итерациями, например list()
.
Проще говоря, вызов list(finding) заставляет работать все генераторы в коде.
И так, собираем код вместе:
file_name = "big-data.csv" # выражение-генератор, который получает строки из файла lines = (line for line in open(file_name)) # выражение-генератор, который получает список полей из каждой строки list_line = (s.rstrip().split(",") for s in lines) # получение списка имен колонок - это первая строка cols = next(list_line) # выражение-генератор, создающий словари log_dicts = (dict(zip(cols, data)) for data in list_line) # выражение-генератор, фильтрующий нужные значения finding = ( log_dict["ip_address"] for log_dict in log_dicts if "YandexBot" in log_dict["user_agent"] ) # преобразование генератора в итоговый список list_ip = list(finding)
Этот сценарий объединяет все созданные генераторы, и все они функционируют как один конвейер больших данных.