マルチインデックスの作成
基本
df.set_index(カラム名)
- カラム名をリスト型で指定することにより、複数のインデックスを作成することができます。
- 個別に2回指定した場合は、以前のインデックスは削除されます。
df = pd.DataFrame( [ ["Tokyo","a",1,2], ["Tokyo","b",3,4], ["Osaka","a",5,6], ["Osaka","b",7,8], ["Kyoto","a",9,10], ], columns = ["pref", "aaa", "bbb", "ccc"] ) df = df.set_index(['pref',"aaa"], drop = True) print(df) # bbb ccc # pref aaa # Tokyo a 1 2 # b 3 4 # Osaka a 5 6 # b 7 8 # Kyoto a 9 10
日時(datetime型)を用いたマルチインデックスの作成
インデックスの日時(datetime型)から年月日や時分秒、曜日などを取得してインデックスを追加します。
こちらもdf.set_index(値)
を使用しますが、カラム名を指定するのではなく、インデックスの値を指定し、その後、インデックス名を設定します。
df = df.set_index([df.index, df.index.hour, df.index.minute])
- インデックスの値を指定する際に、元のインデックスを指定しないと削除されます。
上記の場合は、元のインデックスと時、分をマルチインデックスとして指定しています。
df.index.names = ['date', 'hour', 'minute']
- リスト型のデータを用いて、一括でインデックス名を指定します。
DatetimeIndexから取得できる年月日や時分秒、曜日などのデータは以下のドキュメントを参照してください。
df = pd.DataFrame( [ ["2019-01-01 00:00:00+00:00",1,2], ["2019-01-01 00:30:00+00:00",3,4], ["2019-01-01 01:00:00+00:00",5,6], ["2019-01-01 01:30:00+00:00",7,8], ["2019-01-01 02:00:00+00:00",9,10], ], columns = ["date", "aaa", "ccc"] ) df.index = pd.DatetimeIndex(pd.to_datetime(df['date']), name='date') del df['date'] print(df) # aaa ccc # date # 2019-01-01 00:00:00+00:00 1 2 # 2019-01-01 00:30:00+00:00 3 4 # 2019-01-01 01:00:00+00:00 5 6 # 2019-01-01 01:30:00+00:00 7 8 # 2019-01-01 02:00:00+00:00 9 10 df = df.set_index([df.index, df.index.hour, df.index.minute]) df.index.names = ['date', 'hour', 'minute'] print(df) # aaa ccc # date hour minute # 2019-01-01 00:00:00+00:00 0 0 1 2 # 2019-01-01 00:30:00+00:00 0 30 3 4 # 2019-01-01 01:00:00+00:00 1 0 5 6 # 2019-01-01 01:30:00+00:00 1 30 7 8 # 2019-01-01 02:00:00+00:00 2 0 9 10
マルチインデックスの解除(削除)
df.reset_index(level='インデックスのカラム名', drop=True)
- 通常のインデックスの削除と同じですが、
level
でインデックスのカラム名を指定します。 df.reset_index()
のみの場合は、全てインデックスが削除されます。drop=True
は削除後に、単に削除する(True)か、カラムとして残すか(False)です。
デフォルトはFalseです。
df = df.reset_index(level='minute', drop=True) print(df) # aaa ccc # date hour # 2019-01-01 00:00:00+00:00 0 1 2 # 2019-01-01 00:30:00+00:00 0 3 4 # 2019-01-01 01:00:00+00:00 1 5 6 # 2019-01-01 01:30:00+00:00 1 7 8 # 2019-01-01 02:00:00+00:00 2 9 10
集計
基本
df.max()のような形で通常通り集計することができます。
print(df.max()) # aaa 9 # ccc 10 # dtype: int64
インデックスのレベルを指定して集計
単一のレベルを指定して集計
df.max(level='hour')
上記のようにインデックス名をlevel
で指定するだけです。
print(df.max()) # aaa 9 # ccc 10 # dtype: int64 print(df.max(level='hour')) # aaa ccc # hour # 0 3 4 # 1 7 8 # 2 9 10 print(df.max(level='minute')) # aaa ccc # minute # 0 9 10 # 30 7 8
複数のレベルを指定して集計
df.max(level=[‘hour’,’minute’])
上記のようにインデックス名をリスト型で複数個を指定するだけです。
print(df.max(level=['hour','minute'])) # aaa ccc # hour minute # 0 0 1 2 # 30 3 4 # 1 0 5 6 # 30 7 8 # 2 0 9 10
マルチインデックスの順番(レベル)を変更
df.swaplevel(カラム名1,カラム名2)
- 順番を変更するカラム名を指定します。
df = df.swaplevel('hour', 'minute') print(df) # aaa ccc # date minute hour # 2019-01-01 00:00:00+00:00 0 0 1 2 # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8 # 2019-01-01 02:00:00+00:00 0 2 9 10
データフレームをソート(順番を変更)
df.sort_index(level='minute', ascending=False)
- 通常のデータフレームのソートと同じく、
df.sort_index
を使用しますが、をlevel
指定します。 ascending
は通常のdf.sort_index
と同じく表示順を指定します。
(True=昇順(デフォルト)、Flase=降順)
以下の後半は、通常のソートです。
df = df.sort_index(level='minute', ascending=False) print(df) # aaa ccc # date minute hour # 2019-01-01 01:30:00+00:00 30 1 7 8 # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 02:00:00+00:00 0 2 9 10 # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 00:00:00+00:00 0 0 1 2 df = df.sort_index() print(df) # aaa ccc # date minute hour # 2019-01-01 00:00:00+00:00 0 0 1 2 # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8 # 2019-01-01 02:00:00+00:00 0 2 9 10
インデックスでデータを選択
マルチインデックスは、少し癖があり、データベースのように簡単に範囲指定などができません。
前提(データ)
以下のデータがある前提です。
- df:インデックスが文字列
- df_date:インデックスが日時(datetime)
df = pd.DataFrame( [ ["Tokyo","a",1,2], ["Tokyo","b",3,4], ["Osaka","a",5,6], ["Osaka","b",7,8], ["Kyoto","a",9,10], ], columns = ["pref", "aaa", "bbb", "ccc"] ) df = df.set_index(['pref',"aaa"], drop = True) print(df) # bbb ccc # pref aaa # Tokyo a 1 2 # b 3 4 # Osaka a 5 6 # b 7 8 # Kyoto a 9 10 df_date = pd.DataFrame( [ ["2019-01-01 00:00:00+00:00",1,2], ["2019-01-01 00:30:00+00:00",3,4], ["2019-01-01 01:00:00+00:00",5,6], ["2019-01-01 01:30:00+00:00",7,8], ["2019-01-01 02:00:00+00:00",9,10], ], columns = ["date", "aaa", "ccc"] ) df_date.index = pd.DatetimeIndex(pd.to_datetime(df_date['date']), name='date') del df_date['date'] df_date = df_date.set_index([df_date.index, df_date.index.hour, df_date.index.minute]) df_date.index.names = ['date', 'hour', 'minute'] df_date = df_date.swaplevel('hour', 'minute') print(df_date) # aaa ccc # date minute hour # 2019-01-01 00:00:00+00:00 0 0 1 2 # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8 # 2019-01-01 02:00:00+00:00 0 2 9 10
インデックスを単一で指定
df.loc[インデックスの値]
locを使用します。
以下のように、日時の場合、単一での指定でも、LIKEで指定することが可能です。
print(df.loc['Tokyo']) # bbb ccc # aaa # a 1 2 # b 3 4 print(df_date.loc['2019-01-01 01']) # aaa ccc # date hour minute # 2019-01-01 01:00:00+00:00 1 0 5 6 # 2019-01-01 01:30:00+00:00 1 30 7 8
インデックスを単一で指定し、単一のカラムの値のみ取得
df.loc[インデックスの値, : カラム名]
- コロンで区切ってカラム名を指定します。
- インデックスの値のあとに、カンマ(,) がないとエラーになります。
df.loc[‘Tokyo’, : “bbb”]
print(df.loc['Tokyo', : "bbb"]) # bbb # aaa # a 1 # b 3 print(df_date.loc['2019-01-01 01',:"aaa"]) # aaa # date minute hour # 2019-01-01 01:00:00+00:00 0 1 5 # 2019-01-01 01:30:00+00:00 30 1 7
同一インデックスを複数で指定
df.loc[['Tokyo', 'Kyoto']]
- 同じようにlocを使用しますが、リスト型でインデックスの値を指定します。
- 日時については先程のようにLIKEで指定するとエラーになります。
- なお、この複数指定は範囲指定ではありません。
print(df.loc[['Tokyo', 'Kyoto']]) # bbb ccc # pref aaa # Tokyo a 1 2 # b 3 4 # Kyoto a 9 10 print(df_date.loc[['2019-01-01 01:00:00+00:00', '2019-01-01 02:00:00+00:00']]) # aaa ccc # date minute hour # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 02:00:00+00:00 0 2 9 10
異なるインデックスを複数で指定
df.loc[(第1レベルのインデックスの値, 第2レベルのインデックスの値), ]
- タプル型(カッコで囲いカンマ区切り)で第2レベルの値を指定します。
- 注意点としてタプル型の終わりにカンマがないとエラーになります。
df.loc[([‘Tokyo’, ‘Kyoto’], ‘a’), ]
print(df.loc[(['Tokyo', 'Kyoto'], 'a'), ]) # bbb ccc # pref aaa # Tokyo a 1 2 # Kyoto a 9 10 print(df_date.loc[('2019-01-01 01', 30), ]) # aaa ccc # date minute hour # 2019-01-01 01:30:00+00:00 30 1 7 8
第2階層のインデックスを複数で指定
- 第1階層と同じように、第2階層のインデックス値をリスト型で指定します。
- 日時データの場合は、コロン(:)がないとエラーになります。
df_date.loc[‘2019-01-01 01’,[0,10], :] - なお、この複数指定は範囲指定ではありません。
print(df.loc[('Tokyo', ['a', 'b']), ]) # bbb ccc # pref aaa # Tokyo a 1 2 # b 3 4 print(df_date.loc['2019-01-01 01',[0,10], :]) # aaa ccc # date minute hour # 2019-01-01 01:00:00+00:00 0 1 5 6 print(df_date.loc['2019-01-01 01',[0,30], :]) # aaa ccc # date minute hour # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8
第2階層のインデックスのみで指定
df.loc[(slice(None), 第2レベルのインデックスの値), ]
- 第1レベルはslice(None)としてエスケープさせ、第2レベルのみ指定します。
print(df.loc[(slice(None), 'a'), :]) # bbb ccc # pref aaa # Tokyo a 1 2 # Osaka a 5 6 # Kyoto a 9 10 print(df_date.loc[(slice(None), 30), ]) # aaa ccc # date minute hour # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:30:00+00:00 30 1 7 8
範囲指定・期間指定(スライス)
範囲や期間指定するスライスには以下の2種類があります。
- df.loc + pd.IndexSlice
各階層を指定する際に少し面倒ですが、実行結果はインデックス値も残ります。 - df.xs + slice
各階層を指定するのが簡単ですが、実行結果に指定したインデックスは出力されません。
前提(データ)
データは上記の「インデックスでデータを選択」で使用したものですが、以下のようにdfについてはソートしています。
ソートしない範囲選択でエラーになります。
df = df.sort_index() print(df) # bbb ccc # pref aaa # Kyoto a 9 10 # Osaka a 5 6 # b 7 8 # Tokyo a 1 2 # b 3 4
df.loc + pd.IndexSlice
第1階層の範囲指定
df.loc[pd.IndexSlice['Kyoto':'Osaka', ], ]
pd.IndexSlice
を用いて通常のようにスライスします。- なお、以下のように2つのカンマがないとエラーになります。
df.loc[pd.IndexSlice[‘Kyoto’:’Osaka’, ], ]
print(df.loc[pd.IndexSlice['Kyoto':'Osaka', ], ]) # bbb ccc # pref aaa # Kyoto a 9 10 # Osaka a 5 6 # b 7 8 print(df_date.loc[pd.IndexSlice['2019-01-01 00':'2019-01-01 01', ], ]) # aaa ccc # date minute hour # 2019-01-01 00:00:00+00:00 0 0 1 2 # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8
第1階層と第2階層で範囲指定
- 第1階層のみと同じように、第2階層に
pd.IndexSlice
で指定しますが、タプル型(カッコで囲う)にします。 - また終わりのカンマを忘れないように注意してください。
print(df.loc[(pd.IndexSlice['Kyoto':'Osaka', ], pd.IndexSlice['b':, ]), ]) # bbb ccc # pref aaa # Osaka b 7 8 print(df_date.loc[(pd.IndexSlice['2019-01-01 00':'2019-01-01 01', ], pd.IndexSlice[1:, ]), ]) # aaa ccc # date minute hour # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:30:00+00:00 30 1 7 8
第2階層または第3階層のみで範囲指定
第1階層はslice(None)
でエスケープさせます。
print(df.loc[(slice(None), pd.IndexSlice['b':, ]), ]) # bbb ccc # pref aaa # Osaka b 7 8 # Tokyo b 3 4 print(df_date.loc[(slice(None), pd.IndexSlice[1:, ]), ]) # aaa ccc # date minute hour # 2019-01-01 00:30:00+00:00 30 0 3 4 # 2019-01-01 01:30:00+00:00 30 1 7 8 print(df_date.loc[(slice(None), slice(None), pd.IndexSlice[1:, ]), ]) # aaa ccc # date minute hour # 2019-01-01 01:00:00+00:00 0 1 5 6 # 2019-01-01 01:30:00+00:00 30 1 7 8 # 2019-01-01 02:00:00+00:00 0 2 9 10
df.xs + slice
1つのレベルの範囲指定
print(df.xs(slice('Kyoto', 'Osaka'), level='pref')) # bbb ccc # aaa # a 9 10 # a 5 6 # b 7 8 print(df.xs(slice('a', 'b'), level='aaa')) # bbb ccc # pref # Kyoto 9 10 # Osaka 5 6 # Osaka 7 8 # Tokyo 1 2 # Tokyo 3 4 print(df_date.xs(slice('2019-01-01 00', '2019-01-01 01'), level='date')) # aaa ccc # minute hour # 0 0 1 2 # 30 0 3 4 # 0 1 5 6 # 30 1 7 8 print(df_date.xs(slice(1, ), level='hour')) # aaa ccc # date minute # 2019-01-01 00:00:00+00:00 0 1 2 # 2019-01-01 00:30:00+00:00 30 3 4 # 2019-01-01 01:00:00+00:00 0 5 6 # 2019-01-01 01:30:00+00:00 30 7 8
2つのレベルの範囲指定
範囲、レベルともにタプル型で指定します。
しかし、以下のように、文字列の場合、第2レベルの指定の挙動がおかしいです。
この場合は、df.loc + pd.IndexSliceを使った方がいいと思います。
print(df.xs((slice('Kyoto', 'Osaka'), slice('b', ),), level=('pref', 'aaa'))) # bbb ccc # pref aaa # Kyoto a 9 10 # Osaka a 5 6 # b 7 8 print(df_date.xs((slice('2019-01-01 00', '2019-01-01 01'), slice(1, )), level=('date', 'hour'))) # aaa ccc # minute # 0 1 2 # 30 3 4 # 0 5 6 # 30 7 8
一つの範囲指定と一つのインデックス指定
単に、インデックスの範囲とインデックスの指定のミックスです。
print(df.xs((slice('Kyoto', 'Osaka'), 'a'), level=('pref', 'aaa'))) # bbb ccc # pref aaa # Kyoto a 9 10 # Osaka a 5 6 print(df_date.xs((slice('2019-01-01 00', '2019-01-01 01'), 1), level=('date', 'hour'))) # aaa ccc # minute # 0 5 6 # 30 7 8
コメント