Técnicas para codificar las variables categóricas (I): codificación ordinal y one-hot

Ene 16, 2021 | Preprocesamiento de datos | 0 Comentarios

¿Por qué es necesario codificar las variables categóricas?

Muchas de las variables que nos encontramos en los conjuntos de datos son categóricas, es decir, variables no numéricas que adquieren valores de un número limitado de clases o categorías. Asimismo, las variables categóricas pueden ser de dos tipos:

  • Variables ordinales: sus valores pueden ser ordenados jerárquicamente, como, por ejemplo, el nivel de educación de una persona (sin estudios, primaria, secundaria o superiores).
  • Variables nominales: no se puede establecer un orden en sus categorías. Algunos ejemplos son el sexo de una persona (hombre o mujer), el color del pelo (castaño, rubio, negro o rojo) o el país de nacimiento (España, Italia, Francia, etc.).

El problema es que la mayoría de los modelos de machine learning en Python, entre ellos las redes neuronales, solo pueden leer valores numéricos puesto que que hacen uso de diversas operaciones matemáticas. Por esto mismo, en estos casos es importante convertir este tipo de variables en numéricas para que los modelos puedan utilizarlas.

Afortunadamente, tenemos a nuestra disposición diversas técnicas fáciles de implementar en Python para codificar este tipo de variables y cada una de estas tiene sus ventajas y desventajas.

En esta publicación y en posteriores descubriremos diferentes aproximaciones para codificar las variables categóricas. En esta ocasión nos centraremos en los métodos más populares: la codificación ordinal y la codificación one-hot.

 

El conjunto de datos

En los casos prácticos utilizaremos el conjunto de datos público denominado Adult, que pertenece al repositorio UCI Machine Learning. Este conjunto de datos contiene 48.842 instancias que representan datos personales (edad, género, raza, etc.) de ciudadanos anónimos extraídos por Barry Becker de la base de datos del censo de 1994 de los Estados Unidos.

Este dataset es usado para predecir si una persona gana más de 50 mil dólares al año a partir de variables como la edad, género, raza o país de nacimiento de una persona.

Así, para cargar el dataset simplemente empleamos la función read_csv de la librería pandas, a la que le pasamos la ruta donde se alojan los datos y nos devuelve un objeto DataFrame. Una vez tenemos nuestro DataFrame, le asignamos el nombre de cada variable.

In [1]:
import pandas as pd

# Cargamos el dataset
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning'
                 '-databases/adult/adult.data', encoding = 'utf-8',
                 header = None)

# Añadimos el nombre de cada variable
df.columns = ["age", "workclass", "fnlwgt", "education", 
              "education-num", "marital-status", "occupation", 
              "relationship", "race", "sex", "capital-gain", 
              "capital-loss", "hours-per-week", "native-country", 
              "class"]

De esta forma obtendremos un objeto DataFrame con todos los datos. A continuación, echamos un primer vistazo a estos datos y mostramos las primeras cinco observaciones.

In [2]:
df.head()
Out[2]:
ageworkclassfnlwgteducationeducation-nummarital-statusoccupationrelationshipracesexcapital-gaincapital-losshours-per-weeknative-countryclass
039State-gov77516Bachelors13Never-marriedAdm-clericalNot-in-familyWhiteMale2174040United-States<=50K
150Self-emp-not-inc83311Bachelors13Married-civ-spouseExec-managerialHusbandWhiteMale0013United-States<=50K
238Private215646HS-grad9DivorcedHandlers-cleanersNot-in-familyWhiteMale0040United-States<=50K
353Private23472111th7Married-civ-spouseHandlers-cleanersHusbandBlackMale0040United-States<=50K
428Private338409Bachelors13Married-civ-spouseProf-specialtyWifeBlackFemale0040Cuba<=50K

Entre las variables independientes encontramos una mezcla de variables numéricas como age o capital-gain y categóricas, tanto de tipo ordinal (education) como nominal (race o workclass).

 

Codificación ordinal

Comenzamos con esta popular técnica de codificación que consiste simplemente en remplazar cada valor de la variable con un número entero distinto. Es por ello que la codificación ordinal resulta útil cuando tenemos datos ordinales, es decir, aquellos en los que se puede establecer un orden entre sus categóricas. En consecuencia, esta técnica nos ayudará a representar esta relación de orden de las distintas categorías.

En nuestro conjunto de datos nos centraremos en el atributo ordinal education, en el cual claramente podemos asignar un orden a los distintos niveles de estudios. Además, conocemos ese orden jerárquico que va desde el nivel de estudios más bajo (Preschool) al que el algoritmo le asignará un 0, hasta el nivel máximo de educación (Doctorate) que tendrá asignado un 15.

Antes que nada, mostramos todos los valores incluidos en la variable education.

In [5]:
df.education.value_counts()
Out[5]:
 HS-grad         10501
 Some-college     7291
 Bachelors        5355
 Masters          1723
 Assoc-voc        1382
 11th             1175
 Assoc-acdm       1067
 10th              933
 7th-8th           646
 Prof-school       576
 9th               514
 12th              433
 Doctorate         413
 5th-6th           333
 1st-4th           168
 Preschool          51
Name: education, dtype: int64
  •  

Como podemos ver en la tabla superior, hay 16 niveles de educación a los que debemos asignar un número entero. Para ello, la librería scikit-learn nos proporciona el objeto OrdinalEncoder para aplicar este tipo de codificación y al cual le podemos indicar el orden de las categorías. Veamos cómo podemos utilizarlo en nuestro dataset.

In [5]:
from sklearn.preprocessing import OrdinalEncoder

# Creamos el codificador indicandole el orden de la variables
encoder = OrdinalEncoder(categories=[[" Preschool", " 1st-4th", 
                                      " 5th-6th", " 7th-8th", " 9th", 
                                      " 10th", " 11th", " 12th", 
                                      " HS-grad", " Some-college", 
                                      " Assoc-voc", " Assoc-acdm", 
                                      " Bachelors", " Masters", 
                                      " Prof-school", " Doctorate"]])

# Ajustamos el codificador con la variable education y la transformamos
encoder.fit(df[["education"]])
df["education-encoded"] = encoder.transform(df[["education"]])

A continuación, mostramos los valores de la variable antes y después de aplicar la codificación ordinal.

In [6]:
df[["education", "education-encoded"]].head(10)
Out[6]:
educationeducation-encoded
0Bachelors12.0
1Bachelors12.0
2HS-grad8.0
311th6.0
4Bachelors12.0
5Masters13.0
69th4.0
7HS-grad8.0
8Masters13.0
9Bachelors12.0

En el anterior ejemplo, claramente la variable Education sí que contiene valores que pueden ser ordenados jerárquicamente. Por el contrario, en el caso de las variables nominales que no posean un orden, no es recomendable el uso de este tipo de codificación.

Esto se debe a que, asignando números a cada categoría, estamos creando una jerarquía en los datos que en muchos casos no existe. Por ejemplo, no tiene sentido aplicar esta técnica a la variable native-country, que indica el país de nacimiento, ya que no existe ningún tipo de orden entre países y no se podría hacer afirmaciones de tipo Cuba > India.

Por esa razón, esto puede provocar que los modelos de machine learning malinterpreten los datos y como consecuencia que su rendimiento sea peor. Por suerte para nosotros, existen alternativas que nos permiten solventar este tipo de problema como la codificación one-hot.

 

Codification one-hot

Para las variables nominales que no tengan un orden jerárquico en sus categorías, una mejor solución es la llamada codificación one-hot. Esta segunda técnica consiste en crear una nueva variable binaria (también llamada dummy) por cada categoría existente en la variable a codificar.  Así, estas nuevas variables contendrán 1 en aquellas observaciones que pertenezcan a esa categoría y 0 en el resto.

En muchas tareas, tales como la regresión lineal, es común usar k-1 variables binarias en lugar de k, donde k es el número total de categorías. Esto se debe a que estamos añadiendo una variable extra redundante que no es más que una combinación lineal de las otras y seguramente afectará de manera negativa al rendimiento del modelo. Además, al eliminar una variable no estamos perdiendo información, ya que se entiende que, si el resto de las categorías contienen un 0, la categoría correspondiente es la de la variable eliminada.

Igualmente, esta técnica de codificación la podemos aplicar fácilmente usando la función get_dummies de la librería pandas, que se encarga automáticamente de crear las variables binarias. Además, para no crear k-1 variables y evitar así la redundancia disponemos del argumento drop_first.

En nuestro ejemplo, aplicaremos este método a la variable race que indica la raza de la persona y tiene 5 categorías únicas.

In [8]:
df.race.value_counts()
Out[8]:
 White                 27816
 Black                  3124
 Asian-Pac-Islander     1039
 Amer-Indian-Eskimo      311
 Other                   271
Name: race, dtype: int64

Vemos que las categorías existentes son: White, Black, Asian-Pac-Islander, Amer-Indian-Eskimo y Other. Creamos las variables binarias con el método get_dummies y ajustamos el argumento drop_first a True para eliminar primera variable y evitar así los problemas de redundancia.

In [9]:
# Creamos las variables binarias
dummies = pd.get_dummies(df['race'], drop_first = True)
dummies.head()
Out[9]:
Asian-Pac-IslanderBlackOtherWhite
00001
10001
20001
30100
40100

Como se muestra en la tabla, se han añadido 4 nuevas columnas ya que existen 5 valores únicos y no se ha incluido la correspondiente a la categoría Amer-Indian-Eskimo. Observamos que la primera observación solo contiene un 1 en la columna White al pertenecer a esta categoría.

Ya solo nos queda añadir estas nuevas variables a nuestro DataFrame y eliminar la variable original race.

In [10]:
# Añadimos las variables binarias al DataFrame
df = pd.concat([df, dummies], axis = 1)

# Eliminamos la vairable original race
df = df.drop(columns=['race'])

En esta ocasión hemos aprendido las dos aproximaciones más conocidas para codificar variables categóricas, pero existen numerosas técnicas para ello. En futuras publicaciones se presentarán otras formas de codificación no tan conocidas ¡Estar atentos!

El código usado en este artículo lo podéis obtener desde nuestra cuenta de GitHub.

También te puede interesar:

Técnicas para codificar las variables categóricas (II): codificación binaria y hashing

Aprende la codificación binaria y hashing: dos métodos muy útiles en caso de que tengamos variables categóricas de alta cardinalidad.

Técnicas para codificar las variables categóricas (III): codificación target

Aprende con ejemplos prácticos el funcionamiento de la codificación target, que utiliza información sobre la variable objetivo.

Identificación e imputación de valores perdidos en Python

Dedicaremos esta nueva publicación a explicar cómo detectar, eliminar e imputar los valores perdidos en nuestros conjuntos de datos en Python mediante un ejemplo práctico.