pandasの欠損値NaN・None・pd.NAの違い|isna・dropna・fillnaの使い方

pandasの欠損値は、数値の列ではNaN(np.nan)、object型の列ではNone、Int64・boolean・stringなどの「nullable型」ではpd.NAで表されます。どれもisna()で欠損として判定でき、dropna()で削除、fillna()・ffill()・bfill()で補完できます。

見た目は似ていても、3つは型も比較のふるまいも違います。この記事では、公式ドキュメント「Working with missing data」に沿って違いを整理し、検出・削除・補完のやり方を実際に動かした結果つきで説明します。コードはすべて Python 3.13・pandas 3.0.6・NumPy 2.5.4 で実行しました(2026年10月15日確認)。pandas 3.0 では文字列の列とfillnaの書き方が変わったので、古い記事のコードがそのまま動かない場合の直し方も載せています。

NaN・None・pd.NAの違い(早見表)

値 どこで使われるか 型 同じ値どうしの ==
np.nan(NaN) float64などNumPyの数値型。pandas 3.0では既定の文字列型(str)でも欠損はNaN float False
None object型の列。数値の列に入れるとNaNに置き換わる NoneType True
pd.NA Int64・Float64・boolean・stringなどのnullable型、ArrowDtype NAType <NA>(判定不能)
pd.NaT datetime64・timedelta64・Period NaTType False

公式ドキュメントでは、どの値が使われるかはデータ型(dtype)で決まると説明されています。つまり「どれを使うか」を自分で選ぶというより、列の型によって自動的に決まると考えるのが実態に近いです。判定は値そのものではなく isna() で行う、というのが3つに共通する一番大事なルールです。

NaN:数値の列の欠損値

NaN(Not a Number)は浮動小数点数の特別な値で、NumPyの np.nan として使います。整数のリストに1つでもNaNが混ざると、列全体が float64 になります。

import pandas as pd
import numpy as np

print(pd.__version__)
s = pd.Series([1, 2, np.nan, 4])
print(s)
print(type(np.nan))

実行結果:

3.0.6
0    1.0
1    2.0
2    NaN
3    4.0
dtype: float64
<class 'float'>

1・2・4 が 1.0・2.0・4.0 に変わっているのがポイントです。整数のまま欠損を持ちたい場合は、後で説明する Int64 型を使います。

None:Pythonの「値なし」

None はPython標準の「値がない」ことを表すオブジェクトです。pandasでは、どの型の列に入れたかで扱いが変わります。

import pandas as pd

s_obj = pd.Series([1, "a", None])
print(s_obj)

s_str = pd.Series(["A", "B", None, "D"])
print(s_str)
print(repr(s_str[2]))

実行結果:

0       1
1       a
2    None
dtype: object
0      A
1      B
2    NaN
3      D
dtype: str
nan

いろいろな型が混ざったobject型の列では None がそのまま残ります。一方、文字列だけの列はpandas 3.0から専用の文字列型(str)になり、None は NaN に置き換わります。公式の移行ガイド「Migration guide for the new string data type」にも、新しい文字列型では欠損値は常にNaNになると書かれています。

同じコードをpandas 2.3.3で動かすと、文字列の列も dtype: object のままで、欠損は None と表示されました。x is None で欠損を探しているコードは、pandas 3.0では見落としが出るので pd.isna(x) に書き換えてください。数値の列に None を入れた場合は、バージョンに関係なくNaNになります(次の例の最後の行)。

pd.NA:型を崩さない欠損値

pd.NA はpandas 1.0で加わった欠損値で、Int64(Iは大文字)・boolean・string などのnullable型で使われます。整数や真偽値の列に欠損があっても、型が float や object に変わりません。

import pandas as pd

s_int = pd.Series([1, 2, None, 4], dtype="Int64")
print(s_int)
print(s_int[2] is pd.NA)

s_bool = pd.Series([True, False, None], dtype="boolean")
print(s_bool)

s_plain = pd.Series([1, 2, None, 4])
print(s_plain.dtype)

実行結果:

0       1
1       2
2    <NA>
3       4
dtype: Int64
True
0     True
1    False
2     <NA>
dtype: boolean
float64

最後の行のように、dtypeを指定しないと従来どおり float64 になります。公式ドキュメントにも、nullable型は既定では使われないので dtype を明示する必要があると書かれています。なお pd.NA のふるまいは公式ドキュメント上「Experimental(実験的)」と明記されており、将来変わる可能性があります。

比較と論理演算での違い

3つの違いが一番はっきり出るのが、== での比較です。

import pandas as pd
import numpy as np

print(None == None)
print(np.nan == np.nan)
print(pd.NA == pd.NA)
print(pd.NA + 1)
print(True | pd.NA, False & pd.NA)
print(pd.isna(np.nan), pd.isna(None), pd.isna(pd.NA))
try:
    if pd.NA:
        pass
except TypeError as e:
    print("TypeError:", e)

実行結果:

True
False
<NA>
<NA>
True False
True True True
TypeError: boolean value of NA is ambiguous
  • None == None は True、np.nan == np.nan は False になる
  • pd.NA との比較や計算は、結果も <NA>(わからない)になる
  • True | pd.NA は、もう一方が何でも結果が決まるので True(三値論理)
  • if pd.NA: のように真偽を直接問うと TypeError になる

このため、Seriesを == np.nan と比べても欠損は1つも見つかりません。欠損を探すときは必ず isna() を使います。また、sum() や mean() などの集計は既定で欠損を飛ばして計算します。

import pandas as pd
import numpy as np

s = pd.Series([1.0, np.nan, 3.0])
print(s == np.nan)          # 比較では見つからない
print(s.isna())             # isna() なら見つかる
print(s.sum(), s.mean())    # 集計では欠損を飛ばす
print(s.sum(skipna=False))

実行結果:

0    False
1    False
2    False
dtype: bool
0    False
1     True
2    False
dtype: bool
4.0 2.0
nan

欠損値を見つける:isna()・notna()

isna() は欠損ならTrue、notna() は欠損でなければTrueを返します。isnull()・notnull() は同じ動きをする別名です。NaN・None・pd.NAのどれが入っていても同じように判定できます。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "点数": [80, np.nan, 65, np.nan],
    "名前": ["佐藤", None, "鈴木", "高橋"],
    "合格": pd.array([True, None, False, True], dtype="boolean"),
})
print(df)
print(df.dtypes)
print(df.isna())
print(df.isna().sum())
print(df.notna().all(axis=1))

実行結果:

     点数   名前     合格
0  80.0   佐藤   True
1   NaN  NaN   <NA>
2  65.0   鈴木  False
3   NaN   高橋   True
点数    float64
名前        str
合格    boolean
dtype: object
      点数     名前     合格
0  False  False  False
1   True   True   True
2  False  False  False
3   True  False  False
点数    2
名前    1
合格    1
dtype: int64
0     True
1    False
2     True
3    False
dtype: bool

よく使うのは次の3つです。

  • df.isna():セルごとの欠損の有無
  • df.isna().sum():列ごとの欠損の個数
  • df.notna().all(axis=1):欠損が1つもない行かどうか

列ごとの欠損以外の件数をざっと見たいときは df.info() の Non-Null Count も便利です。

欠損値を削除する:dropna()

dropna() は欠損を含む行(または列)を取り除いた新しいDataFrameを返します。元のDataFrameは変わりません。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "ID": [1, 2, 3, 4],
    "点数": [80, np.nan, 65, np.nan],
    "名前": ["佐藤", None, "鈴木", "高橋"],
})
print(df.dropna())                  # 欠損が1つでもある行を削除
print(df.dropna(subset=["名前"]))    # 名前が欠けている行だけ削除
print(df.dropna(axis=1))            # 欠損を含む列を削除
print(df.dropna(thresh=3))          # 欠損でない値が3つ以上ある行を残す

実行結果:

   ID    点数  名前
0   1  80.0  佐藤
2   3  65.0  鈴木
   ID    点数  名前
0   1  80.0  佐藤
2   3  65.0  鈴木
3   4   NaN  高橋
   ID
0   1
1   2
2   3
3   4
   ID    点数  名前
0   1  80.0  佐藤
2   3  65.0  鈴木
書き方 意味
dropna() 欠損が1つでもある行を削除
dropna(subset=["名前"]) 指定した列に欠損がある行だけ削除
dropna(axis=1) 欠損を含む列を削除
dropna(how="all") すべての値が欠損の行だけ削除
dropna(thresh=3) 欠損でない値が3つ以上ある行を残す

引数の一覧は公式リファレンスの DataFrame.dropna にあります。行を消すとデータ数が減るので、件数が少ないデータでは次の「補完」も検討してください。

欠損値を埋める:fillna()

fillna() は欠損を指定した値で置き換えます。列ごとに違う値で埋めたいときは辞書で渡すのが安全です。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "点数": [80, np.nan, 65, np.nan],
    "名前": ["佐藤", None, "鈴木", "高橋"],
})
print(df.fillna({"点数": 0, "名前": "不明"}))
print(df["点数"].fillna(df["点数"].mean()))

実行結果:

     点数  名前
0  80.0  佐藤
1   0.0  不明
2  65.0  鈴木
3   0.0  高橋
0    80.0
1    72.5
2    65.0
3    72.5
Name: 点数, dtype: float64

平均値で埋める場合は、まず df["点数"].mean() で平均を求め(欠損は自動で除外されます)、その値を fillna() に渡します。中央値なら median() に置き換えます。

前後の値で埋める:ffill()・bfill()

時系列データなどで「直前の値で埋めたい」ときは ffill()、「直後の値で埋めたい」ときは bfill() を使います。

