note.nkmk.me

pandasのMultiIndexから任意の行・列を選択、抽出

Date: 2018-02-01 / Modified: 2018-05-25 / tags: Python, pandas

pandas.DataFramepandas.Seriesのインデックスを階層的に構成できるマルチインデックス(階層型インデックス)を使うと、階層ごとに合計や平均などの統計量を算出できて便利。

マルチインデックスについてのもろもろの処理は以下の記事を参照。

マルチインデックスの行・列の選択(抽出)方法に若干クセがあるので注意が必要。

公式ドキュメントでは以下の節で説明されている。

例として以下のcsvデータを使用する。インデックス列にそれぞれlevel_xという名前が付いている。

import pandas as pd

df = pd.read_csv('data/src/sample_multi.csv', index_col=[0, 1, 2])
print(df)
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0          98     90
#                 C1          44      9
#         B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
#         B3      C2          47      6
#                 C3          16      5
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40
# A3      B2      C0          64     54
#                 C1          27     96
#         B3      C2         100     77
#                 C3          22     50

print(df.index)
# MultiIndex(levels=[['A0', 'A1', 'A2', 'A3'], ['B0', 'B1', 'B2', 'B3'], ['C0', 'C1', 'C2', 'C3']],
#            labels=[[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3], [0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]],
#            names=['level_1', 'level_2', 'level_3'])

ここでは以下の内容について説明する。

  • locで任意の行・列を選択、抽出
    • 特殊なスライス指定: slice(), pd.IndexSlice[]
  • xsメソッド
  • 選択範囲への値の代入
スポンサーリンク

locで任意の行・列を選択、抽出

通常のインデックスと同様にloc[]を使って任意の行・列を選択、抽出することができる。

例はindexがマルチインデックスになっているが、columnsがマルチインデックスになっている場合も同様の考え方。

上の階層(外側の階層)を選択する場合は、通常のloc[]と変わらない。

列全体を指定する場合、うしろのスライス:を省略できるが、あとで説明するslice(None)pd.IndexSliceを使う場合などは省略できない(エラーになる)ので、明示的に指定しておいたほうが無難。

print(df.loc['A0', 'val_1'])
# level_2  level_3
# B0       C0         98
#          C1         44
# B1       C2         39
#          C3         75
# Name: val_1, dtype: int64

print(df.loc['A0', :])
#                  val_1  val_2
# level_2 level_3              
# B0      C0          98     90
#         C1          44      9
# B1      C2          39     17
#         C3          75     71

print(df.loc['A0'])
#                  val_1  val_2
# level_2 level_3              
# B0      C0          98     90
#         C1          44      9
# B1      C2          39     17
#         C3          75     71

スライスやリストで範囲を選択することも可能。

