Оптимизированные операции ввода-вывода в Python |
Хаки ввода-вывода для ускорения аналитики с использованием стека обработки данных Python
При работе с приложениями, интенсивно использующими данные, я часто сталкиваюсь с проблемами ввода-вывода (I/O), которые являются узким местом для каждого приложения, критичного к производительности. С увеличением объема хранимых данных возникает необходимость хранения данных на дисках, чтобы компенсировать нехватку оперативной памяти путем загрузки данных с диска в оперативную память и наоборот. Таким образом, операции ввода-вывода являются очень важными задачами, когда речь идет об обработке финансовых данных или любых научных данных.
В этом посте я пытаюсь пролить свет на несколько библиотек и их хитрости. Python имеет встроенные возможности, которые можно использовать для хранения объекта на диске и для его чтения с диска в ОЗУ. Кроме того, Python хорошо справляется с обработкой текстовых файлов и баз данных SQL. Библиотека Pandas предоставляет множество классов и методов для чтения и записи файлов в широком диапазоне форматов.
Здесь мы собираемся изучить следующие области методов хранения и извлечения данных:
- Сериализованное хранилище с использованием модуля Pickle
- Операции ввода/вывода с текстовыми данными
- Базы данных SQL
- Ввод-вывод с PyTables
При оптимизации операций ввода-вывода в языке Python учитываются два основных фактора: эффективность (производительность) и гибкость. Давайте погрузимся прямо в него:
Сериализованное хранилище с использованием модуля Pickle
В языке Python есть множество модулей, которые можно легко использовать в условиях крупномасштабного развертывания.
Использование модуля pickle для чтения и записи файлов
Вам необходимо хранить данные на диске, чтобы делиться ими, документировать или использовать их позже. У нас есть модуль pickle, который сериализует объект Python, чтобы быстро выполнять операции чтения и записи.
# On running the above code snippet, you'll see:
CPU times: user 40.9 ms, sys: 14 ms, total: 54.9 ms
Wall time: 54.5 ms
Случайные числа с плавающей запятой создают файл размером 9 МБ, который сериализуется в поток байтов и записывается на диск за 54,9 мс. Вы будете использовать свалка а также нагрузка функция модуля pickle для записи и чтения файла соответственно. Чтобы утвердить сериализованные и десериализованные данные, вы можете использовать Numpy’s всезакрыть метод. Вот как вы можете это сделать:
np.allclose(np.array(a1), np.array(a2))
# here a2 is the deserialized object after reading the same file
# using the load function.
Таким образом, модуль pickle сохраняет список python, dict и т. д. после преобразования их в поток символов на диске. Суть здесь в том, что этот поток байтов содержит информацию, необходимую для восстановления объекта в другом скрипте Python.
Операции ввода/вывода с текстовыми данными
Python был популярным языком, особенно при работе с текстовыми файлами из-за надежности и простоты обработки текстовых данных. Существует несколько вариантов работы со строковыми объектами и с текстовыми файлами в целом.
Чтобы написать CSV (значения, разделенные запятыми), мы можем использовать методы записи и чтения:
csv_file.write(header)
# time is time array and data is the dummy numpy array
for time, (a, b, c, d, e) in zip(time, data):
s="%s,%f,%f,%f,%f,%f\n" % (time, a, b, c, d, e) csv_file.write(s)
csv_file.close()
# to read the file, we can use readlines function
content = csv_file.readlines()
Хотя python предоставляет методы для обработки текстовых файлов, у нас есть библиотека pandas, которая может читать и записывать различные форматы данных, и она намного лучше и проще в использовании.
Будь то CSV (значения, разделенные запятыми), SQL (язык структурированных запросов), XLS/XLSX (файлы Microsoft Excel), JSON (нотация объектов JavaScript) или HTML (язык гипертекстовой разметки).
Pandas делает весь процесс чтения и записи файлов CSV более удобным, лаконичным и быстрым.
%time data.to_csv(filename + '.csv')
# CPU times: user 5.59 s, sys: 137 ms, total: 5.69 s
# And to read the files back from the disk
pd.read_csv(<path to the CSV file>)
Базы данных SQL
Python поставляется с поддержкой базы данных SQL, которая называется SQLite3. Мы можем работать практически с любой базой данных (SQL или NoSQL) с помощью Python.
SQL-запросы записываются в виде строковых объектов, где синтаксис и типы данных зависят от используемой базы данных. Иллюстрация создания таблицы Todo с помощью python в базе данных SQLite:
import sqlite3 as sq
# query string to create the table
query = 'CREATE TABLE TODO_NUMBER (Num1 real, Num2 real, Num3 real)'
con = sq.connect(path + 'todo.db')
con.execute(query)
con.commit()
Попробуем вставить некоторые данные в созданную базу данных,
data = np.random.standard_normal((1000000, 3))
%%time
con.executemany('INSERT INTO TODO_NUMBER VALUES (?, ?, ?, ?, ?)', data)
con.commit()
# Time taken: CPU times: user 10.3 s, sys: 316 ms, total: 10.6 s
Wall time: 11 s
Запись 1 миллиона строк в базу данных — немного тяжелая и трудоемкая задача. Чтение базы данных происходит намного быстрее:
con.execute('SELECT * FROM TODO_NUMBER').fetchall()
Если вы имеете дело с большим количеством чисел и массивов в своей базе данных, вы можете использовать массивы Numpy для чтения данных непосредственно в numpy ndarray.
np_query = 'SELECT * FROM TODO_NUMBER WHERE Num1 > 0 AND Num2 < 0'
res = np.array(con.execute(np_query).fetchall()).round(3)
Это очень хороший прием для чтения и построения результатов запроса без лишней суеты. Чтобы сделать чтение более эффективным и оптимизированным, мы должны читать целые таблицы и запрашивать результаты с помощью pandas. Аналитика и обработка становятся намного быстрее, когда вся таблица загружается в память. Это достигается с помощью подбиблиотеки pandas.io.sql
import pandas.io.sql as pds
data_df = pds.read_sql('SELECT * FROM TODO_NUMBERS', con)
Теперь таблица загружается в память, что позволяет выполнять обработку намного быстрее. SQL-запрос, который занимает несколько секунд с SQLite3, завершается за миллисекунды с pandas в памяти:
%time data_df[(data_df['Num1'] > 0) & (data_df['Num2'] < 0)].head()
# CPU times: user 50 ms, sys: 0 ns, total: 50 ms
# Wall time: 49.9 ms
Мы можем справиться с гораздо более сложными запросами с пандами, которые дадут результат намного быстрее, чем SQL, но не могут заменить SQL. Учитывая, что pandas может реплицировать SQL-запрос, мы можем значительно ускорить аналитику, используя обработку в памяти pandas.
Здесь следует отметить, что панды не были созданы для замены базы данных SQL и не могут сделать то же самое в данный момент. Pandas не поддерживает реляционные структуры данных.
Ввод-вывод с PyTables
PyTables — это привязка Python к стандарту базы данных/файла HDF5. Он специально спроектирован и разработан для повышения производительности операций ввода-вывода и максимально эффективного использования доступного оборудования. Он очень хорошо ускоряет аналитику и быстрее генерирует результаты. База данных PyTables может содержать множество таблиц и поддерживает сжатие и индексирование, а также нетривиальные запросы к таблицам.
PyTables имеет файловый формат базы данных. Пройдемся по работе столов,
Это создаст для нас таблицу с необходимыми полями с указанными типами. Теперь давайте заполним базу данных, нам нужно создать несколько случайных значений и записать их построчно в таблицу следующим образом:
Но опять же, у нас есть еще один оптимизированный и Pythonic способ добиться того же результата, используя структурированные массивы NumPy:
dty = np.dtype([('Num1', 'i4'), ('Num2', '<i4')])
sarray = np.zeros(len(ran_int), dtype=dty)
Теперь, когда у нас есть полные данные в нашей таблице, все сводится к созданию таблицы следующим образом:
%%time
h5.create_table('/', 'ints_from_array', sarray,
title="Integers", expectedrows=rows, filters=filters)
Этот подход быстрее, и мы достигаем тех же результатов с меньшим количеством строк кода. Мы можем удалить повторяющуюся таблицу, используя это:
h5.remove_node(‘/’, ‘ints_from_array’)
И pandas, и PyTables имеют возможность обрабатывать сложные SQL-подобные запросы, индексировать и выбирать. Они спроектированы и оптимизированы по скорости операций ввода-вывода.
Основным преимуществом использования PyTables является его способ сжатия. Он использует сжатие не только для экономии места на диске, но и для повышения производительности операций ввода-вывода.
Выводы
Многие области приложений корпоративного уровня в области финансов или науки в целом могут быть успешными только с помощью моделирования данных на основе массивов. В большинстве этих случаев заметные улучшения производительности могут быть достигнуты с помощью комбинации возможностей ввода-вывода NumPy и PyTables. Магазины на основе HDF5 оказались отличительным дополнением ко всем подходам.
У реляционных баз данных есть свои плюсы, если у вас есть сложные структуры данных, которые демонстрируют ряд отношений между отдельными объектами/таблицами. Это может оказаться оправданным в определенных обстоятельствах, когда существует недостаток производительности по сравнению с чистыми подходами на основе NumPy ndarray или pandas на основе DataFrame.