import pandas as pd
import numpy as np

s = pd.Series([10, np.nan, np.nan, 40])
print(s.ffill())
print(s.bfill())
print(s.ffill(limit=1))
try:
    s.fillna(method="ffill")
except TypeError as e:
    print("TypeError:", e)

実行結果:

0    10.0
1    10.0
2    10.0
3    40.0
dtype: float64
0    10.0
1    40.0
2    40.0
3    40.0
dtype: float64
0    10.0
1    10.0
2     NaN
3    40.0
dtype: float64
TypeError: NDFrame.fillna() got an unexpected keyword argument 'method'

以前の記事でよく見かける fillna(method="ffill") は、pandas 2.1で非推奨になり(What’s new in 2.1.0)、pandas 3.0では引数そのものがなくなったため TypeError になります(DataFrame.fillna の引数一覧にも method はありません)。手元のコードに残っていたら、df.ffill()・df.bfill() に置き換えてください。limit=1 を付けると、連続する欠損のうち最初の1つだけを埋められます。

まとめてnullable型にそろえる:convert_dtypes()

列ごとにdtypeを指定するのが面倒なときは convert_dtypes() が便利です。公式リファレンスの説明どおり、pd.NAを扱える型に一括で変換します。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "点数": [80, np.nan, 65],
    "人数": [3, 5, 2],
    "名前": ["佐藤", None, "鈴木"],
})
print(df.dtypes)
df2 = df.convert_dtypes()
print(df2.dtypes)
print(df2)

実行結果:

点数    float64
人数      int64
名前        str
dtype: object
点数     Int64
人数     Int64
名前    string
dtype: object
     点数  人数    名前
0    80   3    佐藤
1  <NA>   5  <NA>
2    65   2    鈴木

小数点以下がない 点数 列は Int64 に、文字列は string になり、欠損はすべて <NA> 表示にそろいます。型がそろうと、整数がいつの間にか小数になる、といった事故を防げます。

pandas 3.0で変わった点のまとめ

pandas 3.0.0は2026年1月21日に公開されました(What’s new in 3.0.0)。欠損値まわりで影響が大きいのは次の3つです。

  • 文字列の列が既定で専用の str 型になり、欠損は None ではなく NaN で入る
  • fillna() の method 引数がなくなり、ffill()・bfill() を使う
  • fillna() に value=None を渡せるようになり、object以外の型ではその型の欠損値が使われる

公式の更新情報では、まずpandas 2.3に上げて警告が出ないことを確認してから3.0へ移るよう勧めています。どちらのバージョンでも動くコードにするには、欠損の判定を pd.isna() に統一するのが確実です。

よくある質問

Q. pandasでNaNとNoneの違いは何ですか?

NaNは浮動小数点数の特別な値で、数値の列の欠損に使われます。NoneはPython標準の「値なし」で、object型の列ではそのまま残りますが、数値の列に入れるとNaNに置き換わります。pandas 3.0では文字列の列でもNoneはNaNになります。

Q. pd.NAはいつ使えばいいですか?

整数や真偽値の列に欠損があっても型を変えたくないときです。dtype=”Int64″ や “boolean” を指定するか、convert_dtypes() で変換すると、欠損はpd.NAで表されます。

Q. 欠損値かどうかを == で判定してはいけないのはなぜですか?

np.nan == np.nan は False、pd.NA との比較は <NA> になるため、== では欠損を見つけられないからです。pd.isna()、Series.isna() を使えばNaN・None・pd.NAのどれでも判定できます。

Q. fillna(method=”ffill”) がエラーになります。

pandas 3.0で fillna() の method 引数が削除されたためです。df.ffill() または df.bfill() に書き換えてください。pandas 2.1〜2.3でも非推奨の警告が出ます。

Q. notna() と notnull() は違いますか?

同じ動きをする別名です。isna() と isnull() も同様です。公式ドキュメントは isna()・notna() を使って説明しています。

まとめ

  • 欠損値の表し方は列の型で決まる:数値はNaN、object型はNone、nullable型はpd.NA
  • 判定は == ではなく isna()・notna() で行う
  • 削除は dropna()、補完は fillna()・ffill()・bfill()
  • 整数や真偽値の型を保ちたいなら Int64・boolean か convert_dtypes()
  • pandas 3.0では文字列列の欠損がNaNになり、fillna(method=…) は使えない

欠損値の処理は、小さなDataFrameを作って結果を print しながら確かめるのが一番の近道です。一人で詰まりやすい人は、自分のペースで進めながらわからないところをトレーナーに質問できる通学型スクールのテックジム(東京本校は永田町)や、無料講座も活用してみてください。


pandas|欠損値の扱いの関連記事

NaNの判定・削除・補完など、欠損値処理に関する記事です。

pandas全体の使い方は「pandasの使い方を初心者向けに解説」でまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら