Категориальный тип данных
Преобразуйте строковые столбцы с небольшим числом уникальных значений в pandas Categorical, чтобы уменьшить расход памяти и ускорить операции группировки.
«Категориальный тип данных» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Что такое тип данных Categorical
Тип данных Categorical в Pandas предназначен для столбцов с ограниченным набором дискретных значений (низкой кардинальностью), например пола, статуса, региона или категории товара. Вместо хранения полной строки в каждой строке Pandas сохраняет уникальные значения (называемые категориями) один раз и использует целочисленный код для ссылки на них в каждой строке. Это похоже на использование таблиц поиска или перечислений в базах данных.
import pandas as pd
df = pd.DataFrame({
'region': ['North', 'South', 'East', 'North', 'East', 'South'] * 1000
})
# Without Categorical: object dtype stores every string
print('object dtype memory:', df['region'].memory_usage(deep=True))
# With Categorical: only stores 3 unique values + integer codes
df['region_cat'] = df['region'].astype('category')
print('category dtype memory:', df['region_cat'].memory_usage(deep=True))Создание Series типа Categorical
Преобразуйте столбец в Categorical, вызвав .astype('category') для любого Series. Получившийся Series типа Categorical хранит уникальные значения в .cat.categories, а целочисленные коды позиций — в .cat.codes. Можно также напрямую создать Categorical с помощью pd.Categorical(), заранее задав категории и их порядок.
import pandas as pd
s = pd.Series(['low', 'high', 'med', 'high', 'low'])
s_cat = s.astype('category')
print(s_cat.cat.categories) # Index(['high', 'low', 'med'], dtype='object')
print(s_cat.cat.codes) # integer codes per row
# 0 1
# 1 0
# 2 2
# 3 0
# 4 1Экономия памяти с Categorical
Экономия памяти при использовании типа Categorical зависит от соотношения кардинальности (уникальные значения ÷ общее число строк). В столбце из 1 000 000 строк с 5 уникальными строками тип object хранит 1 миллион указателей на строки (примерно по 50 и более байт каждый), тогда как Categorical хранит всего 5 строк и 1 миллион 8-битных целых чисел. Экономия может составлять от 5 до 20 раз: столбец размером 50 MB уменьшается до 2–5 MB.
import pandas as pd
import numpy as np
n = 1_000_000
statuses = np.random.choice(['active', 'inactive', 'pending'], size=n)
df = pd.DataFrame({'status': statuses})
object_mem = df['status'].memory_usage(deep=True) / 1e6
df['status'] = df['status'].astype('category')
cat_mem = df['status'].memory_usage(deep=True) / 1e6
print(f'Object dtype: {object_mem:.1f} MB')
print(f'Category dtype: {cat_mem:.1f} MB')
print(f'Reduction: {object_mem/cat_mem:.1f}x')Упорядоченный Categorical для ранжирования
Иногда категории имеют естественный порядок: например, 'low' < 'medium' < 'high' или размеры футболок XS < S < M < L < XL. Упорядоченный Categorical сохраняет этот порядок, позволяя выполнять осмысленные операции сравнения (<, >= и т. д.) и гарантируя, что результаты groupby будут отсортированы в правильном смысловом порядке, а не по алфавиту.
import pandas as pd
df = pd.DataFrame({
'priority': ['high', 'low', 'medium', 'high', 'low']
})
priority_type = pd.CategoricalDtype(
categories=['low', 'medium', 'high'],
ordered=True
)
df['priority'] = df['priority'].astype(priority_type)
# Comparison now works correctly
print(df['priority'] >= 'medium')
# 0 True
# 1 False
# 2 True
# 3 True
# 4 FalseСортировка упорядоченных категорий
При сортировке столбца с упорядоченным Categorical Pandas использует заданный порядок категорий, а не алфавитный порядок. Поэтому 'low' идёт перед 'medium', а 'medium' — перед 'high', независимо от того, как эти значения сортировались бы как строки. Это крайне важно для правильного порядка сводных таблиц и диаграмм.
import pandas as pd
df = pd.DataFrame({
'severity': pd.Categorical(
['high', 'low', 'medium', 'low', 'high'],
categories=['low', 'medium', 'high'],
ordered=True
),
'count': [5, 20, 8, 15, 3]
})
# sort_values respects the categorical order
print(df.sort_values('severity')[['severity', 'count']])
# severity count
# 1 low 20
# 3 low 15
# 2 medium 8
# 0 high 5
# 4 high 3Скорость GroupBy с Categorical
Pandas может оптимизировать операции groupby() для столбцов Categorical, поскольку заранее знает все возможные группы. На больших DataFrame это может ускорить groupby в 2–5 раз. Кроме того, groupby для Categorical возвращает все категории, включая пустые, поэтому сводные таблицы всегда содержат строку для каждой ожидаемой группы, даже если для некоторых групп нет наблюдений.
import pandas as pd
import numpy as np
np.random.seed(0)
df = pd.DataFrame({
'region': pd.Categorical(
np.random.choice(['North', 'South', 'East', 'West'], 10),
categories=['North', 'South', 'East', 'West']
),
'sales': np.random.randint(100, 1000, 10)
})
# observed=False shows ALL categories even empty ones
print(df.groupby('region', observed=False)['sales'].sum())Добавление и удаление категорий
Используйте средство доступа .cat для управления списком категорий. .cat.add_categories() добавляет новые допустимые значения (это полезно перед вставкой новых данных), а .cat.remove_unused_categories() удаляет метки категорий, которым не соответствуют строки, что удобно после фильтрации. Нельзя присвоить значение, отсутствующее среди категорий, не добавив его сначала.
import pandas as pd
s = pd.Categorical(['a', 'b', 'a'], categories=['a', 'b', 'c'])
s = pd.Series(s)
print(s.cat.categories) # Index(['a', 'b', 'c'], dtype='object')
print(s.value_counts()) # a:2, b:1, c:0
# Remove unused category 'c'
s = s.cat.remove_unused_categories()
print(s.cat.categories) # Index(['a', 'b'], dtype='object')Переименование меток категорий
.cat.rename_categories() позволяет переименовывать категории, не изменяя лежащие в основе коды. Это полезно, когда нужно отображать более понятные названия (например, 'M' → 'Male') или исправить непоследовательный регистр меток без обратного преобразования в object и повторного сопоставления. Метод принимает список (позиционный) или словарь (для выборочного переименования).
import pandas as pd
s = pd.Series(pd.Categorical(['M', 'F', 'M', 'F'], categories=['M', 'F']))
# Rename using a dict
s = s.cat.rename_categories({'M': 'Male', 'F': 'Female'})
print(s)
# 0 Male
# 1 Female
# 2 Male
# 3 Female
print(s.cat.categories) # Index(['Male', 'Female'], dtype='object')Когда НЕ следует использовать Categorical
Тип данных Categorical плохо подходит для высокой кардинальности: если почти каждая строка содержит уникальное значение (например, идентификаторы пользователей, произвольные комментарии или UUID), индекс категорий почти такой же большой, как исходные данные, и экономии памяти не будет. Эвристическое правило: используйте Categorical только тогда, когда число уникальных значений составляет примерно менее 50% от общего числа строк, особенно если уникальных значений несколько десятков или сотен.
import pandas as pd
import numpy as np
df = pd.DataFrame({'user_id': range(1_000_000)})
# High-cardinality: every value is unique — don't use Categorical
object_mem = df['user_id'].astype(str).memory_usage(deep=True) / 1e6
cat_mem = df['user_id'].astype(str).astype('category').memory_usage(deep=True) / 1e6
print(f'String: {object_mem:.1f} MB')
print(f'Category: {cat_mem:.1f} MB')
# Category is WORSE for high-cardinality data!Categorical в сводных таблицах и Groupby
Использование Categorical обеспечивает согласованную форму результата при работе с groupby и сводными таблицами. Без Categorical группы, в которых случайно нет наблюдений, незаметно исключаются из результата, что может привести к несовпадению результатов при сравнении разных срезов данных. При использовании Categorical и observed=False все группы всегда присутствуют в результате.
import pandas as pd
df = pd.DataFrame({
'quarter': pd.Categorical(
['Q1', 'Q1', 'Q3'],
categories=['Q1', 'Q2', 'Q3', 'Q4']
),
'revenue': [100, 200, 300]
})
# observed=False includes Q2 and Q4 even though they have no rows
result = df.groupby('quarter', observed=False)['revenue'].sum()
print(result)
# quarter
# Q1 300
# Q2 0
# Q3 300
# Q4 0Categorical и машинное обучение
Многие модели scikit-learn ожидают числовые входные данные. Чтобы преобразовать столбец Categorical в числа для модели, используйте .cat.codes (кодирование метками) или pd.get_dummies() (унитарное кодирование). Унитарное кодирование не создаёт ложного предположения о порядковой связи между категориями. Для упорядоченных категорий, например уровней приоритета, подходит кодирование метками (непосредственное использование кодов), поскольку оно сохраняет информацию о порядке.
import pandas as pd
df = pd.DataFrame({
'colour': pd.Categorical(['red', 'blue', 'green', 'red'])
})
# One-hot encode for unordered categories
one_hot = pd.get_dummies(df['colour'], prefix='colour')
print(one_hot)
# colour_blue colour_green colour_red
# 0 0 0 1
# 1 1 0 0
# 2 0 1 0
# 3 0 0 1Быстрая проверка
Проверьте, насколько хорошо Вы понимаете тип данных Categorical.
Итоги урока
В этом уроке Вы узнали: категориальный тип данных хранит уникальные значения только один раз и использует целочисленные коды для каждой строки, что позволяет значительно экономить память в столбцах с небольшим числом уникальных значений; упорядоченный категориальный тип поддерживает осмысленные сравнения и правильную сортировку, а groupby с observed=False включает все категории, даже пустые. Не используйте категориальный тип для столбцов с большим числом уникальных значений. Далее Вы научитесь правильно разбирать строки с датами с помощью pd.to_datetime().
Часто задаваемые вопросы
Урок «Категориальный тип данных» бесплатный?
Да — полный текст урока «Категориальный тип данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Чему я научусь в уроке «Категориальный тип данных»?
Преобразуйте строковые столбцы с небольшим числом уникальных значений в pandas Categorical, чтобы уменьшить расход памяти и ускорить операции группировки. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?
Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Категориальный тип данных»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?
Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Проверка типов данных столбцов
- Приведение типов с помощью astype()
- Категориальный тип данных
- Корректный разбор дат