インデックスシステム【Pandasの核心機能】
Index(インデックス)は、Pandasの中で最も過小評価されている一方で、最も強力な機能です。Indexは単なる「行ラベル」ではなく、データアラインメントの中核となる仕組みであり、SeriesやDataFrameが算術演算でやり取りする際に、値を自動的に照合する「キー」として機能します。Indexを理解すれば、なぜPandasがNumPyよりもデータ分析に適しているのかが分かるでしょう。本セクションでは、Indexの本質、種類、そしてアラインメントの仕組みについて深く掘り下げます。
注意: 以下のコードは、ローカルのPython環境で実行する必要があります。
1. 学習内容
- ❶ Indexの本質と型の階層構造
- ❷ set_index / reset_index の操作
- ❸ インデックスアラインメント(Pandasで最も重要な機能)
- ❹ reindex と align
- ❺ よく使うIndexのプロパティとメソッド
2. アリスの2ヶ月間売上アラインメント問題
(1) 課題:行が揃っていない問題
アリスのコーヒーショップは、1月と2月で営業日が異なります(1月は31日、2月は28日)。また、休業日もあります。彼女は2ヶ月分のデータを統合して比較する必要があります:
import pandas as pd
# 1月の売上(一部休業日あり)
jan = pd.Series({'Mon': 320, 'Tue': 280, 'Wed': 350, 'Thu': 410, 'Fri': 390})
# 2月の売上(異なる曜日、月・火は休業)
feb = pd.Series({'Wed': 370, 'Thu': 420, 'Fri': 400, 'Sat': 550, 'Sun': 510})
# NumPy方式:位置ごとに足し算してしまう — 間違い!
# Pandas:インデックスラベルで自動的にアラインメント
total = jan + feb
print(total)
# Fri 790.0 ← 一致!
# Mon NaN ← janにのみ存在
# Sat NaN ← febにのみ存在
# Sun NaN ← febにのみ存在
# Thu 830.0 ← 一致!
# Tue NaN ← janにのみ存在
# Wed 720.0 ← 一致!
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(2) 解決策:自動インデックスアラインメント
Pandasが算術演算を実行する際、インデックスラベルに基づいて自動的にアラインメントを行います。一致するラベルの値同士が加算され、一致しないラベルはNaNで埋められます。データの行を手動で照合する必要は一切ありません。
▶ サンプル:アラインメント操作とfill_value(難易度 ⭐⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
jan = pd.Series({'Mon': 320, 'Tue': 280, 'Wed': 350, 'Thu': 410, 'Fri': 390})
feb = pd.Series({'Wed': 370, 'Thu': 420, 'Fri': 400, 'Sat': 550, 'Sun': 510})
# 欠損値を0で埋めて、よりクリーンに比較
total = jan.add(feb, fill_value=0)
print(total.sort_index())
# Fri 790.0
# Mon 320.0 ← janのみ、feb=0
# Sat 550.0 ← febのみ、jan=0
# Sun 510.0 ← febのみ、jan=0
# Thu 830.0
# Tue 280.0 ← janのみ、feb=0
# Wed 720.0
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(3) メリット:インデックスアラインメントは微妙なバグを防ぐ
NumPyを使って位置で加算した場合、jan[0] + feb[0] は 320 + 370 = 690 となりますが、月曜日と水曜日はまったく同じ日ではありません!インデックスアラインメントを使えば、エラーの温床となる「位置による照合」から解放されます。
3. Indexの本質と種類
(1) Indexは通常の配列ではない
Indexは、ラベルベースのインデックス指定とアラインメントのために設計された、不変のndarrayに似たオブジェクトです。主な特徴は以下の通りです:
- 不変(Immutable): 作成後は変更できません(データの安全性を確保)
- ハッシュ可能(Hashable): 辞書のキーや集合の要素として使用できます
- 重複を許可: 重複するラベルも許容されます(ただしパフォーマンスは低下します)
- 複数のサブタイプ: RangeIndex / DatetimeIndex / CategoricalIndex など
graph TB
IDX["Index(基底)"] --> RI["RangeIndex<br/>自動生成される 0,1,2,..."]
IDX --> DI["DatetimeIndex<br/>時間ベースのラベル"]
IDX --> CI["CategoricalIndex<br/>有限のカテゴリ集合"]
IDX --> MI["MultiIndex<br/>階層的なラベル"]
IDX --> GI["汎用Index<br/>任意のラベル"]
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(2) Indexの種類の比較
| 種類 | 作成方法 | 典型的なユースケース | 特殊な機能 |
|---|---|---|---|
| RangeIndex | デフォルト | インデックスを指定しない場合 | メモリ効率が良い |
| Index | 手動で指定 | 文字列/数値ラベル | 汎用的 |
| DatetimeIndex | pd.date_range | 時系列データ | 頻度/オフセット/リサンプリング |
| CategoricalIndex | dtype='category' | 有限のカテゴリ | 順序付き/順序なしカテゴリ |
| MultiIndex | from_tuples/from_product | 階層データ | 複数レベルの選択 |
▶ サンプル:Indexの種類の確認(難易度 ⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
# RangeIndex(デフォルト)
s1 = pd.Series([1, 2, 3])
print(type(s1.index)) # <class 'pandas.core.indexes.range.RangeIndex'>
# 汎用Index(文字列ラベル)
s2 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
print(type(s2.index)) # <class 'pandas.core.indexes.base.Index'>
# DatetimeIndex
dates = pd.date_range('2026-01-01', periods=5)
s3 = pd.Series([100, 200, 150, 300, 250], index=dates)
print(type(s3.index)) # <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
4. set_index と reset_index
(1) set_index:列を行ラベルにする
▶ サンプル:set_indexの使い方(難易度 ⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
df = pd.DataFrame({
'product': ['Coffee', 'Tea', 'Juice'],
'price': [4.50, 3.80, 5.20],
'category': ['Hot', 'Hot', 'Cold']
})
# productをインデックスに設定
df_indexed = df.set_index('product')
print(df_indexed)
# price category
# product
# Coffee 4.50 Hot
# Tea 3.80 Hot
# Juice 5.20 Cold
print(df_indexed.loc['Coffee', 'price']) # 4.5
# 設定後に列を削除(デフォルトはTrue)
# 列を残す場合:drop=False
df_keep = df.set_index('product', drop=False)
print(df_keep.columns) # 'product' がまだ残っている
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(2) reset_index:行ラベルを列に戻す
▶ サンプル:reset_indexの使い方(難易度 ⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
df = pd.DataFrame({
'price': [4.50, 3.80, 5.20]
}, index=['Coffee', 'Tea', 'Juice'])
# インデックスを列に戻す
df_reset = df.reset_index()
print(df_reset)
# index price
# 0 Coffee 4.50
# 1 Tea 3.80
# 2 Juice 5.20
# 新しい列に名前を付ける
df_reset2 = df.reset_index(names='product')
print(df_reset2.columns) # Index(['product', 'price'])
# インデックスを完全に破棄(列として残さない)
df_drop = df.reset_index(drop=True)
print(df_drop)
# price
# 0 4.50
# 1 3.80
# 2 5.20
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(3) set_index と reset_index の比較
| 操作 | set_index | reset_index |
|---|---|---|
| 方向 | 列 → 行ラベル | 行ラベル → 列 |
| 元の列 | デフォルトで削除(drop=True) | 新しい列として保持 |
| デフォルトのインデックス | なし(列を指定する必要がある) | RangeIndex(0,1,2,...) |
| インプレース変更 | デフォルトでは行わない(inplace=False) | デフォルトでは行わない |
5. インデックスアラインメントの詳細
(1) アラインメントのルール
2つのPandasオブジェクト(SeriesまたはDataFrame)を演算で組み合わせる場合:
- 両方のIndexの和集合が計算されます
- 一致するラベルに対して演算が行われます
- 一致しないラベルはNaNで埋められます
graph LR
A["Series A<br/>{a:1, b:2, c:3}"] --> ADD["A + B"]
B["Series B<br/>{b:20, c:30, d:40}"] --> ADD
ADD --> R["結果<br/>{a:NaN, b:22, c:33, d:NaN}"]
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
▶ サンプル:DataFrameのアラインメント(難易度 ⭐⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
df1 = pd.DataFrame({'A': [1, 2, 3]}, index=['x', 'y', 'z'])
df2 = pd.DataFrame({'A': [10, 20, 30]}, index=['y', 'z', 'w'])
result = df1 + df2
print(result)
# A
# w NaN
# x NaN
# y 22.0
# z 33.0
# fill_valueを指定した場合
result2 = df1.add(df2, fill_value=0)
print(result2)
# A
# w 30.0
# x 1.0
# y 22.0
# z 33.0
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
ヒント: インデックスアラインメントは、+, -, *, / 演算子だけでなく、add/sub/mul/div などのメソッドでも発生します。
fill_valueパラメータは、ラベルが欠けている側にのみ適用されます。両側に欠けている場合、結果は依然としてNaNになります。
6. reindex と align
(1) reindex:インデックスを再構成する
▶ サンプル:reindexの使い方(難易度 ⭐⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
s = pd.Series([320, 280, 350], index=['Mon', 'Tue', 'Wed'])
# 新しいラベルを含むようにインデックスを再構成(NaNで埋める)
s_full = s.reindex(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'])
print(s_full)
# Mon 320.0
# Tue 280.0
# Wed 350.0
# Thu NaN
# Fri NaN
# 欠損値を埋める
s_filled = s.reindex(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], fill_value=0)
print(s_filled)
# 前方埋め(前の値を使用)
s_ffill = s.reindex(['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], method='ffill')
print(s_ffill)
# ThuにはWedの値(350)が入る
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(2) align:2つのSeriesを同時にアラインメントする
▶ サンプル:alignメソッド(難易度 ⭐⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
s1 = pd.Series({'a': 1, 'b': 2, 'c': 3})
s2 = pd.Series({'b': 20, 'c': 30, 'd': 40})
# 両方をインデックスの和集合にアラインメント
s1_aligned, s2_aligned = s1.align(s2)
print(s1_aligned)
# a 1.0
# b 2.0
# c 3.0
# d NaN
print(s2_aligned)
# a NaN
# b 20.0
# c 30.0
# d 40.0
# fill_valueを指定してアラインメント
s1_f, s2_f = s1.align(s2, fill_value=0)
print(s1_f)
# a 1
# b 2
# c 3
# d 0
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
(3) reindex と align の比較
| メソッド | 目的 | ユースケース |
|---|---|---|
| reindex | 単一のオブジェクトを新しいインデックスに再構成する | インデックスの拡張や並べ替え |
| align | 2つのオブジェクトを同時にアラインメントする | 2つのオブジェクトを互いに整合させる |
7. よく使うIndexのプロパティとメソッド
(1) クイックリファレンス
| プロパティ/メソッド | 説明 | 結果の例 |
|---|---|---|
idx.is_unique |
すべてのラベルが一意かどうか | True / False |
idx.is_monotonic_increasing |
ラベルが単調増加かどうか | True / False |
idx.has_duplicates |
重複が存在するかどうか | True / False |
idx.nunique() |
一意な値の数 | 7 |
idx.duplicated() |
重複のブールマスク | ブール配列 |
idx.drop_duplicates() |
重複を削除 | 新しいIndex |
▶ サンプル:Indexプロパティの確認(難易度 ⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
idx1 = pd.Index(['a', 'b', 'c', 'd'])
idx2 = pd.Index(['a', 'b', 'b', 'c'])
print(idx1.is_unique) # True
print(idx2.is_unique) # False
print(idx2.has_duplicates) # True
print(idx1.nunique()) # 4
print(idx2.duplicated()) # [False, False, True, False]
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
8. 総合サンプル:月次売上データのアラインメントと再構成
▶ サンプル:2ヶ月間の売上アラインメントワークフロー(難易度 ⭐⭐⭐)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
import pandas as pd
import numpy as np
# ============================================
# 総合サンプル:Indexを使った月次売上データの
# アラインメントと再構成
# ============================================
# 1. 2ヶ月分の日次売上(異なる日付)
jan = pd.Series({
'2026-01-06': 320, '2026-01-07': 280, '2026-01-08': 350,
'2026-01-09': 410, '2026-01-10': 390
}, name='jan_sales')
feb = pd.Series({
'2026-02-04': 370, '2026-02-05': 420, '2026-02-06': 400,
'2026-02-07': 550
}, name='feb_sales')
# 2. インデックスの情報
print("=== インデックス分析 ===")
print(f"1月の一意性: {jan.index.is_unique}") # True
print(f"2月の一意性: {feb.index.is_unique}") # True
# 3. 両月を共通フォーマットにアラインメント
jan_a, feb_a = jan.align(feb, fill_value=0)
print(f"\n=== アラインメント後のデータ ===")
comparison = pd.DataFrame({'jan': jan_a, 'feb': feb_a})
print(comparison)
# 4. DataFrameにインデックスを設定してラベルベースでアクセス
df = pd.DataFrame({
'product': ['Latte', 'Cappuccino', 'Espresso', 'Mocha', 'Americano'],
'price': [5.50, 4.80, 3.50, 5.20, 3.00],
'category': ['Specialty', 'Specialty', 'Classic', 'Specialty', 'Classic']
})
print(f"\n=== カテゴリ別の商品 ===")
df_cat = df.set_index('category')
print(df_cat.loc['Specialty', ['product', 'price']])
# 5. すべての曜日が揃うようにインデックスを再構成
weekday_sales = pd.Series(
[320, 280, 350, 410, 390],
index=['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
)
full_week = weekday_sales.reindex(
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
fill_value=0
)
print(f"\n=== 1週間すべての売上 ===")
print(full_week)
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていないため、ローカルにインストール(`pip install pandas`)してから実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
❓ よくある質問
s.index.is_unique を使用してください。可能な限りIndexを一意に保つことがベストプラクティスです。df.set_index('col', drop=False) を使用してください。インデックスの情報は、reset_indexでいつでも列に戻すことができます。df.index = new_index で完全に置き換えるか、df.rename(index=...) でラベルの名前を変更できます。Indexの個々の要素に代入することはできません。📖 まとめ
- IndexはPandasにおけるデータアラインメントの中核となる仕組みであり、演算はラベルに基づいて自動的に照合されます
- Indexは不変でハッシュ可能、重複を許容し、5つのサブタイプ(Range/Datetime/Categorical/Multi/汎用)を持ちます
- set_indexは列を行ラベルに変換し、reset_indexは行ラベルを列に戻します
- アラインメントのルール:Indexの和集合を計算し、一致するラベルで演算し、一致しないラベルはNaNで埋めます
- reindexは単一オブジェクトのインデックスを再構成し、alignは2つのオブジェクトを同時にアラインメントします
- よく使うプロパティ:is_unique / has_duplicates / nunique() / duplicated()
- fill_valueパラメータを使えば、算術演算時の不要なNaNを避けられます
📝 練習問題
- 基礎(難易度 ⭐):5つの都市の人口データを持つSeriesを作成してください。DataFrameに対してset_indexとreset_indexを使い、列とインデックスの間で変換を行い、インデックスがどのように変化するか観察してください。
- 中級(難易度 ⭐⭐):部分的に重複するインデックスを持つ2つのSeriesを作成してください。
+演算子とadd(fill_value=0)の両方を使って加算し、その違いを比較してください。その後、alignメソッドを使って両方のSeriesをアラインメントしてください。 - 挑戦(難易度 ⭐⭐⭐):DatetimeIndexを使って2ヶ月分(1月と2月、日付は異なる)の売上データを作成してください。reindexを使って両月のすべての日付を埋め(欠損は0で埋める)、2ヶ月間の前日比成長率を計算してください。



