pandas

【pandas】Indexの基本

【pandas】Indexの基本
naoki-hn

pandas の SeriesDataFrame のインデックスで使用されている Index の基本について解説します。

pandas の Index

pandas でよく使用される SeriesDataFrame では、要素のアクセスにインデックスを使用します。pandas では、このインデックスは Index クラスとして実装されています。Index は、値とそのデータ型 (dtype) を持っています。

この記事では、 Index の基本について説明していきます。

以下の SeriesDataFrame の作成例で見てみましょう。

import numpy as np
import pandas as pd

# Series
ser = pd.Series([1, 2, 3], index=["A", "B", "C"])
print(ser)
print(ser.index)
print("\n")

# DataFrame
df = pd.DataFrame(
    np.arange(9).reshape((3, 3)), index=[1, 2, 3], columns=["A", "B", "C"]
)
print(df)
print(df.index)
print(df.columns)
【実行結果】
A    1
B    2
C    3
dtype: int64
Index(['A', 'B', 'C'], dtype='object')


   A  B  C
1  0  1  2
2  3  4  5
3  6  7  8
Index([1, 2, 3], dtype='int64')
Index(['A', 'B', 'C'], dtype='object')

上記例で indexcolumns を表示しいますが、型が Index オブジェクトとなっていることが分かります。また、dtype には、インデックス値の型が示されています。

この Index クラスのオブジェクトは、それ自体に特徴的な部分があります。以降で、Index オブジェクトの操作や特徴について詳細を説明していきます。

Index オブジェクトの操作の基本

Index オブジェクトの作成方法

Index オブジェクトは、以下のようにリストを受け取って作成することできます。

import pandas as pd

ind = pd.Index([1, 2, 3])
print(ind)

ind = pd.Index(["A", "B", "C"])
print(ind)
【実行結果】
Index([1, 2, 3], dtype='int64')
Index(['A', 'B', 'C'], dtype='object')

Index オブジェクトに対するアクセス

Index オブジェクトの要素へは、リストと同じようにアクセスすることが可能です。

import pandas as pd

ind = pd.Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# 要素へのアクセス
print(ind[1])
# スライスによるアクセス
print(ind[1:9:2])
# 各種情報へのアクセス
print(ind.size)
print(ind.shape)
print(ind.ndim)
print(ind.dtype)
【実行結果】
2
Index([2, 4, 6, 8], dtype='int64')
10
(10,)
1
int64

例のように、位置を指定したり、スライス記法によって一部を抽出することができます。また、NumPy の配列(ndarray)のように sizeshapendimdtype といった属性値にアクセスが可能です。

Index オブジェクトはイミュータブル(immutable)

Index オブジェクトは、「イミュータブル(immutable)」で変更ができないという特徴があります。

インデックスはデータを識別するために非常に重要な情報です。簡単にインデックスが変更されてしまうと、データフレームの行の識別が困難になり、誤ったデータ操作や分析結果を引き起こす原因となります。pandas においては、インデックスを表現する Index オブジェクトがイミュータブル(immutable)であることで安全性が向上するため、この性質は非常に重要です。

Index オブジェクトがイミュータブル(immutable)であるということを簡単な例で見てみましょう。

import pandas as pd

ind = pd.Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# Indexオブジェクトはイミュータブル(immutable)なため要素の変更はできない
ind[0] = 10
【実行結果例】
TypeError: Index does not support mutable operations

上記のように、インデックス要素の値を変更しようとすると、TypeError となります。これは、変更を行う操作はサポートしていないということを示すエラーです。

Index オブジェクトの集合演算

pandas の Index オブジェクトは、複数データの結合といった操作を容易にできるようにするために集合演算ができます。Python 組み込みの集合である set と同じように演算が可能です。

import pandas as pd

indA = pd.Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
indB = pd.Index([6, 7, 8, 9, 10, 11, 12, 13, 14, 15])

print(f"積集合 A&B: {indA.intersection(indB)}")
print(f"和集合 A|B: {indA.union(indB)}")
print(f"差集合 A-B: {indA.difference(indB)}")
print(f"差集合 B-A: {indB.difference(indA)}")
print(f"排他的論理和 A^B: {indA.symmetric_difference(indB)}")
【実行結果】
積集合 A&B: Index([6, 7, 8, 9, 10], dtype='int64')
和集合 A|B: Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], dtype='int64')
差集合 A-B: Index([1, 2, 3, 4, 5], dtype='int64')
差集合 B-A: Index([11, 12, 13, 14, 15], dtype='int64')
排他的論理和 A^B: Index([1, 2, 3, 4, 5, 11, 12, 13, 14, 15], dtype='int64')

例のように、積集合 (intersection)、和集合 (union)、差集合 (difference)、排他的論理和 (symmetric_difference) といった組み込みの集合 (set) と同じメソッドを使用することができます。

集合 (set) の演算については「集合(set)の演算と包含関係の判定」を参考にしてください。

様々なタイプのインデックス

