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件) を見る