閉じる

Pandasのメモリ不足を解消!数値型を最適化して処理を高速化する手法

ビッグデータの解析が当たり前となった2026年において、PythonのライブラリであるPandasはデータサイエンティストにとって欠かせないツールであり続けています。

しかし、数百万行、数千万行といった大規模なデータセットを扱う際、多くのユーザーがメモリ不足(Out of Memory)という課題に直面します。

Pandasは標準的な設定でデータを読み込むと、必要以上に大きなメモリ領域を確保する傾向があります。

特に数値データにおいては、適切なデータ型(dtype)を選択するだけで、メモリ消費量を劇的に削減することが可能です。

この記事では、Pandasにおける数値型の最適化手法に焦点を当て、メモリを節約しながら処理速度を向上させる具体的なテクニックを紹介します。

なぜPandasのメモリ管理が重要なのか

Pandasはデータをメモリ上に展開して処理を行うため、利用可能なメモリ量を超えるとシステムがクラッシュしたり、極端に動作が重くなったりします。

特に最近のデータ分析現場では、クラウド上の限られたリソース内で効率的に計算を回すことが求められています。

メモリ消費を抑えることは、単にエラーを防ぐだけでなく、データの転送速度や計算効率の向上にも直結します。

CPUのキャッシュメモリにより多くのデータが収まるようになれば、計算のボトルネックが解消されるからです。

そのため、データ型を最適化することは、大規模データ分析における最初の一歩と言えます。

Pandasがデフォルトで採用する数値型とその問題点

PandasでCSVファイルを読み込む際、数値列はデフォルトで「int64」や「float64」として扱われることがほとんどです。

「64」という数字は、そのデータが64ビット(8バイト)のメモリを使用していることを示しています。

しかし、0から100までの値しか含まない列に対して、64ビットの領域を割り当てるのは非常に効率が悪いです。

例えば、0から255までの整数であれば、8ビット(1バイト)の「int8」で十分に表現できます。

この場合、デフォルトの設定と比較してメモリ使用量を8分の1にまで削減できる計算になります。

整数型の種類と表現できる範囲

Pandas(およびベースとなるNumPy)で利用可能な主な整数型を以下の表にまとめました。

データ型ビット数最小値最大値
int88 bit-128127
int1616 bit-32,76832,767
int3232 bit-2,147,483,6482,147,483,647
int6464 bit-9.22 x 10^189.22 x 10^18

符号なし整数(uint8など)を使用すれば、負の値を扱えない代わりに、正の方向にさらに広い範囲を表現できます。

例えば、uint8は0から255までの値を保持でき、カテゴリIDやフラグ情報を格納するのに最適です。

浮動小数点型の種類と精度

浮動小数点についても、同様にビット数を調整することが可能です。

データ型ビット数特徴
float1616 bit半精度。非常に軽量だが精度が低い。
float3232 bit単精度。機械学習のモデル訓練でよく使われる。
float6464 bit倍精度。Pandasのデフォルト設定。

金融データのように高い精度が求められる場合を除き、多くの場合「float32」で十分な精度を確保できます。

深層学習などの分野では「float16」を活用することで、メモリ消費を4分の1に抑えるテクニックが一般化しています。

現在のメモリ使用量を正確に把握する方法

最適化を始める前に、まずは現在のDataFrameがどれだけのメモリを消費しているかを確認しましょう。

df.info()メソッドを使用するのが最も簡単ですが、より詳細な情報を得るためのオプションがあります。

Python
import pandas as pd
import numpy as np

# サンプルデータの作成(100万行)
df = pd.DataFrame({
    'id': range(1000000),
    'score': np.random.randint(0, 100, 1000000),
    'ratio': np.random.rand(1000000)
})

# メモリ使用量の表示
df.info(memory_usage='deep')
実行結果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 3 columns):
 #   Column  Non-Null Count    Dtype  
---  ------  --------------    -----  
 0   id      1000000 non-null  int64  
 1   score   1000000 non-null  int64  
 2   ratio   1000000 non-null  float64
dtypes: float64(1), int64(2)
memory usage: 22.9 MB

ここで注目すべきは「memory usage: 22.9 MB」という部分です。

この値を基準にして、最適化によってどれだけ削減できるかを評価します。

列ごとの詳細な使用量を知りたい場合は、df.memory_usage(deep=True)を実行してください。

数値型のダウンキャストによる最適化

Pandasには、数値を可能な限り小さなデータ型に自動変換してくれるpd.to_numeric()関数が用意されています。

この関数のdowncast引数を利用することで、データの値を維持したまま、最小のビット数を選択できます。

