DataFrameをインデクスラベルではなく行番号で指定したい場合にはixではなくilocを使う方が無難

長い時間バグの原因がわからずハマって相当イライラさせられたのでメモ。これのおかげでixとilocの違いがわかった気がする。
こんなDataFrameを用意する。インデクス名が行番号と一致していない。けれどint型(ここがポイント)。DataFrameから部分的にDataFrameを取り出した場合に起こりうる状況である。

>>> d = pd.DataFrame([[1,2,3],[4,5,6], [7,8,9]], columns=['A','B','C'], index=[3,4,5])
>>> d
   A  B  C
3  1  2  3
4  4  5  6
5  7  8  9

さて、最初の2行を取り出したいなぁと思ってixを使って行指定をしてみる。

>>> d.ix[ [0,1] ]
    A   B   C
0 NaN NaN NaN
1 NaN NaN NaN

>>> d.ix[ [3] ]
   A  B  C
3  1  2  3

あれ? 値が全部NaNのDataFrameが返ってきた??インデクスに存在する値だとちゃんと返ってくる。

ilocで試すと期待どおりの結果が返ってくる。

>>> d.iloc[[0,1]]
   A  B  C
3  1  2  3
4  4  5  6

インデクスがint型でない場合にはixは行番号指定と判断されるので、ilocと挙動が同じになる。

>>> d.index=['E','F','G']
>>> d
   A  B  C
E  1  2  3
F  4  5  6
G  7  8  9

>>> d.ix[ [0,1] ]
   A  B  C
E  1  2  3
F  4  5  6

>>>: d.iloc[ [0,1] ]
   A  B  C
E  1  2  3
F  4  5  6

どうやらインデクスラベルがintの場合には、ixを使って行番号指定ができなくなる模様。公式にもそんなことが書いてある。

.ix supports mixed integer and label based access. It is primarily label based, but will fall back to integer positional access unless the corresponding axis is of integer type.

(http://pandas.pydata.org/pandas-docs/stable/indexing.html)

DataFrameは気を抜くと行列と思ってしまって、ついついndarrayの行指定の感覚でixを使ってしまっていたけれど、ixはあくまでインデクスラベル指定の方法だと思っておいた方がよさそう。明示的にインデクスラベルで指定する場合にはloc。


なお、Pandas本(?)にはこのことは書かれていなかった。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理