【Pandas徹底解説】欠損値NaN, None, pd.NAを理解してデータ前処理をマスターしよう!
データ分析の現場では、欠損値の扱いは避けて通れない重要なテーマです。Pandasでは、欠損値を表現するために**NaN (Not a Number)、Python標準のNone、そしてPandas 1.0以降で導入されたpd.NA**という複数の方法があります。これらの違いを理解し、適切に処理することは、正確なデータ分析を行う上で不可欠です。この記事では、それぞれの欠損値がどのように扱われるのか、そしてPandasで欠損値を効率的に処理する方法を丁寧に解説します。
欠損値とは?なぜ理解する必要があるのか?
欠損値とは、データが存在しない、または利用できない状態を指します。例えば、アンケートの未回答や、センサーの故障によるデータ抜けなどがこれに該当します。欠損値が適切に処理されていないと、集計結果が歪んだり、機械学習モデルの精度が低下したりする原因となります。
Pandasにおける欠損値の種類
Pandasは、異なるデータ型に応じて欠損値を表現するためのいくつかの方法を提供します。
NaN (Not a Number)
NaNは、主に数値型の欠損値を表すために使用されます。IEEE 754浮動小数点標準で定義されており、NumPyからPandasに継承されています。
import pandas as pd
import numpy as np
s_nan = pd.Series([1, 2, np.nan, 4])
print("NaNを含むSeries:\n", s_nan)
print("NaNの型:", type(np.nan))
解説:
np.nanはNumPyライブラリからインポートして使用します。NaNを含むSeriesのデータ型は、自動的に浮動小数点数型(float64)に変換されることが多いです。これは、NaNが浮動小数点数として扱われるためです。
None
Python標準のオブジェクトである**Noneは、主にオブジェクト型**(文字列など)の欠損値を表すために使用されます。Pandasは、オブジェクト型データにおいてはNoneをそのまま欠損値として扱います。
s_none = pd.Series(['A', 'B', None, 'D'])
print("\nNoneを含むSeries:\n", s_none)
print("Noneの型:", type(None))
解説:
NoneはPythonの組み込み型であり、特に文字列型など、数値以外のデータに欠損がある場合によく見られます。
pd.NA
pd.NAは、Pandas 1.0以降で導入された、より統一的な欠損値表現です。特に、整数型やブール型のSeriesにおいてNaNやNoneでは扱いにくいケースに対応するために設計されました。pd.NAを使用することで、元のデータ型を維持したまま欠損値を表現できるようになります。
s_pd_na = pd.Series([1, 2, pd.NA, 4], dtype='Int64') # 大文字'I'に注意
print("\npd.NAを含むSeries:\n", s_pd_na)
print("pd.NAの型:", type(pd.NA))
# ブール型での例
s_bool_na = pd.Series([True, False, pd.NA], dtype='boolean')
print("\npd.NAを含むブール型Series:\n", s_bool_na)
解説:
pd.NAを整数型やブール型のSeriesで使用する際は、dtypeを’Int64’(大文字のI)や’boolean’のように指定する必要があります。これにより、数値型でNaNを使うとfloatに変換されてしまう問題や、ブール型でNoneを使うとオブジェクト型になってしまう問題を解決できます。
欠損値の検出と確認
欠損値を処理する前に、まずはどこに欠損値があるのかを特定する必要があります。
isnull() / isna()
isnull() (またはそのエイリアスisna()) メソッドは、各要素が欠損値であるかどうかを示すブール値のSeries/DataFrameを返します。
df = pd.DataFrame({
'A': [1, np.nan, 3],
'B': ['x', None, 'z'],
'C': [True, False, pd.NA]
})
print("オリジナルDataFrame:\n", df)
print("\n欠損値の確認 (isnull):\n", df.isnull())
解説:
Trueは欠損値があることを、Falseは欠損値がないことを示します。
notnull() / notna()
notnull() (またはnotna()) は、isnull()の逆で、欠損値でない要素を示します。
print("\n欠損値でない値の確認 (notnull):\n", df.notnull())
info()
DataFrameのinfo()メソッドは、各列の非null値の数を確認するのに便利です。
print("\nDataFrame情報 (info):\n")
df.info()
解説:
Non-Null Countの列を見ることで、各列の欠損値の有無と数を把握できます。
欠損値の処理方法
欠損値の扱い方には、主に「削除」と「補完」の2つのアプローチがあります。
欠損値の削除: dropna()
dropna()メソッドは、欠損値を含む行または列を削除します。
欠損値を含む行を削除
デフォルトでは、dropna()は欠損値が1つでも含まれる行を削除します。
df_dropped_rows = df.dropna()
print("\n欠損値を含む行を削除:\n", df_dropped_rows)
欠損値を含む列を削除
axis=1を指定することで、欠損値を含む列を削除できます。
df_dropped_cols = df.dropna(axis=1)
print("\n欠損値を含む列を削除:\n", df_dropped_cols)
特定の列に欠損値がある場合のみ行を削除
subset引数に列名のリストを指定すると、その列に欠損値がある行のみを削除します。
df_sub_dropna = df.dropna(subset=['A'])
print("\n'A'列に欠損値がある行を削除:\n", df_sub_dropna)
欠損値の補完: fillna()
fillna()メソッドは、欠損値を特定の値で埋めるために使用されます。
固定値で補完
最もシンプルな方法で、すべての欠損値を指定した値で埋めます。
df_filled_const = df.fillna(0) # 数値列は0、文字列列はNaNのまま
print("\n固定値 (0) で補完:\n", df_filled_const)
解説:
文字列型の列では0は適用されません。異なる型の列を持つDataFrameで一括で埋める場合は注意が必要です。
前の行の値で補完 (forward fill)
method='ffill' (forward fill) を指定すると、前の有効な値で欠損値を埋めます。
df_ffill = df.fillna(method='ffill')
print("\n前の行の値で補完 (ffill):\n", df_ffill)
次の行の値で補完 (backward fill)
method='bfill' (backward fill) を指定すると、次の有効な値で欠損値を埋めます。
df_bfill = df.fillna(method='bfill')
print("\n次の行の値で補完 (bfill):\n", df_bfill)
平均値や中央値で補完
数値列の欠損値を、その列の平均値や中央値で補完するのは一般的な手法です。
df_mean_fill = df.copy()
df_mean_fill['A'] = df_mean_fill['A'].fillna(df_mean_fill['A'].mean())
print("\n'A'列を平均値で補完:\n", df_mean_fill)
まとめ
Pandasにおける欠損値NaN、None、pd.NAの違いを理解し、isnull()/notnull()での検出、dropna()による削除、fillna()による補完といった処理方法を使いこなすことは、データ分析の品質を大きく向上させます。これらの手法を適切に組み合わせることで、データの信頼性を高め、より正確な分析結果を得ることができるでしょう。

