【Pandas】欠損値(NaN)の扱い方:dropna・fillnaの使い分け

Pandas

こんにちは、かつコーチです。
実務のデータには、必ずと言っていいほど欠損値が含まれています。
「削除すればいいのか、埋めればいいのか」で迷う方も多いと思うので、この記事ではdropnafillnaの使い分けを、判断基準とあわせて解説します。

欠損値とは?

欠損値(NaN)の定義

欠損値とは、本来あるはずのデータが記録されていない状態のことです。
pandasでは欠損値をNaN(Not a Number)という特別な値で表現します。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "名前": ["田中", "佐藤", "鈴木"],
    "年齢": [25, np.nan, 28]
})
print(df)
   名前    年齢
0  田中  25.0
1  佐藤   NaN
2  鈴木  28.0

アンケートの未回答、システム連携時の取得漏れ、Excelでの入力漏れなど、欠損値が生まれる原因はさまざまです。

なぜ欠損値の処理が必要なのか

欠損値をそのままにして集計や機械学習に使うと、計算結果が意図せずNaNになったり、正確な平均値・合計値が計算できなくなったりします。
「欠損値をどう扱うか」は、分析結果の信頼性に直結する重要な工程です。

基本の書き方:dropnaとfillnaの使い方

手順1:欠損値の有無を確認する

処理を始める前に、まず欠損値がどこにどれだけあるかを確認します。

print(df.isnull().sum())
名前    0
年齢    1
dtype: int64

isnull()はNaNならTrueを返すDataFrameを作り、.sum()で列ごとの欠損数を合計しています。

手順2:dropnaで欠損値のある行を削除する

dropnaは、欠損値が含まれる行(または列)をまるごと削除するメソッドです。

df_dropped = df.dropna()
print(df_dropped)
   名前    年齢
0  田中  25.0
2  鈴木  28.0

列単位で削除したい場合はaxis=1を指定します。

df_dropped_col = df.dropna(axis=1)

手順3:fillnaで欠損値を埋める

fillnaは、欠損値を指定した値で埋めるメソッドです。

df_filled = df.fillna({"年齢": df["年齢"].mean()})
print(df_filled)
   名前    年齢
0  田中  25.0
1  佐藤  26.5
2  鈴木  28.0

列ごとに異なる値で埋めたい場合は、辞書形式で指定するのが便利です。

つまずきやすい設定・注意点

  • dropna()はデフォルトで元のDataFrameを変更せず、新しいDataFrameを返します。上書きしたい場合はdf = df.dropna()のように再代入するか、inplace=Trueを使います
  • fillnaで平均値を使う場合、平均を計算する前に他の欠損値処理を済ませておかないと、平均値自体がずれてしまうことがあります

dropnaとfillna、どちらを選ぶべきか

判断軸の比較表

観点dropna(削除)fillna(埋める)
データ量への影響行が減る=データ量が減少するデータ量を維持できる
向いているケース欠損が少数で、削除しても分析に支障がない場合欠損が多く、削除するとデータが不足する場合
リスク重要なデータまで失う可能性がある埋めた値が分析結果を歪める可能性がある
代表的な使い所ID列など必須項目が欠けている行の除外アンケートの未回答を平均値・中央値で補完

どう選べばいいか

迷ったときの判断基準はシンプルです。

  • その行・列が分析に不可欠な項目で、欠損が全体の数%程度ならdropna(多少データが減っても影響が小さいため)
  • 欠損の割合が大きく、削除するとデータが偏ってしまうならfillna(平均値・中央値・0など、業務的に妥当な値で補完する)

私が実際の案件でアンケートデータを扱った際、「未回答」を安易に0で埋めてしまい、平均点が実態より大幅に低く算出されてしまったことがあります。
fillna(0)は便利ですが、「0という値が業務的に何を意味するか」を考えずに使うと、分析結果を歪める原因になります。
このときは0埋めをやめ、未回答の行をdropna()で除外する方針に切り替えて、正しい平均点を算出し直しました。

よくあるつまずきポイント・エラー対処

fillnaの値が数値じゃない列にも影響してしまう

❌Before:DataFrame全体に一律の値でfillnaをかけてしまう

df_filled = df.fillna(0)

数値列だけでなく、本来空欄のままにしておきたい文字列列まで0で埋まってしまうことがあります。

✅After:列を指定して、列ごとに適切な値で埋める

df_filled = df.fillna({
    "年齢": df["年齢"].mean(),
    "備考": "未入力"
})

「全部まとめて処理」ではなく「列ごとの意味を考えて処理」することが、欠損値処理の基本姿勢です。

応用・一歩先の使い方

前後の値で埋める(時系列データ向け)

センサーデータや株価のような時系列データでは、前の値や後ろの値で埋める方法もよく使われます。

df_filled = df.ffill()  # 前の値で埋める(forward fill)
df_filled = df.bfill()  # 後ろの値で埋める(backward fill)

なお、fillna(method="ffill")という書き方は古い記事でよく見かけますが、pandas 2.x系では非推奨(FutureWarning)になっています。
2.x系ではdf.ffill()df.bfill()のように専用メソッドを直接使う書き方が推奨されているので、新しくコードを書くときはこちらを使いましょう。

特定の列にだけdropnaを適用する

subset引数を使うと、特定の列に欠損値がある行だけを削除対象にできます。

df_dropped = df.dropna(subset=["年齢"])

「名前」列は欠損していても構わないが「年齢」列は必須、といった業務要件に対応できます。

まとめ

この記事のポイント

  • 欠損値はpandasではNaNとして表現される
  • dropnaは欠損のある行・列を削除、fillnaは指定した値で埋める
  • 判断基準は「欠損の割合」と「削除して困る項目かどうか」
  • 一律の値で埋めると、意味のない値が分析結果を歪めるリスクがある
  • 列ごとの意味を考えて、dropnafillnaを使い分ける

次に読むべき記事

欠損値の処理ができるようになったら、次はデータをグループごとに集計する「groupbyでデータをグループ化・集計する」に進みましょう。
きれいになったデータを使って、実際の集計作業に入っていきます。

タグ: Pandas, 中級者向け, データ加工

タイトルとURLをコピーしました