YutaKaのPython教室

Python の文法やライブラリ、API、環境構築について画像・動画・ソースコード付きで徹底解説!

pandas 欠損値NaN処理一覧|抽出、除去、補間

pandasを使用すると欠損値NaNを効率的かつ高速に処理することができます。

しかし、実際に欠損値NaNの処理をしようとしても、次のような疑問に直面してしまうことも…。

  • NaNのある行や列を抽出する方法は?
  • NaNのある行や列を削除したい!
  • NaNを他の値で置き換えたり、線形補間したりするにはどうするの?

そこで、pandasの欠損値NaNの抽出方法、除去方法、補間方法について、実例付きでわかりやすく解説していきます。

そもそもpandasの欠損値NaNとは?

NaNはNot a Numberの略称で、一般に欠損値と呼ばれます。

データ内で有効値が欠けているため、欠損値と呼ぶわけですね。

欠損値は主に次のようなデータがあるときに発生します。

  • None:pythonのNone
  • np.nan:NumPyのnan
  • 空白:リストなどで、一部空白となっている場合
  • ExcelやCSVなど外部データを読み込んだ際に無効データがある場所など

実際にサンプルコードで、None, np.nan, 空白から欠損値が生じる例を見てみましょう。

import pandas as pd
import numpy as np
 
data = [[1,   2,   None], # NoneからNaN発生
        [4,   5, np.nan], # np.nanからNaN発生
        [7,   8,       ], # 空白からNaN発生
        [10, 11,     12]]
 
index = ["Row1", "Row2", "Row3", "Row4"]
columns = ["Col1", "Col2", "Col3"]
 
pd.DataFrame(data, index=index, columns=columns)
#       Col1  Col2  Col3
# Row1     1     2   NaN
# Row2     4     5   NaN
# Row3     7     8   NaN
# Row4    10    11  12.0

pandasには、これらの欠損値NaNを除去したり、補間したりする機能が豊富にそろっています。

以下では、次の順番で欠損値処理を解説していきます。

  • NaNの個数・位置確認
  • NaNを含む行・列の抽出
  • NaNを含む行・列の除去
  • NaNの補間方法

欠損値処理の基本を理解して、データ分析を効率的に行っていきましょう!

ここではDataFrameを例に欠損値処理を紹介していきますが、多くの処理内容はSeriesでも使用できるので、ぜひ参考にしてください。

また、サンプルデータのようにリストからDataFrameを生成する方法は次の記事で解説しています。

≫pandas リストからDataFrameを生成|インデックスとコラムの設定も!
pandasでは、リストからデータフレームを生成することもできます。しかし、pandasの操作に慣れていないうちは、ちょっとした操作も難しいですよね。この記事では、①リストからデータフレームを生成、行と列を追加する方法、②インデックス名、コラム名を設定する方法、③DataFrameを転値する方法(行と列が期待と逆だった時の対応)をサンプルコード付きで紹介!
www.yutaka-note.com/entry/pandas_list
 

NaNの個数・位置確認

まずはDataFrameが欠損値を持っているか、欠損値の位置はどこかを確認する方法を紹介していきます。

下表が欠損値の個数・位置確認方法のサマリー表です。

処理内容 メソッドやプロパティ 出力形式
各行・列(Series)の欠損値有無 df.loc["行名"].hasnans
df["列名"].hasnans
  • True:欠損値あり
  • False:欠損値なし
欠損値の位置確認 df.isna() or df.isnull() dfと同じ形状の真偽値DataFrame
  • True:欠損値
  • False:有効値
有効値の位置確認 df.notna() dfと同じ形状の真偽値DataFrame
  • True:有効値
  • False:欠損値
行・列の欠損値有無を一覧出力 列ごと:df.isna().any()
行ごと:df.isna().any(axis=1)
行または列名をラベルに持つSerise
  • True:有効値
  • False:欠損値
行・列の欠損値数を出力 列ごと:df.isna().sum()
行ごと:df.isna().sum(axis=1)
行または列名をラベルに持つSerise
  • 要素は欠損値数

ここでは、次のように欠損値を持ったサンプルデータを使用していきます。

df
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row2     4   NaN   NaN
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0

各行・列の欠損値有無確認|.hasnan

