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

Объекты Categorical и CategoricalIndex модуля pandas

Представление данных с помощью категорий (индексация дубликатов) в pandas

Объект pandas.CategoricalIndex() представляет собой тип индекса, который полезен для поддержки индексации дубликатов. Это контейнер вокруг элементов pandas.Categorical(), который позволяет эффективно индексировать и хранить индекс с большим количеством повторяющихся элементов.

Содержание:


pandas.Categorical(values, categories=None, ordered=None):

Объект pandas.Categorical() представляет собой категориальную переменную.

Categorical могут принимать только ограниченное и обычно фиксированное количество возможных значений (категорий). Категориальные переменные могут иметь порядок, но числовые операции (сложение, деление и т. д.) невозможны.

Все значения values, соответствуют либо категориям categories, либо имеют значение numpy.nan. Присвоение значений вне категорий приведет к возникновению ошибки ValueError. Порядок определяется порядком появления категорий, а не лексическим порядком значений.

Принимаемые значения:

  • values - список/массив значений категорий. Если указан аргумент categories, то значения values, не входящие в последовательность categories, будут заменены на NaN.
  • categories=None - список/массив самих категорий, должны быть уникальными. Если не указан, то предполагается, что категории представляют собой уникальные значения values (по возможности отсортированные, в противном случае в порядке их появления).
  • ordered=None - если True, то результирующая Categorical будет упорядоченный. Упорядоченная категория при сортировке учитывает порядок своего атрибута .categories (который, в свою очередь, является аргументом categories, если он указан).
  • dtype=None - экземпляр CategoricalDtype, то его нельзя использовать вместе с аргументами categories и ordered.

Категориальные значения могут принимать только ограниченное и обычно фиксированное количество возможных categories. Объект Categorical может иметь порядок, но числовые операции (сложения, деления, ...) невозможны.

Все значения values будут соответствовать категориям categories, либо будут иметь numpy.nan. Присвоение значений вне категорий вызовет ValueError . Порядок определяется порядком категорий, а не лексическим порядком значений.

Примеры создания категорий pandas.Categorical():

>>> pd.Categorical([1, 2, 3, 1, 2, 3])
# [1, 2, 3, 1, 2, 3]
# Categories (3, int64): [1, 2, 3]

>>> pd.Categorical(['a', 'b', 'c', 'a', 'b', 'c'])
# ['a', 'b', 'c', 'a', 'b', 'c']
# Categories (3, object): ['a', 'b', 'c']

Отсутствующие значения не включаются в категорию.

>>> c = pd.Categorical([1, 2, 3, 1, 2, 3, np.nan])
>>> c
# [1, 2, 3, 1, 2, 3, NaN]
# Categories (3, int64): [1, 2, 3]

# их наличие указано в атрибуте `codes` кодом -1.
>>> c.codes
# array([ 0,  1,  2,  0,  1,  2, -1], dtype=int8)

Упорядоченные объекты Categorical могут быть отсортированы в соответствии с пользовательским порядком и могут иметь минимальное и максимальное значение.

>>> c = pd.Categorical(['a', 'b', 'c', 'a', 'b', 'c'], ordered=True, categories=['c', 'b', 'a'])
>>> c
# ['a', 'b', 'c', 'a', 'b', 'c']
# Categories (3, object): ['c' < 'b' < 'a']
>>> c.min()
# 'c'

pandas.CategoricalIndex(data=None, categories=None, ordered=None, dtype=None, name=None):

Объект pandas.CategoricalIndex() представляет собой тип индекса, который полезен для поддержки индексации дубликатов. Это контейнер вокруг элементов pandas.Categorical(), который позволяет эффективно индексировать и хранить индекс с большим количеством повторяющихся элементов.

Принимаемые значения:

  • data=None - список/массив значений категорий. Если аргумент categories указан, то значения data, не входящие в categories, будут заменены на NaN.
  • categories=None - список/массив самих категорий, должны быть уникальными. Если не указан (а также не указан dtype), то они будут выведены как уникальные значения data.
  • ordered=None - если True, то результирующая Categorical будет упорядоченный. Упорядоченная категория при сортировке учитывает порядок своего атрибута .categories (который, в свою очередь, является аргументом categories, если он указан).
  • dtype=None - экземпляр CategoricalDtype, то его нельзя использовать вместе с аргументами categories и ordered.
  • name=None - строка с именем, которое будет храниться в индексе.

Объект pandas.CategoricalIndex, как и pandas.Categorical(), может принимать только ограниченное и обычно фиксированное количество возможных значений (категорий). Также, как и Categorical, он может иметь порядок, но числовые операции (сложения, деления, ...) невозможны

>>> import pandas as pd
>>> pd.CategoricalIndex(["a", "b", "c", "a", "b", "c"])
# CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], 
# ordered=False, dtype='category')

# `CategoricalIndex` может быть создан из `Categorical`
>>> c = pd.Categorical(["a", "b", "c", "a", "b", "c"])
>>> pd.CategoricalIndex(c)
# CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['a', 'b', 'c'], 
# ordered=False, dtype='category')

