Técnicas para codificar las variables categóricas (III): codificación target
Continuamos con nuestra serie que tiene como objetivo explicar diferentes procedimientos para codificar variables categorías. En la primera parte explicamos los métodos más comúnmente usados: la codificación ordinal y one-hot.
En la segunda publicación de la serie nos centraremos en otros dos procesos recomendables cuando contamos con variables categóricas de alta cardinalidad: la codificación binaria y hashing.
Para esta ocasión os mostraremos un método perteneciente al conjunto de las llamadas codificaciones bayesianas, que se caracterizan por incorporar información sobre la variable objetivo, es decir, la variable que queremos predecir.
La técnica que trataremos aquí se denomina codificación target (también llamada mean). La idea general de este método consiste en reemplazar el valor de cada categoría por el valor medio de la variable objetivo asociado a esa categoría. Veamos cómo funciona en más profundidad y por qué puede resultarnos útil.
El conjunto de datos Automobile Data Set
Al contrario que en las otras dos entregas, en esta ocasión sí que estamos interesados en la variable objetivo. Debido a ello, hemos escogido el conjunto de datos Automobile Data Set perteneciente al repositorio UCI Machine Learning para realizar el nuestro ejercicio práctico.
Este clásico dataset contiene 26 características de 205 modelos de automóviles del año 1985 como, por ejemplo, el tipo de gasolina que usa, la marca del automóvil o el número de cilindradas. La variable que utilizaremos como objetivo es price
, que representa el precio del automóvil.
Comenzamos cargando los datos y rellenando los valores perdidos de la variable price
para evitar posibles problemas posteriores. También mostramos las primeras observaciones para hacernos una primera idea.
import pandas as pd
# Cargamos el dataset
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-'
'databases/autos/imports-85.data', encoding = 'utf-8',
header = None)
# Añadimos el nombre de cada variable
df.columns = ["symboling","normalized-losses","make","fuel-type",
"aspiration","num-of-doors","body-style","drive-wheels",
"engine-location","wheel-base","length","width",
"height","curb-weight","engine-type","num-of-cylinders",
"engine-size","fuel-system","bore","stroke",
"compression-ratio","horsepower","peak-rpm","city-mpg",
"highway-mpg","price"]
# Rellenamos los valores perdidos
df['price'] = df['price'].replace(['?'], 10000)
# Convertimos columna "price" a numerica
df["price"] = pd.to_numeric(df["price"])
df.head()
¿En qué consiste la codificación target?
Mediante la codificación target sustituimos nuestra variable categórica por una única nueva variable de tipo numérico que, como hemos dicho, contiene información de la variable objetivo. El funcionamiento en su forma básica es bastante simple:
- En primer lugar, por cada categoría única que tenga la variable a codificar se calcula la media correspondiente a sus valores en la variable objetivo.
- Después, se reemplaza cada valor por el de la media calculada.
Eso sería en el supuesto de que nuestra variable objetivo sea numérica, pero también se puede recurrir a esta técnica para varaibles categóricas, en cuyo caso se calcula la probabilidad asociada a esa categoría en lugar de la media.
La ventaja de este método es que es bastante rápido y no añade dimensiones a nuestro conjunto de datos.
Ejemplo práctico
Veamos ahora un ejemplo práctico para entender cómo podemos utilizarlo en nuestro dataset. De nuevo la librería category_encoders
viene a nuestro rescate y nos proporciona el objeto TargetEncoder
para poder aplicar esta técnica de manera sencilla.
Aplicaremos esta codificación a la variable make
, que contiene 22 categorías únicas correspondientes a la marca del automóvil y que tomará valores como, por ejemplo, audi
, volvo
o alfa-romero
.
Comenzamos creando el codificador, indicándole para ello en nombre de la variable a codificar make
. Para conseguir el mismo resultado que el algoritmo explicado antes, debemos ajustar el parámetro smoothing
a 0
. Después explicaremos por qué no es recomendable hacer esto, pero por ahora y con fines didácticos lo dejamos a 0
.
Después, ajustamos el codificador pasándole las dos columnas: la variable objetivo, que como recordareis es price
, y la que queremos codificar. Por último, transformamos la variable.
from category_encoders import TargetEncoder
# Creamos el codificador Target indicando nuestra variable
encoder = TargetEncoder(cols = ['make'], smoothing = 0)
# Ajustamos el codificador indicandole la variable objetivo
encoder.fit(df["make"], df['price'])
new_make = encoder.transform(df["make"], df['price'])
# Concatenamos la nueva variable con el resto
df = pd.concat((df, new_make.make.rename('new_make')), axis=1)
make
junto con la nueva codificada (new_make
) y price
para ver los resultados.df[["make", "price", "new_make"]].head(8)
En la anterior tabla se puede ver claramente que a las observaciones pertenecientes a la categoría alfa-romero
se les ha asignado el valor 15498.333
, que no es más que la media del precio en las tres instancias que pertenecen a esa categoría: (13495 + 16500 + 16500) / 3 = 15498.333. Sencillo, ¿verdad?
Es importante señalar que en un escenario en el que tengamos un conjunto de datos de entrenamiento y otro de validación, solo deberemos ajustar el codificador con el conjunto de entrenamiento, es decir, calcularemos las medias teniendo en cuentas los datos de entrenamiento.
Como evitar el sobreajuste
Debido a la fuerte relación que tiene con la variable objetivo, el problema de utilizar este método es que puede dar lugar a sobreajuste. Esto se puede dar sobre todo en los casos en los tenemos pocos valores en las categorías y tenemos que calcular la media de esos valores. Debido a ello, no es recomendable usar esta técnica sin algún tipo de regularización para evitar el sobreajuste.
Afortunadamente, hay varios mecanismos para tratar este problema. Uno de ellos consiste en utilizar la técnica de validación cruzada (cross-validation en inglés) y calcular en cada iteración la media de los datos de entrenamiento. Finalmente se realiza la media de los resultados de cada iteración para obtener el resultado final.
Otra técnica de regularización es el llamado smoothing, que además nos viene por defecto en la librería category_encoders
. No entraremos en los detalles sobre el funcionamiento de esta técnica. En este paper encontrareis una explicación en profundidad de este tipo de regularización. En líneas generales, mediante este proceso de suavizado, cuanto más pequeño sea nuestro conjunto de datos, el resultado sera mas cercano al valor medio global de la variable objetivo.
Volvamos a nuestro ejemplo práctico. Como ya vimos, al construir el objeto TargetEncoder
, le podemos indicar el nivel de regularización que queremos mediante el argumento smoothing
. Un valor más alto significa una regularización más fuerte.
En el ejemplo anterior, al ajustar el valor de smoothing
a 0
, no estábamos usando regularización y en consecuencia nos devolvía simplemente la media correspondiente a sus valores en la variable objetivo como ya comprobamos.
De manera análoga, aplicamos la codificación a nuestra variable, pero en esta ocasión ajustando el valor de smoothing
a 3
:
# Creamos el codificador Target indicando nuestra variable
encoder = TargetEncoder(cols = ['make'], smoothing = 3)
# Ajustamos el codificador indicandole la variable objetivo
encoder.fit(df["make"], df['price'])
new_make = encoder.transform(df["make"], df['price'])
# Concatenamos la nueva variable con el resto
df = pd.concat((df, new_make.make.rename('new_make_smooth')), axis=1)
new_make_smooth
junto con el resto con el fin de comparar los resultados.df[["make", "price", "new_make", "new_make_smooth"]].head(8)
Observamos que aplicando la técnica de smoothing los valores son distintos y más bajos que cuando no lo aplicamos.
Con este post, hemos aprendido a utilizar la codificación target y aplicar la regularización mediante la técnica smoothing. Si queréis repasar todas las técnicas de codificación que hemos visto hasta ahora, no dudes en echar un vistazo a nuestra serie completa:
- Técnicas para codificar las variables categóricas (I): codificación ordinal y one-hot
- Técnicas para codificar las variables categóricas (II): codificación binaria y hashing
Para más información recordar que podéis ver todo el código usado en este artículo desde nuestra cuenta de GitHub y si tienes cualquier duda, nos la podéis hacer llegar a través de los comentarios.