上記では、一般的でシンプルなケースでの Index オブジェクトの話を紹介しました。pandas では、利用ケースに特化したようなインデックスタイプも用意されているので、それらについても紹介したいと思います。

具体的には MultiIndexDatetimeIndexPeriodIndexCategoricalIndex について説明します。利用ケースをまとめてみると以下のようになります。

名称用途
MultiIndex階層的なデータ構造を扱う場合
DatetimeIndex時系列データ処理等、日時データをインデックスとして使用する場合
PeriodIndex特定の期間(月、四半期、年など)の単位で分析する場合
CategoricalIndex限られた数のカテゴリーを持つデータを扱う場合

これらのインデックスタイプは、特定種類のデータや特定の分析ニーズに合わせて最適化されているため、データ構造や分析の目的に応じて選択を検討することが重要です。

MultiIndex

MultiIndex は、階層的なデータ構造を扱う場合に適したインデックスです。

import pandas as pd

# MultiIndexを作成 (from_tuples)
multi_index = pd.MultiIndex.from_tuples(
    [("A", 1), ("A", 2), ("B", 1), ("B", 2), ("B", 3)],
    names=["Level 1", "Level 2"],
)
print(multi_index, "\n")

# データフレームの作成
df_multi = pd.DataFrame(
    data=[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]],
    index=multi_index,
    columns=["col1", "col2", "col3"],
)
print(df_multi)
【実行結果】
MultiIndex([('A', 1),
            ('A', 2),
            ('B', 1),
            ('B', 2),
            ('B', 3)],
           names=['Level 1', 'Level 2']) 

                 col1  col2  col3
Level 1 Level 2                  
A       1           1     2     3
        2           4     5     6
B       1           7     8     9
        2          10    11    12
        3          13    14    15

例のように、MultiIndex を作る場合には、MultiIndex クラスの from_tuples メソッドを使用します。引数にはタプルで各行に対するインデックスの情報を列挙したリストを指定します。タプルの 1 要素目が Level 1、2 要素目が Level 2、… というように該当しています。また、names 引数に各レベルの名称をリストで指定します。

作成されるインデックスは MultiIndex オブジェクトとなります。DataFrame を作成時に index 引数に指定することで階層構造を持ったインデックスの DataFrame を作ることが可能です。

なお、同様のことを from_arrays メソッドでも実現できます。

# MultiIndexを作成 (from_arrays)
multi_index = pd.MultiIndex.from_arrays(
    [["A", "A", "B", "B", "B"], [1, 2, 1, 2, 3]],
    names=["Level 1", "Level 2"],
)

上記を見ていただけばわかるように from_tuples の場合とインデックス情報の渡し方が異なっています。from_tuples では各行のインデックス情報のリストを渡していましたが、from_arrays では、各レベルの要素を含むリストを渡しています。

from_tuplesfrom_arrays は、どちらでも MultiIndex が作成できますので、持っているデータ形式や開発者の使いやすさで選択してもらえばよいかと思います。

DatetimeIndex

DatetimeIndex は、時系列データ処理等、日時データをインデックスとして使用する場合に適したインデックスです。

import pandas as pd

# 時間単位のDatetimeIndexを作成
date_index_h = pd.date_range(
    "2024-01-01",
    periods=5,
    freq="H",
)
print(date_index_h, "\n")

# データフレームの作成
df_time_value = pd.DataFrame(
    data=[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]],
    index=date_index_h,
    columns=["val1", "val2", "val3"],
)
print(df_time_value)
【実行結果】
DatetimeIndex(['2024-01-01 00:00:00', '2024-01-01 01:00:00',
               '2024-01-01 02:00:00', '2024-01-01 03:00:00',
               '2024-01-01 04:00:00'],
              dtype='datetime64[ns]', freq='H') 

                     val1  val2  val3
2024-01-01 00:00:00     1     2     3
2024-01-01 01:00:00     4     5     6
2024-01-01 02:00:00     7     8     9
2024-01-01 03:00:00    10    11    12
2024-01-01 04:00:00    13    14    15

例のように DatetimeIndex を作成する場合には、date_range 関数を使用します。引数は、開始日時と共に時間の数を periods で、時間間隔を freq で指定します。

上記例では "2024-01-01"period=5freq="H" を指定していますので、2024-01-01 00:00:00 から 1 時間単位で 5 つのインデックスを作成します。なお、freq には、"T": 分単位、"H": 時間単位、"D": 日単位等いろいろなオプションがあるので目的に合わせて調べて使ってみてください。

作成されるインデックスは DatetimeIndex オブジェクトとなります。DataFrame を作成時に index 引数に指定することで各時間をインデックスに持つようなデータを作成することができます。例えば、等間隔の時系列データを表す場合に最適です。

少し異なる作成方法として、startend を指定する方法もあります。

# 最初(start)と最後(end)を指定することも可能
# start、end、periods(時間の数)の3引数を同時に使用することはできないので注意
date_index_s_e = pd.date_range(
    start="2024-01-01 00:00:00",
    end="2024-01-01 04:00:00",
    freq="H",
)
print(date_index_s_e)

