web-dev-qa-db-ja.com

パンダのデータフレームで使用されているメモリを解放するにはどうすればよいですか?

私はパンダで次のように開いた本当に大きなcsvファイルを持っています...

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

これを行うと、メモリ使用量が2GB増加します。これは、このファイルに数百万行が含まれているためです。この問題は、このメモリを解放する必要があるときに発生します。私は走った....

del df

ただし、メモリ使用量は低下しませんでした。これはパンダのデータフレームで使用されているメモリを解放する間違ったアプローチですか?もしそうなら、適切な方法は何ですか?

65
b10hazard

Pythonは実際にメモリをオペレーティングシステムに解放しない であるため、Pythonのメモリ使用量を減らすことは困難です。オブジェクトを削除すると、メモリは新しいPythonオブジェクトで使用できますが、free()でシステムに戻せません( この質問を参照 )。

数値のnumpy配列に固執する場合、それらは解放されますが、ボックス化されたオブジェクトは解放されません。

>>> 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

データフレーム数の削減

Pythonはメモリを最高水準に保ちますが、作成するデータフレームの総数を減らすことができます。データフレームを変更するときは、inplace=Trueを優先して、コピーを作成しないようにしてください。

もう1つの一般的な落とし穴は、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')を実行して、オブジェクトを含むメモリ使用量を確認することもできます。

71
Wilfred Hughes

コメントに記載されているように、試してみるべきことがいくつかあります。たとえば、gc.collect(@EdChum)は内容をクリアします。少なくとも私の経験から、これらのことは時々機能し、しばしば機能しません。

ただし、言語レベルではなくOSレベルで実行されるため、常に機能することが1つあります。

中間の巨大な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は使用したすべてのリソースを取り戻します。 Python、パンダ、ガベージコレクターがそれを止めるためにできることは本当に何もありません。

22
Ami Tavory

これは私のためにメモリを解放する問題を解決します!!!

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

データフレームは明示的にnullに設定されます

8
hardi

削除時にdfへの参照がある場合、del dfは削除されません。したがって、メモリを解放するには、del dfを使用してすべての参照を削除する必要があります。

したがって、ガベージコレクションをトリガーするには、dfにバインドされているすべてのインスタンスを削除する必要があります。

objgragh を使用して、オブジェクトを保持しているものを確認します。

1
Marlon Abeykoon