Pythonで欠損値(NaN)を処理する方法!Pandasのfillna()活用
生徒
「Pythonでデータ分析の練習をしているのですが、表の中に『NaN』という文字が出てきて困っています。これって何ですか?」
先生
「それは『欠損値』といって、データが入っていない空っぽの状態を指します。料理で例えるなら、レシピに必要な材料が足りない状態ですね。そのままでは計算や分析がうまく進みません。」
生徒
「材料がないと料理が完成しないのと同じですね。どうすればいいんでしょうか?」
先生
「Pandasというライブラリの『fillna』という機能を使えば、その空っぽの部分を別の値で埋めることができます。基礎から丁寧に解説しますね!」
1. 欠損値(NaN)とは何かを理解しよう
プログラミングの世界、特にPythonを使ったデータ分析において、避けて通れないのが「欠損値(けっそんち)」です。これは、本来あるべき場所にデータが存在しない状態を指します。
Pythonでは、この欠損値を「NaN(ナン)」と表現します。これは「Not a Number(数値ではない)」の略称です。例えば、アンケート結果を集計しているときに、ある人が年齢を書き忘れた場合、その項目は「空欄」になりますよね。この空欄こそが、データ分析における欠損値です。
パソコンは、数字を計算するのが得意ですが、この「空っぽ」が混ざっていると、足し算や平均の計算でエラーが出たり、結果がおかしくなったりすることがあります。そのため、分析を始める前に「この空っぽをどう扱うか」を決めて、処理してあげる必要があるのです。
2. Pandas(パンダス)ライブラリの準備
Pythonでデータを表形式で扱うために最もよく使われる道具が「Pandas(パンダス)」です。エクセルのような表計算をPythonで行うための、非常に便利な機能が集まったツールセットだと考えてください。
Pandasを使うためには、まず「これからPandasを使いますよ」とプログラムに教えてあげる必要があります。これを「インポート」と呼びます。通常、Pandasは「pd」という短いあだ名をつけて読み込むのが一般的です。
まずは、わざと欠損値を含んだ簡単な表データ(データフレームと呼びます)を作ってみましょう。以下のコードで、実際に「NaN」が表示される様子を確認できます。
import pandas as pd
import numpy as np
# わざと欠損値(NaN)を含んだデータを作成します
data = {
'名前': ['田中', '佐藤', '鈴木', '高橋'],
'テストの点数': [80, np.nan, 95, np.nan]
}
df = pd.DataFrame(data)
print(df)
名前 テストの点数
0 田中 80.0
1 佐藤 NaN
2 鈴木 95.0
3 高橋 NaN
上記の実行結果を見ると、佐藤さんと高橋さんの点数が「NaN」になっていますね。これが欠損値です。この状態を解決するのが今回の目的です。
3. fillna()関数の基本的な使い方
欠損値を処理する最も代表的な方法が、「fillna()(フィルエヌエー)」という命令を使うことです。これは名前の通り、「fill(埋める)」「na(欠損値)」という意味を持っています。
fillnaを使うと、表の中にある全てのNaNを、指定した好きな値で一気に置き換えることができます。例えば、テストを受けていない人の点数を一律で「0点」にしたい場合は、fillna(0)と書くだけで完了します。
このように、欠損値をあらかじめ決めた値で埋めることで、その後の計算処理がスムーズに動くようになります。初心者の方でも、まずは「fillnaは穴埋めをするための魔法の言葉」と覚えておけば大丈夫です。
# 欠損値をすべて「0」で埋めてみます
df_zero = df.fillna(0)
print("--- 欠損値を0で埋めた結果 ---")
print(df_zero)
--- 欠損値を0で埋めた結果 ---
名前 テストの点数
0 田中 80.0
1 佐藤 0.0
2 鈴木 95.0
3 高橋 0.0
4. 特定の列だけを狙って穴埋めする方法
実際のデータ分析では、表の中にたくさんの項目(列)があります。「年齢の欠損値は0で埋めたいけれど、住所の欠損値は『不明』という文字で埋めたい」といったように、列ごとに処理を変えたい場面がよくあります。
そんな時は、データフレーム全体に対してではなく、特定の列を指定してからfillnaを実行します。特定の列を選ぶには「df['列の名前']」という書き方をします。
例えば、名前の列に欠損値があったら「名無し」とし、点数の列には「未受験」という言葉を入れるといった使い分けが可能です。これにより、データの意味を壊さずに適切な穴埋めが行えます。
# 新しいデータを作成
data2 = {
'商品名': ['リンゴ', np.nan, 'バナナ', 'メロン'],
'価格': [100, 200, np.nan, 400]
}
df2 = pd.DataFrame(data2)
# 商品名の欠損値を「不明」で埋める
df2['商品名'] = df2['商品名'].fillna('不明')
# 価格の欠損値を「0」で埋める
df2['価格'] = df2['価格'].fillna(0)
print(df2)
商品名 価格
0 リンゴ 100.0
1 不明 200.0
2 バナナ 0.0
3 メロン 400.0
5. 平均値を使って賢く欠損値を埋める
数値を扱うデータ分析でよく使われるテクニックに、「平均値で埋める」という手法があります。一律で0にしてしまうと、全体の平均点が大きく下がってしまうため、実態に近い分析ができなくなることがあるからです。
Pandasには、数値の平均を計算する「mean()(ミーン)」という機能があります。これとfillnaを組み合わせることで、「空欄の部分に、他の人たちの平均スコアを自動で入れる」という高度な処理が1行で書けてしまいます。
科学計算や統計学の世界では、このように既存のデータから推測して値を補うことを「補完(ほかん)」と呼びます。専門的に聞こえますが、Pythonを使えば初心者でも簡単に実践できる、非常に強力なテクニックです。
# テストの点数の平均値を計算します
heikin = df['テストの点数'].mean()
print(f"平均点: {heikin}")
# 平均値で欠損値を埋めます
df_mean = df.fillna({'テストの点数': heikin})
print("--- 平均値で埋めた結果 ---")
print(df_mean)
平均点: 87.5
--- 平均値で埋めた結果 ---
名前 テストの点数
0 田中 80.0
1 佐藤 87.5
2 鈴木 95.0
3 高橋 87.5
6. 前後のデータを使って穴埋めする便利な設定
時系列データ(時間の経過とともに記録されたデータ)を扱う場合、ひとつ前のデータや、ひとつ後のデータと同じ値で埋めるのが自然な場合があります。例えば、気温の変化を記録していて、ある時間の測定に失敗したとき、その前後の気温と大きく変わらないはずだと推測できますね。
fillnaには、「method(メソッド)」というオプションを指定することで、こうした特殊な埋め方ができるようになっています。
- ffill(フォワードフィル):ひとつ前のデータで埋める
- bfill(バックフィル):ひとつ後のデータで埋める
これらは「forward(前方)」と「back(後方)」を意味しており、株価のデータやセンサーのログデータなど、流れが重要な情報の処理によく使われます。手動で値を入力する手間が省けるため、非常に効率的です。
7. 欠損値を処理する際の注意点とinplaceについて
最後に、非常に重要な注意点をお伝えします。実は、これまでのコードで紹介した df.fillna(0) を実行しただけでは、元のデータフレーム(df)自体は書き換わっていません。「書き換えた後の新しいデータ」を作って返しているだけなのです。
もし、元のデータそのものを上書きして変更したい場合は、inplace=True という呪文を付け足すか、自分自身に代入し直す必要があります。これを忘れると、「穴埋めしたはずなのにデータが変わっていない!」という初心者が最も陥りやすい罠にはまってしまいます。
また、何でもかんでも埋めれば良いというわけでもありません。データがあまりにも欠損している場合は、その行そのものを削除してしまう(dropnaという機能を使います)判断が必要なこともあります。状況に合わせて最適な方法を選べるようになりましょう。
# inplace=True を使うと、元の df が直接書き換わります
df.fillna(0, inplace=True)
print("--- inplace=True実行後の元のデータ ---")
print(df)
--- inplace=True実行後の元のデータ ---
名前 テストの点数
0 田中 80.0
1 佐藤 0.0
2 鈴木 95.0
3 高橋 0.0
これで、欠損値 NaN の恐怖からは卒業です!Pandasのfillnaを使いこなして、綺麗なデータで分析を楽しみましょう。
まとめ
本記事では、Pythonのデータ分析ライブラリであるPandasを用いた欠損値(NaN)の処理方法について、基本から応用まで詳しく解説してきました。データサイエンスや機械学習の現場において、データの欠損は避けて通れない課題です。アンケートの未回答やシステムの不具合、センサーの計測ミスなど、現実に存在するデータには必ずと言っていいほど「空っぽ」の項目が含まれています。
この「欠損値」を放置したまま分析を進めてしまうと、統計量の算出ミスや、予測モデルの精度低下、最悪の場合にはプログラムの強制終了を招く恐れがあります。そのため、fillna()関数を正しく使いこなし、データの性質に合わせた適切なデータクリーニング(前処理)を行うスキルは、エンジニアやアナリストにとって必須の能力と言えるでしょう。
これまでに学んだ重要なポイントの再確認
改めて、Pandasのfillna()を活用する上での主要なテクニックを振り返ります。
- 定数での穴埋め:
fillna(0)やfillna('欠損')のように、特定の数値や文字列で一律に埋める方法。最もシンプルで分かりやすい手法です。 - 特定の列への適用:
df['列名'].fillna()と記述することで、特定の項目だけを対象に処理できます。数値データとカテゴリデータが混在する表では非常に重要です。 - 統計量(平均値・中央値)の活用:
mean()などを用いて計算した平均値で埋めることで、データ全体の分布への影響を最小限に抑えることが可能です。 - 時系列データの補完:
method='ffill'(前の値で埋める)やmethod='bfill'(後の値で埋める)を使い、データの流れを考慮した補完が行えます。 - 破壊的変更(inplace): 元のデータを直接書き換える場合は
inplace=Trueを指定するか、再代入を行う必要があります。
実戦的なサンプルプログラム:複数列の同時処理
現場では、複数の列に対して異なるルールで欠損値を処理することがよくあります。辞書型(dict)を使って一気に処理する方法を紹介します。この方法を使えば、コードがスッキリとまとまり、可読性が向上します。
import pandas as pd
import numpy as np
# 実践的な顧客データを作成
customer_data = {
'顧客ID': [101, 102, 103, 104, 105],
'年齢': [25, np.nan, 45, 30, np.nan],
'最終購入日': ['2023-01-01', '2023-01-02', np.nan, '2023-01-05', '2023-01-06'],
'ランク': ['A', 'B', np.nan, 'A', np.nan]
}
df_cust = pd.DataFrame(customer_data)
# 列ごとに異なる値で埋めるためのルールを辞書で定義
fill_rules = {
'年齢': df_cust['年齢'].mean(), # 年齢は平均値で補完
'最終購入日': '2023-01-01', # 日付は特定の日で固定
'ランク': '不明' # カテゴリは「不明」で統一
}
# 辞書を引数に渡して一括処理
df_cleaned = df_cust.fillna(value=fill_rules)
print("--- 複数列を個別のルールで穴埋めした結果 ---")
print(df_cleaned)
このように、データの種類(連続値かカテゴリ値か)を考慮して、ビジネスロジックに基づいた適切な値を設定することが、質の高い分析への第一歩となります。
SEOキーワードを意識したデータ分析のコツ
Pythonでデータ分析を効率化するためには、Pandasだけでなく、周辺ライブラリとの連携も意識しましょう。例えば、欠損値の分布を視覚化するためにMatplotlibやSeabornといったグラフ描画ライブラリを併用すると、「どこに欠損が集中しているか」を直感的に把握できます。
また、欠損値処理(Missing Value Imputation)は、AI(人工知能)や機械学習(Machine Learning)のモデル構築において「ガベージイン・ガベージアウト(ゴミを入れればゴミが出てくる)」を防ぐために最も時間をかけるべき工程です。今回のfillna()のテクニックをマスターすることで、より高度なアルゴリズムや予測手法に挑戦するための盤石な基礎が整います。
最後に、データクリーニングは「一度やって終わり」ではありません。新しいデータが追加されるたびに、欠損の発生パターンが変わっていないか、平均値が大きく変動していないかを確認するプロセスを自動化することも検討してみてください。Pythonの柔軟な記述力を活かせば、それらの作業も驚くほど簡単に実装できるはずです。
生徒:
「先生、ありがとうございました!fillna()を使うことで、あんなに不気味だった『NaN』が綺麗に消えて、表がスッキリしました。データが整うと、なんだか達成感がありますね!」
先生:
「その調子です!データ分析の時間の約8割は、こうした『前処理』に費やされると言われているんですよ。地味な作業に見えますが、ここを疎かにすると、どんなに優れたAIを使っても正しい答えは導き出せません。」
生徒:
「8割もですか!?驚きです。でも、ただ0で埋めるだけじゃなくて、平均値を使ったり、前後のデータから推測したりと、色々な戦略があることが分かりました。使い分けが難しそうですが、コツはありますか?」
先生:
「良い質問ですね。コツは『そのデータがなぜ欠損したのか』を考えることです。例えば、たまたま測定器が故障したのなら前後の値が参考になりますし、アンケートの『年収』が無回答なら、平均値で埋めるよりも『非公開』という新しいカテゴリを作ったほうが分析として正しい場合もあります。」
生徒:
「なるほど、ただ機械的に埋めるんじゃなくて、データの背景を考えるのが大事なんですね。あと、inplace=Trueについても勉強になりました。最初、コードを書いても元のデータが変わっていなくて焦ったのですが、理由がわかって安心しました。」
先生:
「それは初心者が必ず通る道ですよ(笑)。Pandasは安全のために、元のデータを壊さないように『コピー』を返す設計になっていることが多いんです。もし不安なら、最初はdf2 = df.fillna(0)のように別の変数に保存して、中身を確認しながら進めるのがおすすめですよ。」
生徒:
「分かりました!まずは慎重に、でも大胆にデータを掃除していこうと思います。Pythonでのデータ分析がもっと楽しくなりそうです!」
先生:
「素晴らしい意気込みですね。欠損値の処理ができるようになれば、次はデータの並び替えやグループ化など、もっと面白い操作がたくさん待っています。これからも一歩ずつ進んでいきましょう。応援していますよ!」
生徒:
「はい!ありがとうございます。次は複雑な条件でのフィルタリングにも挑戦してみたいです!」
※この会話は、Pandasの基礎を学ぶためのイメージです。実際のデータ分析では、ドメイン知識(その分野の専門知識)を組み合わせて判断することが推奨されます。