Pandasは、Pythonにおけるデータ分析の強力なライブラリであり、Seriesはその中心的なデータ構造の一つです。Seriesは、NumPyのndarrayに似た一次元のラベル付き配列であり、様々なデータ型(整数、浮動小数点数、文字列など)を格納できます。
通常、Seriesは単一のインデックスを持ち、各要素に一意のラベルが割り当てられています。しかし、より複雑なデータ構造を扱う場合、単一のインデックスだけでは不十分なことがあります。そこで登場するのが MultiIndex(階層型インデックス) です。
MultiIndexを使用すると、Seriesに複数のインデックスレベルを追加し、より多次元的なデータ構造を表現できます。これは、階層的なカテゴリ、時間的な階層構造、地理的な階層構造など、さまざまな種類のデータを扱う際に非常に役立ちます。
この記事では、Pandas Seriesに新しいインデックスレベルを追加する方法に焦点を当て、MultiIndexを効果的に活用するための具体的な手順と応用例を紹介します。append()やreindex()といった関数を用いて、既存のSeriesに新しいレベルを追加し、より複雑なデータ構造を操作する方法を習得しましょう。MultiIndexを使いこなすことで、Pandas Seriesの表現力を高め、データ分析の幅を広げることができます。
Pandas Seriesを作成する最も基本的な方法は、リストやNumPy配列から直接作成する方法です。この場合、Pandasは自動的に整数型のインデックス(0, 1, 2, …)を割り当てます。また、index
引数を指定することで、カスタムのインデックスラベルを設定することも可能です。
以下に、単一インデックスを持つSeriesの作成例をいくつか示します。
例1:リストからの作成(自動インデックス)
import pandas as pd
data = [10, 20, 30, 40, 50]
series1 = pd.Series(data)
print(series1)
このコードを実行すると、以下のようなSeriesが出力されます。
0 10
1 20
2 30
3 40
4 50
dtype: int64
例2:リストからの作成(カスタムインデックス)
import pandas as pd
data = [10, 20, 30, 40, 50]
index = ['A', 'B', 'C', 'D', 'E']
series2 = pd.Series(data, index=index)
print(series2)
このコードを実行すると、以下のようなSeriesが出力されます。
A 10
B 20
C 30
D 40
E 50
dtype: int64
例3:NumPy配列からの作成
import pandas as pd
import numpy as np
data = np.array([10, 20, 30, 40, 50])
series3 = pd.Series(data)
print(series3)
この例では、NumPy配列を基にSeriesを作成しています。出力は例1と同様になります。
例4:辞書からの作成
辞書を使用すると、キーをインデックスラベルとして、値をSeriesのデータとして扱うことができます。
import pandas as pd
data = {'A': 10, 'B': 20, 'C': 30, 'D': 40, 'E': 50}
series4 = pd.Series(data)
print(series4)
出力は例2と同様になります。
これらの例からわかるように、Pandas Seriesは柔軟なデータ構造であり、様々な方法で作成できます。しかし、これらの例ではすべて単一のインデックスレベルしか持っていません。次のセクションでは、これらのSeriesに新しいインデックスレベルを追加する方法について説明します。
単一インデックスのSeriesに新しいインデックスレベルを追加し、MultiIndexに変換するには、主に2つの方法があります。
-
append()
メソッドの使用:既存のSeriesに、MultiIndexを持つ別のSeriesを連結することで、新しいレベルを追加します。 -
reindex()
メソッドの使用:既存のSeriesを、新しいMultiIndexで再インデックスすることで、新しいレベルを追加します。
これらの方法は、それぞれ異なる状況で役立ちます。append()
は、既存のデータの一部を別のカテゴリに分割したい場合に適しています。reindex()
は、既存のデータに新しいカテゴリを追加したり、インデックス構造を完全に変更したい場合に適しています。
以降のセクションでは、これらのメソッドの使い方を具体的なコード例とともに詳しく解説します。また、MultiIndex Seriesの操作方法や、レベル名設定、インデックスレベルの並び替えといった、MultiIndexを活用するための重要なテクニックについても説明します。
append()
メソッドは、Pandas Series同士を連結するために使用されます。これを利用して、新しいインデックスレベルを追加することができます。基本的な手順は以下の通りです。
- 追加したいレベルの情報をSeriesとして作成する: 新しいレベルのインデックスと、対応する値を格納したSeriesを作成します。このSeriesは、新しいレベルのインデックスを持つMultiIndexである必要があります。
-
append()
メソッドで連結する: 元のSeriesと、新しいレベルの情報を格納したSeriesをappend()
メソッドで連結します。verify_integrity=True
を指定することで、インデックスの重複をチェックできます。
以下に具体的な例を示します。
例:都道府県データに地方情報を追加する
import pandas as pd
# 単一インデックスのSeries (都道府県データ)
data = {'東京': 13929286, '神奈川': 9203762, '埼玉': 7344733, '千葉': 6284480}
pref_series = pd.Series(data)
print("元のSeries:\n", pref_series)
# 追加したい地方情報のMultiIndex Series
region_data = {'関東': ['東京', '神奈川', '埼玉', '千葉']}
region_index = pd.MultiIndex.from_product([region_data.keys(), region_data['関東']], names=['地方', '都道府県'])
region_series = pd.Series([True] * len(region_index), index=region_index)
print("\n追加するSeries:\n", region_series)
# append()で連結
new_series = pref_series.append(region_series[pref_series.index], verify_integrity=False) #verify_integrity=Falseとする必要がある
print("\n連結後のSeries:\n", new_series)
# NaNを削除 (都道府県データに地方情報がない場合)
new_series = new_series.dropna()
print("\nNaN削除後のSeries:\n", new_series)
このコードを実行すると、以下の処理が行われます。
-
pref_series
は、都道府県名をインデックス、人口を値とする単一インデックスのSeriesとして作成されます。 -
region_series
は、地方名と都道府県名によるMultiIndexを持ちます。値はTrue
で、これはその都道府県がその地方に属することを示します。(この例では説明のためにTrueを入れていますが、別の値でも構いません。) -
append()
メソッドを使ってpref_series
とregion_series
を連結します。verify_integrity=False
を指定しないと、インデックスの重複でエラーになります。また、region_series[pref_series.index]
とすることで、region_series
からpref_series
のインデックスに対応するものだけを抽出しています。 - 最後に、
dropna()
を使って、pref_series
のインデックスに存在しない都道府県(例:pref_series
に大阪府が含まれていない場合)に対応するNaN
値を削除します。
この例では、append()
を使って、既存の都道府県データに地方情報を追加し、MultiIndexを持つSeriesを作成しました。ただし、この方法では、元のSeriesの値を新しいレベルに反映させることはできません。あくまで新しいレベルとして情報を追加する形になります。
注意点:
-
append()
を使用する際には、インデックスの重複に注意する必要があります。verify_integrity=True
を指定すると、重複がある場合にエラーが発生します。重複を許容する場合は、verify_integrity=False
を指定します。 -
append()
はSeriesを連結する操作であり、元のSeries自体は変更されません。新しいSeriesとして結果が返されます。
次のセクションでは、reindex()
メソッドを使って新しいインデックスレベルを追加する方法について説明します。
reindex()
メソッドは、Seriesのインデックスを再構築するために使用されます。これを利用して、既存のSeriesに新しいインデックスレベルを追加し、MultiIndexに変換することができます。reindex()
を使うことで、インデックスの順序変更、新しいインデックスの追加、既存のインデックスの削除などが可能です。
新しいインデックスレベルを追加する基本的な手順は以下の通りです。
- 新しいMultiIndexを作成する: 新しいインデックスレベルを含んだ、目的のMultiIndexを作成します。
-
reindex()
メソッドで再インデックスする: 元のSeriesを、作成したMultiIndexで再インデックスします。新しいインデックスに対応するデータが存在しない場合は、デフォルトでNaN
が挿入されます。
以下に具体的な例を示します。
例:都道府県データに地方情報を追加する (reindex版)
import pandas as pd
# 単一インデックスのSeries (都道府県データ)
data = {'東京': 13929286, '神奈川': 9203762, '埼玉': 7344733, '千葉': 6284480}
pref_series = pd.Series(data)
print("元のSeries:\n", pref_series)
# 追加したい地方情報のMultiIndex
region_data = {'関東': ['東京', '神奈川', '埼玉', '千葉'], '関西': []} # 関西は空
region_index = pd.MultiIndex.from_product([region_data.keys(), [pref for region in region_data.values() for pref in region]], names=['地方', '都道府県']) #region_dataに含まれる全ての都道府県を含むMultiIndexを生成
print("\n新しいMultiIndex:\n", region_index)
# reindex()で再インデックス
new_series = pref_series.reindex(region_index)
print("\n再インデックス後のSeries:\n", new_series)
# NaNを削除 (都道府県データに地方情報がない場合)
new_series = new_series.dropna()
print("\nNaN削除後のSeries:\n", new_series)
このコードを実行すると、以下の処理が行われます。
-
pref_series
は、都道府県名をインデックス、人口を値とする単一インデックスのSeriesとして作成されます。 -
region_index
は、地方名と都道府県名によるMultiIndexを作成します。from_product
を使用することで、地方と都道府県の組み合わせを効率的に生成できます。 この例では、あらかじめ「関東」と「関西」という地方を定義し、各地方に所属する都道府県をリストで定義しています。関西は空リストですが、後でデータが追加される可能性を考慮して定義されています。重要なのは、再インデックス後のSeriesに含めたい全てのインデックス(地方と都道府県の組み合わせ)を、このMultiIndexに含める必要があるということです。 -
reindex()
メソッドを使ってpref_series
を再インデックスします。reindex()
は、元のSeriesのインデックスを新しいMultiIndexに合わせます。新しいMultiIndexに存在するが、元のSeriesに存在しないインデックスに対しては、NaN
が挿入されます。 - 最後に、
dropna()
を使って、元のSeriesに存在しない都道府県(例: 関西地方の都道府県)に対応するNaN
値を削除します。
この例では、reindex()
を使って、既存の都道府県データに地方情報を追加し、MultiIndexを持つSeriesを作成しました。reindex()
を使うことで、新しいインデックスレベルだけでなく、新しいインデックス自体を追加することもできます。
ポイント:
-
reindex()
は、元のSeriesのインデックスを新しいインデックスに合わせます。新しいインデックスに存在するが、元のSeriesに存在しないインデックスに対しては、デフォルトでNaN
が挿入されます。fill_value
引数を使うことで、NaN
以外の値を挿入することもできます。 -
reindex()
は、元のSeries自体は変更しません。新しいSeriesとして結果が返されます。 - MultiIndexを生成する際に、
from_product
だけでなく、from_tuples
やfrom_arrays
なども活用できます。データの構造に合わせて適切な方法を選択しましょう。
append()
との比較:
-
append()
は、Series同士を連結する操作です。主に、既存のデータの一部を別のカテゴリに分割したい場合に適しています。 -
reindex()
は、Seriesのインデックスを再構築する操作です。既存のデータに新しいカテゴリを追加したり、インデックス構造を完全に変更したい場合に適しています。
どちらのメソッドを使用するかは、目的やデータの構造によって異なります。状況に応じて適切な方法を選択してください。
次のセクションでは、MultiIndex Seriesの操作方法について説明します。
MultiIndexを持つSeriesは、単一インデックスのSeriesよりも複雑な構造を持つため、操作方法も少し異なります。ここでは、MultiIndex Seriesを効果的に操作するための基本的なテクニックを紹介します。
1. 要素へのアクセス:
MultiIndex Seriesの要素にアクセスするには、loc[]
やiloc[]
を使用します。loc[]
はラベルベースのアクセス、iloc[]
は整数ベースのアクセスです。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['level1', 'level2'])
series = pd.Series([1, 2, 3, 4], index=index)
print("MultiIndex Series:\n", series)
# loc[]を使ったアクセス
print("\nseries.loc[('A', 'X')]:", series.loc[('A', 'X')])
print("series.loc['A']:\n", series.loc['A']) # level1が'A'の全てのデータ
# iloc[]を使ったアクセス
print("\nseries.iloc[0]:", series.iloc[0])
print("series.iloc[:2]:\n", series.iloc[:2])
2. スライシング:
MultiIndex Seriesも、単一インデックスのSeriesと同様にスライシングが可能です。loc[]
を使用すると、ラベルベースのスライシングが行えます。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B', 'C'], ['X', 'Y']], names=['level1', 'level2'])
series = pd.Series(range(6), index=index)
print("MultiIndex Series:\n", series)
# loc[]を使ったスライシング
print("\nseries.loc['A':'B']:\n", series.loc['A':'B']) # level1が'A'から'B'までのデータ
print("\nseries.loc[('A', 'X'):('B', 'Y')]:\n", series.loc[('A', 'X'):('B', 'Y')]) # 詳細なスライシング
3. インデックスレベルの指定:
特定のインデックスレベルに基づいてデータを選択することもできます。xs()
メソッドを使うと、特定のレベルの値を指定してデータを抽出できます。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B', 'C'], ['X', 'Y']], names=['level1', 'level2'])
series = pd.Series(range(6), index=index)
print("MultiIndex Series:\n", series)
# xs()を使った抽出
print("\nseries.xs('X', level='level2'):\n", series.xs('X', level='level2')) # level2が'X'の全てのデータ
print("\nseries.xs('A', level='level1'):\n", series.xs('A', level='level1')) # level1が'A'の全てのデータ
4. 集約:
groupby()
メソッドを使用すると、特定のインデックスレベルに基づいてデータを集約できます。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B', 'C'], ['X', 'Y']], names=['level1', 'level2'])
series = pd.Series(range(6), index=index)
print("MultiIndex Series:\n", series)
# groupby()を使った集約
print("\nseries.groupby(level='level1').sum():\n", series.groupby(level='level1').sum()) # level1ごとに合計
print("\nseries.groupby(level='level2').mean():\n", series.groupby(level='level2').mean()) # level2ごとに平均
5. インデックスのソート:
sort_index()
メソッドを使用すると、MultiIndexをソートできます。レベルを指定してソートすることも可能です。
import pandas as pd
# MultiIndex Seriesの作成 (順不同)
index = pd.MultiIndex.from_tuples([('B', 'Y'), ('A', 'X'), ('C', 'Y'), ('A', 'Y'), ('B', 'X'), ('C', 'X')], names=['level1', 'level2'])
series = pd.Series(range(6), index=index)
print("元のSeries:\n", series)
# sort_index()を使ったソート
print("\nseries.sort_index():\n", series.sort_index()) # 全てのレベルでソート
print("\nseries.sort_index(level='level1'):\n", series.sort_index(level='level1')) # level1でソート
これらのテクニックを組み合わせることで、MultiIndex Seriesを柔軟かつ効率的に操作できます。次のセクションでは、レベル名の設定とアクセス、インデックスレベルの並び替えといった、MultiIndexをさらに活用するためのテクニックについて説明します。
MultiIndex Seriesにおいて、インデックスレベルに名前を設定することは、可読性を高め、データ操作をより直感的に行う上で非常に重要です。また、名前を使って特定のレベルに簡単にアクセスできます。
1. レベル名の設定
MultiIndexを作成する際に、names
引数を指定することでレベル名を設定できます。
import pandas as pd
# MultiIndexの作成 (レベル名あり)
index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Level_One', 'Level_Two'])
series = pd.Series([1, 2, 3, 4], index=index)
print("レベル名ありのMultiIndex Series:\n", series)
# MultiIndexのレベル名を後から変更する
series.index.names = ['First_Level', 'Second_Level']
print("\nレベル名を変更したMultiIndex Series:\n", series)
MultiIndex.from_product
以外にも、MultiIndex.from_tuples
やMultiIndex.from_arrays
を使用する際にも、names
引数を指定できます。また、作成後にseries.index.names
にリスト形式でレベル名を代入することで、後からレベル名を変更することも可能です。
2. レベル名を使ったアクセス
レベル名を使ってデータにアクセスする方法はいくつかあります。
-
xs()
メソッド: 特定のレベルの値を指定してデータを抽出する際に、レベル名を指定できます。import pandas as pd # MultiIndex Seriesの作成 (レベル名あり) index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Level_One', 'Level_Two']) series = pd.Series([1, 2, 3, 4], index=index) # xs()でレベル名を指定 print("\nseries.xs('X', level='Level_Two'):\n", series.xs('X', level='Level_Two'))
-
groupby()
メソッド: 特定のレベルに基づいてデータを集約する際に、レベル名を指定できます。import pandas as pd # MultiIndex Seriesの作成 (レベル名あり) index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Level_One', 'Level_Two']) series = pd.Series([1, 2, 3, 4], index=index) # groupby()でレベル名を指定 print("\nseries.groupby(level='Level_One').sum():\n", series.groupby(level='Level_One').sum())
-
get_level_values()
メソッド: 特定のレベルの値を取得できます。import pandas as pd # MultiIndex Seriesの作成 (レベル名あり) index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Level_One', 'Level_Two']) series = pd.Series([1, 2, 3, 4], index=index) # get_level_values()でレベル名を使う print("\nseries.index.get_level_values('Level_One'):\n", series.index.get_level_values('Level_One'))
これらの方法を使うことで、レベル名を使ってMultiIndex Seriesのデータを柔軟に操作できます。レベル名を適切に設定することで、コードの可読性が向上し、より効率的なデータ分析が可能になります。
次のセクションでは、インデックスレベルの並び替えについて説明します。
MultiIndex Seriesでは、インデックスレベルの順序を変更することで、データの見やすさや操作性を向上させることができます。Pandasでは、swaplevel()
メソッドとreorder_levels()
メソッドを使って、インデックスレベルを並び替えることができます。
1. swaplevel()
メソッド
swaplevel()
メソッドは、2つのインデックスレベルの順序を入れ替えます。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y']], names=['Level_One', 'Level_Two'])
series = pd.Series([1, 2, 3, 4], index=index)
print("元のSeries:\n", series)
# swaplevel()でレベルを入れ替え
swapped_series = series.swaplevel('Level_One', 'Level_Two')
print("\nswaplevel()後のSeries:\n", swapped_series)
この例では、’Level_One’と’Level_Two’の順序が入れ替わっています。swaplevel()
メソッドは、元のSeriesを変更せず、新しいSeriesを返します。
2. reorder_levels()
メソッド
reorder_levels()
メソッドは、複数のインデックスレベルの順序を一度に変更できます。レベル名をリストで指定することで、任意の順序に並び替えることができます。
import pandas as pd
# MultiIndex Seriesの作成
index = pd.MultiIndex.from_product([['A', 'B'], ['X', 'Y'], ['1', '2']], names=['Level_One', 'Level_Two', 'Level_Three'])
series = pd.Series(range(8), index=index)
print("元のSeries:\n", series)
# reorder_levels()でレベルを並び替え
reordered_series = series.reorder_levels(['Level_Three', 'Level_One', 'Level_Two'])
print("\nreorder_levels()後のSeries:\n", reordered_series)
この例では、’Level_Three’, ‘Level_One’, ‘Level_Two’の順序に並び替えられています。reorder_levels()
メソッドも、元のSeriesを変更せず、新しいSeriesを返します。
並び替えのメリット:
- データ構造の整理: インデックスレベルを適切な順序に並び替えることで、データの構造をより理解しやすくすることができます。
- 効率的なデータアクセス: 特定のレベルに基づいてデータを抽出したり集約したりする際に、レベルの順序が適切であれば、より効率的なデータアクセスが可能になります。
- 可読性の向上: レベル名を考慮して並び替えることで、コードの可読性を高めることができます。
swaplevel()
とreorder_levels()
を使いこなすことで、MultiIndex Seriesの操作性を大幅に向上させることができます。
次のセクションでは、応用例として、階層構造データの表現について説明します。
MultiIndex Seriesは、階層構造を持つデータを表現するのに非常に適しています。例えば、組織構造、地理的な階層、時間的な階層など、さまざまな種類の階層構造を表現できます。
ここでは、MultiIndex Seriesを使って、店舗の売上データを階層的に表現する例を紹介します。
例:店舗売上データの表現
ある会社が複数の地域に店舗を持ち、各店舗で複数の商品を販売しているとします。このデータをMultiIndex Seriesで表現してみましょう。
import pandas as pd
# データの準備
data = {
('東京', '渋谷', '商品A'): 100,
('東京', '渋谷', '商品B'): 150,
('東京', '新宿', '商品A'): 120,
('東京', '新宿', '商品C'): 200,
('大阪', '梅田', '商品B'): 180,
('大阪', '梅田', '商品C'): 220,
('大阪', '難波', '商品A'): 90,
('大阪', '難波', '商品B'): 130
}
# MultiIndex Seriesの作成
sales_series = pd.Series(data)
sales_series.index.names = ['地域', '店舗', '商品'] #レベル名を設定
print("店舗売上データ:\n", sales_series)
# 特定の地域の売上合計
print("\n東京の売上合計:", sales_series['東京'].sum())
# 特定の店舗の売上データ
print("\n渋谷店の売上データ:\n", sales_series[('東京', '渋谷')])
# 特定の商品の売上データ (xs()を使用)
print("\n商品Aの売上データ:\n", sales_series.xs('商品A', level='商品'))
# 地域ごとの売上合計
print("\n地域ごとの売上合計:\n", sales_series.groupby(level='地域').sum())
この例では、sales_series
は、地域、店舗、商品の3つのレベルを持つMultiIndex Seriesです。このように、MultiIndex Seriesを使うことで、階層構造を持つデータを効率的に表現し、様々な分析を行うことができます。
MultiIndex Seriesの利点:
- データの構造化: 階層構造を明示的に表現できるため、データの構造を理解しやすくなります。
- 柔軟なデータアクセス: 特定のレベルに基づいてデータを抽出したり集約したりすることが容易になります。
- 複雑な分析: 階層構造を考慮した、より高度な分析を行うことができます。
この例は簡単なものですが、MultiIndex Seriesは、より複雑な階層構造を持つデータにも適用できます。例えば、時間的な階層(年、月、日など)や、組織構造(部門、チーム、個人など)を表現することも可能です。
MultiIndex Seriesを使いこなすことで、データ分析の幅を広げ、より深い洞察を得ることができます。
最後のセクションでは、この記事のまとめとして、MultiIndexによる柔軟なデータ表現の重要性について改めて述べます。
この記事では、Pandas Seriesに新しいインデックスレベルを追加し、MultiIndexを活用する方法について詳しく解説しました。append()
メソッドやreindex()
メソッドを使って既存のSeriesに新しいレベルを追加する方法、MultiIndex Seriesの操作方法、レベル名設定、インデックスレベルの並び替えなど、MultiIndexを使いこなすための様々なテクニックを紹介しました。
MultiIndexの最大の利点は、柔軟なデータ表現を可能にすることです。単一のインデックスだけでは表現しきれない、階層構造を持つデータや複雑な関係性を持つデータを、MultiIndexを用いることで効率的に表現し、分析することができます。
店舗の売上データのように、地域、店舗、商品といった複数のカテゴリを持つデータを分析する際に、MultiIndexは非常に強力なツールとなります。また、時間的な階層や組織構造など、さまざまな種類の階層構造を持つデータを表現するのにも適しています。
MultiIndexを使いこなすためには、append()
、reindex()
、loc[]
、xs()
、groupby()
といった様々なメソッドを理解し、適切に使いこなす必要があります。また、レベル名の設定やインデックスレベルの並び替えといったテクニックも、データ分析の効率を向上させる上で重要です。
MultiIndex Seriesは、最初は複雑に感じるかもしれませんが、その強力な表現力と柔軟性は、データ分析の可能性を大きく広げます。ぜひ、この記事で学んだ知識を活かして、MultiIndex Seriesを積極的に活用し、より深いデータ分析に取り組んでみてください。MultiIndexを使いこなすことで、今まで見えなかった新たな洞察が得られるはずです。