Técnicas para codificar las variables categóricas (I): codificación ordinal y one-hot
¿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.
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.
df.head()
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
.
df.education.value_counts()
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.
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.
df[["education", "education-encoded"]].head(10)
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.
df.race.value_counts()
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.
# Creamos las variables binarias
dummies = pd.get_dummies(df['race'], drop_first = True)
dummies.head()
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
.
# 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.