Как использовать NumPy или Pandas для быстрой сортировки категориальных функций

Работа с категориальными данными в целях машинного обучения (ML) иногда может создавать сложные проблемы. В конечном итоге эти функции должны быть каким-то образом численно закодированы, чтобы алгоритм машинного обучения мог действительно работать с ними.

Вы также захотите рассмотреть дополнительные методы подготовки категориальных функций к моделированию. Например, производительность вашей модели может быть улучшена за счет группирования категориальных функций. По сути, это означает объединение нескольких категорий в одну категорию. Применяя знания предметной области, вы сможете создавать новые категории и функции, которые лучше представляют структуру ваших данных.

В этом посте мы кратко расскажем, почему группирование категориальных функций может быть полезным. Затем мы рассмотрим три различных метода объединения категориальных функций с конкретными примерами с использованием NumPy и Pandas.

Почему категории мусорных баков?

При использовании категориальных функций вы можете столкнуться с проблемами редких меток, категорий / групп, которые крайне редко встречаются в вашем наборе данных. Эта проблема часто связана с функциями, имеющими высокую мощность, другими словами, с множеством разных категорий.

Слишком много категорий, особенно редких, приводит к зашумлению набора данных. Алгоритму ML может быть сложно преодолеть этот шум и учиться на более значимых сигналах в данных.

Высокая кардинальность также может усугубить проклятие размерности, если вы выберете одно горячее кодирование своих категориальных характеристик. Если исходная переменная имеет 50 различных категорий, вы просто добавляете 49 столбцов в свой набор данных.

Наличие слишком большого количества категорий также может привести к проблемам при обучении и тестировании вашей модели. Вполне возможно, что категория появится в тестовом наборе, но не в обучающем наборе. Ваша модель не знает, как обращаться с этой категорией, потому что она никогда ее раньше не «видела».

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

В следующих примерах мы будем изучать и разрабатывать функции из набора данных с информацией о демографических характеристиках и участии избирателей. Я выбрал 3 категориальных переменных для работы:

  1. party_cd: принадлежность зарегистрированного избирателя к политической партии.
  2. voting_method: как зарегистрированный избиратель проголосовал на выборах
  3. birth_state: штат или территория США, где родился зарегистрированный избиратель.

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

import pandas as pd
import numpy as np
view raw pd_np.py hosted with ❤ by GitHub

Использование np.where () для категорий корзины

Во-первых, давайте посмотрим, почему я выбрал party_cd. На изображении ниже показано, сколько отдельных избирателей принадлежит каждой политической партии.

Зарегистрированных либертарианцев, конституционалистов и членов Партии зеленых так мало, что мы едва можем увидеть их на графике. Это были бы хорошие примеры редких лейблов. В этом посте мы определим редкие ярлыки как те, которые составляют менее 5% наблюдений. Это общий порог для определения редких ярлыков, но в конечном итоге это зависит от вас.

Давайте посмотрим на фактические цифры в разбивке:

Каждая из этих трех категорий составляет менее 5% населения. Даже если мы объединим их всех в одну категорию, эта новая категория все равно будет представлять менее 1% избирателей.

«REP» и «DEM» представляют две основные политические партии, тогда как «UNA» представляет избирателей, которые зарегистрировались как не аффилированные с политической партией. Итак, здесь имеет смысл объединить наши три редких ярлыка в эту неаффилированную группу, чтобы у нас было три категории: по одной для каждой из двух основных партий, а третья представляет лиц, которые предпочли не присоединяться ни к одной из основных сторон.

Это очень легко сделать с помощью np.where(), который принимает 3 аргумента:

  1. условие
  2. что вернуть, если условие выполнено
  3. что вернуть, если условие не выполнено

Следующий код создает новую функцию party_grp из исходной переменной party_cd с использованием np.where():

df['party_grp'] = np.where(df['party_cd'].isin(['REP', 'DEM']),
df['party_cd'].str.title(),
'Other')

Он проверяет, находится ли исходное значение в списке ['REP', 'DEM']. Если это так, то np.where() просто возвращает исходный код партии (хотя я возвращал его как регистр заголовка, потому что лично я ненавижу смотреть на вещи, написанные заглавными буквами). Если исходного кода партии нет в этом списке, np.where() возвращает «Другое». Наша недавно разработанная функция party_grp теперь намного более сбалансирована без каких-либо редких меток:

Сопоставление категорий с новыми группами с помощью map ()

Теперь давайте посмотрим на распределение voting_method:

Не самый красивый из графиков, но картина понятная. У нас есть 8 различных категорий методов голосования. Рискну предположить, что половина из них соответствует нашему определению редких этикеток.

Ага! Четыре из наших категорий - редкие лейблы. Теперь мы могли бы просто сгруппировать их всех в категорию «Другое» и прекратить работу, но это, возможно, не самый подходящий метод.

На основании проведенного мной исследования того, как кодируются эти методы, я знаю, что «заочное» означает, что кто-то проголосовал досрочно. Таким образом, мы можем сгруппировать любой метод «Заочное» в категорию «Раннее», «Личное участие» и «Обочина» в категорию «День выборов», оставить «Без голосования» в качестве отдельной категории и сгруппировать «Предварительно» и «Перенести» в категорию «Другое».

