Сообщить об ошибке.

Объекты Interval и IntervalIndex модуля pandas

Представление данных при помощи ограниченных интервалов pandas

Объект pandas.IntervalIndex() представляет собой тип индекса, который полезен для поддержки индексации значений, которые будут соответствовать набору ограниченных интервалов. Это контейнер вокруг объектов pandas.Interval().

Содержание:


pandas.Interval(left, right, closed='right'):

Объект pandas.Interval() представляет собой ограниченный интервал (неизменяемый объект), подобный срезу, где аргументы left и right - границы интервала, а closed отвечает за закрытие границ (принимает значения 'left', 'right', 'both', 'neither')

Аргументы left и right должны быть одного типа, должны иметь возможность операций сравнивания, а также должны удовлетворять условию left <= right.

Замкнутый интервал (в математике обозначаемый квадратными скобками) содержит свои конечные точки, т. е. замкнутый интервал [0, 5] характеризуется условиями 0 <= x <= 5. Это то, что означает closed='both'. Открытый интервал (в математике обозначаемый круглыми скобками) не содержит своих конечных точек, т. е. открытый интервал (0, 5) характеризуется условиями 0 < x < 5. Это то, что означает closed='neither'. Интервалы также могут быть полуоткрытыми или полузакрытыми, т.е. [0, 5) описывается 0 <= x < 5 (closed='left'), а (0, 5] описывается 0 < x <= 5 (closed='right').

Можно создать интервалы разных типов, например, числовые:

>>> import pandas as pd
>>> iv = pd.Interval(left=0, right=5)
>>> iv
# Interval(0, 5, closed='right')

# принадлежность к интервалу
>>> 2.5 in iv
# True
>>> pd.Interval(left=2, right=5, closed='both') in iv
# True

Можно проверить границы (closed='right', как 0 < x <= 5):

>>> 0 in iv
# False
>>> 5 in iv
# True
>>> 0.0001 in iv
# True

# длина интервала
>>> iv.length
# 5

С интервалом можно производить сложение + и умножение *, и операция применяется к каждой из его границ, поэтому результат зависит от типа связанных элементов.

>>> shifted_iv = iv + 3
>>> shifted_iv
# Interval(3, 8, closed='right')
>>> extended_iv = iv * 10.0
>>> extended_iv
# Interval(0.0, 50.0, closed='right')

Чтобы создать временной интервал, можно использовать временные метки в качестве границ

year = pd.Interval(pd.Timestamp('2023-01-01 00:00:00'),
                        pd.Timestamp('2024-01-01 00:00:00'),
                        closed='left')
>>> pd.Timestamp('2023-10-10 00:00') in year
# True
>>> year.length
# Timedelta('365 days 00:00:00')

pandas.IntervalIndex(data, closed=None, dtype=None, copy=False, name=None, verify_integrity=True):

Объект pandas.IntervalIndex вместе со своим собственным типом IntervalDtype, а также скалярным типом pandas.Interval() обеспечивает первоклассную поддержку записи интервалов.

Объект pandas.IntervalIndex допускает некоторую уникальную индексацию, а также используется в качестве возвращаемого типа для категорий в функциях pandas.cut() и pandas.qcut().

Новый индекс pandas.IntervalIndex обычно создается с помощью функции pandas.interval_range(). Его также можно создать с помощью одного из методов конструктора:

  • IntervalIndex.from_arrays(left, right, closed='right', name=None, dtype=None),

    >>> import pandas as pd
    >>> pd.IntervalIndex.from_arrays([0, 1, 2], [1, 2, 3])
    # IntervalIndex([(0, 1], (1, 2], (2, 3]],
    #               dtype='interval[int64, right]')
    
  • IntervalIndex.from_breaks(breaks, closed='right', name=None, copy=False, dtype=None),

    >>> pd.IntervalIndex.from_breaks([0, 1, 2, 3])
    # IntervalIndex([(0, 1], (1, 2], (2, 3]],
    #               dtype='interval[int64, right]')
    
  • IntervalIndex.from_tuples(data, closed='right', name=None, copy=False, dtype=None).

    >>> pd.IntervalIndex.from_tuples([(0, 1), (1, 2)])
    # IntervalIndex([(0, 1], (1, 2]],
    #                dtype='interval[int64, right]')
    

Индексирование с помощью IntervalIndex

Объект pandas.IntervalIndex можно использовать в DataFrame и Series в качестве индекса.

df = pd.DataFrame(
    {"A": [1, 2, 3, 4]}, index=pd.IntervalIndex.from_breaks([0, 1, 2, 3, 4])
)

>>> df
#         A
# (0, 1]  1
# (1, 2]  2
# (2, 3]  3
# (3, 4]  4

Индексирование на основе меток через .loc по краям интервала работает так, как и ожидается.

>>> df.loc[2]
# A    2
# Name: (1, 2], dtype: int64

>>> df.loc[[2, 3]]
#         A
# (1, 2]  2
# (2, 3]  3

Если выбрать метку, которая находится внутри интервала, то это также выберет интервал.

>>> df.loc[2.5]
# A    3
# Name: (2, 3], dtype: int64

>>> df.loc[[2.5, 3.5]]
#         A
# (2, 3]  3
# (3, 4]  4

Выбор данных с использованием класса Interval() вернет только точные совпадения.

