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
パンダ0.17.1以降、df.info(memory_usage='deep')
オブジェクトを含むメモリ使用量を確認することもできます。
gc
ます。モジュールをインポートして呼び出すこともできますがgc.collect()
、メモリを回復できない可能性があります