Pandas 데이터 프레임에서 사용하는 메모리를 어떻게 해제합니까?


111

다음과 같이 팬더에서 연 정말 큰 CSV 파일이 있습니다 ....

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

이렇게하면 메모리 사용량이 2GB만큼 증가하는데, 이는이 파일에 수백만 개의 행이 포함되어 있기 때문입니다. 이 메모리를 해제해야 할 때 문제가 발생합니다. 달렸어 ....

del df

그러나 내 메모리 사용량은 떨어지지 않았습니다. 팬더 데이터 프레임에서 사용하는 메모리를 해제하는 잘못된 접근 방식입니까? 그렇다면 올바른 방법은 무엇입니까?


3
올바른지, 바로 메모리를 해제하지 않을 수 있습니다 가비지 컬렉터은 또한 가져올 수있는 gc모듈을하고 전화를 gc.collect()하지만 메모리 복구 할 수 있습니다
EdChum

del dfdf 생성 후 직접 호출되지 않습니까? df를 삭제할 때 df에 대한 참조가 있다고 생각합니다. 따라서 삭제되지 않고 대신 이름이 삭제됩니다.
Marlon Abeykoon

4
가비지 수집기에 의해 회수 된 메모리가 실제로 OS에 반환되는지 여부는 구현에 따라 다릅니다. 가비지 수집기가 만드는 유일한 보장은 현재 Python 프로세스 에서 OS에서 더 많은 메모리를 요청하거나 더 많은 메모리를 요청하는 대신 다른 작업을 위해 회수 된 메모리를 사용할 수 있다는 것 입니다.
chepner

나는 창조 직후에 del df를 부르고 있습니다. df에 대한 다른 참조를 추가하지 않았습니다. 내가 한 것은 ipython을 열고 그 세 줄의 코드를 실행하는 것뿐이었습니다. numpy 배열과 같이 많은 메모리를 사용하는 다른 객체에서 동일한 코드를 실행하면. 델의 nparray 완벽하게 작동
b10hazard

@ b10hazard : df = ''코드 끝 부분 에있는 것과 같은 것은 어떻습니까? 데이터 프레임에서 사용하는 RAM을 지우는 것 같습니다.
jibounet

답변:


119

Python에서 실제로 메모리를 운영 체제로 다시 릴리스하지 않기 때문에 Python 에서 메모리 사용량을 줄이는 것은 어렵습니다 . 객체를 삭제하면 새 Python 객체에서 메모리를 사용할 수 있지만 free()시스템으로 돌아가지는 않습니다 ( 이 질문 참조 ).

숫자가 많은 배열을 고수하면 해제되지만 박스형 객체는 해제되지 않습니다.

>>> import os, psutil, numpy as np
>>> def usage():
...     process = psutil.Process(os.getpid())
...     return process.get_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

데이터 프레임 수 줄이기

파이썬은 우리의 메모리를 하이 워터 마크로 유지하지만 우리가 만드는 총 데이터 프레임 수를 줄일 수 있습니다. 데이터 프레임을 수정할 때를 선호 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).

데이터 프레임 크기 줄이기

가능하면 개체 dtype을 사용하지 마십시오.

>>> 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()
<class 'pandas.core.frame.DataFrame'>
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()
<class 'pandas.sparse.frame.SparseDataFrame'>
Int64Index: 39681584 entries, 0 to 39681583
Data columns (total 1 columns):
foo    float64
dtypes: float64(1)
memory usage: 543.0 MB

메모리 사용량보기

메모리 사용량을 볼 수 있습니다 ( docs ).

>>> df.info()
<class 'pandas.core.frame.DataFrame'>
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')객체를 포함한 메모리 사용량을 볼 수도 있습니다.


2
이것은 'Accepted Answer'로 표시되어야합니다. 간단하지만 명확하게 파이썬이 실제로 필요하지 않을 때에도 어떻게 메모리를 유지하는지 설명합니다. 메모리 절약을위한 팁은 모두 현명하고 유용합니다. 또 다른 팁으로 '다중 처리'를 사용하여 추가합니다 (@Ami의 답변에 설명 됨).
pedram bashiri

46

댓글에서 언급했듯이 시도 할 몇 가지가 gc.collect있습니다. 예를 들어 (@EdChum)은 항목을 지울 수 있습니다. 적어도 내 경험에 비추어 볼 때 이러한 것들은 때때로 작동하지만 종종 작동하지 않습니다.

그러나 항상 작동하는 것이 한 가지 있습니다. 언어 수준이 아닌 OS 수준에서 수행되기 때문입니다.

중간 거대한 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]

그런 다음 함수는 다른 프로세스에서 실행됩니다 . 해당 프로세스가 완료되면 OS는 사용한 모든 리소스를 다시 가져옵니다. 파이썬, 팬더, 가비지 컬렉터가 그것을 막을 수있는 일은 정말 없습니다.


1
@ b10hazard 팬더가 없어도 실제로 파이썬 메모리가 어떻게 작동하는지 완전히 이해하지 못했습니다. 이 조잡한 기술은 내가 의지하는 유일한 것입니다.
Ami Tavory

9
정말 잘 작동합니다. 그러나 ipython 환경 (예 : jupyter 노트북)에서 생성 된 프로세스를 제거하려면 풀을 .close () 및 .join () 또는 .terminate ()해야한다는 것을 알았습니다. Python 3.3 이후 가장 쉬운 방법은 컨텍스트 관리 프로토콜을 사용하는 with multiprocessing.Pool(1) as pool: result = pool.map(huge_intermediate_calc, [something])것입니다. 완료되면 풀을 닫습니다.
Zertrin

2
이것은 잘 작동합니다. 작업이 완료된 후 풀 종료 및 참여를 잊지 마십시오.
Andrey Nikishaev

1
파이썬 객체에서 메모리를 되 찾는 방법에 대해 여러 번 읽은 후 이것이 가장 좋은 방법 인 것 같습니다. 프로세스를 만들고 해당 프로세스가 종료되면 OS가 메모리를 해제합니다.
muammar

1
풀을 만들 때 프로세스를 해제하고 작업이 완료된 후 새 프로세스를 생성하기 위해 maxtasksperchild = 1을 사용하는 것이 도움이 될 수 있습니다.
giwiro

22

이것은 나를 위해 메모리를 해제하는 문제를 해결합니다 !!!

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

데이터 프레임은 명시 적으로 null로 설정됩니다.


1
하위 목록 [[df_1, df_2]]에 데이터 프레임이 추가 된 이유는 무엇입니까? 특별한 이유가 있습니까? 설명 해주십시오.
goks

5
마지막 두 문장을 사용하지 않는 이유는 무엇입니까? 처음 두 문장은 필요하지 않다고 생각합니다.
spacedustpi

3

del df삭제시에 대한 참조가있는 경우 삭제되지 않습니다 df. 따라서 del df메모리를 해제 하려면 모든 참조를 삭제해야합니다 .

따라서 가비지 수집을 트리거하려면 df에 바인딩 된 모든 인스턴스를 삭제해야합니다.

objgragh 를 사용 하여 개체를 잡고있는 것을 확인합니다.


objgraph (링크 포인트 mg.pov.lt/objgraph는 objgragh가없는), 그것은 당신의 대답에 오타입니다
SATZ

1

Pandas의 메모리 할당에 영향을 미치는 glibc에 문제가있는 것 같습니다 : 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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.