>>> df.loc[pd.Interval(1, 2)]
# A    2
# Name: (1, 2], dtype: int64

Попытка выбрать интервал, который не содержится в IntervalIndex, вызовет ошибку KeyError.

>>> df.loc[pd.Interval(0.5, 2.5)]
# Traceback (most recent call last):
# ...
# KeyError: Interval(0.5, 2.5, closed='right')

Выбор всех интервалов, перекрывающих имеющийся интервал, можно выполнить с помощью метода IntervalIndex.overlaps() для создания логического индексатора.

>>> idxr = df.index.overlaps(pd.Interval(0.5, 2.5))
>>> idxr
# array([ True,  True,  True, False])

Функции pandas.cut() и pandas.qcut() возвращают объект Categorical, а созданные ими интервалы сохраняются как IntervalIndex в его атрибуте .categories.

# сгенерируем интервалы
>>> c = pd.cut(range(4), bins=2)
>>> c
# [(-0.003, 1.5], (-0.003, 1.5], (1.5, 3.0], (1.5, 3.0]]
# Categories (2, interval[float64, right]): [(-0.003, 1.5] < (1.5, 3.0]]
>>> c.categories
# IntervalIndex([(-0.003, 1.5], (1.5, 3.0]], dtype='interval[float64, right]')

Функция pandas.cut() может принимать IntervalIndex() в качестве аргумента bins, это позволяет использовать полезную идиому pandas. Чтобы сгенерировать интервалы, сначала нужно вызвать функцию pandas.cut() с некоторыми данными и интервалами, установленными на фиксированное число. Затем, при последующих вызовах функции pandas.cut() необходимо передать значения атрибута .categories в качестве аргумента bins.

# аргументу `bins` передаем категории, сгенерированные ранее
>>> pd.cut([0, 3, 5, 1], bins=c.categories)
# [(-0.003, 1.5], (1.5, 3.0], NaN, (-0.003, 1.5]]
# Categories (2, interval[float64, right]): [(-0.003, 1.5] < (1.5, 3.0]]

Любому значению, которое выходит за пределы имеющихся интервалов, будет присвоено значение NaN.

Генерация интервалов диапазонов

Если необходимы интервалы с постоянной частотой, то для создания IntervalIndex можно использовать функцию pandas.interval_range(), используя различные комбинации аргументов start, end, и periods. Частота по умолчанию для числовых интервалов - 1, а для интервалов, подобных дате и времени, один календарный день:

>>> pd.interval_range(start=0, end=5)
# IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]')
>>> pd.interval_range(start=pd.Timestamp("2017-01-01"), periods=4)
# IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03], (2017-01-03, 2017-01-04], 
# (2017-01-04, 2017-01-05]], dtype='interval[datetime64[ns], right]')
>>> pd.interval_range(end=pd.Timedelta("3 days"), periods=3)
# IntervalIndex([(0 days 00:00:00, 1 days 00:00:00], (1 days 00:00:00, 2 days 00:00:00], 
# (2 days 00:00:00, 3 days 00:00:00]], dtype='interval[timedelta64[ns], right]')

Аргумент freq функции pandas.interval_range() может использоваться для указания частот, отличных от значений по умолчанию, и может использовать различные псевдонимы частот с интервалами, подобными дате и времени:

>>> pd.interval_range(start=0, periods=5, freq=1.5)
# IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0], 
# (6.0, 7.5]], dtype='interval[float64, right]')
>>> pd.interval_range(start=pd.Timestamp("2017-01-01"), periods=4, freq="W")
# IntervalIndex([(2017-01-01, 2017-01-08], (2017-01-08, 2017-01-15], 
# (2017-01-15, 2017-01-22], (2017-01-22, 2017-01-29]], dtype='interval[datetime64[ns], right]')
>>> pd.interval_range(start=pd.Timedelta("0 days"), periods=3, freq="9H")
# IntervalIndex([(0 days 00:00:00, 0 days 09:00:00], (0 days 09:00:00, 0 days 18:00:00], 
# (0 days 18:00:00, 1 days 03:00:00]], dtype='interval[timedelta64[ns], right]')

Кроме того, можно использовать аргумент closed (принимает значения 'left', 'right', 'both', 'neither') для указания того, с какой стороны (сторон) интервалы закрыты. По умолчанию интервалы закрыты с правой стороны.

>>> pd.interval_range(start=0, end=4, closed="both")
# IntervalIndex([[0, 1], [1, 2], [2, 3], [3, 4]], dtype='interval[int64, both]')
>>> pd.interval_range(start=0, end=4, closed="neither")
# IntervalIndex([(0, 1), (1, 2), (2, 3), (3, 4)], dtype='interval[int64, neither]')

При указании start, end и periods будет создан диапазон равномерно распределенных интервалов от начала до конца включительно с количеством элементов periods в результирующем индексе IntervalIndex:

>>> pd.interval_range(start=0, end=6, periods=4)
# IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]], dtype='interval[float64, right]')
>>> pd.interval_range(pd.Timestamp("2018-01-01"), pd.Timestamp("2018-02-28"), periods=3)
# IntervalIndex([(2018-01-01, 2018-01-20 08:00:00], (2018-01-20 08:00:00, 2018-02-08 16:00:00], 
# (2018-02-08 16:00:00, 2018-02-28]], dtype='interval[datetime64[ns], right]')