pandas DataFrame内にNaNありますか？

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





お題は「pandas DataFrame内にNaNありますか？」
データがちゃんと処理されているかの簡易的なチェックとして、**データフレーム内にNaN値あるか、それがどこにあるか**調べたい。
NaNを埋めたい/消したいなら`fillna()`/`dropna()`使えばいいのだけど、今ここでやりたいのは**「NaNがあるか調べて、その行(列)を表示すること」**

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

```python:データ作成
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](http://pandas.pydata.org/pandas-docs/stable/missing_data.html)

isnullメソッドを使う

```python: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`メソッド**





```python: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を二つ重ねる。

```python: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](http://stackoverflow.com/questions/29530232/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を含む列だけ抜き出せる。

```python: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
```

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


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