Python
numpy
pandas

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列目は抜き出されずエラー


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