データクレンジングの技術:Pandasでの欠損値処理

プログラミング

データクレンジングの技術:Pandasでの欠損値処理

データ分析において、データの品質は分析結果の信頼性に直結します。そのため、生データに含まれるノイズや不整合を取り除く「データクレンジング」は、前処理の重要なステップです。特に、データセット内の「欠損値」の扱いは、分析手法の選択や精度に大きな影響を与えます。本稿では、Pythonのデータ分析ライブラリであるPandasを用いた欠損値処理の様々な技術について、具体的なコード例を交えながら解説します。

Pandasにおける欠損値の表現

Pandasでは、欠損値は一般的にNaN(Not a Number)として表現されます。これは、数値型だけでなく、オブジェクト型(文字列など)のデータにおいても同様です。DataFrameやSeriesオブジェクトで欠損値が存在するかどうかを確認するには、主に以下のメソッドが利用されます。

`isnull()`と`notnull()`

`isnull()`メソッドは、DataFrameやSeriesの各要素が欠損値である場合にTrue、そうでない場合にFalseを返します。一方、`notnull()`メソッドはその逆で、欠損値でない場合にTrueを返します。これらのメソッドは、欠損値の存在を把握するために不可欠です。

例:
“`python
import pandas as pd
import numpy as np

data = {‘col1’: [1, 2, np.nan, 4],
‘col2’: [‘A’, np.nan, ‘C’, ‘D’]}
df = pd.DataFrame(data)

print(df.isnull())
print(df.notnull())
“`

`info()`

`info()`メソッドは、DataFrameの各列のデータ型、非欠損値の数、メモリ使用量などをまとめて表示します。この出力結果から、各列にどれだけの欠損値が存在するかを概観することができます。

例:
“`python
print(df.info())
“`

`describe()`

`describe()`メソッドは、数値列の統計量(個数、平均、標準偏差、最小値、四分位数、最大値)を計算して表示します。この際、欠損値はカウントに含まれません。そのため、非欠損値の数とデータセット全体の行数を比較することで、欠損値の存在を確認することも可能です。

例:
“`python
print(df.describe())
“`

欠損値の処理方法

欠損値の処理方法は、データの特性や分析の目的に応じて選択する必要があります。主な処理方法としては、欠損値を含む行や列を削除する方法と、欠損値を何らかの値で補完する方法があります。

欠損値の削除

欠損値を含む行または列を削除する方法は、最もシンプルですが、データ量を大きく損なう可能性があるため注意が必要です。

`dropna()`

`dropna()`メソッドは、欠損値を含む行または列を削除するために使用されます。

* **行の削除(デフォルト)**:`df.dropna()`とすると、1つでも欠損値を含む行が削除されます。
* **列の削除**:`df.dropna(axis=1)`とすると、1つでも欠損値を含む列が削除されます。
* **特定の条件での削除**:`how=’all’`を指定すると、全ての値が欠損値である行のみを削除します。また、`thresh`引数に整数を指定すると、その数以上の非欠損値を持つ行のみを残すことができます。

例:
“`python
# 欠損値を含む行を削除
df_dropped_rows = df.dropna()
print(df_dropped_rows)

# 欠損値を含む列を削除
df_dropped_cols = df.dropna(axis=1)
print(df_dropped_cols)

# 全て欠損値である行を削除
data_all_nan = {‘col1’: [1, np.nan, np.nan], ‘col2′: [2, np.nan, np.nan]}
df_all_nan = pd.DataFrame(data_all_nan)
df_dropped_all_nan_rows = df_all_nan.dropna(how=’all’)
print(df_dropped_all_nan_rows)

# 非欠損値が2つ以上の行を残す
data_thresh = {‘col1’: [1, 2, np.nan, 4], ‘col2’: [5, np.nan, 7, 8]}
df_thresh = pd.DataFrame(data_thresh)
df_kept_thresh = df_thresh.dropna(thresh=2)
print(df_kept_thresh)
“`

欠損値の補完

欠損値を何らかの値で置き換えることで、データ量を維持しながら分析を進めることができます。補完方法は、データの性質によって最適なものが異なります。

`fillna()`

`fillna()`メソッドは、欠損値を指定した値で補完するために使用されます。

* **特定の値で補完**:`df.fillna(value)`のように、具体的な数値を指定して補完します。
* **前後の値で補完(Forward Fill / Backward Fill)**:
* `method=’ffill’`(Forward Fill):直前の非欠損値で補完します。
* `method=’bfill’`(Backward Fill):直後の非欠損値で補完します。
これらの補完は、時系列データなどで有効な場合があります。
* **平均値・中央値・最頻値で補完**:
* 平均値:`df[‘column’].fillna(df[‘column’].mean())`
* 中央値:`df[‘column’].fillna(df[‘column’].median())`
* 最頻値:`df[‘column’].fillna(df[‘column’].mode()[0])`
数値データの場合、平均値や中央値での補完が一般的です。最頻値はカテゴリカルデータで有効です。mode()は複数の最頻値が存在する場合にSeriesを返すため、[0]で最初の最頻値を取得します。
* **グループごとの補完**:`groupby()`と組み合わせて、特定のグループ内での平均値や中央値などで補完することも可能です。

