Discretización de un Pandas dataframe en Python manteniendo el nombre de las columnas

Discretizar datos quiere decir convertir variables que son continuas en variables agrupadas por intervalos. Por ejemplo, podemos discretizar un listado que contiene la edad de ciertas personas, que de forma continua podrían tener un valor entre 0 y 90, en variables discretas de (por ejemplo) 5 intervalos de diferentes edades: infantes, niños, jóvenes, adultos y ancianos.

La discretización de variables es muy útil en aprendizaje supervisado. El científico de datos puede decidir qué variables conviene que sean discretizadas antes de aplicar los modelos y cuales pueden funcionar mejor de forma continua. En mi opinión, aquellas variables que puedan ser descritas, desde el punto de vista del algoritmo, en forma de intervalos son buenas candidatas a la discretización. Es decir, que aquello que nos convenga describir en grupos (tales como bajo, medio y alto) podrían ser discretizadas.

1. KBinsDiscretizer

La biblioteca Sklearn nos ofrece la magnifica herramienta KBinsDiscretizer. Esta clase, nos permite discretizar de forma automática una variable en un número de intervalos predefinidos por nosotros y en función de una estrategia también indicada. Su configuración es la siguiente:

KBinsDiscretizer(n_bins=5,encode='onehot',strategy='quantile')
  • n_bins: número de intervalos a discretizar. Tiene que ser mayor que 2 y por defecto toma el valor de 5.
  • encode: Método utilizado para codificar el resultado transformado. Tiene tres opciones: ‘onehot‘ , ‘onehot-dense‘ y ‘ordinal‘. La primera de las codificaciones devuelve una matriz dispersa por cada valor, la segunda codificación devuelve un array y la última un valor entero. Por defecto toma la codificación onehot.
  • strategy: Es la estrategia usada para definir cuántos valores hay en cada intervalo discretizado. Tiene tres opciones: ‘uniform‘, donde todos los intervalos tienen el mismo ancho, ‘quantile‘, donde cada intervalo tiene el mismo número de valores y ‘kmeans‘, que agrupa los datos de cada intervalo según su centroide más cercano. Por defecto toma el valor de quantile.

2. Ejemplo de uso para una única columna

Vamos a utilizar un dataframe Pandas con datos ficticios creados para el ejemplo. Nuestro dataframe contiene 15 registros y 5 variables:

Dataframe usado para el ejemplo.

Supongamos que queremos discretizar exclusivamente la columna A1. Además, queremos discretizarla en 11 intervalos siguiendo la estrategia kmeans:

A1_dis = KBinsDiscretizer(n_bins=11, encode='ordinal',
                           strategy = "kmeans").fit_transform(df[['A1']])

El resultado es el siguiente:

Columna A1 discretizada.

Observamos como los valores continuos de la columna A1 ahora son valores discretos entre 0 y 10 (11 bines o intervalos diferentes). Notamos también que no devuelve un dataframe, sino un array NumPy, por lo que el nombre de la columna lo perdemos.

Convertimos el array en dataframe de Pandas y renombramos la columna con su nombre original:

A1_dis = pd.DataFrame(A1_dis)
A1_dis = A1_dis.rename(columns = {0: 'A1'})

Por último, sustituimos la columna continua por la discretizada en el dataframe original:

df[['A1']] = A1_dis

3. Discretización automática de todo el dataframe conservando el nombre de las columnas

Es probable que deseemos hacer pruebas con un dataframe completamente discretizado. Si tenemos muchas columnas, ir discretizando una a una puede ser un auténtico incordio, por no hablar que tenemos que renombrar cada columna discretizada.

Partimos del dataframe original y automatizamos todo este proceso de una forma sencilla:

bines = 7 # Elegir el número de bines
cabecera = list(df) # Guardamos los nombres de las columnas.

ind = 0 # Contador para iterar por columnas.

while (ind < len(cabecera)):
    disc = df.iloc[:,ind] 
    disc = disc.to_frame() 
    disc = KBinsDiscretizer(n_bins=bines, encode='ordinal',
                            strategy = "quantile").fit_transform(disc)
    df[cabecera[ind]] = disc 
    ind = ind + 1
    del(disc)   
 
del(ind)
del(cabecera) 
del(bines)

Con la variable bines elegimos el número de intervalos a discretizar. En la lista cabecera guardamos todos los nombres de nuestras columnas. Después, con un bucle while() iteramos columna a columna realizando la discretización y renombrando cada columna con su nombre original.

En esta ocasión hemos discretizado usando 7 intervalos y la estrategia de cuantiles, donde cada intervalo tiene el mismo número de elementos:

Dataframe discretizado en intervalos de 0 a 6.

Por último, hay que indicar que si ya tenemos alguna columna discretizada en nuestro dataframe, podemos tener problemas si la volvemos a discretizar. En concreto debemos tener cuidado en variables con menos intervalos que bines establecidos en la función KBinsDiscretizer . Por ejemplo, su hubiésemos tenido una columna con sólo tres posibles valores (0, 1 y 2 o A, B y C) al discretizarla a 7 bines tendríamos problemas.

Sin embargo, la columna E5 del dataframe original parece ser una variable discreta, ya que solo tiene valores enteros con algunos de ellos repetidos. Notamos que estos valores varían entre 6 y 74 (bien podrían ser edades) por lo que una discretización/agrupación en 7 intervalos ha funcionado bien y puede ser conveniente.

En resumen: la discretización puede ser una herramienta muy útil en aprendizaje supervisado, quedando a criterio del data scientist qué variables discretizar y cómo. Sin olvidar que en el proceso de discretización estamos “perdiendo” inexorablemente información de nuestros datos.

Para saber más:
Discretizar variables continuas con Python.
Un método de Discretización Supervisada para Variables Cuantitativas y Cualitativas Ordenadas.
Why One-Hot Encode Data in Machine Learning?
Preprocessing with sklearn: a complete and comprehensive guide.

Deja una respuesta