Как оценить, сколько памяти потребуется для DataFrame Pandas?

126

Мне было интересно ... Если я читаю, скажем, файл csv размером 400 МБ в фрейм данных pandas (с использованием read_csv или read_table), есть ли способ предположить, сколько памяти для этого потребуется? Просто пытаюсь лучше понять фреймы данных и память ...

Энн
источник
Вы всегда можете посмотреть на процесс и использование памяти для одного файла. Если вы используете Linux, попробуйте topзатем Shift + Mотсортировать использование моей памяти.
JayQuerie.com 06
Я чувствую, что должен рекламировать эту открытую проблему с пандами .
Энди Хайден
3
У меня большой фрейм данных с 4 миллионами строк. Я обнаружил , что его пустое подмножество x=df.loc[[]]занимает 0.1секунды , чтобы вычислить (для извлечения нулевых строк) и, кроме того, занимает сотню мегабайта памяти, так же , как оригинал dataframe, вероятно , из - за некоторое копирование внизу.
osa
новая ссылка на старый пост от ведущего разработчика pandas
salati

Ответы:

98

df.memory_usage() вернет, сколько занимает каждый столбец:

>>> df.memory_usage()

Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

Чтобы включить индексы, пройдите index=True.

Итак, чтобы получить общее потребление памяти:

>>> df.memory_usage(index=True).sum()
731731000

Кроме того, передача deep=Trueпозволит составить более точный отчет об использовании памяти, который учитывает полное использование содержащихся объектов.

Это связано с тем, что использование памяти не включает память, потребляемую элементами, которые не являются компонентами массива if deep=False(случай по умолчанию).

Алексей Сивоконь
источник
1
действительно ли сумма использований памяти всех столбцов влияет на использование памяти? Могу представить, что накладных расходов будет больше.
firelynx 02
14
Вы действительно тоже хотитеdeep=True
smci
Сумма df.memory_usage () не равна sys.getsizeof (df)! Есть много накладных расходов. Как уже упоминалось в smci, You needdeep=True
vagabond
11
FYI, memory_usage()возвращает использование памяти в байтах (как и следовало ожидать).
engelen
2
Почему такая огромная разница между с / без deep = True?
Нгуай аль
83

Вот сравнение разных методов - самое sys.getsizeof(df)простое.

В данном примере dfэто фрейм данных с 814 строками, 11 столбцами (2 интервала, 9 объектов) - считывается из шейп-файла 427 КБ.

sys.getsizeof (DF)

>>> import sys
>>> sys.getsizeof (df)
(дает результаты в байтах)
462456

df.memory_usage ()

>>> df.memory_usage ()
...
(перечисляет каждый столбец по 8 байтов в строке)

>>> df.memory_usage (). sum ()
71712
(примерно строки * столбцы * 8 байт)

>>> df.memory_usage (deep = True)
(перечисляет полное использование памяти каждого столбца)

>>> df.memory_usage (deep = True) .sum ()
(дает результаты в байтах)
462432

df.info ()

Печатает информацию о фрейме данных в стандартный вывод. Технически это кибибайты (KiB), а не килобайты - как сказано в строке документации: «Использование памяти показано в единицах измерения, удобочитаемых человеком (представление base-2)». Таким образом, чтобы получить байты, нужно умножить на 1024, например 451,6 КиБ = 462 438 байтов.

>>> df.info ()
...
использование памяти: 70.0+ КБ

>>> df.info (memory_usage = 'deep')
...
использование памяти: 451,6 КБ
Брайан Бернс
источник
К какому объекту или модулю относится g приведенный выше код?
zozo
@zozo woops - это была опечатка - исправлено
Брайан Бернс
2
Я использую df.info(memory_usage="deep"), она возвращает «392,6 МБ», в то время как sys.getsizeof(df)и df.memory_usage(index=True, deep=True).sum()как возвращение примерно «411718016» (~ 411MB). Не могли бы вы объяснить, почему 3 результата не совпадают? спасибо
Catbuilts
2
@BrianBurns: df.memory_usage(deep=True).sum()возвращает почти то же самое, что и df.memory_usage(index=True, deep=True).sum(). в моем случае indexне занимает много памяти. Интересно, что я обнаружил, что 411718016/1024/1024 = 392.6поэтому df.info(memory_usage="deep")можно использовать 2^10для преобразования байта в МБ , что меня смущает. В любом случае спасибо за вашу помощь: D.
Catbuilts
1
@Catbuilts Ах, вот и все! df.infoвозвращает мебибайты (2 ^ 10), а не мегабайты (10 ^ 6) - поправим ответ.
Брайан Бернс
43

Я подумал, что внесу еще несколько данных в обсуждение.

Я провел серию тестов по этому вопросу.

Используя resourceпакет python , я получил использование памяти моим процессом.

И, записав csv в StringIOбуфер, я мог легко измерить его размер в байтах.

Я провел два эксперимента, в каждом из которых было создано 20 фреймов данных увеличивающегося размера от 10 000 до 1 000 000 строк. У обоих по 10 столбцов.

