Как использовать NumPy или Pandas для быстрой сортировки категориальных функций
Работа с категориальными данными в целях машинного обучения (ML) иногда может создавать сложные проблемы. В конечном итоге эти функции должны быть каким-то образом численно закодированы, чтобы алгоритм машинного обучения мог действительно работать с ними.
Вы также захотите рассмотреть дополнительные методы подготовки категориальных функций к моделированию. Например, производительность вашей модели может быть улучшена за счет группирования категориальных функций. По сути, это означает объединение нескольких категорий в одну категорию. Применяя знания предметной области, вы сможете создавать новые категории и функции, которые лучше представляют структуру ваших данных.
В этом посте мы кратко расскажем, почему группирование категориальных функций может быть полезным. Затем мы рассмотрим три различных метода объединения категориальных функций с конкретными примерами с использованием NumPy и Pandas.
Почему категории мусорных баков?
При использовании категориальных функций вы можете столкнуться с проблемами редких меток, категорий / групп, которые крайне редко встречаются в вашем наборе данных. Эта проблема часто связана с функциями, имеющими высокую мощность, другими словами, с множеством разных категорий.
Слишком много категорий, особенно редких, приводит к зашумлению набора данных. Алгоритму ML может быть сложно преодолеть этот шум и учиться на более значимых сигналах в данных.
Высокая кардинальность также может усугубить проклятие размерности, если вы выберете одно горячее кодирование своих категориальных характеристик. Если исходная переменная имеет 50 различных категорий, вы просто добавляете 49 столбцов в свой набор данных.
Наличие слишком большого количества категорий также может привести к проблемам при обучении и тестировании вашей модели. Вполне возможно, что категория появится в тестовом наборе, но не в обучающем наборе. Ваша модель не знает, как обращаться с этой категорией, потому что она никогда ее раньше не «видела».
Один из способов решения этих проблем - разработка новых функций, имеющих меньшее количество категорий. Это может быть выполнено путем объединения (группировки) нескольких категорий в одну категорию.
В следующих примерах мы будем изучать и разрабатывать функции из набора данных с информацией о демографических характеристиках и участии избирателей. Я выбрал 3 категориальных переменных для работы:
party_cd
: принадлежность зарегистрированного избирателя к политической партии.voting_method
: как зарегистрированный избиратель проголосовал на выборахbirth_state
: штат или территория США, где родился зарегистрированный избиратель.
Если вы хотите начать применять эти методы в своих проектах, вам просто нужно убедиться, что у вас установлены и NumPy, и Pandas, а затем импортировать оба.
import pandas as pd | |
import numpy as np |
Использование np.where () для категорий корзины
Во-первых, давайте посмотрим, почему я выбрал party_cd
. На изображении ниже показано, сколько отдельных избирателей принадлежит каждой политической партии.
Зарегистрированных либертарианцев, конституционалистов и членов Партии зеленых так мало, что мы едва можем увидеть их на графике. Это были бы хорошие примеры редких лейблов. В этом посте мы определим редкие ярлыки как те, которые составляют менее 5% наблюдений. Это общий порог для определения редких ярлыков, но в конечном итоге это зависит от вас.
Давайте посмотрим на фактические цифры в разбивке:
Каждая из этих трех категорий составляет менее 5% населения. Даже если мы объединим их всех в одну категорию, эта новая категория все равно будет представлять менее 1% избирателей.
«REP» и «DEM» представляют две основные политические партии, тогда как «UNA» представляет избирателей, которые зарегистрировались как не аффилированные с политической партией. Итак, здесь имеет смысл объединить наши три редких ярлыка в эту неаффилированную группу, чтобы у нас было три категории: по одной для каждой из двух основных партий, а третья представляет лиц, которые предпочли не присоединяться ни к одной из основных сторон.
Это очень легко сделать с помощью np.where()
, который принимает 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()
, Pandasmap()
, пользовательская функция с Pandasapply()
)
Я надеюсь, что вы нашли это информативным и сможете применить полученные знания в своей работе. Спасибо за прочтение!
Подробнее о разработке функций: