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

import pandas
df = pandas.read_csv('large_txt_file.txt')

Как только я это сделаю, мое использование памяти увеличится на 2 ГБ, что и ожидается, потому что этот файл содержит миллионы строк. Моя проблема возникает, когда мне нужно освободить эту память. Я побежал ....

del df

Однако использование моей памяти не упало. Это неправильный подход к освобождению памяти, используемой фреймом данных pandas? Если да, то каков правильный путь?

b10hazard

Ответов: 6

Ответы (6)

Это решает для меня проблему освобождения памяти !!!

import gc
import pandas as pd

del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()

фрейм данных будет явно установлен на null

в приведенных выше утверждениях

Во-первых, собственная ссылка фрейма данных удаляется, что означает, что фрейм данных больше не доступен для python после того, как все ссылки фрейма данных собираются сборщиком мусора (gc.collect ()), а затем явно устанавливаются все ссылки на пустой фрейм данных.

больше о работе сборщика мусора хорошо объясняется в https://stackify.com/python-garbage-collection/

Как отмечено в комментариях, есть кое-что, что можно попробовать: gc.collect (@EdChum) может, например, очистить материал. По крайней мере, по моему опыту, эти вещи иногда работают, а часто - нет.

Есть одна вещь, которая работает всегда, потому что это делается на уровне ОС, а не языка.

Предположим, у вас есть функция, которая создает промежуточный огромный DataFrame и возвращает меньший результат (который также может быть DataFrame):

def huge_intermediate_calc(something):
    ...
    huge_df = pd.DataFrame(...)
    ...
    return some_aggregate

Тогда если вы сделаете что-то вроде

import multiprocessing

result = multiprocessing.Pool(1).map(huge_intermediate_calc, [something_])[0]

Тогда функция выполняется в другом процессе. Когда этот процесс завершается, ОС забирает все использованные ресурсы. На самом деле Python, pandas, сборщик мусора, ничего не могут сделать, чтобы остановить это.

Уменьшить использование памяти в Python сложно, потому что Python фактически не возвращает память операционной системе. Если вы удаляете объекты, тогда память доступна для новых объектов Python, но не free ()'d обратно в систему (см. Этот вопрос).

Если вы придерживаетесь числовых массивов numpy, они освобождаются, а упакованные объекты - нет.

>>> import os, psutil, numpy as np # psutil may need to be installed
>>> def usage():
...     process = psutil.Process(os.getpid())
...     return process.memory_info()[0] / float(2 ** 20)
... 
>>> usage() # initial memory usage
27.5 

>>> arr = np.arange(10 ** 8) # create a large array without boxing
>>> usage()
790.46875
>>> del arr
>>> usage()
27.52734375 # numpy just free()'d the array

>>> arr = np.arange(10 ** 8, dtype='O') # create lots of objects
>>> usage()
3135.109375
>>> del arr
>>> usage()
2372.16796875  # numpy frees the array, but python keeps the heap big

Уменьшение количества фреймов данных

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

Еще одна распространенная проблема - это сохранение копий ранее созданных фреймов данных в ipython:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'foo': [1,2,3,4]})

In [3]: df + 1
Out[3]: 
   foo
0    2
1    3
2    4
3    5

In [4]: df + 2
Out[4]: 
   foo
0    3
1    4
2    5
3    6

In [5]: Out # Still has all our temporary DataFrame objects!
Out[5]: 
{3:    foo
 0    2
 1    3
 2    4
 3    5, 4:    foo
 0    3
 1    4
 2    5
 3    6}

Вы можете исправить это, набрав % reset Out, чтобы очистить историю. В качестве альтернативы вы можете настроить объем истории ipython с помощью ipython --cache-size = 5 (по умолчанию 1000).

Уменьшение размера фрейма данных

По возможности избегайте использования типов объектов.

>>> df.dtypes
foo    float64 # 8 bytes per value
bar      int64 # 8 bytes per value
baz     object # at least 48 bytes per value, often more

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

В то время как numpy поддерживает строки фиксированного размера в массивах, pandas - нет (это вызвало путаницу у пользователей). Это может иметь большое значение:

>>> import numpy as np
>>> arr = np.array(['foo', 'bar', 'baz'])
>>> arr.dtype
dtype('S3')
>>> arr.nbytes
9

>>> import sys; import pandas as pd
>>> s = pd.Series(['foo', 'bar', 'baz'])
dtype('O')
>>> sum(sys.getsizeof(x) for x in s)
120

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

Если у вас есть фрейм данных, который содержит много повторяющихся значений (NaN очень распространено), вы можете использовать разреженную структуру данных, чтобы уменьшить использование памяти:

>>> df1.info()

Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo    float64
dtypes: float64(1)
memory usage: 605.5 MB

>>> df1.shape
(39681584, 1)

>>> df1.foo.isnull().sum() * 100. / len(df1)
20.628483479893344 # so 20% of values are NaN

>>> df1.to_sparse().info()

Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo    float64
dtypes: float64(1)
memory usage: 543.0 MB

Просмотр использования памяти

Вы можете просмотреть использование памяти (документы):

>>> df.info()

Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 14 columns):
...
dtypes: datetime64[ns](1), float64(8), int64(1), object(4)
memory usage: 4.4+ GB

Начиная с pandas 0.17.1, вы также можете выполнить df.info (memory_usage = 'deep'), чтобы увидеть использование памяти, включая объекты.

Кажется, есть проблема с glibc, которая влияет на выделение памяти в Pandas: https://github.com/pandas-dev/pandas/issues/2659

Патч обезьяны , подробно описанный по этой проблеме решил проблему для меня:

# monkeypatches.py

# Solving memory leak problem in pandas
# https://github.com/pandas-dev/pandas/issues/2659#issuecomment-12021083
import pandas as pd
from ctypes import cdll, CDLL
try:
    cdll.LoadLibrary("libc.so.6")
    libc = CDLL("libc.so.6")
    libc.malloc_trim(0)
except (OSError, AttributeError):
    libc = None

__old_del = getattr(pd.DataFrame, '__del__', None)

def __new_del(self):
    if __old_del:
        __old_del(self)
    libc.malloc_trim(0)

if libc:
    print('Applying monkeypatch for pd.DataFrame.__del__', file=sys.stderr)
    pd.DataFrame.__del__ = __new_del
else:
    print('Skipping monkeypatch for pd.DataFrame.__del__: libc or malloc_trim() not found', file=sys.stderr)

del df не будет удален, если во время удаления есть ссылка на df. Поэтому вам нужно удалить все ссылки на него с помощью del df, чтобы освободить память.

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

Используйте objgragh, чтобы проверить, какие объекты удерживаются.

Вот что я делаю для решения этой проблемы.

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

Вместо того, чтобы считывать наборы данных в отдельные переменные фреймов данных, считайте их в словарь фреймов данных.

df_file_contents [имя_файла] = pd.read_csv (..)

Для очистки словаря был предоставлен API-интерфейс. Это вызывает метод словаря clear (). Его можно настроить для вызова, когда sys.getsizeof (df_file_contents) имеет определенный размер, или его можно использовать для удаления определенных ключей.

df_file_contents.clear ()

2022 WebDevInsider