Edited at

pandas DataFrame内にNaNありますか?

More than 1 year has passed since last update.

ちょっと探して見つからず、何とかかんとかして出し方わかったのでメモ。

お題は「pandas DataFrame内にNaNありますか?」

データがちゃんと処理されているかの簡易的なチェックとして、データフレーム内にNaN値あるか、それがどこにあるか調べたい。

NaNを埋めたい/消したいならfillna()/dropna()使えばいいのだけど、今ここでやりたいのは「NaNがあるか調べて、その行(列)を表示すること」

例として、このデータフレームの2-4行目、または1-3列目だけを抜き出したい。


データ作成

df=pd.DataFrame(np.random.randn(5,5))

df.ix[2:, 1:3] = np.nan
df.columns=list('abcde')
df
#[Out]# a b c d e
#[Out]# 0 -0.678873 -1.277486 -1.062232 0.097525 -2.386115
#[Out]# 1 -1.063709 -1.919997 -0.131733 -0.606348 0.101888
#[Out]# 2 -1.701473 NaN NaN NaN 0.201468
#[Out]# 3 -0.624932 NaN NaN NaN -0.654297
#[Out]# 4 0.345065 NaN NaN NaN -0.232199


NaNをbool値で出力

NaNがあるかどうかはisnull()/notnull()を使う。以下参考

pandas公式によるNaNの扱い方: pandas 0.19.1 documentation » Working with missing data

isnullメソッドを使う


isnull()

df.isnull()

#[Out]# a b c d e
#[Out]# 0 False False False False False
#[Out]# 1 False False False False False
#[Out]# 2 False True True True False
#[Out]# 3 False True True True False
#[Out]# 4 False True True True False

返ってくるのはdfと同じ大きさでbool値の入ったデータフレーム。

NaNのところだけTrueになる。


notnull()はisnull()で返ってくるデータフレームのTrue/Falseが逆になったもの


ちょっとこれはやりたいことと違う


行(列)にNaNがあるかどうかまとめる

やりたいこと 「NaNがあるか調べて、その行(列)を表示すること」 を分解すると


  • NaNが一個以上ある行(列)を調べる

  • その行(列)をスライス/loc/ix/...で抜き出す

になるんじゃないかな。

ホニャララが一個以上ある、といえばnumpyのanyメソッド


np.any()

df.isnull().any()

#[Out]# a False
#[Out]# b True
#[Out]# c True
#[Out]# d True
#[Out]# e False
#[Out]# dtype: bool

df.isnull().any(axis=1)
#[Out]# 0 False
#[Out]# 1 False
#[Out]# 2 True
#[Out]# 3 True
#[Out]# 4 True
#[Out]# dtype: bool

df.isnull().any(axis=0) # df.isnull().any()と同じ
#[Out]# a False
#[Out]# b True
#[Out]# c True
#[Out]# d True
#[Out]# e False
#[Out]# dtype: bool

any()のデフォルトの走査方向は行方向(axis=0)なのでdf.isnull().any()は列にTrue(isnull()による変換で、すなわちNaN)が一個以上含まれるならTrue / 含まれないならFalseを返す。

any(axis=1)としてやると走査方向を変えて列方向(axis=1)にTrue(すなわちNaN)が含まれるかどうかを探す。


axis=は省略可能なので、df.isnull().any(1)と書いてもdf.isnull().any(axis=1)と同じ。



行列に一個でもNaNがあるか

少しやりたいこととは話が逸れて、どこか一箇所にでもNaNがあればTrueを返すようにするにはanyを二つ重ねる。


NaNが一個でも含まれる?

df.isnull().any().any()  # NaNが含まれている

#[Out]# True
dff=pd.DataFrame(np.random.randn(5,5)) # NaNが含まれていない
dff.isnull().any().any()
#[Out]# False

stack overflowにも同じようなことやってました。

stack overflow - Python pandas: check if any value is NaN in DataFrame

df.any().any()以外にも


  • df.isnull().values.sum()

  • df.isnull().sum().sum()

  • df.isnull().values.any()

とか使っている。

%timeitで計測して時間が最も早かったのはdf.isnull().values.any()

一個でもNaNが含まれるか知りたいときは使ってみましょう。


NaNが含まれる行(列)を抜き出す

やりたいことがようやくできる。

df.isnull().any(1)行にNaNが含まれるかどうかのbool値作成して、スライスすると、NaNを含む列だけ抜き出せる。


NaN含む行抜き出し

df[df.isnull().any(1)]

#[Out]# a b c d e
#[Out]# 2 -1.701473 NaN NaN NaN 0.201468
#[Out]# 3 -0.624932 NaN NaN NaN -0.654297
#[Out]# 4 0.345065 NaN NaN NaN -0.232199


NaN含む列抜き出し

df.ix[:,df.isnull().any()]

#[Out]# b c d
#[Out]# 0 -1.277486 -1.062232 0.097525
#[Out]# 1 -1.919997 -0.131733 -0.606348
#[Out]# 2 NaN NaN NaN
#[Out]# 3 NaN NaN NaN
#[Out]# 4 NaN NaN NaN

以上!

より簡単な方法もありそうだけど、ないんですかね。誰か教えてください。

あと、pandasの行だけの抜き出しにloc, ilocとかあるのに対して、列の抜き出しはdf.<カラム名>とかdf.ix[:, <カラム名>]とかあるけど、美しくないので何か美しいやり方(行のloc, ilocと対になる列のloc, iloc的なの)ないんですか(*ω*)

更新2017/4/15

df.icol(3)で3列目抜き出し

df.icol([0,2])で0,2列目抜き出し

df.icol([0:2])で0,1,2列目は抜き出されずエラー


コメント欄に速度比較を載せました。