В первом эксперименте я использовал в своем наборе данных только числа с плавающей запятой.

Таким образом объем памяти увеличился по сравнению с файлом CSV в зависимости от количества строк. (Размер в мегабайтах)

Память и размер CSV в мегабайтах в зависимости от количества строк с записями с плавающей запятой

Во втором эксперименте у меня был тот же подход, но данные в наборе данных состояли только из коротких строк.

Объем памяти и CSV в мегабайтах как функция количества строк со строковыми записями

Кажется, что соотношение размера csv и размера фрейма данных может сильно различаться, но размер в памяти всегда будет больше в 2-3 раза (для размеров фрейма в этом эксперименте)

Я хотел бы дополнить этот ответ дополнительными экспериментами, прокомментируйте, если вы хотите, чтобы я попробовал что-то особенное.

firelynx
источник
Какая у вас ось Y?
Илья В. Щуров 05
1
max_rss и размер csv на диске в мегабайтах
firelynx 06
31

Вы должны сделать это в обратном порядке.

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')

In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

Технически память об этом (включая индексы)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

Итак, 168 МБ в памяти с файлом 400 МБ, 1 млн строк из 20 столбцов с плавающей запятой.

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')

!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

НАМНОГО компактнее при записи в виде двоичного файла HDF5

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')

In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

Данные были случайными, поэтому сжатие не слишком помогает

Джефф
источник
Это очень умно! Есть идеи, как измерить объем памяти, необходимый для чтения файла read_csv?
Энди Хайден
Не знаю, как измерить КАК вы читаете; IIRC, это может быть в 2 раза больше, чем окончательная память, необходимая для хранения данных (из статьи Уэса), но я думаю, что он сократил ее до постоянной + последней памяти
Джефф
Ах, мне нужно перечитать, я вспомнил, что 2x - это какой-то удобный теоретический минимум для определенного алгоритма, если он еще меньше, это круто.
Энди Хайден
Вы можете использовать iotopлайк top/ htopдля просмотра (в реальном времени) производительности ввода-вывода.
Филлип Клауд
1
nbytesбудет сильно недооценено, если у вас есть, например, строки в фрейме данных.
osa
10

Если вы знаете dtypes вашего массива, вы можете напрямую вычислить количество байтов, которое потребуется для хранения ваших данных + некоторые для самих объектов Python. Полезный атрибут numpyмассивов - это nbytes. Вы можете получить количество байтов из массивов в пандах DataFrame, выполнив

nbytes = sum(block.values.nbytes for block in df.blocks.values())

objectМассивы dtype хранят 8 байтов на объект (массивы dtype объектов хранят указатель на непрозрачный объект PyObject), поэтому, если у вас есть строки в вашем csv, вам нужно принять во внимание, что read_csvпревратит их в objectмассивы dtype и соответствующим образом скорректирует ваши вычисления.

РЕДАКТИРОВАТЬ:

См. numpyСтраницу скалярных типов для получения дополнительных сведений о object dtype. Поскольку сохраняется только ссылка, необходимо также учитывать размер объекта в массиве. Как говорится на этой странице, массивы объектов чем-то похожи на listобъекты Python .

Филипп Клауд
источник
Спасибо, Филипп! Просто чтобы уточнить - для строки нам потребуется 8 байтов для указателя на строковый объект плюс фактический строковый объект?
Энн
1
Да, для любого типа объекта вам понадобится 8-байтовый указатель + размер (объект)
Виктор Керкез, 07
1
Предложите df.blocks.values ​​() Похоже, что df.blocks теперь
диктат
8

Да, есть. Pandas будет хранить ваши данные в двумерных ndarrayструктурах numpy, группируя их по типам. ndarrayв основном представляет собой необработанный массив данных C с небольшим заголовком. Таким образом, вы можете оценить его размер, просто умножив размер, который dtypeон содержит, на размеры массива.

Например: если у вас есть 1000 строк с 2 np.int32и 5 np.float64столбцами, ваш DataFrame будет иметь один np.int32массив 2x1000 и один np.float64массив 5x1000, который:

4 байта * 2 * 1000 + 8 байтов * 5 * 1000 = 48000 байтов

Виктор Керкез
источник
@AndyHayden Что значит стоимость строительства? Размер экземпляра DataFrame?
Филлип Клауд
Спасибо, Виктор! @Andy - Есть идеи, сколько стоит строительство?
Anne
Он не включает, но pandasимеет очень эффективную реализацию read_tableв Cython (это намного лучше, чем loadtxt numpy), поэтому я предполагаю, что он анализирует и сохраняет данные непосредственно в ndarray.
Виктор Керкез 06
@PhillipCloud, вы должны его построить, это требует памяти .. Кажется, я помню, как упоминалось в два раза больше? ...
Энди Хайден,
6

Я считаю, что это дает размер в памяти любого объекта в python. Необходимо проверить внутренности на предмет pandas и numpy

>>> import sys
#assuming the dataframe to be df 
>>> sys.getsizeof(df) 
59542497
Захер Абдул Азиз
источник