다음과 같이 팬더에서 연 정말 큰 CSV 파일이 있습니다 ….
import pandas
df = pandas.read_csv('large_txt_file.txt')
이렇게하면 메모리 사용량이 2GB만큼 증가하는데, 이는이 파일에 수백만 개의 행이 포함되어 있기 때문입니다. 이 메모리를 해제해야 할 때 문제가 발생합니다. 달렸어 ….
del df
그러나 내 메모리 사용량은 떨어지지 않았습니다. 팬더 데이터 프레임에서 사용하는 메모리를 해제하는 잘못된 접근 방식입니까? 그렇다면 올바른 방법은 무엇입니까?
답변
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')
객체를 포함한 메모리 사용량을 볼 수도 있습니다.
답변
댓글에서 언급했듯이 시도 할 몇 가지가 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는 사용한 모든 리소스를 다시 가져옵니다. 파이썬, 팬더, 가비지 컬렉터가 그것을 막을 수있는 일은 정말 없습니다.
답변
이것은 나를 위해 메모리를 해제하는 문제를 해결합니다 !!!
del [[df_1,df_2]]
gc.collect()
df_1=pd.DataFrame()
df_2=pd.DataFrame()
데이터 프레임은 명시 적으로 null로 설정됩니다.
답변
del df
삭제시에 대한 참조가있는 경우 삭제되지 않습니다 df
. 따라서 del df
메모리를 해제 하려면 모든 참조를 삭제해야합니다 .
따라서 가비지 수집을 트리거하려면 df에 바인딩 된 모든 인스턴스를 삭제해야합니다.
objgragh 를 사용 하여 개체를 잡고있는 것을 확인합니다.
답변
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)
답변