行や列名を指定して列を抽出すると、その列のhanans属性で欠損値の有無を確認できます。

  • df["列名"].hasnans
  • df.loc["行名"].hasnans
    • Trueは欠損値あり
    • Falseは欠損値なし

.hasnansはもともとSeriesのプロパティです。

行や列は単独で抽出されると、Seriesで返されるため、.hasnans属性で欠損値有無を確認できます。

df["Col1"].hasnans
False
 
df["Col3"].hasnans
True
 
df.loc["Row2"].hasnans
True

特定の行、列の欠損値有無を確認する場合は、この処理で十分です。

とは言っても、実際には欠損値の有無だけではなくて、その場所の確認が必要な場合が多いですね。

そういった場合は、次に紹介する方法を使用します。

欠損値の位置確認|.isna()

df.isna()メソッドを使用すると、dfと同じ形状の真偽値DataFrameを返します。

  • 欠損値の位置:df.isna() or df.isnull()
    • True:欠損値
    • False:有効値

.isnull().isna()のエイリアス(別名)なので、どちらを使っても挙動は同じです。

df.isna()
#        Col1   Col2   Col3
# Row1  False  False  False
# Row2  False   True   True
# Row3  False  False   True
# Row4  False  False  False

逆に欠損値以外の場所(有効値)をTrueとして返す場合は、df.notna()を使用します。

  • 有効値の位置:df.notna()
    • True:有効値
    • False:欠損値

df.isna()TrueFalseが逆のDataFrameが出力されます。

df.notna()
#       Col1   Col2   Col3
# Row1  True   True   True
# Row2  True  False  False
# Row3  True   True  False
# Row4  True   True   True

df.notna()df.isna()は、真偽値が逆になるので、自分がどちらを使っているのかわからなくならないように注意しましょう。

真偽値をうまく使うと、欠損値の数を数えたり、行・列を抽出したりできます。

まずは、欠損値の個数を数える方法を確認してみましょう。

行・列の欠損値有無を一覧出力|.isna().any()

各行、列内に一つでも欠損値が存在するか否かを判定する場合は、.any()メソッドを使用します。

.any()メソッドは列内に一つでもTrueがあるとTrueを返すため、一つでも欠損値があると.isna().any()Trueを返します。

  • 列ごとの欠損値有無出力:df.isna().any()
  • 行ごとの欠損値有無出力:df.isna().any(axis=1)

デフォルトでは列方向に走査しますが、axis=1を指定すると行方向に走査します。

df.isna().any()
<a id="_Hlk102753377"></a># Col1    False
# Col2     True
# Col3     True
# dtype: bool
 
df.isna().any(axis=1)
# Row1    False
# Row2     True
# Row3     True
# Row4    False
# dtype: bool

どの行・列に欠損値があるかをざっと確認したいときに便利ですね。

行・列の欠損値数を出力|.isna().sum ()

df.isna()を使用すると、欠損値をTrueとする真偽値DataFrameが出力されます。

真偽値を数値として扱うと次のように振る舞います:

  • True1
  • False0

そのため、Trueの合計数から欠損値の個数を集計できます。

行方向、列方向の合計数の算出には.sum()メソッドを使用します。

  • 列ごとの欠損値数出力:df.isna().sum()
  • 行ごとの欠損値数出力:df.isna().sum(axis=1)

.sum()メソッドはデフォルトでは列方向に合計を計算、axis=1を指定すると行方向に合計を計算します。

df.isna().sum()
# Col1    0
# Col2    1
# Col3    2
# dtype: int64
 
df.isna().sum(axis=1)
# Row1    0
# Row2    2
# Row3    1
# Row4    0
# dtype: int64

類似手法として、平均値を出力すると各行・列内の総データ数に対する欠損値の割合を算出することもできます。

df.isna().mean()
# Col1    0.00
# Col2    0.25
# Col3    0.50
# dtype: float64

ざっと見でも"Col3"に欠損値が多いことがわかりますね。

NaNのある行、列の抽出

欠損値の位置がわかっていても、なかなかわかりにくいのが欠損値のある行・列の抽出です・・・。

しかし、欠損値のある行・列を抽出する場合は、次の手順を踏むとそれほど難しくないです。

  1. 欠損値を持つ行na_rowまたは列na_colの特定
  2. df.loc[na_row, :]またはdf.loc[:, na_col]で行・列を抽出

