Панды с кофеином, часть 2 из 4

Оптимизация хранения в Pandas: на 98% быстрее чтение с диска и на 72% меньше места

В своей серии Введение я описал четырех убийц производительности, которые замедляли развитие моего портфеля акций и финансовой модели: память, диск, процессор и эффективность кодирования. Собранные мной решения должны помочь любому, кто работает с Python и Pandas, независимо от дисциплины. В этой статье содержится исправление второй проблемы…

Слишком долгое чтение и запись на диск
Я строю модели, используя ежедневные данные о котировках акций. Недавняя полная загрузка базы данных котировок, на которую я подписан, дала около 50 миллионов строк, которые занимали 3,3 ГБ в виде файла .csv и 1,08 ГБ в формате .zip’d.

Открытие файла .csv или .zip и загрузка в Pandas занимает почти 2 минуты. Для меня ожидание пары минут каждый раз, когда я хочу запустить модель, является болезненным для уровня 8 по шкале от 1 до 10.

Но экономия еще хуже! Если я добавлю в файл улучшения, сохранение в .csv увеличит уровень боли до 10, поскольку для сохранения потребуется более 6 минут. Сохранение в файл .zip занимает около 11 минут. Передайте, пожалуйста, морфий!

Что, если ... тот же самый набор данных занял всего 2 секунды для чтения и всего 2 секунды для повторного сохранения? Правильно, время чтения сократилось на 97,9%, а время записи - на 99,3%! Добавьте к этому уменьшение размера файла на 72%!

Тестирование форматов файлов
Я протестировал 9 различных форматов файлов, которые легко поддерживаются Python и Pandas.

Я сначала дисквалифицировал несколько форматов просто потому, что они не справлялись с задачей для такого большого количества данных. Например, формат Excel отлично подходит для написания экстрактов Dataframe для бизнес-пользователей, но он может хранить только 1 миллион строк. Другим плохим исполнителем был формат JSON - из-за большого количества данных и избыточности меток он фактически не работал на моем компьютере.

.csv - это простой текстовый формат файла с разделенными запятыми значениями, который широко используется для обмена данными между системами. Практически любая система может импортировать и экспортировать файлы .csv. Хотя это не самый гибкий и эффективный формат хранения файлов, он является универсальным стандартом.

Обратной стороной формата .csv является то, что он хранит все данные в текстовом, удобочитаемом формате, тогда как более экономичные методы хранят данные в двоичных, нечитаемых человеком форматах. В этом есть большая разница: число 1 000 000 000 занимает 10 символов, что требует 10 байтов памяти в формате .csv, в то время как такое же число в двоичном формате занимает всего 4 байта памяти.

.csv zip’d - это файл .csv, сжатый с помощью протокола сжатия .zip, который для стандартных данных уменьшает размер файла примерно до 25% от исходного. Преимущество файла .zip’d в том, что он занимает меньше места на диске, а если это удаленный файл, то его загрузка занимает меньше времени.

.parquet - это формат файла, разработанный в 2013 году как проект с открытым исходным кодом между Twitter и Cloudera. В то время как файл .csv обрабатывает и хранит данные по строкам, Parquet обрабатывает и сохраняет по столбцам, а также может разбивать данные на относительно небольшие файлы. Это эффективно, поскольку столбцы текста могут быть сжаты иначе, чем столбцы чисел, не все данные должны быть считываться и возвращаться, если требуются только определенные столбцы данных, а файлы могут быть распределены по разным дискам. Однако как проект с открытым исходным кодом Parquet принял несколько форм и методов. В приведенном ниже примере сценария используется вариант Parquet под названием «Стрелка». Другой популярный вариант - FastParquet, который, как ни странно, оказался медленнее.

.hdf5 / .h5 - это «формат иерархических данных», созданный Национальным центром суперкомпьютерных приложений. Преимущество формата заключается в том, что он может хранить множество различных типов данных в единой структуре. Поэтому, если вы хотите хранить 100 000 изображений и их метаданные о местоположении в структуре, подобной папке, вы можете сделать все это в одном месте. В моем тесте я буду использовать его только для сохранения одного файла данных, поэтому я не буду использовать все возможности этого формата.

