Python データ分析における区間解析のベストプラクティス:Pandas IntervalIndex の contains メソッドと高度なテクニック


メソッドの引数

  • key: 判定対象となる値。整数、浮動小数点、または Interval オブジェクトを指定できます。

メソッドの返値

  • 各区間が key を含むかどうかを示すブーリアンマスクを返します。マスクの長さは IntervalIndex の長さに等しく、各要素は True または False の値を持ちます。

メソッドの使い方

import pandas as pd

# IntervalIndex の作成
index = pd.IntervalIndex([pd.Interval(0, 2), pd.Interval(1, 3), pd.Interval(2, 4)])

# 値の包含判定
result = index.contains(1.5)
print(result)

上記のコードは、以下の出力を生成します。

[ True  True False]

この例では、result[True, True, False] となり、最初の 2 つの区間は 1.5 を含み、最後の区間は 1.5 を含まないことを示しています。

  • 時系列データにおける値の出現状況を分析する
  • 区間データの重複を検出する
  • 特定の値を含むデータレコードを抽出する
  • メソッドの引数に closed キーワード引数を渡すことで、区間判定の基準となる閉区間端を設定できます。デフォルトでは 'right' となり、右端点のみを判定します。
  • key として Interval オブジェクトを指定すると、その区間と IntervalIndex 内の各区間の包含関係を判定します。


サンプル 1:特定の値を含むデータレコードの抽出

import pandas as pd

# 気温データの生成
data = {'city': ['Seattle', 'San Francisco', 'New York'],
        'temperature': [15.2, 21.5, 12.7]}

df = pd.DataFrame(data)

# IntervalIndex の作成
temperature_index = pd.IntervalIndex([pd.Interval(10, 20), pd.Interval(20, 30)])

# 特定の気温範囲に該当するデータレコードの抽出
df_filtered = df[df['temperature'].index.contains(temperature_index)]

print(df_filtered)
     city  temperature
0  Seattle     15.200000
1  San Francisco  21.500000

この例では、temperature_index という IntervalIndex を作成し、10 度から 20 度、および 20 度から 30 度の 2 つの気温範囲を定義します。その後、df['temperature'].index.contains(temperature_index) を用いて、各データレコードの気温がこれらの範囲のいずれかに含まれているかどうかを判定し、該当するレコードのみを含む新しい DataFrame df_filtered を作成します。

サンプル 2:区間データの重複を検出

import pandas as pd

# 販売データの生成
data = {'product': ['Laptop', 'Phone', 'Tablet'],
        'sales_period': [pd.Interval(2023, 2024), pd.Interval(2022, 2023), pd.Interval(2023, 2024)]}

df = pd.DataFrame(data)

# IntervalIndex の作成
sales_index = pd.IntervalIndex([pd.Interval(2022, 2023), pd.Interval(2023, 2024), pd.Interval(2024, 2025)])

# 重複する販売期間の検出
df_overlaps = df[df['sales_period'].index.overlaps(sales_index)]

print(df_overlaps)
   product  sales_period
0  Laptop      [2023, 2024)
2  Tablet      [2023, 2024)

この例では、sales_index という IntervalIndex を作成し、3 つの販売期間を定義します。その後、df['sales_period'].index.overlaps(sales_index) を用いて、各データレコードの販売期間が sales_index 内のいずれかの期間と重複しているかどうかを判定し、重複する期間を含むレコードのみを含む新しい DataFrame df_overlaps を作成します。

import pandas as pd

# 株価データの生成
data = {'date': pd.to_datetime(['2022-01-01', '2022-01-02', '2022-01-03']),
        'price': [100, 105, 110]}

df = pd.DataFrame(data)

# IntervalIndex の作成
price_index = pd.IntervalIndex([pd.Interval(100, 105), pd.Interval(105, 110)])

# 特定の株価水準が出現する頻度の分析
price_counts = df[df['price'].isin(price_index)].groupby('price').size()

print(price_counts)
price
100    1
105    1
dtype: int64


pandas.IntervalIndex.overlaps を使用する

pandas.IntervalIndex.overlaps メソッドは、IntervalIndex 内の各区間と指定された区間が重なるかどうかを要素ごとに判定します。このメソッドは、contains メソッドよりも柔軟性が高く、部分的な包含関係も検出できます。

利点

  • 複雑な判定条件を記述しやすい
  • 部分的な包含関係も検出できる

欠点

  • contains メソッドよりも計算量が多くなる場合がある


import pandas as pd

# IntervalIndex の作成
index = pd.IntervalIndex([pd.Interval(0, 2), pd.Interval(1, 3), pd.Interval(2, 4)])

# 値の包含判定
result = index.overlaps(pd.Interval(1, 2))
print(result)
[ True  True False]

この例では、result[True, True, False] となり、最初の 2 つの区間は 1 から 2 までの範囲と重なり、最後の区間は重ならないことを示しています。

pandas.Series.between を使用する

pandas.Series.between メソッドは、Series の各要素が指定された範囲内にあるかどうかを判定します。このメソッドは、IntervalIndex ではなく Series を扱う場合に有効です。

利点

  • IntervalIndex 以外のデータ構造にも適用できる

欠点

  • IntervalIndex を明示的に作成する必要がある


import pandas as pd

# Series の作成
series = pd.Series([0, 1, 2, 3], index=pd.IntervalIndex([pd.Interval(0, 2), pd.Interval(1, 3), pd.Interval(2, 4), pd.Interval(3, 5)]))

# 値の包含判定
result = series.between(1, 2)
print(result)
[ True  True False False]

この例では、result[True, True, False, False] となり、最初の 2 つの要素は 1 から 2 までの範囲内にあり、最後の 2 つの要素は範囲外であることを示しています。

カスタム関数を使用する

上記の方法でニーズを満たせない場合は、カスタム関数を作成して、IntervalIndex 内の各区間と指定された値の包含関係を判定することができます。

利点

  • 完全な制御が可能

欠点

  • コードが複雑になる場合がある


import pandas as pd

def is_contained(interval, value):
    if interval.left <= value <= interval.right:
        return True
    else:
        return False

# IntervalIndex の作成
index = pd.IntervalIndex([pd.Interval(0, 2), pd.Interval(1, 3), pd.Interval(2, 4)])

# 値の包含判定
result = [is_contained(interval, 1.5) for interval in index]
print(result)
[ True  True False]

この例では、is_contained というカスタム関数を作成し、Interval と値の包含関係を判定します。その後、この関数を用いて、IntervalIndex 内の各区間と 1.5 の包含関係を判定し、結果をリストに格納します。