例:
“`python
# 0で補完
df_filled_zero = df.fillna(0)
print(df_filled_zero)

# 直前の値で補完 (ffill)
data_ffill = {‘col1’: [1, 2, np.nan, 4], ‘col2’: [‘A’, np.nan, ‘C’, ‘D’]}
df_ffill = pd.DataFrame(data_ffill)
df_filled_ffill = df_ffill.fillna(method=’ffill’)
print(df_filled_ffill)

# 直後の値で補完 (bfill)
df_filled_bfill = df_ffill.fillna(method=’bfill’)
print(df_filled_bfill)

# 平均値で補完
data_mean = {‘col1’: [1, 2, np.nan, 4], ‘col2’: [5, 6, 7, np.nan]}
df_mean = pd.DataFrame(data_mean)
df_filled_mean = df_mean.fillna({‘col1’: df_mean[‘col1’].mean(), ‘col2’: df_mean[‘col2’].mean()})
print(df_filled_mean)

# グループごとの平均値で補完
data_group = {‘group’: [‘A’, ‘A’, ‘B’, ‘B’],
‘value’: [1, np.nan, 3, 4]}
df_group = pd.DataFrame(data_group)
df_group[‘value_filled’] = df_group.groupby(‘group’)[‘value’].transform(lambda x: x.fillna(x.mean()))
print(df_group)
“`

欠損値の補間(Interpolation)

補間は、隣接するデータポイント間の関係性を考慮して欠損値を推定する方法です。特に時系列データや連続的なデータで有効な手法です。

`interpolate()`

`interpolate()`メソッドは、様々な補間方法を提供します。

* **線形補間(linear、デフォルト)**:隣接する非欠損値の間を直線で結び、その線上の値を補間します。
* **多項式補間(polynomial)**:指定した次数(order引数)の多項式で近似して補間します。
* **スプライン補間(spline)**:より滑らかな曲線で補間します。
* **時系列特有の補間**:time(日付/時刻インデックスの場合)、pad(直前の値で埋める)、nearest(最も近い非欠損値で埋める)なども指定可能です。

例:
“`python
data_interp = {‘col1′: [1, 2, np.nan, np.nan, 5]}
df_interp = pd.DataFrame(data_interp)

# 線形補間
df_interpolated_linear = df_interp.interpolate()
print(df_interpolated_linear)

# 多項式補間 (次数2)
# df_interpolated_poly = df_interp.interpolate(method=’polynomial’, order=2)
# print(df_interpolated_poly)
“`
※多項式補間やスプライン補間は、より複雑な計算を伴うため、データセットのサイズによっては処理に時間がかかる場合があります。

欠損値処理の戦略と注意点

欠損値処理は、単にメソッドを適用するだけでなく、データ分析全体の戦略に基づいて慎重に進める必要があります。

* **欠損値の発生原因の理解**:なぜ欠損値が発生したのかを理解することは、適切な処理方法を選択する上で重要です。例えば、アンケートで特定の質問に回答しなかった、センサーの故障、データ収集のエラーなど、原因によって補完方法や削除の判断が変わってきます。
* **欠損値の割合の確認**:特定の列や行の欠損値の割合が非常に高い場合、その列や行を削除した方が分析のノイズを減らせる可能性があります。逆に、欠損値の割合が低い場合は、補完によってデータ量を維持することが望ましい場合もあります。
* **分析手法との適合性**:使用する分析手法によっては、欠損値の扱いに制約がある場合があります。例えば、一部の機械学習アルゴリズムは欠損値を直接扱うことができません。
* **ドメイン知識の活用**:分析対象のドメインに関する知識は、欠損値処理の判断に大いに役立ちます。例えば、ある特徴量の値が特定範囲外である場合に欠損値として扱われている場合など、その特性を理解して処理を行うことが重要です。
* **複数手法の比較検討**:可能であれば、いくつかの異なる欠損値処理手法を試して、分析結果への影響を比較検討することをお勧めします。

まとめ

Pandasは、欠損値の特定から削除、そして様々な方法での補完・補間まで、データクレンジングにおける欠損値処理のための強力な機能を提供します。`isnull()`、`dropna()`、`fillna()`、`interpolate()`といったメソッドを効果的に活用することで、データの品質を向上させ、より信頼性の高い分析結果を得ることが可能になります。欠損値処理は、分析の成否を左右する重要なプロセスであり、データの特性と分析の目的に応じた最適な戦略を立てることが不可欠です。