Pandas isnull() / isna() / notnull() の違いと使い方 – 欠損値の有無を確認

こんにちは。前回は、Pandas DataFrameから条件を指定して特定の行をフィルタリング(抽出)する方法について学びました。比較演算子や論理演算子を使いこなすことで、目的のデータを自在に絞り込めるようになりましたね。

さて、これまでの記事では、比較的「きれいな」サンプルデータを使ってきました。しかし、現実の世界で扱うデータは、残念ながらいつも完璧ではありません。データ収集の過程での入力ミス、システムエラー、あるいは意図的に入力されなかった情報など、様々な理由でデータの一部が欠けていることがあります。

このような「データの欠け」を、データ分析の世界では「欠損値(けっそんち)」と呼びます。欠損値は、分析を進める上で様々な問題を引き起こす可能性があるため、データ分析や機械学習の前処理(データを整える作業)において、まず最初に取り組むべき重要な課題の一つです。

今回の記事では、この「欠損値」に焦点を当て、以下の内容を学びます。

  • そもそも欠損値とは何か、なぜデータに含まれるのか?
  • 欠損値が分析においてなぜ問題になるのか
  • Pandasを使って、DataFrameの中に欠損値があるかどうかを確認する基本的な方法 (.isnull(), .isna(), .notnull())。
  • DataFrameの列ごとに欠損値がいくつあるかを効率的に数える方法 (.isnull().sum())。
  • すでにお馴染みのinfo()メソッドを使って、欠損値の情報を得る方法(復習)。

今回は、まず「欠損値を見つけて理解する」ことに集中します。具体的な対処法(削除や補完など)については、次回の記事で詳しく扱います。データのお掃除、いわば「データクリーニング」の第一歩を踏み出しましょう!

欠損値とは何か? なぜ発生する?

欠損値とは、文字通り「値が存在しない」「欠けている」データのことを指します。何らかの理由で、本来データが入っているべきセルに値がない状態です。

Pandas(やデータ分析でよく使われるNumPyライブラリ)では、欠損値は多くの場合、NaNという特別な値で表現されます。NaNは “Not a Number” の略ですが、数値データだけでなく、文字列データなどの欠損もNaNで表されることが一般的です。(※文脈によってはNoneというPythonの組み込みオブジェクトが使われることもあります)

では、なぜデータに欠損値が発生するのでしょうか? 原因は様々です。

  • 入力漏れ・忘れ: アンケート調査で回答者が特定の質問に答えなかった、データ入力担当者が入力を忘れた、など。
  • データ収集時のエラー: センサーの故障で一時的にデータが取得できなかった、システムの不具合でデータが記録されなかった、など。
  • 意図的な欠損: 特定の条件下では値が存在しない(例:未婚者の配偶者情報)、プライバシー保護のために意図的に削除された、など。
  • データ統合時の不整合: 異なるデータソースを結合した際に、片方のデータにしかなかった情報が欠損値になる、など。

このように、欠損値はデータが生成される様々な段階で発生しうる、ごく自然な現象とも言えます。

なぜ欠損値に対処する必要があるのか?

「データが少し欠けているくらい、気にしなくても良いのでは?」と思うかもしれません。しかし、欠損値をそのままにしておくと、データ分析や機械学習において、以下のような問題を引き起こす可能性があります。

  • 計算エラー: 多くの数学・統計計算(合計、平均、相関係数など)は、NaNが含まれていると正しく計算できなかったり、結果自体がNaNになってしまったりします。これでは分析が進められません。
  • 分析結果の歪み: 欠損値を無視して分析を進めると、データ全体の傾向を誤って捉えてしまう可能性があります。例えば、特定のグループに欠損が多い場合、そのグループの特性が分析結果に十分に反映されないかもしれません。
  • 機械学習モデルの性能低下: 多くの機械学習アルゴリズムは、入力データに欠損値が含まれていると学習できなかったり、学習できても精度が著しく低下したりします。モデルを構築する前に、欠損値への適切な対処が不可欠です。
  • プログラムのエラー: 欠損値を想定していない処理を行うと、予期せぬエラーでプログラムが停止してしまうことがあります。

これらの問題を避けるため、データ分析や機械学習のプロジェクトでは、まずデータにどれくらい欠損値が含まれているかを把握し、その上で適切な対処(削除、補完など)を行うことが非常に重要なのです。

サンプルデータの準備(欠損値を含む)

欠損値の確認方法を学ぶために、意図的に欠損値(NaN)を含んだサンプルDataFrameを作成しましょう。ここでは、NaNを表現するためにNumPyライブラリのnp.nanを使用しますが、Pandasはこれを自動的に認識してくれます。(NumPyの詳しい説明はここでは省略します。np.nanは欠損値を表す特別な値、とだけ覚えておいてください)