行・列のパターン別にテンプレートを整理すると下表のようになります。

抽出対象 ① 行・列内のNaNの有無(真偽値Series ② 行・列の抽出
行内に一つでもNaNのある行 na_row = df.isna().any(axis=1) df.loc[na_row, :]
特定の列にNaNのある行 na_row = df["列名"].isna() df.loc[na_row, :]
列内に一つでもNaNのある列 na_col = df.isna().any() df.loc[:, na_col]
特定の行にNaNのある列 na_col = df.loc["行名"].isna() df.loc[:, na_col]

次のDataFrameを使用して、欠損値行、列の抽出の実行例を確認していきましょう。

df
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row2     4   NaN   NaN
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0

NaNのある行の抽出

まずは、行内に一つでもNaNのある行の抽出方法を紹介します。

行内に一つでも欠損値が存在するか否かを確認するには、.isna().any(axis=1)を使用します。

  1. na_row = df.isna().any(axis=1)で各行のNaN有無を評価
  2. na_rowをもとにdf.loc[na_row, :]で行を抽出

サンプルデータで実行例を確認してみましょう。

# ①欠損値を持つ行na_rowの特定
na_row = df.isna().any(axis=1)
# Row1    False
# Row2     True
# Row3     True
# Row4    False
# dtype: bool
 
# ②df.loc[na_row, :]で行を抽出
df.loc[na_row, :]
#       Col1  Col2  Col3
# Row2     4   NaN   NaN
# Row3     7   8.0   NaN

欠損値のある行さえ特定してしまえば、あとは単にdf.loc[]で参照しているだけですね。

次に特定の列に欠損値がある行を抽出する方法を確認してみましょう。

  1. na_row = df["列名"].isna()で、"列名"内のNaNの有無を評価
  2. na_rowをもとにdf.loc[na_row, :]で行を抽出

欠損値有無の評価結果をもとに、df.loc[na_row, :]で行を抽出すればOKですね。

サンプルデータで実行例を確認してみましょう。

# ①列を指定して各行のNaNの有無を評価
na_row = df["Col2"].isna()
# Row1    False
# Row2     True
# Row3    False
# Row4    False
# Name: Col2, dtype: bool
  
# ②df.loc[na_row, :]で行を抽出
df.loc[na_row, :]
#       Col1  Col2  Col3
# Row2     4   NaN   NaN

DataFrameの要素へのアクセス方法については、次の記事で詳しく解説しています。

≫pandas インデックス列の基本操作|要素にアクセス、検索、欠損値処理
pandasのDataFrameでは、インデックス列の操作方法に関して、網羅的に解説!①インデックスの基本構造②インデックス内要素へのアクセス方法③インデックス内のデータ検索、並べ替え、重複処理、欠損値処理。サンプルコード付きでわかりやすく解説!
www.yutaka-note.com/entry/pandas_index_manip
 

NaNのある列の抽出

まずは、列内に一つでもNaNのある列の抽出方法を紹介します。

列内に一つでも欠損値が存在するか否かを確認するには、.isna().any()を使用します。

  1. na_col = df.isna().any()で各列のNaN有無を評価
  2. na_colをもとにdf.loc[:, na_col]で列を抽出

サンプルデータで実行例を確認してみましょう。

# ①欠損値を持つ列na_colの特定
na_col = df.isna().any()
# Col1    False
# Col2     True
# Col3     True
# dtype: bool
 
# ②df.loc[:, na_col]で行を抽出
df.loc[:, na_col]
#       Col2  Col3
# Row1   2.0   3.0
# Row2   NaN   NaN
# Row3   8.0   NaN
# Row4  11.0  12.0

欠損値のある列さえ特定してしまえば、あとは単にdf.loc[]で参照しているだけですね。

次に特定の列に欠損値がある行を抽出する方法を確認してみましょう。

  1. na_col = df.loc["行名"].isna()で、"行名"内のNaNの有無を評価
  2. na_colをもとにdf.loc[:, na_col]で列を抽出

欠損値有無の評価結果をもとに、df.loc[:, na_col]で列を抽出すればOKですね。

サンプルデータで実行例を確認してみましょう。

# ①行を指定して各列のNaNの有無を評価
na_col = df.loc["Row3"].isna()
# Col1    False
# Col2    False
# Col3     True
# Name: Row3, dtype: bool  
 
# ②df.loc[:, na_col]で行を抽出
df.loc[:, na_col]
#       Col3
# Row1   3.0
# Row2   NaN
# Row3   NaN
# Row4  12.0

欠損値を含む行・列の除去

.dropna()メソッドを使用すると、欠損値を含む行または列を簡単に除去することができます。

.dropna()のデフォルト設定では、NaNを一つでも含む行が削除されますが、引数で除去方法の詳細を設定できます。

指定内容 引数 設定例
行または列どちらを除去するか指定 axis
  • axis=0 or "index"(デフォルト):行を除去
  • axis=1 or "columns":列を除去
NaNを含む行・列の除去基準 how
  • how="any"(デフォルト):一つでもあると除去
  • how="all":全てNaNだと除去
NaNを調べる行・列指定 subset
  • subset="列名" or "行名"
  • subset =["列名" or "行名"のリストなど]
行・列を残すのに必要な有効値の個数 thresh
  • thresh = "最低限必要な有効値の個数"
もとのオブジェクトを更新するか否か inplace
  • False:新たにDataFrameを生成
  • True:もとのDataFrame自身を更新

次のDataFrameを使用して、欠損値の除去の実行例を確認していきましょう。

df
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row2     4   NaN   NaN
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0

行・列どちらを除去か指定|axis

デフォルト設定では、NaNを一つでも含む行が除去されます。

axisを設定することで、行ではなく、列を除去するように設定できます。

  • axis=0 or "index"(デフォルト):行を除去
  • axis=1 or "columns":列を除去

デフォルト設定の挙動をみてみましょう。

df.dropna() # df.dropna(axis=0) or df.dropna(axis="index")でも同様
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row4    10  11.0  12.0
print(df.dropna())

この例では、NaNが含まれている"Row2", "Row3"が除去されていますね

次にaxis="columns"を指定して、列を除去してみましょう。

df.dropna(axis=1) # df.dropna(axis="columns")でも同様
#       Col1
# Row1     1
# Row2     4
# Row3     7
# Row4    10
print(df.dropna(axis=1))

NaNが含まれている"Col2", "Col3"が除去されていますね

全てNaNで除去|how

デフォルト設定では、NaNが一つでも含まれている行が削除されます。

how="all"を指定すると、行の内容が全てNaNの行だけが削除されます。

  • how="any"(デフォルト):一つでもあると除去
  • how="all":全てNaNだと除去

挙動を確認するためにサンプルデータを少し改変して、全ての要素がNaNの行をもつdf2を作成します。

df2 = df.copy()
df2.loc["Row2", "Col1"] = None
#       Col1  Col2  Col3
# Row1   1.0   2.0   3.0
# Row2   NaN   NaN   NaN
# Row3   7.0   8.0   NaN
# Row4  10.0  11.0  12.0

このdf2how="any""all"の挙動をそれぞれ確認してみましょう。

# how="any"(デフォルト):一つでもあると除去
df2.dropna(how="any")
#       Col1  Col2  Col3
# Row1   1.0   2.0   3.0
# Row4  10.0  11.0  12.0
 
# how="all":全てNaNだと除去
df2.dropna(how="all")
#       Col1  Col2  Col3
# Row1   1.0   2.0   3.0
# Row3   7.0   8.0   NaN
# Row4  10.0  11.0  12.0

how="any"では"Row2""Row3"が除去されていますが、how="all"のではRow2だけが除去されていますね。

NaNを調べる行・列指定|subset

デフォルト設定では、全ての行・列内のNaNを調査して、一つでもNaNがあると行を除去します。

subsetNaNを調査する行・列を指定することができます・

  • 単独の行・列を指定:subset="列名" or "行名"
  • 複数の列・行を指定:subset =["列名" or "行名"のリストなど]
    • axis=0 or "index"のときは"列名"を指定
    • axis=1 or "columns"のときは"行名"を指定

subsetで、調査する行・列を指定した場合の挙動を確認してみましょう。

# デフォルト設定:全ての列がNaN調査対象
df.dropna()
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row4    10  11.0  12.0
 
# subset=["Col1", "Col2"]で、"Col1", "Col2"内のNaNを調査
df.dropna(subset=["Col1", "Col2"])
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0

subset=["Col1", "Col2"]では、"Col3"NaNは調査対象外になっています。

そのため、"Col3"NaNを含む"Ro3"が除去されずに残ったのですね。

有効値の最低必要数|thresh

threshを指定すると、行・列を保持するために必要最低限の有効値の数を指定できます。

  • thresh = "最低限必要な有効値の個数"

threshNaNの数ではなく、行・列内の有効値が不足する場合に行・列を除去するための引数です。

欠損値ではなく、有効値を基準にした考え方という点に注意しましょう。

# thread=2、有効値の2個以上の行のみ保持
df.dropna(thresh=2)
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0
 
# thread=3で、有効値の3個以上の行のみ保持
# df.dropna(thresh=3)
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row4    10  11.0  12.0

df更新 or 新規作成|inplace

デフォルト設定では、.dropna()NaNを除去した新たなDataFrameを生成します。

inplaceを指定すると、新たにDataFrameを生成するか、もとのDataFrameを更新するか指定できます。

  • False:新たにDataFrameを生成
  • True:もとのDataFrame自身を更新

サンプルデータで挙動を確認してみましょう。

# デフォルト設定:新たにDataFrameを生成。もとのdfは変更されない。
df_res = df.dropna()
print(df)
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row2     4   NaN   NaN
# Row3     7   8.0   NaN
# Row4    10  11.0  12.0
 
print(df_res)
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row4    10  11.0  12.0
 
# inplace=True:もとのDataFrame自身を更新
df.dropna(inplace=True)
print(df)
#       Col1  Col2  Col3
# Row1     1   2.0   3.0
# Row4    10  11.0  12.0

欠損値の補間|.fillna()

.fillna()メソッドを使用すると、欠損値を他の値で補間することができます。

.fillna()のデフォルト設定では、NaNを指定した値で補間しますが、引数で除去方法の詳細を設定できます。

また、.interploate()メソッドを使用すると前後のデータからNaNを線形補間することも可能です。

補間方法 指定方法 設定例
特定の数値で一括補間 .fillna(数値) df.fillna(0)
NaN0で補間
行・列毎に異なる値で補間 .fillna(辞書やSeries)
  • 辞書={"列名1":補間値1,…}
  • Series="列名", 補間値の組み合わせ
fill_dict={"Col1":100, "Col2":200}
df.fillna(fill_dict)
"Col1"100, "Col2"200で補間
既存のDataFrameをもとに補間 .fillna(既存df) df.fillna(df_template)
dfの欠損値をdf_templateの値で補間
一つ前のデータで補間 .fillna(method="ffill")
  • df.fillna(method="ffill")
⇒一つ前の行の値で補間
  • df.fillna(method="ffill", axis=1)
⇒一つ前の列の値で補間
一つ後のデータで補間 .fillna(method="bfill")
  • df.fillna(method="bfill")
⇒一つ後の行の値で補間
  • df.fillna(method="bfill", axis=1)
⇒一つ後の列の値で補間
前後の値から線形補間 .interpolate() df["Data"].interpolate()
⇒欠損値が線形補間される

次のDataFrameを使用して、欠損値の補間の実行例を確認していきましょう。

df
#       Col1  Col2  Col3  Col4
# Row1   NaN   2.0   3.0   4.0
# Row2   5.0   NaN   NaN   8.0
# Row3   9.0  10.0   NaN  12.0
# Row4  13.0   NaN   NaN   NaN

特定の値で補間|.fillna(val)

特定の値でNaNを補間するパターンとしては次の3つがあります。

  • 特定の数値で一括補間|df.fillna(数値)
  • 行・列毎に異なる値で補間|df.fillna(辞書やSeries)
  • 既存のDataFrameをもとに補間|df.fillna(既存df)

最も簡単な方法は、特定の値で補間する方法です。

サンプルデータのNaN0で補間してみましょう。

df.fillna(0)
#       Col1  Col2  Col3  Col4
# Row1   0.0   2.0   3.0   4.0
# Row2   5.0   0.0   0.0   8.0
# Row3   9.0  10.0   0.0  12.0
# Row4  13.0   0.0   0.0   0.0

NaN0埋めは最も基本的な補間方法ですね。

次に行、列毎に異なる値で補間する例を確認してみましょう。

fill_dict = {"Col1":100, "Col2":200, "Col3":300}
df.fillna(fill_dict)
#        Col1   Col2   Col3  Col4
# Row1  100.0    2.0    3.0   4.0
# Row2    5.0  200.0  300.0   8.0
# Row3    9.0   10.0  300.0  12.0
# Row4   13.0  200.0  300.0   NaN

列毎に異なる補間値を設定する場合は、この方法が便利ですね。

列毎に異なる補間値を設定する方法の応用例として、列毎の平均値でNaNを補間する方法があります。

df.mean()は列毎の平均値をSeriesで返します。

col_mean = df.mean()
col_mean
# Data1    9.0
# Data2    6.0
# Data3    3.0
# Data4    8.0
# dtype: float64

列毎の平均値を持つSeriesで、もとのdfNaNを補間してみましょう。

df.fillna(col_mean)
#       Col1  Col2  Col3  Col4
# Row1   9.0   2.0   3.0   4.0
# Row2   5.0   6.0   3.0   8.0
# Row3   9.0  10.0   3.0  12.0
# Row4  13.0   6.0   3.0   8.0

簡単に平均値での穴埋めができましたね。

最後に既存のDataFrameNaNを補間する例を紹介します。

まずは補間用DataFrameを作成します。

data_fill = [[-1, -2,  -3, -4],
             [-5, -6,  -7,  -8],
             [-9, -10, -11,  -12],
             [-13,-14,  -15, -16]]
 
index = ["Row1", "Row2", "Row3", "Row4"]
columns = ["Col1", "Col2", "Col3", "Col4"]
 
df_fill = pd.DataFrame(data_fill, index=index, columns=columns)
df_fill
 
#       Col1  Col2  Col3  Col4
# Row1    -1    -2    -3    -4
# Row2    -5    -6    -7    -8
# Row3    -9   -10   -11   -12
# Row4   -13   -14   -15   -16

補間用DataFrameをもとにサンプルデータのNaNを補間してみましょう。

df.fillna(df_fill)
#       Col1  Col2  Col3  Col4
# Row1  -1.0   2.0   3.0   4.0
# Row2   5.0  -6.0  -7.0   8.0
# Row3   9.0  10.0 -11.0  12.0
# Row4  13.0 -14.0 -15.0 -16.0

既に類似データを所持している場合の補間には便利かもしれないですね。

前後の値で補間|.fillna(method="ffill")

dfの前後の値でNaNを補間する場合は、.fillna()のmethod引数を指定します。

補間方法 行方向のデータ採用 列方向のデータ採用
一つ前のデータ df.fillna(method="ffill") df.fillna(method="ffill", axis=1)
一つ後のデータ df.fillna(method="bfill") df.fillna(method="bfill", axis=1)

一つ前、一つ後の行のデータで補間する例を確認してみましょう。

# 補間前のデータ
df
#       Col1  Col2  Col3  Col4
# Row1   NaN   2.0   3.0   4.0
# Row2   5.0   NaN   NaN   8.0
# Row3   9.0  10.0   NaN  12.0
# Row4  13.0   NaN   NaN   NaN
 
# 一つ前のデータで補間
df.fillna(method="ffill")
#       Col1  Col2  Col3  Col4
# Row1   NaN   2.0   3.0   4.0
# Row2   5.0   2.0   3.0   8.0
# Row3   9.0  10.0   3.0  12.0
# Row4  13.0  10.0   3.0  12.0
 
# 一つ後のデータで補間
df.fillna(method="bfill")
#       Col1  Col2  Col3  Col4
# Row1   5.0   2.0   3.0   4.0
# Row2   5.0  10.0   NaN   8.0
# Row3   9.0  10.0   NaN  12.0
# Row4  13.0   NaN   NaN   NaN

前や後ろにデータがない場合は、NaNNaNのままですね。

次に列方向に補間する例を見てみましょう。

# 一つ前の列のデータで補間
df.fillna(method="ffill", axis=1)
#       Col1  Col2  Col3  Col4
# Row1   NaN   2.0   3.0   4.0
# Row2   5.0   5.0   5.0   8.0
# Row3   9.0  10.0  10.0  12.0
# Row4  13.0  13.0  13.0  13.0
 
# 一つ後の列のデータで補間
# df.fillna(method="bfill", axis=1)
#       Col1  Col2  Col3  Col4
# Row1   2.0   2.0   3.0   4.0
# Row2   5.0   8.0   8.0   8.0
# Row3   9.0  10.0  12.0  12.0
# Row4  13.0   NaN   NaN   NaN

前後の値から線形補間|.interpolate()

.interpolate()メソッドを使用すると、前後の値からNaNを線形補間することができます。

次のような欠損値を持つデータで実行例を確認してみましょう。

data = [0, 1, None, 9, None, 10, 36]
 
df = pd.DataFrame(data, columns=["Data"])
#    Data
# 0   0.0
# 1   1.0
# 2   NaN
# 3   9.0
# 4   NaN
# 5  10.0
# 6  36.0

グラフにすると、次のようにデータが欠損していることがわかります。

df.plot(y="Data", marker="o", linestyle="", markerfacecolor="None", markeredgecolor="blue", markersize=10)

.interpolate()メソッドで欠損値を補間してみましょう。

df["Linear"] = df["Data"].interpolate()
#    Data  Linear
# 0   0.0     0.0
# 1   1.0     1.0
# 2   NaN     5.0
# 3   9.0     9.0
# 4   NaN     9.5
# 5  10.0    10.0
# 6  36.0    36.0

無事、線形補間できていますね。

グラフにすると次のように、欠損値が埋められていることがわかります。

fig, ax = plt.subplots()
df.plot(ax=ax, y="Data", marker="o", linestyle="", markerfacecolor="None", markeredgecolor="blue", markersize=10)
df.plot(ax=ax, y="Linear", color="green", marker=".", linewidth=0.5, markerfacecolor="None")

DataFrameのグラフ化については、次の記事で詳しく解説しています。ぜひ参考にしてみてください。

≫pandas plotによるグラフ化|サンプルでわかりやすく解説
pandasの.plot()メソッドを使用すると、簡単にDataFrameをグラフ化することができます。プロットするデータの指定方法、グラフの種類変更や軸の設定、Matplotlibとの連携について、サンプルコード付きで詳しく解説しています。この記事で、plotメソッドの使い方を一通り把握することができると思います!
www.yutaka-note.com/entry/pandas_plot
 

オススメ|データ分析勉強法

今回はpandasの欠損値NaNの処理について解説しました。

pandasは便利すぎて操作方法がわかりにくいことがよくあります…。

結局はコツコツ学ぶのが、pandasマスターの近道ですよね!≫【ブログカテゴリー:pandas】

私がこれまで勉強、実務で経験してきたことをもとに考えたおススメの勉強本の紹介記事も参考にしてみてください!

何から始めて、どうやってレベルアップしていけばいいのか、初心者の方にぜひおススメしたい本をまとめてみました。

≫独学でデータ分析を勉強するオススメ学習本
独学でのpythonデータ分析勉強に役立ったおススメ書籍を紹介していきます。業務でそれなりにデータ分析を行えるまで、いろいろな試行錯誤をしてきましたが、もし自分が今ゼロから勉強する立場ならどうするのがいいのか考えてみました。以下では、入門書、個別モジュール用、実践用の3つの視点でおススメ本を紹介していきます。
www.yutaka-note.com/entry/data_analysis
 

各本のレビューは次の記事にもまとめています!

≫【レビュー】「Pythonによるデータ分析入門」| pandas開発者によるpandasユーザーのためのpandasの教科書!
「Pythonによるデータ分析入門」を、最初から最後まで実際に実践してみたレビューです。具体的にどのようなことができるようになったかを実例付きで紹介します!・DataFrameの生成方法・欠損値の処理方法・グラフ化の方法気になる学習時間は…?
www.yutaka-note.com/entry/2019/12/07/230219
 
≫【レビュー】「Python実践データ分析100本ノック」|100本終えたらpandasが好きになっていた
Python実践データ分析100本ノックで、実際に100本終了したレビューです。pythonでのデータ分析の入門書としてかなりの良書だったと思います。・python2~3冊目に何を勉強しようか迷っている人・時間をかけずにデータ分析の基本を学びたい人・pandasへの抵抗を減らしたい人
www.yutaka-note.com/entry/nock_100