Шаг 117.
Введение в машинное обучение с использованием Python. Типы данных ... . Проверка категориальных данных, закодированных в виде строк

    На этом шаге мы рассмотрим реализацию такой проверки.

    Прочитав набор данных, аналогичный приведенному выше, как правило, неплохо было бы сперва проверить, содержит ли столбец на самом деле осмысленные категориальные данные. При работе с данными, которые были введены людьми (например, пользователями на сайте), получить фиксированный набор категорий невозможно, наличие различных вариантов написания слов может потребовать предварительной обработки. Например, некоторые могут определить свой пол как "мужской", а некоторые просто напишут "мужчина" и нам, возможно, потребуется поместить эти два варианта в одну и ту же категорию. Хороший способ проверить содержимое столбца - воспользоваться функцией value_counts для пандасовского типа данных Series (каждый столбец DataFrame является структурой Series), чтобы посмотреть, что представляют из себя уникальные значения и как часто они встречаются:

[In 3]:
print(data.gender.value_counts())

Male      21790
Female    10771
Name: gender, dtype: int64

    Видно, что в этом наборе данных пол имеет строго два значения, Male и Female, то есть данные уже находятся в подходящем формате, чтобы записать их, используя прямое кодирование. В реальном примере вы должны просмотреть все столбцы и проверить их значения. Мы пропустим этот момент для краткости.

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

[In 4]:
print("Исходные признаки:\n", list(data.columns), "\n")
data_dummies = pd.get_dummies(data)
print("Признаки после get_dummies:\n", list(data_dummies.columns))

Исходные признаки:
 ['age', 'workclass', 'education', 'gender', 'hours-per-week', 'occupation', 'income'] 

Признаки после get_dummies:
 ['age', 'hours-per-week', 'workclass_ ?', 'workclass_ Federal-gov', 'workclass_ Local-gov', 
  'workclass_ Never-worked', 'workclass_ Private', 'workclass_ Self-emp-inc', 
  'workclass_ Self-emp-not-inc', 'workclass_ State-gov', 'workclass_ Without-pay', 
  'education_ 10th', 'education_ 11th', 'education_ 12th', 'education_ 1st-4th', 
  'education_ 5th-6th', 'education_ 7th-8th', 'education_ 9th', 'education_ Assoc-acdm', 
  'education_ Assoc-voc', 'education_ Bachelors', 'education_ Doctorate', 'education_ HS-grad', 
  'education_ Masters', 'education_ Preschool', 'education_ Prof-school', 
  'education_ Some-college',   'gender_ Female', 'gender_ Male', 'occupation_ ?', 
  'occupation_ Adm-clerical', 'occupation_ Armed-Forces', 'occupation_ Craft-repair', 
  'occupation_ Exec-managerial',   'occupation_ Farming-fishing', 
  'occupation_ Handlers-cleaners',  'occupation_ Machine-op-inspct', 
  'occupation_ Other-service', 'occupation_ Priv-house-serv', 
  'occupation_ Prof-specialty', 'occupation_ Protective-serv', 'occupation_ Sales', 
  'occupation_ Tech-support', 'occupation_ Transport-moving', 'income_ <=50K', 'income_ >50K']

    Видно, что непрерывные признаки age и hours-per-week остались неизменными, тогда как для каждого возможного значения категориального признака были созданы новые характеристики.

[In 5]:
data_dummies.head()


Рис.1. Результирующая таблица (изображение кликабельно)

    Теперь мы можем воспользоваться атрибутом values, что преобразовать пандасовский дата-фрейм data_dummies в массив NumPy, а затем обучить на его основе модель машинного обучения. Перед построением модели убедитесь в том, что зависимая переменная (которая теперь кодируется в двух столбцах income) отделена от данных. Включение зависимой переменной или некоторых признаков, являющимися производными от зависимой переменной, в пространство входных признаков является очень распространенной ошибкой при построении моделей машинного обучения с учителем.


Будьте осторожны: индексация столбцов в pandas включает конец диапазона, поэтому 'age' : 'occupation_ Transport-moving' включает в себя occupation_ Transport-moving. Данная операция отличается от нарезки массива NumPy, в котором конец диапазона не включается: например, np.arange (11) [0:10] не включает в себя элемент с индексом 10.

    В данном случае мы извлечем только те столбцы, которые содержат входные признаки, то есть, все столбцы от age до occupation_ Transport. Этот диапазон содержит все входные признаки, при этом зависимая переменная в него не включена:

[In 6]:
# Берем только те столбцы, которые содержат признаки,
# то есть все столбцы, начиная с 'age' и заканчивая 'occupation_ Transport-moving'
# Этот диапазон содержит все признаки, кроме целевой переменной
features = data_dummies.loc[:, ('age':'occupation_ Transport-moving']
# Извлекаем массивы NumPy
X = features.values
y = data_dummies[('income_ >50K'].values
print(("форма массива X: {} форма массива y: {}".format(X.shape, y.shape))

форма массива X: (32561, 44) форма массива y: (32561,)

    Теперь данные представлены в том формате, который scikit-learn умеет обрабатывать, и мы можем продолжить построение модели в обычном режиме:

[In 7]:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
print("Правильность на тестовом наборе: {:.2f}".format(logreg.score(X_test, y_test)))

Правильность на тестовом наборе: 0.81


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

    Представьте, что у нас обучающий и тестовые наборы записаны в двух разных пандасовских дата-фреймах. Если в тестовом наборе у признака workclass будет отсутствовать значение "Private Employee", pandas предположит, что существуют только три возможных значения этого признака и создаст лишь три новых дамми-переменных. Теперь у признака workclass разное количество дамми-переменных в обучающем и тестовом наборах и мы уже больше не можем применить к тестовому набору модель, построенную на обучающей выборке. Возьмем ситацию еще хуже, представьте себе, что признак workclass принимает значения "Government Employee" и "Private Employee" в обучающем наборе и "Self Employed" и "Self Employed Incorporated" в тестовом наборе. В обоих случаях pandas создаст две новые дамми-переменные, таким образом перекодированные дата-фреймы будут иметь одинаковое количество дамми-переменных. Однако эти две дамми-переменные имеют совершенно различный смысл в обучающем и тестовом наборах. Столбец, соответствующий значению "Government Employee" в обучающем наборе, будет закодирован как "Self Employed" в тестовом наборе.

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


    На следующем шаге мы рассмотрим как можно закодировать числа в виде категорий.




Предыдущий шаг Содержание Следующий шаг