pandas
では、インデックス列を階層構造にしてMultiindex
として扱うことができます。
Multiindex
のDataFrame
, Series
はデータの階層構造が視覚的にも把握しやすく、データの集計・分析が効率的に行える場合があります。
一方で、Multiindex
では次のような問題に直面することも…
- 普段使わないから
Multiindex
の中身がよくわからない - どうやって
Multiindex
を設定・解除するの? - インデックスを変更、入れ替え、ソートする方法は?
この記事では、これらの問題を解決するためにMultiindex
の基本構造、設定・解除方法、変更・入れ替え・ソートについてわかりやすく図解付きで解説していきます。
- Multiindexの基本構造
- Multiindexを設定
- Multiindex解除方法
- インデックス名・ラベル名変更
- インデックス列の入れ替え
- インデックスラベルのソート
- オススメ|pandasとデータ分析の勉強方法
Multiindexの基本構造
まずはMultiindex
の構造を整理していきましょう。
Multiindex
は、次のようにインデックスまたはカラムが複数設定されているものです。
Multiindex
は、公式ドキュメントではHierarchical indexing
(階層的インデックス)とも呼ばれています。
外側のインデックスが最上位階層で大枠的なグルーピング、内側に行くほど細かなグルーピングになっていくわけですね。
サンプルコードでMultiindex
のDataFrame
を生成してみましょう。
import pandas as pd
import numpy as np
# インデックスがMultiindexの例
mult_index = pd.MultiIndex.from_product([["Apple", "Banana", "Carrot"], ["ShopA", "ShopB"]],
names=["Item", "Shop"])
df = pd.DataFrame({2020:[1,2,3,4,5,6], 2021:[10,20,30,40,50,60]},
index=mult_index)
# 2020 2021
# Item Shop
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
次のようにカラムがMultiidex
の場合もあります。
df.T # サンプルデータを転置
# Item Apple Banana Carrot
# Shop ShopA ShopB ShopA ShopB ShopA ShopB
# 2020 1 2 3 4 5 6
# 2021 10 20 30 40 50 60
この記事内では、インデックスがMultiindex
の例を取り上げますが、カラムの場合もほぼ同様です。
Multiindexの構成要素
まずはMultiindex
の重要な構成要素を確認していきましょう。
Multiindex
の各インデックスは、level
と呼ばれる連番が振られています。
- 一番外側から
level = 0, 1, 2,…
または - 一番内側から
level = -1, -2, -3,…
通常は、level=0,1,2
を使うことが多いと思います。
また、各インデックスにはインデックス名を設定することも可能です。
Multiindex
の操作をする際は、基本的にlevel
かインデックス名
を指定することになります。
- 【参考】階層ごとにデータを集計する
.groupby()
については、次の記事を参考にして下さい。
Multiindexを設定
DataFrame
やSeries
にMultiindex
を設定する方法を紹介します。
主に次の3パターンで設定することができます。
- ファイル読み込み時に設定
- 既存
DataFrame
をMultiindex
化 DataFrame
生成時にMultiindex
化
以下では、各パターンについて順番に紹介していきます。
ファイル読み込み時に設定
実務レベルでは、ファイル読み込み時にMultiindex
化することが多いと思います。
CSV, Excel読み込み時、引数index_col
をリスト等で指定すると、Multiindex
化できます。
index_col = ["列番号1", "列番号2", …]
またはindex_col = ["ラベル名1", "ラベル名2", …]
(←こちらは.read_csv()
のみ)
.read_csv()
, .read_excel()
メソッドの細かい使用方法は次の記事を参考にしてください。
以下では次のサンプルファイルで、Multiindex
関連の挙動を確認してみます。
まずは、index_col="インデックス名"
で、シングルインデックスを設定してみましょう。
df_single = pd.read_csv("SampleData.csv", index_col="Item")
# Shop 2020 2021
# Item
# Apple ShopA 1 10
# Apple ShopB 2 20
# Banana ShopA 3 30
# Banana ShopB 4 40
# Carrot ShopA 5 50
# Carrot ShopB 6 60
次にindex_col=["インデックス名"のリスト]
で、Multiindex
化して読み込みます。
df_multi = pd.read_csv("SampleData.csv", index_col=["Item", "Shop"])
# 2020 2021
# Item Shop
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
インデックスの順番は、index_col = ["ラベル名1", "ラベル名2", …]
に与えたラベル名順番で設定されます。
今度は順序を変えて、["Shop", "Item"]
の順番で読み込んでみましょう。
df_multi = pd.read_csv("SampleData.csv", index_col=["Shop", "Item"])
# 2020 2021
# Shop Item
# ShopA Apple 1 10
# ShopB Apple 2 20
# ShopA Banana 3 30
# ShopB Banana 4 40
# ShopA Carrot 5 50
# ShopB Carrot 6 60
.read_excel()
でのMultiindex
化も同様です。
ただし、read_excel()
は、ラベル名を指定できないので、0
から始まる列番号で指定しましょう。
df_multi = pd.read_excel("SampleData.xlsx", index_col=[0, 1])
# NG: df_multi = pd.read_excel("SampleData.xlsx", index_col=["Item", "Shop"])
# 2020 2021
# Item Shop
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
既存dfをMultiindex化
既存df
をMultiindex
化する場合は、.set_index()
で複数のカラム名を指定します。
既存のインデックスにインデックスを追加する場合は、append=True
を指定します。
- 既存インデックスを置き換え →
.set_index(["カラム名"のリスト])
- 既存インデックスに追加 →
.set_index("カラム名", append=True)
次のサンプルdf
で挙動を確認してみます。
df = pd.DataFrame({"Item": ["Apple", "Apple", "Banana", "Banana", "Carrot", "Carrot"],
"Shop": ["ShopA", "ShopB", "ShopA", "ShopB", "ShopA", "ShopB"],
2020:[1,2,3,4,5,6],
2021:[10,20,30,40,50,60]})
# Item Shop 2020 2021
# 0 Apple ShopA 1 10
# 1 Apple ShopB 2 20
# 2 Banana ShopA 3 30
# 3 Banana ShopB 4 40
# 4 Carrot ShopA 5 50
# 5 Carrot ShopB 6 60
.set_index()
で既存インデックスをMultiindex
で置き換えてみましょう。
df.set_index(["Item", "Shop"])
# 2020 2021
# Item Shop
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
次にappend=True
を指定して、既存インデックスに追加してみます。
df.set_index(["Item", "Shop"], append=True)
# 2020 2021
# Item Shop
# 0 Apple ShopA 1 10
# 1 Apple ShopB 2 20
# 2 Banana ShopA 3 30
# 3 Banana ShopB 4 40
# 4 Carrot ShopA 5 50
# 5 Carrot ShopB 6 60
既存インデックスの内側にインデックスが追加されていますね。
- 【参考】通常のインデックスの設定について、次の記事を参考にしてください。
新規df生成時にMultiindex化
Multiindex
オブジェクトを自前で作成して、DataFrame
をMultiindex
化することも可能です。
次の手順でDataFrame
生成時にMultiindex
を適用しましょう。
multi_index=pd.Multiindex.各種関数()
pd.DataFrame(データ, index=multi_index)
Multiindex
を生成する関数は次のとおりです。
生成方法 | 関数名 | 引数例 |
---|---|---|
インデックス要素のタプル | .from_tuples() |
|
インデックス要素の配列 | .from_arrays() |
|
複数配列の全ての組合せ | .from_product() |
|
既存dfの要素 | .from_frame() |
引数例は、次のMultiindex
を生成する際に指定する引数です。
インデックス名を設定するには、引数names
で指定します。
names=["インデックス名のリスト"]
各種関数の使用例を見てみましょう。
.from_tuples()
で、各データ行ごとのインデックスの組合せをタプルで指定する例です。
mult_index = pd.MultiIndex.from_tuples([("Apple", "ShopA"), ("Apple", "ShopB"),
("Banana","ShopA"), ("Banana", "ShopB"),
("Carott","ShopA"), ("Carott", "ShopB")],
names=["Item", "Shop"])
df = pd.DataFrame(
{2020:[1,2,3,4,5,6], 2021:[10,20,30,40,50,60]} ,index=mult_index)
# 2020 2021
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carott ShopA 5 50
# ShopB 6 60
.from_array()
で、各インデックス要素を配列形式で指定する例です。
mult_index = pd.MultiIndex.from_arrays([["Apple", "Apple", "Banana", "Banana", "Carrot", "Carrot"],
["ShopA", "ShopB", "ShopA", "ShopB", "ShopA", "ShopB", ]])
df = pd.DataFrame(
{2020:[1,2,3,4,5,6], 2021:[10,20,30,40,50,60]}
,index=mult_index)
# 2020 2021
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carott ShopA 5 50
# ShopB 6 60
.from_product()
で、複数配列を渡してその組合せからMultiindex
を設定する例です。
mult_index = pd.MultiIndex.from_product([["Apple", "Banana", "Carrot"],
["ShopA", "ShopB"]],
names=["Item", "Shop"])
df = pd.DataFrame(
{2020:[1,2,3,4,5,6], 2021:[10,20,30,40,50,60]}, index=mult_index)
# 2020 2021
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carott ShopA 5 50
# ShopB 6 60
.from_dataframe()
で、既存df
からMultiindex
を生成する例です。
# multindex用DataFrame
df_ind = pd.DataFrame({"Item": ["Apple", "Apple", "Banana", "Banana", "Carrot", "Carrot"],
"Shop": ["ShopA", "ShopB", "ShopA", "ShopB", "ShopA", "ShopB"]})
# Item Shop
# 0 Apple ShopA
# 1 Apple ShopB
# 2 Banana ShopA
# 3 Banana ShopB
# 4 Carrot ShopA
# 5 Carrot ShopB
mult_index = pd.MultiIndex.from_frame(df_ind)
df = pd.DataFrame(
{2020:[1,2,3,4,5,6], 2021:[10,20,30,40,50,60]}, index=mult_index)
# 2020 2021
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carott ShopA 5 50
# ShopB 6 60
- 【参考】各種
DataFrame
生成方法については、次の記事を参考にしてください。
Multiindex解除方法
Multiindex
を解除して、シングルインデックスにしたい場合もあると思います。
.reset_index()
でインデックスを解除できます。
各種引数で、次のような追加設定が可能です。
設定内容 | 引数名 | 適用 |
---|---|---|
インデックス指定 | level=レベルorラベル名 または [上記のリスト] |
指定しない場合、全インデックスを移動 |
インデックスを 削除するか否か |
drop=False or True |
False →インデックス列をデータ列に移動(デフォルト)True →インデックス列は削除 |
df 上書き指定 |
inplace=False or True |
False →新規df を生成(デフォルト)True →もとのdf を更新 |
下記のサンプルDataFrame
で.reset_index
の挙動を確認します。
df
# 2020 2021
# Item Shop
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
全解除(連番で振り直し)
デフォルト設定で.reset_index()
を実行すると、全インデックスがデータ列に移動し、新たに連番のインデックスが振りなおされます。
df.reset_index()
# Item Shop 2020 2021
# 0 Apple ShopA 1 10
# 1 Apple ShopB 2 20
# 2 Banana ShopA 3 30
# 3 Banana ShopB 4 40
# 4 Carrot ShopA 5 50
# 5 Carrot ShopB 6 60
特定のインデックスのみ解除
level
で解除するインデックスを指定することができます。
level=レベル or ラベル名 または [レベル or ラベル名のリスト]
レベル、ラベル名それぞれで移動するインデックスを指定してみましょう。
# レベル番号で指定
df.reset_index(level=0)
# Item 2020 2021
# Shop
# ShopA Apple 1 10
# ShopB Apple 2 20
# ShopA Banana 3 30
# ShopB Banana 4 40
# ShopA Carrot 5 50
# ShopB Carrot 6 60
# インデックス名で指定
df.reset_index(level=["Shop", "Item"])
# Item Shop 2020 2021
# 0 Apple ShopA 1 10
# 1 Apple ShopB 2 20
# 2 Banana ShopA 3 30
# 3 Banana ShopB 4 40
# 4 Carrot ShopA 5 50
# 5 Carrot ShopB 6 60
level
で指定したインデックス列だけがデータ列に移動しています。
解除後インデックスを除去
drop=True
を指定すると、対象のインデックスはデータ列に移動せず、単に除去されます。
df.reset_index(drop=True)
# 2020 2021
# 0 1 10
# 1 2 20
# 2 3 30
# 3 4 40
# 4 5 50
# 5 6 60
df.reset_index(level=0, drop=True)
# 2020 2021
# Shop
# ShopA 1 10
# ShopB 2 20
# ShopA 3 30
# ShopB 4 40
# ShopA 5 50
# ShopB 6 60
対象のインデックス列は、データ列に移動せず単に除去されてしまいましたね。
インデックス名・ラベル名変更
インデックス名・ラベル名の設定変更には、次のメソッドを使用します。
各メソッドの設定方法は次の表のとおりです。
変更箇所 | メソッド名 | 変更方法の説明 |
---|---|---|
インデックス名 | df.index.set_names() |
[リスト] などで上書き |
〃 | df.rename_axis() |
マッパーで変更 or [リスト] で上書き |
ラベル名 | df.rename() |
マッパーで変更 |
マッパーを聞きなれない方は、とりあえず次のようなものだと思ってください。
- 既存のインデックス名/ラベル名をもとに、新規インデックス名/ラベル名を設定するルール
- 辞書や関数でルールを指定可能
サンプルコードで使い方を見ていきましょう。
インデックス名の上書き
df.index.set_names()
で、Multiindex
オブジェクトのnames
プロパティを書き換えできます。
主な使い方は次の通りです。
変更方法 | 引数の設定方法 |
---|---|
リストで全変更 | .set_names(["インデックス名のリスト"]) |
レベルを指定して変更 | .set_names("インデックス名", level=レベル番号orラベル名) または (["インデックス名のリスト"], [levelのリスト]) |
辞書で置き換え | .set_names({"変更前の名前": "変更後の名前"} ) |
デフォルトではdf.index.set_names()
は、新たにIndex
オブジェクトを生成するだけなので、DataFrame
を上書きする場合はinplace=True
を指定しましょう。
.set_names()
の実行例を確認してみましょう。
# リストで全変更
df_new = df.copy()
df_new.index.set_names(["商品", "店舗名"], inplace=True)
# 2020 2021
# 商品 店舗名
# Apple ShopA 1 10
# ShopB 2 20…以下略
# レベルを指定して変更
df_new = df.copy()
df_new.index.set_names("Fruits", level=0, inplace=True)
# 2020 2021
# Fruits Shop
# Apple ShopA 1 10
# ShopB 2 20…以下略
# 辞書で置き換え
df_new = df.copy()
df_new.index.set_names({"Item":"Fruits", "Shop":"ShopList"}, inplace=True)
print(df_new)
# 2020 2021
# Fruits ShopList
# Apple ShopA 1 10
# ShopB 2 20…以下略
df.index.set_names()
メソッドの便利なところは、level
引数でレベルを指定して特定のインデックス名をだけを変更できる点です。
後述のdf.rename_axis()
では、特定のレベルだけ変更することはできません。
インデックス名の変更
df.rename_axis()
でインデックス名を変更する場合は、次の2つの指定方法があります。
mapper
で変更方法を指定、axis
でインデックス名かカラム名かを指定.rename_axis(mapper=["インデックス名/カラム名のリスト"], axis=0 or1 )
index and/or columns
で、インデックス名またはカラム名の変更方法をそれぞれ指定.rename_axis(index,columns)
の指定方法は次の通り
変更方法 | 引数の設定方法 |
---|---|
リストで全変更 | index or columns=["インデックス名のリスト"] |
辞書で置き換え | index or columns={"変更前の名前": "変更後の名前"} |
関数で変換 | index or columns=適用する関数名 |
まずは、.rename_axis(mapper, axis)
の実行例を確認してみましょう。
df.rename_axis(["Fruits", "ShopList"])
# 2020 2021
# Fruits ShopList
# Apple ShopA 1 10
# ShopB 2 20…以下略
次に.rename_axis(index,columns)
の実行例を確認してみましょう。
df.rename_axis(index={"Item":"Fruits", "Shop":"ShopList"}, columns="Year")
# Year 2020 2021
# Fruits ShopList
# Apple ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Carrot ShopA 5 50
# ShopB 6 60
各ラベル名の変更
インデックス/カラムの各ラベル名を変更する場合は、df.rename()
を使用します。
df.rename(mapper)
で変更方法を指定、axis
でインデックスかカラムかを指定df.rename(index, columns)
で、各ラベルの変更方法をそれぞれ指定
mapper, index, columns
の指定方法は次の通りです。
変更方法 | 引数の設定方法 |
---|---|
辞書で置き換え | {"変更前の名前": "変更後の名前"} |
関数で変換 | 適用する関数名 |
各実行例を確認してみましょう。
# df.rename(mapper)の例
df.rename({"Apple":"Ringo", "Carrot":"Ninjin"})
# 2020 2021
# Item Shop
# Ringo ShopA 1 10
# ShopB 2 20
# Banana ShopA 3 30
# ShopB 4 40
# Ninjin ShopA 5 50
# ShopB 6 60
# df.rename(index, columns)の例
df.rename(index=str.upper, columns=lambda x: x-2000)
# 20 21
# Item Shop
# APPLE SHOPA 1 10
# SHOPB 2 20
# BANANA SHOPA 3 30
# SHOPB 4 40
# CARROT SHOPA 5 50
# SHOPB 6 60
インデックス列の入れ替え
インデックスの並べ替えには、次の2つの方法があります。
- 2つのレベルを入れ替え →
.swaplevel(level1, level2)
- 全レベルを並べ替え →
.reorder_levels([level番号またはインデックス名のリスト])
次のMultiindex
のdf
で各挙動を確認しましょう。
mult_index = pd.MultiIndex.from_product([["Apple", "Banana"],
["ShopA", "ShopB"],
["2020", "2021"]],
names=["Item", "Shop", "Year"])
df = pd.DataFrame([1,2,3,4,10,20,30,40], index=mult_index, columns=["Sales"])
# Sales
# Item Shop Year
# Apple ShopA 2020 1
# 2021 2
# ShopB 2020 3
# 2021 4
# Banana ShopA 2020 10
# 2021 20
# ShopB 2020 30
# 2021 40
まずは.swaplevel()
でインデックス列を入れ替えてみます。
df.swaplevel("Item", "Shop")
# or df.swaplevel("Item", "Shop")でもOK
# Sales
# Shop Item Year # ← level=0, 1が入れ替わった
# ShopA Apple 2020 1
# 2021 2
# ShopB Apple 2020 3
# 2021 4
# ShopA Banana 2020 10
# 2021 20
# ShopB Banana 2020 30
# 2021 40
次に.reorder_levels()
で、インデックス列を自由に並べ替えてみます。
df.reorder_levels([-1,-2,-3])
# or df.reorder_levels(["Year", "Shop", "Item"])でもOK
# Sales
# Year Shop Item # ← level=-1, -2, -3の順に入れ替わった
# 2020 ShopA Apple 1
# 2021 ShopA Apple 2
# 2020 ShopB Apple 3
# 2021 ShopB Apple 4
# 2020 ShopA Banana 10
# 2021 ShopA Banana 20
# 2020 ShopB Banana 30
# 2021 ShopB Banana 40
インデックスラベルのソート
.sort_index()
で、インデックスのラベルをソートすることができます。
デフォルト設定では、外側から内側に向かってインデックス毎に順にソートをかけてくれます。
実行例を確認してみましょう。
# ソート前のdf
df_messy = df.reorder_levels([-1,-2,-3])
# Sales
# Year Shop Item
# 2020 ShopA Apple 1
# 2021 ShopA Apple 2
# 2020 ShopB Apple 3
# 2021 ShopB Apple 4
# 2020 ShopA Banana 10
# 2021 ShopA Banana 20
# 2020 ShopB Banana 30
# 2021 ShopB Banana 40
# ソート結果
df_messy.sort_index()
# Sales
# Year Shop Item
# 2020 ShopA Apple 1
# Banana 10
# ShopB Apple 3
# Banana 30
# 2021 ShopA Apple 2
# Banana 20
# ShopB Apple 4
# Banana 40
level
引数で、ソートするインデックス列を指定することができます。
df_messy.sort_index(level="Item") # level=2でもOK
# Sales
# Year Shop Item
# 2020 ShopA Apple 1
# ShopB Apple 3
# 2021 ShopA Apple 2
# ShopB Apple 4
# 2020 ShopA Banana 10
# ShopB Banana 30
# 2021 ShopA Banana 20
# ShopB Banana 40
- 【参考】
DataFrame
のソートについては、次の記事で詳しく解説しています。
オススメ|pandasとデータ分析の勉強方法
今回はMultiindex
の基本構造、設定・解除方法、変更・入れ替え・ソートについて解説しました。
pandas
は便利すぎて操作方法がわかりにくいことがよくあります…。
結局はコツコツ学ぶのが、pandas
マスターの近道ですよね!
データ分析初心者の方にはこちらの記事もおススメです。
私がこれまで勉強してきた経験をもとに考えたおススメの勉強本の紹介記事です。
何から始めて、どうやってレベルアップしていけばいいのか、初心者の方にぜひおススメしたい本を紹介しました。
オススメのpandas
本については、次の記事で紹介しています。