import pandas as pd
import numpy as np # NaNを使うためにNumPyをインポート

# 欠損値を含むサンプルデータ
data_with_nan = {
    '名前': ['佐藤', '鈴木', '高橋', '田中', '伊藤', None, '山本'], # 文字列の欠損 (None -> NaNになる)
    '年齢': [35, 28, np.nan, 31, 25, 38, 29],      # 数値の欠損 (np.nan)
    '部署': ['営業部', '開発部', '営業部', '人事部', None, '営業部', '開発部'], # 文字列の欠損
    '評価スコア': [4.5, 4.8, 4.2, 4.0, 4.9, np.nan, 4.6]       # 数値の欠損
}

df_nan = pd.DataFrame(data_with_nan)

# 作成したDataFrameを表示
print(df_nan)

実行結果:

    名前    年齢   部署  評価スコア
0   佐藤  35.0  営業部    4.5
1   鈴木  28.0  開発部    4.8
2   高橋   NaN  営業部    4.2
3   田中  31.0  人事部    4.0
4   伊藤  25.0   None    4.9
5  None  38.0  営業部    NaN
6   山本  29.0  開発部    4.6

いくつかのセルにNaNNone(これもPandasでは欠損値として扱われます)が表示されているのが確認できますね。特に、数値列(年齢、評価スコア)にNaNが入ると、その列全体のデータ型が自動的に浮動小数点数(float)になる点に注意してください(整数型はNaNを表現できないため)。

このdf_nanを使って、欠損値の確認方法を見ていきましょう。

欠損値の有無をセルごとに確認する:.isnull() と .isna()

まず、DataFrameの各セルが欠損値(NaNNone)であるかどうかを、True/Falseで確認する方法です。これには.isnull()メソッドまたは.isna()メソッドを使います。

この2つのメソッド(.isnull().isna())は、機能的に全く同じです。どちらを使っても構いません。.isna()の方が比較的新しく追加された名前ですが(NA = Not Available の略)、.isnull()も広く使われています。ここでは両方示しますが、どちらか一方を覚えておけばOKです。

使い方:
DataFrame変数の後ろに.isnull()または.isna()をつけます。

# 各セルが欠損値かどうかを判定 (.isnull())
print(df_nan.isnull())

実行結果:

     名前     年齢     部署  評価スコア
0  False  False  False   False
1  False  False  False   False
2  False   True  False   False
3  False  False  False   False
4  False  False   True   False
5   True  False  False    True
6  False  False  False   False
# 各セルが欠損値かどうかを判定 (.isna())
print(df_nan.isna())

実行結果(.isnull() と全く同じ):

     名前     年齢     部署  評価スコア
0  False  False  False   False
1  False  False  False   False
2  False   True  False   False
3  False  False  False   False
4  False  False   True   False
5   True  False  False    True
6  False  False  False   False

どちらのメソッドも、元のDataFrameと同じ形状(行数・列数)で、各セルに対応する位置にTrue(欠損値の場合)またはFalse(欠損値でない場合)を持つ、ブール値のDataFrameを返します。
元のdf_nanNaNNoneだった箇所が、結果のDataFrameではTrueになっているのが分かりますね(赤色で強調)。

このブール値のDataFrameは、データ全体を俯瞰してどこに欠損値があるかを視覚的に確認するのに役立ちます。

欠損値でないことを確認する:.notnull()

逆に、各セルが欠損値ではないことを確認したい場合は、.notnull()メソッドを使います。これは.isnull()(または.isna())の結果を単純に反転させたもの(TrueFalseを入れ替えたもの)を返します。

使い方:
DataFrame変数の後ろに.notnull()をつけます。

# 各セルが欠損値でないかどうかを判定
print(df_nan.notnull())

実行結果:

     名前     年齢     部署  評価スコア
0   True   True   True    True
1   True   True   True    True
2   True  False   True    True
3   True   True   True    True
4   True   True  False    True
5  False   True   True   False
6   True   True   True    True

.isnull()の結果とTrue/Falseが逆になっていることが確認できます。

欠損値の数を列ごとに集計する:.isnull().sum()

.isnull()で欠損値の場所は分かりましたが、データが大きい場合、True/Falseの表を眺めても、具体的に「どの列に」「いくつ」欠損値があるのかを把握するのは大変です。

そこで非常に便利なのが、列ごとに欠損値の数を合計する方法です。これは、.isnull()で得られたブール値のDataFrameに対して、.sum()メソッドを適用することで実現できます。

なぜ.sum()で合計できるのでしょうか? Python(およびPandas)では、ブール値は計算時にはTrue1False0として扱われます。そのため、.isnull()の結果(True/Falseの列)に対して.sum()を適用すると、Trueの数、つまり欠損値の数が列ごとに合計されるのです。

