Pythonライブラリ講座

Pandasの階層型インデックス(マルチインデックス)をマスター!

前のページ|次のページ

14章ではPandasのDataFrameにおける『階層型インデックス』について解説しています。

また、『階層型インデックスの作り方』『データの取り出し』『stack/unstackメソッド』について説明しています。

本記事を読めば階層型インデックスを網羅的に理解できます。

ぜひ最後までご覧ください!

本連載講座【Python ライブラリ編】では、データサイエンスに必要なPythonライブラリやその使い方を基礎から学ぶことができます。

NumPyPandasMatplotlibScipySeabornについて、初学者の方にも分かりやすいよう丁寧に解説しています。

さらに、学習した内容を定着させられるように各章演習問題を用意しています。

・Pythonでデータ分析ができるようになりたい

・Pythonの基礎事項は一通り学んだので、さらに深く学びたい

このように考えている方はTech Teacherが運営する【Python ライブラリ編】で、Pythonによるデータサイエンスの学習をすることをお勧めします!

なお、『Pythonについて全く知らない』・『Pythonの基礎事項がまだ分かっていない』という方は、まずコチラの【Python 基礎編】で基礎を一通り学習してからライブラリ編に取り掛かりましょう!

<ライブラリ編 目次>

<ライブラリの基礎>
1章:ライブラリとは

<NumPy>
2章:NumPyの概要と配列(ndarray)
3章:統計量や次元の取得/ソート
4章:配列のインデックス
5章:numpy.whereによる条件制御
6章:配列の結合/分割
7章:乱数

<SciPy>
8章:SciPyの概要と基本操作

<Pandas>
9章:SeriesDataFrame/統計量の取得
10章:データの読み込み/書き込み
11章:データの取り出し/追加
12章:データのソート
13章:データの結合
14章:階層型インデックス
15章:groupbyによる集計
16章:マッピング処理
17章:欠損値の扱い

<Matplotlib>
18章:Matplotlibの概要
19章:pyplotインターフェース
20章:オブジェクト指向インターフェース

<Seaborn>
21章:Seabornの概要と基本操作

階層型インデックスとは

PandasのDataFrameでは、『階層型インデックス(マルチインデックス)』を使用してデータを扱いやすくすることができます。

階層型インデックスは名前の通りインデックスを階層化したものです。

具体例で見てみましょう。

