🐼

[保存版] pandasのメモリ節約方法まとめ

2023/08/31に公開

pandasのDataFrameで大容量データを扱っていると、すぐにメモリ超過してしまいます。

メモリ超過すると、ついスペック側でメモリを上げたくなってしまいますが、やはり費用が嵩んでしまいます。

しかし、pandasの実装を少し工夫するだけで、使用メモリを大幅に抑えられることが結構あります。

私はこれまでメモリを節約するためにあらゆる方法を試してきましたので、今回はおすすめの手法をまとめてみました!
皆さんのサーバー費用が少しでも抑えられると幸いです。

基本編

カラムの型指定をする

カラムについて、型の指定がないと、数値の場合には自動でint64またはfloat64が付与されてしまいます。int8, int16, int32, float32などを指定することでメモリ削減が可能です。
※指定する型で精度が十分か確認をお願いします!

float64がfloat32になるだけでそのカラムが使用するメモリが半分になるのでこの施策は大きいです。

df = df.astype(
            {
                "id": np.int32,
                "status": np.int8,
                "price": np.int32,
                "weight": np.float32
            }
        )

read_csvする場合は、読み込み時点で型指定しておくと、初めからメモリ使用量を抑えることができます。

df = pd.read_csv(
	"file.csv", 
	dtype={
                "id": np.int32,
                "status": np.int8,
                "price": np.int32,
                "weight": np.float32
            }
    )	

不要な変数を削除して、メモリ解放する

単純ですが、意外と、容量の大きい変数が残っておりメモリを使用したままになっている場合があります。
delで不要な変数を削除することでメモリを開放することができます。
また、gc.collect()を実行することで、使用していないメモリを開放することができます。

del 変数
gc.collect()

不要なカラムを消す、必要なカラムだけにする

上とも通じる内容ですが、計算の途中から不要となったカラムを削除して、メモリを開放できます

del df['column1']
あるいは
df = df.drop(columns=['column1'], axis=1)

read_csvする場合は、そもそも必要なカラムだけを読み込むようにすることもできます。

df = pd.read_csv('file.csv', usecols=['column1', 'column2'])

必要レコードだけ抽出する (不要なレコードを消す)

同様の内容です。必要なレコードだけ抽出することでメモリを節約できます

df = df.loc[df["id"] >= 100000]

実践編

copyの多用を控える

私はDataFrameを複製する場合に参照渡しを嫌ってcopyを多用していましたが、
どうやらcopyした分だけ、メモリを必要としていたようです。。
不要な箇所ではcopyは使わないようにしましょう

df1 = df.copy() # copyした変数分のメモリが新規に消費されてしまう
df2 = df # 参照渡ししても問題ない場合はcopy()を使わない

破壊的代入をする

既存の変数を変更する際、変数名を変えてしまうと、新しい変数ができてしまうので、
極力同じ変数名を使うようにします。

# 非破壊的な例
df3 = df1.loc[df1['id'] >= 10000]
df4 = pd.concat([df3, df2])

# 破壊的な例
df1 = df1.loc[df1['id'] >= 10000]
df1 = pd.concat([df1, df2])

mergeする際は、copy=Falseにする。

デフォルトだとTrueになっており、copyが生成され、メモリを必要以上に使用してしまいます。

df1 = df1.merge(df2, on='column1', how='left', copy=False)

番外編

メモリ使用量の確認

下記のコードで、DataFrameのメモリ使用量を確認することができます。
容量の大きいDataFrameからメモリ節約していくと効率的です。

print(f"df memory usage: {df.memory_usage(deep=True).sum() / 1024 ** 2} MB")

カラム型の確認

知ってる方も多いかと思いますが、dtypesメソッドで、各カラムの型を確認することができます。
メモリ節約したつもりでも、意外と計算過程でint64, float64になっているカラムがあったりします。

print(df.dtypes)

id          int32
status       int8
price       int32
weight    float32
dtype: object

以上となります!
ご指摘など大歓迎です!よろしくお願いします!!

参考文献

https://qiita.com/ninomiyt/items/2e3fb2569427312f28cc

O-KUN Tech Blog

Discussion