Порядок определяется порядком категорий, а не лексическим порядком значений. Упорядоченный CategoricalIndex может иметь минимальное и максимальное значение.

ci = pd.CategoricalIndex(
    ["a", "b", "c", "a", "b", "c"], ordered=True, categories=["c", "b", "a"]
)

>>> ci
# CategoricalIndex(['a', 'b', 'c', 'a', 'b', 'c'], categories=['c', 'b', 'a'], 
# ordered=True, dtype='category')
>>> ci.min()
# 'c'

Возможные исключения:

  • ValueError - Если категории не подтверждаются.
  • TypeError - Если задано явное значение ordered=True, но нет категорий и значения не сортируются.

Обзор объекта pandas.CategoricalIndex

>>> import numpy as np
>>> from pandas.api.types import CategoricalDtype
>>> df = pd.DataFrame({"A": np.arange(6), "B": list("aabbca")})
# установим тип столбца `CategoricalDtype`
>>> df["B"] = df["B"].astype(CategoricalDtype(list("cab")))
# или проще так 
>>> df["B"] = df["B"].astype('category')
>>> df
#    A  B
# 0  0  a
# 1  1  a
# 2  2  b
# 3  3  b
# 4  4  c
# 5  5  a

Установка индекса столбца с типом Categorical создаст CategoricalIndex.

>>> df2 = df.set_index("B")
>>> df2.index
# CategoricalIndex(['a', 'a', 'b', 'b', 'c', 'a'], categories=['c', 'a', 'b'], 
# ordered=False, dtype='category', name='B')

Индексирование с помощью .iloc/.loc работает аналогично Index с дубликатами. Индексаторы должны находиться в категории, иначе операция вызовет ошибку KeyError.

df2.loc["a"]
#    A
# B   
# a  0
# a  1
# a  5

CategoricalIndex сохраняется после индексации:

>>> df2.loc["a"].index
# CategoricalIndex(['a', 'a', 'a'], categories=['c', 'a', 'b'], 
# ordered=False, dtype='category', name='B')

При сортировке индекс будет сортироваться по порядку следования категорий (напомним, что индекс создавался с помощью CategoricalDtype(list('cab')), поэтому порядок сортировки - cab).

>>> df2.sort_index()
#    A
# B   
# c  4
# a  0
# a  1
# a  5
# b  2
# b  3

Операции группировки с CategoricalIndex также сохранят природу индекса.

>>> df2.groupby(level=0, observed=True).sum()
#    A
# B   
# c  4
# a  6
# b  5

>>> df2.groupby(level=0, observed=True).sum().index
# CategoricalIndex(['c', 'a', 'b'], categories=['c', 'a', 'b'], 
# ordered=False, dtype='category', name='B')

Операции переиндексации вернут результирующий индекс на основе типа переданного индексатора. Передача списка вернет старый добрый Index. Индексация с помощью Categorical вернет CategoricalIndex, индексированный в соответствии с категориями переданного dtype: Categorical. Это позволяет индексировать их произвольно даже со значениями, не входящими в категории, аналогично тому, как можно переиндексировать любой индекс pandas.

df3 = pd.DataFrame(
    {"A": np.arange(3), "B": pd.Series(list("abc")).astype("category")}
)

>>> df3 = df3.set_index("B")
>>> df3
#    A
# B   
# a  0
# b  1
# c  2

# переиндексируем `df3` списком
>>> df3.reindex(["a", "e"])
#      A
# B     
# a  0.0
# e  NaN

# смотрим тип индекса
>>> df3.reindex(["a", "e"]).index
# Index(['a', 'e'], dtype='object', name='B')

# переиндексируем `df3` типом `Categorical`
>>> df3.reindex(pd.Categorical(["a", "e"], categories=list("abe")))
#      A
# B     
# a  0.0
# e  NaN

# смотрим тип индекса
>>> df3.reindex(pd.Categorical(["a", "e"], categories=list("abe"))).index
# CategoricalIndex(['a', 'e'], categories=['a', 'b', 'e'], 
# ordered=False, dtype='category', name='B')

Предупреждение. Операции по изменения формы и сравнения с CategoricalIndex должны иметь одинаковые категории, иначе будет выдано TypeError.

>>> df4 = pd.DataFrame({"A": np.arange(2), "B": list("ba")})
>>> df4["B"] = df4["B"].astype(CategoricalDtype(list("ab")))
>>> df4 = df4.set_index("B")
>>> df4.index
# CategoricalIndex(['b', 'a'], categories=['a', 'b'], ordered=False, dtype='category', name='B')
>>> df5 = pd.DataFrame({"A": np.arange(2), "B": list("bc")})
>>> df5["B"] = df5["B"].astype(CategoricalDtype(list("bc")))
>>> df5 = df5.set_index("B")
>>> df5.index
# CategoricalIndex(['b', 'c'], categories=['b', 'c'], ordered=False, dtype='category', name='B')

>>> pd.concat([df4, df5])
#    A
# B   
# b  0
# a  1
# b  0
# c  1