# 階層化された行のインデックスを持つDataFrameを作成する
data = {
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

index = [['A','A','B','B'], [1,2,1,2]]

df = pd.DataFrame(data, index=index)

df
DataFrame1

上の例の場合、「Taro, Jiro」の2人がAグループ、「Saburo, Shiro」の2人がBグループに属しており、さらに各グループで「1,2」というラベルが付けられています。

インデックスを階層化することのメリットとして、「データの検索がしやすくなる」、「データの集計がしやすくなる」などが挙げられます。

階層型インデックスの作り方

DataFrameでは行ラベル・列ラベルともに階層化インデックスを定義することができます。

以下で紹介する行ラベル・列ラベルの階層化を組み合わせることで行ラベル・列ラベルの両方を階層化することも可能です。

なお、今回は簡単のため紹介しませんが、「Multiindexオブジェクト」を用いてインデックスを階層化する方法もあります。

気になる方はぜひ調べてみてください。

行ラベルの階層化

DataFrameの定義と同時にインデックスを作成

DataFrameを定義するときに、pd.DataFrameのindex引数に2次元配列を渡すことでインデックスを階層化できます。

# 階層化された行のインデックスを持つDataFrameを作成する
data = {
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

index = [['A','A','B','B'], [1,2,1,2]]

df = pd.DataFrame(data, index=index)

df
DataFrame1

2次元配列を用いて各データ(行)に対するインデックスを1つずつ指定することで、インデックスを階層化しています。

また以下のようにすると、階層化されたインデックスに名前を付けることができます。

#インデックスに名前を付ける
df.index.names = ['Class', 'Place']
df
DataFrame3

DataFrameの複数の列をインデックスとして指定

既に定義されているDataFrameの列をインデックスとして指定することでも、階層化することができます。

この方法では『set_index』メソッドを使用してインデックスにしたい列を指定します。

まずは、インデックスが指定されていないDataFrameを定義します。

#まず、インデックスを指定せずにDataFrameを作成
data = {
    'Class':['A','A','B','B'],
    'Place':[1,2,1,2],
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

df = pd.DataFrame(data)
df
DataFrame4

上記の列ラベルから「Class, Place」を選び、この順でインデックスに指定します。

すると、外側からClass, Placeの順に階層化されたインデックスが作成されます。

#複数の列をインデックスに指定すると階層化される
df.set_index(['Class', 'Place'])
DataFrame5

インデックスとして指定する列はリストで渡しましょう。

列ラベルの階層化

次に列ラベルを階層化する方法を紹介します。

列ラベルの階層化は、pd.DataFrameのcolumns引数に2次元配列を渡すことで実現できます。

#2次元リストからDataFrameを作成時、引数columnsで列ラベルを指定
data = [
    ['Taro','Jiro','Saburo','Shiro'],
    [97,91,90,85]
]

columns = [['A','A','B','B'], [1,2,1,2]]

df = pd.DataFrame(data, columns=columns)
df
DataFrame8

また列ラベルは、DataFrameの元になる辞書においてキーをタプルで指定することで階層化できます。

#辞書からDataFrameを作成時、タプルで各列のインデックスを指定
data = {
    ('A', 1):['Taro', 97],
    ('A', 2):['Jiro', 91],
    ('B', 1):['Saburo', 90],
    ('B', 2):['Shiro', 85]
}

df = pd.DataFrame(data)
df
DataFrame8

階層化された列ラベルには、行ラベルの階層化と同様に名前を付けることができます。

#列ラベルに名前を付ける
df.columns.names = ['Class', 'Place']
df
DataFrame9

データの取り出し

階層型インデックスを含むDataFrameから特定の範囲のデータを取り出す方法を紹介します。

DataFrameからデータを取り出す基本的な方法については以下の記事で詳しく解説していますので、ぜひ参照しながら読み進めてください。

loc・ilocを徹底解説!Pandasでデータを取り出し・追加する方法 前のページ|次のページ 11章ではPandasにおける『行や列の取り出し』『loc』『iloc』『行や列の追加』『ブールインデッ...

なお、本記事では簡単のために行ラベルのみが階層化された以下のDataFrameを使用します。

data = {
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

index = [['A','A','B','B'], [1,2,1,2]]

df = pd.DataFrame(data, index=index)
df.index.names = ['Class', 'Place']

df
DataFrame5

列ラベルが階層化されている場合も同じようにデータを取り出せるので、余裕のある方は試してみてください。

列名/行名を指定して取り出し

まずはlocilocを使わずに列名/行名を指定してデータを取り出す方法を説明します。

階層化されていないインデックスの指定方法

今回は列ラベルが階層化されていないので、列の取り出しは通常のDataFrameと同様の方法で行えます。

#Name列のみを取り出す
df['Name']
Class  Place
A      1          Taro
       2          Jiro
B      1        Saburo
       2         Shiro

階層化されたインデックスの指定方法

一番外側の階層のみを指定してデータを取り出す場合は、通常のDataFrameと同じように取り出すことができます。

#ClassがAのデータのみを取り出す
df['A':'A']
DataFrame10

2つ目以降の階層も合わせて指定する場合、タプルを使用します。

#2つ目の階層まで含めて取り出し範囲を指定
df[('A', 2):('B', 1)]
DataFrame11

locによる取り出し

一番外側の階層のみを指定する場合は、通常のDataFrameを同じように取り出すことができます。

#ClassがAの行のすべての列を取り出す
df.loc['A', :]
DataFrame12

2つ目以降の階層も含めて指定する場合、以下のようにタプルを使用します。

#2つ目の階層まで含めて指定
df.loc[('B', 1), 'Name']
'Saburo'

複数行を指定する場合、タプルを格納したリストを用いて行を指定します。

#ClassAとBのPlace=2のデータを取り出す
df.loc[[('A', 2), ('B', 2)], :]
DataFrame13

ここで、2つ目以降の階層を指定しつつスライスを行うとエラーが出てしまうことに注意する必要があります。

エラーを回避するためには、『pd.IndexSlice』を以下のように使用します。

#2つ目の階層まで指定してスライス
df.loc[pd.IndexSlice[('A', 2):('B', 2)], :]
DataFrame14

ilocによる取り出し

iloc』を用いる場合、通常のDataFrameを同様にしてデータを取り出すことができます。

具体例で確認してみましょう。

以下の例では「df」の1行目と3行目のデータから0列目(「Name」列)の値を取り出しています。

#1行目と3行目のデータから0列目を取り出す
df.iloc[[1,3], 0]
Class  Place
A      2         Jiro
B      2        Shiro

また、以下の例では「df」の1行目から3行目までのデータからすべての列を取り出しています。

#1行目から3行目までのデータからすべての列を取り出す
df.iloc[1:4, :]
DataFrame14

stack/unstackメソッド

stack』メソッドや『unstack』メソッドを用いると行を列に、列を行に変更する操作が可能です。

ここでは以下のデータを使用します。

data = {
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

index = [['A','A','B','B'], [1,2,1,2]]

df = pd.DataFrame(data, index=index)
df.index.names = ['Class', 'Place']

df
DataFrame5

stackメソッド

stack』メソッドを用いると、一番内側のラベルがラベルに変更されます。

#列ラベルを行ラベルに変更
df.stack()
Class  Place       
A      1      Name       Taro
              Score        97
       2      Name       Jiro
              Score        90
B      1      Name     Saburo
              Score        91
       2      Name      Shiro
              Score        85

列ラベルだった「Name」と「Score」が行ラベルに変更されているのが分かると思います。

なお、変更後は列ラベルがないためSeriesオブジェクトになります。

unstackメソッド

unstack』メソッドを用いると、一番内側のラベルがラベルに変更されます。

#行ラベルを列ラベルに変更
df.unstack()
DataFrame15

行ラベルだった「Place」が列ラベルに変更されているのが分かると思います。

すこし見づらいですが、変更後のDataFrameにおいて「Class」は行ラベルの名前で、「Place」は列ラベルの名前です。

14章の練習問題

以下の練習問題を解いてみましょう。

練習問題

以下のDataFrameを「df」とし、練習問題で使用します。

DataFrame4

問1. 以下の問題(1)~(4)に答えてください。

(1) 上記のDataFrame「df」を定義するプログラムを書いてください。
(2) dfのうち「Class」列と「Place」列をインデックスに設定してください。ただし、「Class」列が外側に来るようにしてください。
(3) dfのからClassがBの行のみを取り出してください。
(4) ClassがA、Placeが1に該当するデータのScoreの値を出力してください。

解答

問1(クリックして解答を表示)

(1)

#(1)
data = {
    'Class':['A','A','B','B'],
    'Place':[1,2,1,2],
    'Name':['Taro','Jiro','Saburo','Shiro'],
    'Score':['97','90','91','85']
}

df = pd.DataFrame(data)
df
DataFrame4

(2)

#(2)
df = df.set_index(['Class', 'Place'])
df
DataFrame5

(3)

#(3)
df['B':'B']
DataFrame16

(4)

#(4)
print(df.loc[('A', 1), 'Score'])
97

次のページへ

groupbyの使い方を徹底解説!Pandasの集計をマスターしよう 前のページ|次のページ 15章ではPandasの『各統計量を取得するメソッド』、『aggメソッド』および『groupbyメソッド...