.pickle / .pkl - это специфичная для Python функция, которая сохраняет объекты Python в файлы. Соленья пригодятся, когда вы запускаете программу и хотите сохранить промежуточный список или словарь в файл для использования в будущем. По большей части, Pickles используется с относительно небольшими объектами данных, но мы все равно их протестируем. Одно предупреждение: не гарантируется, что Pickles будет работать в разных версиях Python, поэтому для ваших собственных процессов это нормально, но совместное использование может вызвать проблемы.

.feather - это проект, созданный Уэсом МакКинни, создателем Pandas, и Хэдли Уикхэмом, главным научным сотрудником RStudio (как на статистическом языке R). Обе платформы популярны среди специалистов по обработке данных, и сотрудничество было создано для разработки быстрого и эффективного формата файлов, который можно было бы полностью переносить между ними. Файловая структура аналогична столбчатой ​​файловой структуре Parquet.

.sqlite - это небольшая база данных, в которой много всего интересного. В отличие от полноценной системы управления базами данных, здесь не нужно ничего устанавливать - несколько строк Python и Pandas создают файл базы данных, записывают Dataframe в таблицу в базе данных, и готово! Обратной стороной является то, что довольно медленно сохранять большие наборы данных, а также относительно медленно получать полную таблицу. Однако запросы, которые возвращают лишь небольшую часть базы данных, выполняются молниеносно, поэтому, если это то, что вам нужно, это может быть хорошей альтернативой. Это также гораздо лучший вариант, чем .csv, если вам нужно передать большой объем данных в такие инструменты, как Tableau или PowerBI.

.json - это формат, широко используемый для обмена данными между разнородными серверными процессами, и он отлично подходит для этой цели. Из-за избыточности меток и данных, а также отсутствия сжатия, на самом деле это худший формат для хранения и извлечения больших объемов данных. Фактически, попытка сохранить более 50 миллионов строк в моих тестовых данных вылетела через несколько минут, предположительно из-за того, что использовалась вся доступная оперативная память и свопинг.

.xlsx - это формат Microsoft Excel, популярный в бизнес-среде. Pandas позволяет легко сохранять один или несколько фреймов данных в виде рабочих листов. Я часто использую это, чтобы делиться сводными данными с коллегами, которые затем создают свои собственные информационные панели и диаграммы. Однако для большого набора данных это недопустимо, поскольку ограничение в книге составляет 1 048 576 строк.

Сжатие
При изучении различных функций Pandas для чтения и записи данных в документации было предложено множество схем сжатия для каждой из них. С милыми именами, такими как «zip», «gzip», «bzip2», «zlib», «snappy» и «brotli», до более загадочных имен, таких как «zstd», «lz4», «lz4hc». «Lzo», «blosc», «blosclz», «xz» и «bz2», действительно замечательно видеть инновации, которые очевидны в сообществах Open Source в области науки о данных!

Тесты
Итак ... тестируем файл на вкус!

Вы можете найти полный сценарий создания образца данных и тестирования файла run_file_storage_tests.py в моем репозитории на GitHub.

Сценарий использует несколько ключевых функций из caffeinated_pandas_utils.py, которые вы найдете в том же репозитории: read_file() и write_file() объединяют форматы, протестированные ниже, поэтому вы можете сразу увидеть, как реализовать каждый формат.

Сценарий состоит из трех этапов.

Шаг 1. Создайте сценарий тестирования по времени
Я создал функцию, которая записывает Dataframe в тип файла, указанный в расширении имени файла (например, «.csv»), а затем считывает файл обратно в Dataframe. Каждый шаг рассчитывается по времени, и размер Dataframe на каждом шаге записывается. Затем я применяю серию операторов select и sort и записываю время каждого из них.

Каждый шаг выполняется трижды, как указано в iterations=line. Я читал, что кэширование и управление памятью операции могут повлиять на время выполнения, поэтому выполнение нескольких раз и усреднение результата является более правильным тестом. На самом деле я не заметил особых различий, но оставлю это на всякий случай.

Затем, чтобы убедиться, что промежуточные фреймы данных полностью удалены из памяти перед следующим тестом, я выполняю явные удаления, а затем выполняю небольшую сборку мусора.