print(df.loc['A0':'A2', :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0          98     90
#                 C1          44      9
#         B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
#         B3      C2          47      6
#                 C3          16      5
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40

print(df.loc[['A0', 'A2'], :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0          98     90
#                 C1          44      9
#         B1      C2          39     17
#                 C3          75     71
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40

上の階層(外側の階層)から順に値を指定して絞り込んでいくこともできる。タプルで順に値を指定する。

print(df.loc[('A0', 'B1'), :])
#          val_1  val_2
# level_3              
# C2          39     17
# C3          75     71

print(df.loc[('A0', 'B1', 'C2'), :])
# val_1    39
# val_2    17
# Name: (A0, B1, C2), dtype: int64

タプルで順に指定する場合、リストは使えるがスライスはエラーになる。

print(df.loc[(['A0', 'A1'], ['B0', 'B3']), :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0          98     90
#                 C1          44      9
# A1      B3      C2          47      6
#                 C3          16      5

# print(df.loc[(:, 'B1'), :])
# SyntaxError: invalid syntax

# print(df.loc[('A1':'A3', 'B2'), :])
# SyntaxError: invalid syntax

特殊なスライス指定: slice, pd.IndexSlice

マルチインデックスで上の階層(外側の階層)から順にタプルで値を指定する場合、スライスにはslice()を使う。

slice(start, stop, step)でスライスstart:stop:stepを生成可能。stopstepは省略できる。全体を表すスライス:slice(None)

print(df.loc[(slice(None), 'B1'), :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B1      C2          39     17
#                 C3          75     71
# A2      B1      C2          25     52
#                 C3          57     40

print(df.loc[(slice('A1', 'A3'), 'B2'), :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A1      B2      C0           1     89
#                 C1          54     60
# A3      B2      C0          64     54
#                 C1          27     96

print(df.loc[(slice('A1', 'A3'), ['B0', 'B2'], 'C1'), :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A1      B2      C1          54     60
# A2      B0      C1          19      4
# A3      B2      C1          27     96

pd.IndexSlice[]を使うと:のまま指定可能。スライスを多用する場合はこっちのほうが楽。

print(df.loc[pd.IndexSlice[:, 'B1'], :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B1      C2          39     17
#                 C3          75     71
# A2      B1      C2          25     52
#                 C3          57     40

print(df.loc[pd.IndexSlice['A1':'A3', 'B2'], :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A1      B2      C0           1     89
#                 C1          54     60
# A3      B2      C0          64     54
#                 C1          27     96

print(df.loc[pd.IndexSlice['A1':'A3', ['B0', 'B2'], 'C1'], :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A1      B2      C1          54     60
# A2      B0      C1          19      4
# A3      B2      C1          27     96

xsメソッド

xs()メソッドで、インデックス列名(引数level)とその値(第一引数key)を指定して選択、抽出することもできる。マルチインデックスのcolumnsに対しては引数axis=1とする。

print(df.xs('B1', level='level_2'))
#                  val_1  val_2
# level_1 level_3              
# A0      C2          39     17
#         C3          75     71
# A2      C2          25     52
#         C3          57     40

インデックス列名ではなく階層levelを表す数値での指定も可能。一番上の階層(一番外側の階層)が0。

print(df.xs('C1', level=2))
#                  val_1  val_2
# level_1 level_2              
# A0      B0          44      9
# A1      B2          54     60
# A2      B0          19      4
# A3      B2          27     96

複数のインデックスに対する値をリストで指定することもできる。

print(df.xs(['B1', 'C2'], level=['level_2', 'level_3']))
#          val_1  val_2
# level_1              
# A0          39     17
# A2          25     52

xs()メソッドでもスライスを指定するときはslice()pd.IndexSlice[]を使う必要がある。

print(df.xs(pd.IndexSlice['A1':'A3'], level='level_1'))
#                  val_1  val_2
# level_2 level_3              
# B2      C0           1     89
#         C1          54     60
# B3      C2          47      6
#         C3          16      5
# B0      C0          75     22
#         C1          19      4
# B1      C2          25     52
#         C3          57     40
# B2      C0          64     54
#         C1          27     96
# B3      C2         100     77
#         C3          22     50

print(df.xs(slice('A1', 'A3'), level='level_1'))
#                  val_1  val_2
# level_2 level_3              
# B2      C0           1     89
#         C1          54     60
# B3      C2          47      6
#         C3          16      5
# B0      C0          75     22
#         C1          19      4
# B1      C2          25     52
#         C3          57     40
# B2      C0          64     54
#         C1          27     96
# B3      C2         100     77
#         C3          22     50

xs()メソッドではリストで複数の値を指定することができない。リストで複数の値を指定したい場合はloc[]を使う。

# print(df.xs(['B1', 'B2'], level='level_2'))
# KeyError: ('B1', 'B2')

print(df.loc[pd.IndexSlice[:, ['B1', 'B2']], :])
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
# A2      B1      C2          25     52
#                 C3          57     40
# A3      B2      C0          64     54
#                 C1          27     96

選択範囲への値の代入

locで選択した場合は選択範囲に値を代入することが可能。

df.loc[(['A0', 'A1'], ['B0', 'B3']), :] = -100

print(df)
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0        -100   -100
#                 C1        -100   -100
#         B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
#         B3      C2        -100   -100
#                 C3        -100   -100
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40
# A3      B2      C0          64     54
#                 C1          27     96
#         B3      C2         100     77
#                 C3          22     50

df.loc[(['A0', 'A1'], ['B0', 'B3']), :] = [-200, -300]

print(df)
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0        -200   -300
#                 C1        -200   -300
#         B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
#         B3      C2        -200   -300
#                 C3        -200   -300
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40
# A3      B2      C0          64     54
#                 C1          27     96
#         B3      C2         100     77
#                 C3          22     50

df.loc[(['A0', 'A1'], ['B0', 'B3']), :] = [[-1, -2], [-3, -4], [-5, -6], [-7, -8]]

print(df)
#                          val_1  val_2
# level_1 level_2 level_3              
# A0      B0      C0          -1     -2
#                 C1          -3     -4
#         B1      C2          39     17
#                 C3          75     71
# A1      B2      C0           1     89
#                 C1          54     60
#         B3      C2          -5     -6
#                 C3          -7     -8
# A2      B0      C0          75     22
#                 C1          19      4
#         B1      C2          25     52
#                 C3          57     40
# A3      B2      C0          64     54
#                 C1          27     96
#         B3      C2         100     77
#                 C3          22     50

xs()は値の取得のみで代入はできない。

# df.xs(['B1', 'C2'], level=['level_2', 'level_3']) = 0
# SyntaxError: can't assign to function call
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事