Python
# 整数列のダウンキャスト
df['id'] = pd.to_numeric(df['id'], downcast='integer')
df['score'] = pd.to_numeric(df['score'], downcast='integer')

# 浮動小数点列のダウンキャスト
df['ratio'] = pd.to_numeric(df['ratio'], downcast='float')

# 最適化後の情報を表示
df.info(memory_usage='deep')
実行結果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 3 columns):
 #   Column  Non-Null Count    Dtype  
---  ------  --------------    -----  
 0   id      1000000 non-null  int32  
 1   score   1000000 non-null  int8   
 2   ratio   1000000 non-null  float32
dtypes: float32(1), int32(1), int8(1)
memory usage: 8.6 MB

最適化の結果、メモリ使用量が22.9 MBから8.6 MBへと、約60%以上も削減されました。

特に「score」列は0〜99の範囲だったため、int64からint8へと劇的なダウンキャストが成功しています。

一括でメモリを削減するカスタム関数の活用

実際の分析業務では、多数の列を持つDataFrameを扱うため、手動で1列ずつ変換するのは非効率です。

全ての数値列をスキャンし、最適な型へ自動的に変換する汎用的な関数を作成しておくと便利です。

Python
def reduce_mem_usage(df):
    start_mem = df.memory_usage().sum() / 1024**2
    
    for col in df.columns:
        col_type = df[col].dtype
        
        if col_type != object:
            c_min = df[col].min()
            c_max = df[col].max()
            
            if str(col_type)[:3] == 'int':
                if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                    df[col] = df[col].astype(np.int8)
                elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                    df[col] = df[col].astype(np.int16)
                elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                    df[col] = df[col].astype(np.int32)
                else:
                    df[col] = df[col].astype(np.int64)  
            else:
                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                    df[col] = df[col].astype(np.float16)
                elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                    df[col] = df[col].astype(np.float32)
                else:
                    df[col] = df[col].astype(np.float64)
                    
    end_mem = df.memory_usage().sum() / 1024**2
    print(f'メモリ消費量が {start_mem:.2f} MB から {end_mem:.2f} MB に減少しました。')
    return df

# 関数の適用
df_optimized = reduce_mem_usage(df)

このような関数を通すだけで、データロード直後のメモリ負荷を最小限に抑えることができます。

ただし、浮動小数点をfloat16に変換する際は、微小な値の欠落が発生する可能性があるため注意が必要です。

読み込み時にデータ型を指定するテクニック

DataFrameを作成した後にメモリを削減するのも有効ですが、最初から小さな型で読み込むのが最も効率的です。

pd.read_csv()の引数dtypeを使用することで、読み込み時のピークメモリを抑制できます。

Python
# 読み込み時に型を指定
dtype_dict = {
    'id': 'int32',
    'score': 'int8',
    'ratio': 'float32'
}

df_fast = pd.read_csv('large_data.csv', dtype=dtype_dict)

これにより、大きなデータファイルを読み込む際の「メモリが足りなくて読み込めない」というトラブルを回避できます。

事前にデータの値の範囲が分かっている場合には、この方法がベストプラクティスとなります。

欠損値(NaN)を含む列の注意点

Pandasの従来の整数型(int64など)は、欠損値(NaN)を保持することができません。

整数列に1つでもNaNが含まれていると、Pandasはその列を自動的にfloat64へ変換してしまいます。

これを防ぐためには、Pandas 1.0以降で導入された「Nullable Integer型」を使用する必要があります。

型名の先頭を大文字にしたInt8Int32を指定することで、整数型のまま欠損値を扱うことが可能になります。

Python
# 欠損値を含む整数列の定義
s = pd.Series([1, 2, None], dtype="Int8")
print(s)
実行結果
0       1
1       2
2    <NA>
dtype: Int8

この新しいデータ型を活用することで、不要な浮動小数点への変換を防ぎ、メモリ消費を抑えつつデータの整合性を保てます。

まとめ

Pandasでのメモリ節約は、単なるリソースの節約にとどまらず、データ処理全体のパフォーマンスを向上させるための重要な工程です。

数値列に対して適切なビット数を選択することで、メモリ使用量を半分以下に削減できるケースも珍しくありません。

まずはdf.info()で現状を把握し、pd.to_numeric()でのダウンキャストや読み込み時の型指定を試してみてください。

特に大規模なデータセットを扱う2026年のエンジニアにとって、こうした細かな最適化が分析の成否を分ける鍵となります。

メモリ不足に悩まされる前に、データ型の最適化をワークフローに取り入れて、より快適なデータ分析環境を構築しましょう。

URLをコピーしました!