使い方はほとんど同じで、startend の間を freq の時間間隔で埋めたインデックスが作成されます。なお、startendperiods の 3 引数は、同時には使用できないので注意してください。

PeriodIndex

PeriodIndex は、特定の期間(月、四半期、年など)単位で分析する場合に適したインデックスです。

DatetimeIndex に似ているかと思うかもしれませんが、DatetimeIndex が時刻に関する概念であるのに対して、PeriodIndex はあくまで期間を表現するためのインデックスである点で違いがあります。

例えば、2024-01 は 2024 年の 1 月全体を指しますが、その中に日付や時刻といった考えは含まれません。このため、一定の期間単位 (月単位、四半期、年単位等) で集計などの分析を行うようなデータの管理に特に適しています。

import pandas as pd

# 月単位のPeriodIndexを作成
monthly_period = pd.period_range(
    start="2024-01",
    end="2024-12",
    freq="M",
)
print(monthly_period, "\n")

# データフレームの作成
df_period = pd.DataFrame(
    data=list(range(1, 13)),
    index=monthly_period,
    columns=["data"],
)
print(df_period)
【実行結果】
PeriodIndex(['2024-01', '2024-02', '2024-03', '2024-04', '2024-05', '2024-06',
             '2024-07', '2024-08', '2024-09', '2024-10', '2024-11', '2024-12'],
            dtype='period[M]') 

         data
2024-01     1
2024-02     2
2024-03     3
2024-04     4
2024-05     5
2024-06     6
2024-07     7
2024-08     8
2024-09     9
2024-10    10
2024-11    11
2024-12    12

例のように PeriodIndex を作成する場合は、period_range 関数を使用します。引数としては、開始日の start、終了日の end、時間間隔の freq を指定します。

上記例では start="2024-01"end="2024-12"freq="M" としていますので、2024 年 1 月 ~ 12 月までを月単位で作成したインデックスとなります。なお、freq は、"D": 日単位、"M": 月単位、"Q": 四半期単位等いろいろなオプションが使用できます。freq で指定できるものは、date_range と似ていますが概念としては異なりますので両方に同じオプションが必ずあるとは限らない点に注意してください。

作成されるインデックスは、PeriodIndexオブジェクトとなります。DataFrame を作成時に index 引数に指定することで期間区分を表すようなデータを作ることができます。一定期間ごとの集計データを扱う場合などに最適です。

CategoricalIndex

CategoricalIndex は、限られた数のカテゴリーを持つデータを扱う場合に適したインデックスです。

import pandas as pd

# カテゴリーで使うラベルのリスト
category_data = ["High", "Medium", "Low", "Low"]

# CategoricalIndexの作成
# categoriesに指定するものがカテゴリーとして許容するリスト
# ordered=Trueとすることで"Low"<"Medium"<"High"の順序があることを示す
category_index = pd.CategoricalIndex(
    category_data,
    categories=["Low", "Medium", "High"],
    ordered=True,
)
print(category_index, "\n")

# データフレームの作成
df_categorical = pd.DataFrame(
    data=[[70, 80, 90], [40, 50, 60], [10, 20, 30], [10, 5, 10]],
    index=category_index,
    columns=["col1", "col2", "col3"],
)
print(df_categorical)
【実行結果】
CategoricalIndex(['High', 'Medium', 'Low', 'Low'], categories=['Low', 'Medium', 'High'], ordered=True, dtype='category') 

        col1  col2  col3
High      70    80    90
Medium    40    50    60
Low       10    20    30
Low       10     5    10

例のように CategoricalIndex を作成する場合は、CategoricalIndex クラスのオブジェクトを生成して使用します。

引数で渡している category_data は、実際にデータに割り当てられるインデックスのカテゴリーリストです。categories 引数で指定しているのは、カテゴリーとして許容するリストとなっており、orderd=True とすると categories で指定した順序性があることを示します。

上記例では、カテゴリーとして許容されるのは "Low""Medium""High" の 3 つで、ordered=True であるため "Low"<"Medium"<"High" の順序があります。

作成されるインデックスは、CategoricalIndexオブジェクトなります。DataFrame を作成時に index 引数に指定することでカテゴリラベルが付与されたデータを作れます。上から HighMediumLowLow というカテゴリラベルが付与されています。このようにデータをカテゴリ分けして扱いたい場合に、最適なインデックスです。

まとめ

pandas の SeriesDataFrame のインデックスで使用されている Index の基本について解説しました。

Index は、SeriesDataFrame での要素アクセスに使用されます。Index の作成方法やアクセス方法、演算などについて紹介をしています。また、その他の様々なインデックス例として MultiIndexDatetimeIndexPeriodIndexCategoricalIndex といったインデックスについても簡単に紹介しました。

Index は、SeriesDataFrame を適切に操作するために理解が必要です。Index の理解を深めて、SeriesDataFrame を適切に扱えるようになりましょう。

Index に関する公式ドキュメントはこちらを参照してください。

ソースコード

上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド
ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました