Python pandas データ選択処理をちょっと詳しく <前編>
概要
書いていて長くなったため、まず前編として pandas で データを行 / 列から選択する方法を少し詳しく書く。特に、個人的にはけっこう重要だと思っている loc と iloc について 日本語で整理したものがなさそうなので。
サンプルデータの準備
import pandas as pd s = pd.Series([1, 2, 3], index = ['I1', 'I2', 'I3']) df = pd.DataFrame({'C1': [11, 21, 31], 'C2': [12, 22, 32], 'C3': [13, 23, 33]}, index = ['I1', 'I2', 'I3']) s # I1 1 # I2 2 # I3 3 # dtype: int64 df # C1 C2 C3 # I1 11 12 13 # I2 21 22 23 # I3 31 32 33
__getitem__ での選択
Series, もしくは DataFrame に対して、__getitem__ でアクセスした場合の挙動は以下のようになる。
Series:indexからの選択DataFrame:columnsからの選択 (例外あり、下表 * の箇所)
なんかあいまいな言い方なのは 下表のとおりいくつかの形式で index, columns を指定できるから。使える形式は Series と DataFrame で少し違うので注意。
Series |
DataFrame |
|---|---|
index の名前 (もしくは名前のリスト) |
columns の名前 (もしくは名前のリスト) |
index の順序 (番号) (もしくは番号のリスト) |
columns の順序 (番号)のリスト (番号のみはダメ) ただし、slice を渡した場合は index からの選択 * |
indexと同じ長さの bool のリスト |
indexと同じ長さの bool のリスト * |
| - | データと同じ次元の bool の DataFrame |
※ 上で "リスト" と書いている箇所は numpy.array, pd.Series, slice なんかのリスト - like でもよい。
11/15追記: 元データと同じ次元の DataFrame が渡せることの記載漏れ、またDataFrame へ slice を渡した場合の挙動が間違っていたので修正。
s[0] # 1 s['I1'] # 1 df['C1'] # I1 11 # I2 21 # I3 31 # Name: C1, dtype: int64 # 番号を数値として渡すとNG! df[1] # KeyError: 1 # 番号のリストならOK (columns からの選択) df[[1]] # C2 # I1 12 # I2 22 # I3 32 # 番号のスライスもOK (index からの選択) df[1:2] # C1 C2 C3 # I2 21 22 23 # NG! df['I1'] # KeyError: 'I1' s[[True, False, True]] # I1 1 # I3 3 # dtype: int64 df[[True, False, True]] # C1 C2 C3 # I1 11 12 13 # I3 31 32 33 # bool の DataFrame を作る df > 21 # C1 C2 C3 # I1 False False False # I2 False True True # I3 True True True # bool の DataFrame で選択 df[df > 21] # C1 C2 C3 # I1 NaN NaN NaN # I2 NaN 22 23 # I3 31 32 33
引数による 返り値の違い
引数を値だけ (ラベル, 数値)で渡すと次元が縮約され、Series では単一の値, DataFrame では Series が返ってくる。元のデータと同じ型の返り値がほしい場合は 引数をリスト型にして渡せばいい。
# 返り値は 値 s['I1'] # 1 # 返り値は Series s[['I1']] # I1 1 # dtype: int64 # 返り値は Series df['C1'] # I1 11 # I2 21 # I3 31 # Name: C1, dtype: int64 # 返り値は DataFrame df[['C1']] # C1 # I1 11 # I2 21 # I3 31
index, columns を元にした選択 ( ix, loc, iloc )
ix プロパティを使うと DataFrame で index, columns 両方を指定してデータ選択を行うことができる ( Series の挙動は __getitem__ と同じ)。
引数として使える形式は __getitem__ と同じだが、ix では DataFrame も以下すべての形式を使うことができる。
- 名前 (もしくは名前のリスト)
- 順序 (番号) (もしくは番号のリスト)
index, もしくはcolumnsと同じ長さのboolのリスト
ix はメソッドではなくプロパティなので、呼び出しは以下のようになる。
Series.ix[?]:?にはindexを特定できるものを指定DataFrame.ix[?, ?]:?にはそれぞれindex,columnsの順に特定できるものを指定
# 名前による指定 s.ix['I2'] # 2 df.ix['I2', 'C2'] # 22 # 順序による指定 s.ix[1] # 2 df.ix[1, 1] # 22 # 名前のリストによる指定 s.ix[['I1', 'I3']] # I1 1 # I3 3 # dtype: int64 df.ix[['I1', 'I3'], ['C1', 'C3']] # C1 C3 # I1 11 13 # I3 31 33 # bool のリストによる指定 s.ix[[True, False, True]] # I1 1 # I3 3 # dtype: int64 df.ix[[True, False, True], [True, False, True]] # C1 C3 # I1 11 13 # I3 31 33 # 第一引数, 第二引数で別々の形式を使うこともできる df.ix[1:, "C1"] # I2 21 # I3 31 # Name: C1, dtype: int64
DataFrame.ix の補足
DataFrameで第二引数を省略した場合は index への操作になる。
df.ix[1] # C1 21 # C2 22 # C3 23 # Name: I2, dtype: int64
DataFrame で columns に対して操作したい場合、以下のように第一引数を空にするとエラーになる ( R に慣れているとやりがち、、 )。第一引数には : を渡す必要がある (もしくは ixを使わず 直接 __getitem__ する )。
df.ix[, 'C3'] # SyntaxError: invalid syntax df.ix[:, 'C3'] I1 13 I2 23 I3 33 Name: C3, dtype: int64
引数による 返り値の違い
引数の型による返り値の違いは __getitem__ の動きと同じ。Series については挙動もまったく同じなので、ここでは DataFrame の場合だけ例示。
# 返り値は 値 df.ix[1, 1] # 22 # 返り値は Series df.ix[[1], 1] # I2 22 # Name: C2, dtype: int64 # 返り値は DataFrame df.ix[[1], [1]] # C2 # I2 22
ということで、だいたいの場合は ix を使えばよしなにやってくれる。
とはいえ
ラベル名 もしくは 番号 どちらかだけを指定してデータ選択したい場合もある。例えば、
index,columnsがint型であるindex,columnsに重複がある
df2 = pd.DataFrame({1: [11, 21, 31],
2: [12, 22, 32],
3: [13, 23, 33]},
index = [2, 2, 2])
df2
# 1 2 3
# 2 11 12 13
# 2 21 22 23
# 2 31 32 33
内部的には ix は ラベルを優先して処理を行うため、上記のデータについては以下のような動作をする。
indexに2を指定すると、3行目ではなくindexのラベルが 2 の行を選択columnsに[1, 2]を指定すると、2, 3列目ではなくcolumnsのラベルが 1, 2 の列を選択
df2.ix[2, [1, 2]] 1 2 2 11 12 2 21 22 2 31 32
つまり 上記のようなデータ、(特にデータによって index, columns の値が変わるとか、、) では ix を使うと意図しない挙動をする可能性がある。明示的に index, columns を番号で指定したい!というときには iloc を使う。
df2.iloc[2, [1, 2]] 2 32 3 33 Name: 2, dtype: int64 # 3列目は存在しないので NG! df2.iloc[2, 3] # IndexError: index out of bounds
同様に、明示的にラベルのみで選択したい場合は loc。
df2.loc[2, [1, 2]] 1 2 2 11 12 2 21 22 2 31 32 # ラベルが 1 の index は存在しないので NG! df.loc[1, 2] # KeyError: 'the label [1] is not in the [index]'
ix, iloc, loc については文法 / 挙動は基本的に一緒で、使える引数の形式のみが異なる。整理すると、
| 使える引数の形式 | ix |
iloc |
loc |
|---|---|---|---|
| 名前 (もしくは名前のリスト) | ○ | - | ○ |
| 順序 (番号) (もしくは番号のリスト) | ○ | ○ | - |
index, もしくは columns と同じ長さの bool のリスト |
○ | ○ | ○ |
一般に、pandas でスクリプトを書くような場合には index, columns をどちらの形式で指定して選択するか決まっているはず。そんなときは loc , iloc を使っておいたほうが安全だと思う。
プロパティによるアクセス
Series の index, DataFrame の columns はプロパティとしてもアクセスできる。が、オブジェクト自体のメソッド/プロパティと衝突した場合は メソッド/プロパティが優先されるので使わないほうがよい。例えば 上の ix を列名に持つデータがあったとすると、
s.I1 # 1 df3 = pd.DataFrame({'C1': [11, 21, 31], 'C2': [12, 22, 32], 'ix': [13, 23, 33]}, index = ['I1', 'I2', 'I3']) df3 # C1 C2 ix # I1 11 12 13 # I2 21 22 23 # I3 31 32 33 df3.C1 # I1 11 # I2 21 # I3 31 # Name: C1, dtype: int64 # NG! df3.ix <pandas.core.indexing._IXIndexer at 0x10bb31890>
bool のリストによってデータ選択ができるということは
pandas.Series や numpy.array への演算は原則 リスト内の各要素に対して適用され、結果は真偽値の numpy.array (もしくは Series ) になる。また、真偽値の numpy.array 同士で論理演算することもできる。
df.columns == 'C3' # array([False, False, True], dtype=bool) df.columns.isin(['C1', 'C2']) # array([ True, True, False], dtype=bool) (df.columns == 'C3') | (df.columns == 'C2') # array([False, True, True], dtype=bool)
そのため、上記のような条件式をそのまま行列選択時の引数として使うことができる。
df.ix[df.index.isin(['I1', 'I2']), df.columns == 'C3'] # C3 # I1 13 # I2 23
上の例ではあまりありがたみはないと思うが、外部関数で bool のリストを作ってしまえばどんなに複雑な行列選択でもできるのは便利。
まとめ
pandas で行 / 列からデータ選択するとき、
- 手元で対話的にちょっと試す場合は
ixが便利。 - ある程度の期間使うようなスクリプトを書く場合は 少し面倒でも
iloc,locが安全。
後編はもう少し複雑なデータ選択( where, query ) あたりを予定。
11/15追記: 取消線した内容とは違うが続きを書いた。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理
- 作者: Wes McKinney,小林儀匡,鈴木宏尚,瀬戸山雅人,滝口開資,野上大介
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/12/26
- メディア: 大型本
- この商品を含むブログ (9件) を見る