Как вы увидите, процедура тестирования вызывает функции read_file() и write_file(), в которых я объединил многие (не все) функции, которые Pandas предоставляет для чтения файлов в фреймы данных и из них. Большинство форматов также предлагают параметры сжатия, специфичные для формата. Если вас интересует полный спектр форматов и параметров сжатия, которые предоставляет Pandas, обратитесь к документации.

Шаг 2. Загрузите некоторые данные
Если вы запустили демонстрационный скрипт в первой статье, у вас будут нужные данные в виде файла .csv. Если вы не запускали демонстрационный скрипт, но пропустили эту статью, просто раскомментируйте create_test_dataframe параметры. Если у вас есть собственные данные для тестирования, обязательно сделайте это - просто загрузите их в Dataframe, как это сделано ниже.

Как я упоминал ранее, мой Dataframe содержит более 50 миллионов строк, но я предлагаю вам начать с меньшего числа с num_symbols=100 и постепенно увеличивать его. Убедитесь, что программа работает до завершения, а затем выполните ее настройку в соответствии с конфигурацией вашего компьютера. Полный прогон с 50 миллионами строк займет 8–10 часов с 3 итерациями.

Шаг 3. Запустите тесты
Я создал 20 тестов с различными форматами файлов и применимыми схемами сжатия. Я пропустил .xlsx, потому что он составляет максимум 1 миллион строк, а форматы и .json съели всю мою память и разбились!

Шаг 4. Прочтите результаты
После каждого теста результаты отображаются на экране. В дополнение к таймингу каждого шага представлены размеры Dataframe в памяти и на диске. Я обобщил результаты в таблице ниже. Красным цветом отмечены три худших участника, а зеленым жирным шрифтом - три лучших.

Образец Dataframe в начале программы составлял 20,33 ГБ. После запуска функции utils.squeeze_dataframe(), разработанной в первой статье, Dataframe был уменьшен до 2,24 ГБ.

Но, как видно из диаграммы, файлы .csv / .zip не сохраняют сжатие при сохранении и считывании, учитывая текстовую природу форматов файлов. Таким образом, вам нужно повторно сжимать его каждый раз, когда вы читаете, что делает медленное форматирование еще медленнее.

И победителями становятся…
В 22 тестах, о которых говорилось выше, есть несколько сильных решений:

Parquet со сжатием brotli выигрывает при оптимизации для дискового пространства. Размер файла вдвое меньше размера файлов Feather, но немного отстает от Feather по времени чтения и записи (хотя он вполне прилично).

Feather со сжатием zstd или lz4 выигрывает при оптимизации времени чтения и записи. Время быстрее, чем у Parquet, но файлы Feather в два раза больше Parquet (хотя, все еще достаточно малы).

Итак, с точки зрения выбора реального «победителя», это действительно зависит от ваших целей.

Я нетерпелив, поэтому выберу скорость и воспользуюсь любым из решений Feather.

Если вы храните данные о тиках или опциях за годы локально, Parquet + brotli может быть лучшим решением. Или, если вы храните файлы в облаке с помощью такой службы, как AWS S3, которая взимает плату в зависимости от используемого дискового пространства, лучше использовать файлы меньшего размера.

В моей следующей статье Многопроцессорная обработка Pandas - улучшения Dataframe на 46-95% быстрее вы узнаете, как использовать всю вычислительную мощность вашего компьютера.

Не только для акций
Хотя в этой серии статей я явно сосредоточен на котировках акций, эти принципы, безусловно, будут работать с любыми столбчатыми данными на основе Pandas для всех типов моделей и анализа. .

Почему я написал и опубликовал это
Мне потребовалось много времени из-за множества ложных запусков, чтобы добраться до того момента, когда я могу уверенно обрабатывать большие массивы данных. Вступая в это, я подумал, что хотел бы об этом написать. Просто зная, что его будут читать другие, я значительно улучшил свой код и его производительность, иногда на порядок, когда искал «лучшие» способы. Также, поделившись информацией, я надеюсь, что другие дадут конструктивную оценку тому, что я мог бы сделать лучше!

Спасибо за чтение панд с кофеином! ПОДПИСАТЬСЯ НА МЕНЯ, если вы хотите получать уведомления о новом содержании.

Больше контента на plainenglish.io