Эффективное хранение строк в Pandas 1.3

автор рубрика ,
Эффективное хранение строк в Pandas 1.3

Библиотека Pandas незаменима для подготовки данных на Python. При загрузке и хранение строк в Pandas используется много памяти. В таком случае можно воспользоваться категориальным типом данных (Categorical), однако он помогает не во всех ситуациях. В Pandas 1.3 появилась возможность экономить память при загрузке строк, изменяя только тип соответствующего столбца на Arrow. Об этом мы расскажем в этой статье.

Что за новый тип данных в Pandas

Столбец представляет собой pandas.Series, у которого есть поле dtype — тип данных хранимого объекта. По умолчанию Pandas хранит строки под типом данных object. Такой тип данных подразумевает хранение строк в виде NumPy-массивов указателей на обычные объекты Python.

В Pandas 1.0 появился новый тип string, но он не сильно повлиял на объем используемой памяти, занимаемой строками. И вот в Pandas 1.3 был добавлен стрелочный тип данных (Arrow), для строк он определяется как string[pyarrow] [1].
Arrow — это тип данных для хранения столбчатых представлений, т.е. как раз тех, которые используются в Pandas. Для нас важно то, что этот тип данных поддерживает эффективное хранение строк.

Как использовать тип Arrow для строковых данных

Сравним используемую память всех трех типов данных. Мы будем хранить столбец из миллиона строк, каждая из которых будет состоять из случайных цифр (до 18 цифр) с добавлением некого префикса. Данный префикс мы будем добавлять в виде аргумента командной строки. Для измерения используемой памяти в байтах воспользуемся методом memory_usage, который будет вызываться для каждого из столбцов с заданным типом данных. Итак, наш скрипт на Python выглядит следующим образом:

import sys
import pandas as pd
from random import random

prefix = sys.argv[1]

# Список строк со случайными цифрами, например, 0.983641272314
random_strings = [prefix + str(random()) for i in range(1_000_000)]

# Тип данных `object'
object_dtype = pd.Series(random_strings)
print("object", object_dtype.memory_usage(deep=True))

# Тип данных `string'
standard_dtype = pd.Series(random_strings, dtype="string")
print("string", standard_dtype.memory_usage(deep=True))

# Тип данных `arrow'
arrow_dtype = pd.Series(random_strings, dtype="string[pyarrow]")
print("arrow ", arrow_dtype.memory_usage(deep=True))

В качестве префикса пока ничего не добавим, поэтому строки будут состоять только из цифр и точки. В результате мы получили результаты:

$ python pandas_save.py ""
object 75270865
string 75270865
arrow  22270869

Как видим, стрелочный тип данных занимает примерно 22 МБ против 75 МБ у двух других.

Работает ли Arrow для длинных строк

Python при хранение строк, вдобавок хранит ещё некоторые метаданные, что как раз таки и занимает пространство памяти. Мы даже можем измерить размер объекта в Python:

>>> import sys
>>> sys.getsizeof("")
49
>>> sys.getsizeof("0.9836412723144355")
67

Для миллиона строк, использованная память равняется сумме самих строк (67 * 1.000.000) и NumPy-массиву указателей (8 * 1.000.000):

>>> (67 + 8) * 1_000_000
75000000

— что очень близко к значению 75.270.865.

Хранение символа ACSII занимает 1 байт. Тогда для 18 таких символов получаются 18 байтов, в отличие от 67 байтов. Хотя полностью получить только 18 байтов для каждой строки не получается, выходит на 4 байта больше:

>>> 22270869 / 1_000_000
22.270869

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

$ python pandas_save.py "XXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \

object 442269336
string 442269336
arrow  389269340

— и преимущество становится не таким существенным. Тем не менее, оно есть, и почему бы им не воспользоваться. Поэтому рекомендуем обновиться до Pandas 1.3.

Ещё больше подробностей о работе с данными, способами их хранении и обработки на реальных примерах интеллектуального анализа данных (Data Mining) вы узнаете на специализированном курсе по машинному обучению «DPREP: Подготовка данных для Data Mining на Python» в лицензированном учебном центре обучения и повышения квалификации разработчиков, менеджеров, архитекторов, инженеров, администраторов, Data Scientist’ов и аналитиков Big Data в Москве.

Источники
  1. Релиз 1.3
Комментировать