В следующем коде это достигается путем определения словаря с использованием исходных voting_method категорий в качестве ключей. Значение для каждого ключа - это новая категория, которая нам действительно нужна.

vote_method_map = {'ABSENTEE ONESTOP': 'Early',
'IN-PERSON': 'Election Day',
'ABSENTEE BY MAIL': 'Early',
'ABSENTEE CURBSIDE': 'Early',
'TRANSFER': 'Other',
'PROVISIONAL': 'Other',
'CURBSIDE': 'Election Day',
'No Vote': 'No Vote'}
df['vote_method_cat'] = df['voting_method'].map(vote_method_map)

Эта последняя строка создает новый столбец vote_method_cat на основе исходных значений в столбце voting_method. Он делает это, применяя метод map() Pandas к исходному столбцу и вводя наш vote_method_map для преобразования ключа в соответствующее значение.

Теперь мы избавились от всех наших редких лейблов, кроме одного. В конце концов, я решил отбросить эти 733 «других» голоса. Метод голосования на самом деле был целевой переменной, которую я пытался предсказать, и что меня действительно интересовало, так это то, как люди выбирают голосование. Предварительные бюллетени и бюллетени для перевода больше отражают процесс и правила, связанные с голосованием, но мой вопрос был конкретно об активном выборе избирателя.

Таким образом, вы можете не только подумать о технических функциях прогнозирования, чтобы лучше представить основную структуру ваших данных, вы можете подумать, как лучше представить вашу целевую переменную относительно вашего конкретного вопроса.

Применение пользовательской функции с помощью apply ()

Наконец, мы собираемся работать над сортировкой birth_state. Эта переменная имеет 57 категорий: по одной для каждого штата, по одной для отсутствующей информации, по одной для каждой территории США и последняя категория для лиц, родившихся за пределами США.

Так что график выглядит до смешного ужасно:

Если вы когда-либо видели такой график при изучении категориальных функций, это хороший признак, что вам следует рассмотреть возможность объединения этой переменной, если вы собираетесь использовать ее в качестве функции в своей модели.

Ниже приводится разбивка по 15 наиболее распространенным категориям birth_state:

Северная Каролина - самый распространенный штат, что имеет смысл, поскольку эти данные предназначены для избирателей в конкретном округе Северной Каролины. Затем мы видим множество пропущенных значений. Жители Нью-Йорка и люди, рожденные за пределами США, также составляют приличную часть населения. Остальные 53 категории являются редкими ярлыками, основанными на нашем определении, и внесут много шума в наши усилия по моделированию.

Сгруппируем состояния по США. Регион переписи (Северо-Восток, Юг, Средний Запад, Запад). Мы также сгруппируем людей, родившихся на территории США или за пределами страны, в группу Другое и оставим Пропавшие без вести в качестве отдельной категории.

Мы сделаем это, определив нашу собственную пользовательскую функцию для перевода из состояния в регион, а затем применим эту функцию к нашей исходной переменной, чтобы получить нашу новую функцию. Вот один из способов написать функцию для проверки каждого состояния и возврата желаемого региона / категории:

## Define function for grouping birth state/country into categories
def get_birth_reg(state):
# check if U.S. territory or out of country
if state in ['AS', 'GU', 'MP', 'PR', 'VI', 'OC']:
return 'Other'
# the rest of the categories are based on U.S. Census Bureau regions
elif state in ['CT', 'ME', 'MA', 'NH', 'RI', 'VT',
'NJ', 'NY', 'PA']:
return 'Northeast'
elif state in ['DE', 'FL', 'GA', 'MD', 'NC', 'SC', 'VA',
'DC', 'WV', 'AL', 'KY', 'MS', 'TN', 'AR',
'LA', 'OK', 'TX']:
return 'South'
elif state in ['IL', 'IN', 'MI', 'OH', 'WI',
'IA', 'KS', 'MN', 'MO', 'NE', 'ND', 'SD']:
return 'Midwest'
elif state in ['AZ', 'CO', 'ID', 'MT', 'NV', 'NM', 'UT',
'WY', 'AK', 'CA', 'HI', 'OR', 'WA']:
return 'West'
else:
return 'Missing'

А теперь воспользуемся методом apply() Pandas для создания нашей новой функции:

df['birth_reg'] = df['birth_state'].apply(get_birth_reg)

Намного лучше! Мы перешли от 57 категорий с 53 редкими ярлыками до 6 категорий, которые по-прежнему имеют большое значение, и только одна из них соответствует нашему определению редких ярлыков. Мы могли бы рассмотреть дополнительную группировку, но вы поняли.

Резюме

Мы рассмотрели:

  • Что означает сортировка категориальных функций
  • Почему и когда вы можете захотеть объединить категориальные функции
  • 3 метода группирования категориальных функций (np.where(), Pandas map(), пользовательская функция с Pandas apply())

Я надеюсь, что вы нашли это информативным и сможете применить полученные знания в своей работе. Спасибо за прочтение!

Подробнее о разработке функций:

Что такое Feature Engineering?

Примеры разработки функций: объединение числовых функций