pandas のカテゴリ型でメモリを節約する

pandas でカテゴリデータを扱うとき、文字列のまま保持するとメモリを大量に消費します。category 型を使えば、メモリ使用量を大幅に削減できます。

文字列とカテゴリ型のメモリ比較

まず、どれくらい違うか確認してみましょう。

import pandas as pd
import numpy as np

# 100万行のデータを作成
n = 1_000_000
df = pd.DataFrame({
    'status': np.random.choice(['active', 'inactive', 'pending'], n)
})

# 文字列型のメモリ使用量
print(f"object型: {df['status'].memory_usage(deep=True) / 1024 / 1024:.2f} MB")

# カテゴリ型に変換
df['status'] = df['status'].astype('category')
print(f"category型: {df['status'].memory_usage(deep=True) / 1024 / 1024:.2f} MB")

結果は環境によりますが、文字列型で約 60MB だったものが、カテゴリ型では 1MB 以下になることもあります。値の種類が少ないほど効果が大きくなります。

カテゴリ型の仕組み

カテゴリ型は、内部的に整数コードとカテゴリ名のマッピングを持っています。

元データ: [‘active’, ‘inactive’, ‘active’, ‘pending’]

カテゴリ: {0: ‘active’, 1: ‘inactive’, 2: ‘pending’}

整数コード: [0, 1, 0, 2]

文字列を繰り返し保持する代わりに、短い整数コードだけを保持するため、メモリ効率がよくなります。

カテゴリ型への変換方法

既存のデータをカテゴリ型に変換するには astype('category') を使います。

df['status'] = df['status'].astype('category')

データ読み込み時に指定することもできます。

df = pd.read_csv('data.csv', dtype={'status': 'category'})

カテゴリの確認と操作

カテゴリの一覧は .cat.categories で確認できます。

print(df['status'].cat.categories)

カテゴリを追加したり、順序を変えたりもできます。

# カテゴリを追加
df['status'] = df['status'].cat.add_categories(['deleted'])

# 使われていないカテゴリを削除
df['status'] = df['status'].cat.remove_unused_categories()

# カテゴリの順序を変更
df['status'] = df['status'].cat.reorder_categories(['pending', 'active', 'inactive'])

順序付きカテゴリ

カテゴリに順序を持たせることもできます。これにより、比較演算が可能になります。

df['size'] = pd.Categorical(
    ['S', 'M', 'L', 'S', 'XL'],
    categories=['S', 'M', 'L', 'XL'],
    ordered=True
)

# 比較演算が可能
print(df[df['size'] > 'M'])

カテゴリ型が効果的なケース

効果が大きい

値の種類が少なく、同じ値が繰り返し出現するデータ。例:国名、ステータス、曜日など。

効果が小さい

値の種類が多い、またはほぼユニークなデータ。例:ID、名前、自由記述など。

注意点

カテゴリ型は便利ですが、いくつか注意点があります。

カテゴリにない値を代入しようとするとエラーになります
文字列メソッド(str アクセサ)は一部制限があります
他のデータフレームと結合するとき、カテゴリが異なると問題が起きることがあります

大規模データを扱うときは、読み込み時にカテゴリ型を指定しておくとメモリを節約でき、処理速度も向上します。特にグループ化やソートの操作が速くなります。