使い方:
df.isnull().sum()のように、.isnull()(または.isna())の後ろに.sum()を繋げます。

# 列ごとの欠損値の数を集計
print(df_nan.isnull().sum())

実行結果:

名前        1
年齢        1
部署        1
評価スコア    1
dtype: int64

結果として、各列名とその列に含まれる欠損値の数がSeries形式で表示されました。

  • ‘名前’列には欠損値が1つ。
  • ‘年齢’列には欠損値が1つ。
  • ‘部署’列には欠損値が1つ。
  • ‘評価スコア’列には欠損値が1つ。

であることが一目で分かります。これは非常に便利で、データ分析の初期段階で必ずと言っていいほど実行される操作です。どの列にどれだけの欠損値があるかを把握することで、その後の対処方針(どの列を重点的に処理するかなど)を立てやすくなります。

欠損値の数をデータ全体で集計する:.isnull().sum().sum()

さらに、DataFrame全体で欠損値が合計いくつあるかを知りたい場合は、.sum()をもう一度適用します。

# DataFrame全体の欠損値の総数を集計
total_nan_count = df_nan.isnull().sum().sum()
print(f"DataFrame全体の欠損値の総数: {total_nan_count}")

実行結果:

DataFrame全体の欠損値の総数: 4

列ごとの欠損数の合計(1 + 1 + 1 + 1 = 4)が計算され、DataFrame全体に4つの欠損値があることが分かりました。

info() メソッドによる欠損値の確認(復習)

以前の記事でも紹介しましたが、info()メソッドも欠損値の情報を得るのに役立ちます。特に、各列の「Non-Null Count」(欠損値ではない値の数)を見ることで、間接的に欠損値の有無や数を確認できます。

# info()メソッドで欠損値の情報を確認
df_nan.info()

実行結果:


RangeIndex: 7 entries, 0 to 6
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   名前      6 non-null      object
 1   年齢      6 non-null      float64
 2   部署      6 non-null      object
 3   評価スコア  6 non-null      float64
dtypes: float64(2), object(2)
memory usage: 352.0+ bytes

出力結果を見てみましょう。

  • RangeIndex: 7 entries, 0 to 6: このDataFrameには全部で7行のデータがあることが分かります。
  • Data columns (total 4 columns):: 列は4つあります。
  • 各列のNon-Null Countを見ると、全ての列で6 non-nullとなっています(赤色で強調)。

全体の行数(7行)と、各列の非欠損値の数(6個)を比較することで、「各列に 7 – 6 = 1個 の欠損値があるな」と推測できます。これは、先ほどdf_nan.isnull().sum()で得た結果と一致しますね。

info()はデータ型やメモリ使用量など他の情報もまとめて表示してくれるため便利ですが、欠損値の数だけをピンポイントで知りたい場合は.isnull().sum()の方が直接的で分かりやすいでしょう。

まとめ

今回は、データクリーニングの第一歩として、Pandas DataFrameにおける「欠損値」の確認方法について学びました。

  • 欠損値とは: データが存在しない箇所のこと。Pandasでは主にNaNで表現される。入力漏れやシステムエラーなど、様々な原因で発生する。
  • 欠損値の問題点: 計算エラー、分析結果の歪み、機械学習モデルの性能低下などを引き起こすため、対処が必要。
  • セルごとの確認:
    • .isnull() / .isna(): 各セルが欠損値ならTrue、そうでなければFalseのブール値DataFrameを返す(両者は同じ機能)。
    • .notnull(): .isnull()と逆の結果(欠損値でなければTrue)を返す。
  • 欠損値の数の集計:
    • .isnull().sum(): 列ごとに欠損値の数を集計する。データ分析の初期に非常に良く使う重要なコマンド。
    • .isnull().sum().sum(): DataFrame全体の欠損値の総数を計算する。
  • info()による確認: 全体の行数と各列のNon-Null Countを比較することで、欠損値の数を把握できる。

まずはデータの中にどれくらい欠損値が存在するのか、どの列に多いのかを正確に把握することが、データクリーニングの出発点です。今回学んだ.isnull().sum()は特に使用頻度が高いので、しっかりと覚えておきましょう。

次回予告

欠損値の存在を確認できるようになったところで、次はいよいよその欠損値にどう対処するかという、具体的な処理方法について学びます。

欠損値を含む行や列を削除する.dropna()メソッドや、欠損値を他の値(平均値、中央値、固定値など)で埋める(補完する).fillna()メソッドの使い方を、具体的な例とともに解説していきます。適切な欠損値処理を行うことで、より信頼性の高い分析やモデル構築が可能になります。お楽しみに!