En este tutorial veremos cómo usar la función minmaxscaler de la librería Scikit-Learn para escalar nuestros datos al momento de construir un modelo de Machine Learning.
Al final del tutorial se encuentran los enlaces de descarga del código fuente y del set de datos.
Video
En el canal de YouTube se encuentra el video de este tutorial:
Introducción
En un tutorial anterior vimos porqué y cuándo debemos escalar nuestros datos al momento de construir un modelo de Machine Learning.
En este tutorial veremos un primer método de escalamiento que permite llevar los datos a un rango específico (0 a 1, -1 a 1) etc., usando la función MinMaxScaler de la librería Scikit-Learn.
En particular veremos el principio de escalamiento, cuándo sugiero usar y cuándo no este método así como un ejemplo práctico de uso de MinMaxScaler.
Comencemos hablando del principio funcionamiento de este escalador.
Principio de escalamiento
MinMaxScaler toma la variable a escalar en su rango original y la transforma a un rango de valores pre-definido usando esta ecuación:
$$x_s = \frac{x – x_{min}}{x_{max}-x_{min}}*(max-min)+min$$
donde:
- $x$ es la variable en su escala original
- $x_{max}$ y $x_{min}$ son el máximo y el mínimo de $x$
- $max$ y $min$ son el máximo y el mínimo pre-definidos (es decir que queremos obtener tras el escalamiento) y
- $x_s$ es la variable obtenida tras el escalamiento
Entendamos este sencillo proceso de escalamiento con un ejemplo. Supongamos que la variable $x$ (en la escala original) tiene un valor máximo de 40 y un mínimo de -20 y que queremos escalarla al rango de -1 a 1. Entonces, en este caso:
- $x_{max} = 40$
- $x_{min} = -20$
- $max = 1$
- $min = -1$
Escalemos primero el valor máximo de la variable original (en este caso $x=40$):
$$x_s = \frac{40-(-20)}{40-(-20)}*(1-(-1))+(-1) = \frac{60}{60}*2-1 = 1$$
Es decir, con este escalamiento el máximo de la variable original (40) es mapeado al máximo obtenido tras el escalamiento (1).
Veamos qué ocurre ahora con el valor mínimo de la variable original (en este caso $x=−20$):
$$x_s = \frac{-20-(-20)}{40-(-20)}*(1-(-1))+(-1) = \frac{0}{60}*2-1 = -1$$
Es decir que el mínimo en la escala original (-20) es mapeado al valor -1 en la escala resultante.
¡Y listo, es así de simple este escalamiento! Y la idea es que lo debemos aplicar:
- A cada columna (variable o feature) del set de datos de entrada al modelo (X)
- A la variable de salida (Y) del modelo en el caso que estemos realizando tareas de regresión
- Antes de entrenar el modelo o de generar predicciones
Aunque de esto hablaremos en detalle en un momento.
Adicionalmente, cuando realizamos tareas de regresión, el modelo es entrenado con datos de entrada y salida escalados, y por tanto aprende a generar predicciones escaladas.
Así que para llevar dichas predicciones a la escala original debemos realizar el escalamiento inverso. La ecuación para este escalamiento inverso se puede obtener de manera sencilla despejando la variable $x$ (es decir la variable en su escala original) de la ecuación para el escalamiento vista anteriormente:
$$x = \frac{x_s-min}{max-min}*(x_{max}-x_{min}) + x_{min}$$
Así, si en el ejemplo anterior calculamos el escalamiento inverso obtendremos estos resultados:
- El valor 1 en la variable escalada se mapea al valor 40 en la escala original
- Y de manera equivalente el valor -1 en la variable escalada es convertido al valor -20 en la escala original
Muy bien, teniendo claro este principio de funcionamiento, veamos cuándo es aconsejable realizar este escalamiento y cuándo no.
¿Cuándo NO usar MinMaxScaler?
Cuando la distribución tiene un sesgo
Una distribución con sesgo es cuando su forma se aleja demasiado de una distribución normal o gaussiana (campana simétrica).
En este caso NO se recomienda el uso de MinMaxScaler pues al escalar los datos comprimimos la distribución de los datos a un rango más pequeño que el original. Es decir que desaprovechamos todo el rango de valores disponible tras el escalamiento
Cuando los datos tienen valores extremos (outliers)
Los valores extremos (u outliers) son simplemente datos cuyos valores se encuentran excepcionalmente fuera del rango de valores de la mayoría de nuestros datos.
En este caso tampoco se recomienda el uso de minmaxscaler. Y esto se debe a que por tratarse de valores extremos, estos outliers generalmente corresponderán a los valores $x_{max}$ y $x_{min}$ que aparecen en la ecuación de escalamiento de nuestra variable original.
Así que al hacer el escalamiento estos valores extremos quedarán mapeados al máximo y mínimo en el rango resultante, mientras que los valores no extremos (que es la mayoría de los datos) quedarán mapeados dentro de un rango resultante muchísimo menor.
Es decir que si usamos minmaxscaler cuando tenemos valores extremos en nuestros datos hace que la mayoría de nuestros datos (que no son extremos) queden como resultado comprimidos a un rango de valores muy pequeño.
¿Cuándo podemos usar MinMaxScaler?
Teniendo en cuenta lo anterior, se sugiere el uso de MinMaxScaler en estas situaciones:
- Cuando tenemos claro el rango de valores esperado a la salida del escalamiento
- Cuando la distribución de los datos NO tiene demasiado sesgo
- Cuando los datos NO contienen outliers
Bien, teniendo en qué situaciones no es recomendable usar minmaxscaler y en cuales sí, veamos cómo sería la secuencia de uso en un proyecto de Machine Learning.
Secuencia de uso de MinMaxScaler
Esta es la secuencia de uso que sugiero llevar a cabo
- El primer paso es tomar nuestro set de datos y dividirlo en lo que serán las entradas (variable
X) y salidas (variableY) del modelo de Machine Learning. - El segundo paso es realizar la partición de los datos (variables
XyY) en los sets de entrenamiento, validación y prueba (o en los subsets usados en la validación cruzada si decidimos usar este enfoque). - El tercer paso consiste en realizar el escalamiento de los datos. En particular:
- Usamos el método
fit_transform()deMinMaxScalerpara tomar el máximo y el mínimo de cada columna del set de entrenamiento y escalar dicho set al rango especificado (que en este caso será el rango por defecto de 0 a 1) - Usamos el método
transform()para usar los máximos y mínimos calculados en el paso anterior y escalar los sets de validación y prueba. Lo hacemos de esta manera pues en una aplicación real sólo conoceremos el set de entrenamiento y los demás datos a procesar deben tomar como referencia este dataset para realizar el escalamiento.
- El cuarto paso consiste en entrenar el modelo, usando el set de entrenamiento (tanto entradas como salidas) escalados obtenidos en el paso anterior.
- Una vez entrenado el modelo podemos realizar predicciones. Para ello introducimos los datos escalados y usamos el método
predict()para generar dichas predicciones. Debemos tener en cuenta que si la tarea es de Regresión las predicciones generadas por el modelo estarán escaladas, así que tendremos que aplicar posteriormente un escalamiento inverso para obtener la variable predicha en su escala original.
Teniendo como referencia estos pasos, veamos cómo usar MinMaxScaler en un problema de Regresión.
Lectura y exploración inicial del set de datos
Usaremos un set de datos que contendrá información asociada a las características de inmueble (edad, distancia a la estación de metro más cercana, número de tiendas en su vecindad, latitud y longitud según su ubicación geográfica) y a partir de las cuales intentaremos predecir el precio (en miles de dólares) del metro cuadrado.
Comencemos leyendo los datos haciendo uso de la librería Pandas:
import pandas as pd
RUTA = '/gdrive/MyDrive/02-CODIFICANDOBITS.COM/04-Academia/02-Tutoriales/2023-10-23-tutorial-minmaxscaler-scikit-learn/data/dataset_regresion.csv'
df = pd.read_csv(RUTA)
df
Al ejecutar este código obtenemos este set de datos:
| | edad_inm | dist_est | nro_tiendas | latitud | longitud | precio_m2_1K |
|-----|----------|-------------|-------------|----------|-----------|--------------|
| 0 | 32.0 | 2186.834670 | 10 | 24.98298 | 121.54024 | 37.9 |
| 1 | 19.5 | 822.880872 | 9 | 24.98034 | 121.53951 | 42.2 |
| 2 | 13.3 | 561.984500 | 5 | 24.98746 | 121.54391 | 47.3 |
| 3 | 13.3 | 561.984500 | 5 | 24.98746 | 121.54391 | 54.8 |
| 4 | 5.0 | 1667.026714 | 5 | 24.97937 | 121.54245 | 43.1 |
| ... | ... | ... | ... | ... | ... | ... |
| 409 | 13.7 | 2725.684379 | 0 | 24.94155 | 121.50381 | 15.4 |
| 410 | 5.6 | 1513.811048 | 9 | 24.97433 | 121.54310 | 50.0 |
| 411 | 18.8 | 1922.792529 | 7 | 24.97923 | 121.53986 | 40.6 |
| 412 | 8.1 | 1052.074479 | 5 | 24.96674 | 121.54067 | 52.5 |
| 413 | 6.5 | 2369.305970 | 9 | 24.97433 | 121.54310 | 63.9 |
En este caso las variables de entrada al modelo serán las columnas:
edad_inm: edad del inmueble (en años)dist_est: distancia a la estación de metro más cercana (en metros)nro_tiendas: número de tiendas en la vecindadlatitudylongitud: coordenadas geográficas del inmueble
Por otra parte la columna precio_m2_1K contiene la variable a predecir (el precio por metro cuadrado del inmueble, en miles de dólares).
Comencemos viendo los rangos de valores de cada variable:
df.describe().loc[['min','max']]
con lo que obtenemos:
| | edad_inm | dist_est | nro_tiendas | latitud | longitud | precio_m2_1K |
|-----|----------|-------------|-------------|----------|-----------|--------------|
| min | 0.0 | 506.114400 | 0.0 | 24.93207 | 121.47353 | 7.6 |
| max | 43.8 | 2989.046544 | 10.0 | 25.01459 | 121.56627 | 78.3 |
Lo anterior nos permite verificar que cada variable (columna) del set de datos tiene un rango de valores diferente. Esta es una primera condición para llevar a cabo el escalamiento usando MinMaxScaler.
Sin embargo, otra condición que se debe cumplir es que nuestros datos no pueden contener outliers o valores extremos, ni tampoco sesgos en la distribución.
Verifiquemos lo anterior dibujando el histograma de cada columna:
import matplotlib.pyplot as plt
# Dibujar el histograma de cada columna
for col in df.columns:
plt.figure()
sns.histplot(df[col])
Al analizar cada una de las figuras obtenidas podemos comprobar que ninguna de las variables tiene una distribución sesgada ni valores extremos.
Así que en este punto ya estamos seguros de que MinMaxScaler será una alternativa adecuada para el escalamiento de los datos. Entonces ya podemos ejecutar cada uno de los pasos mencionados anteriormente.
Paso 1: crear los arreglos de entrada (X) y salida (Y)
Para crear el arreglo de entrada simplemente tomamos las primeras 5 columnas de nuestro set de datos y las convertimos a un arreglo de NumPy:
X = df.iloc[:,0:5].to_numpy()
Mientras que el arreglo de salida será simplemente la última columna del set de datos:
Y = df.iloc[:,-1].to_numpy()
Paso 2: realizar la partición en entrenamiento, validación y prueba
Realicemos la partición en entrenamiento (80%), validación (10%) y prueba (10%) usando la función train_test_split de la librería Scikit-Learn.
Realizaremos esta partición en dos fases:
- En la primera generamos una partición 80% entrenamiento y 20% resto
- Mientras que en la segunda tomamos los datos restantes (20%) y los partimos en dos mitades para generar los sets de validación y prueba
Este sería el código:
from sklearn.model_selection import train_test_split
# Partición 1: entrenamiento (80%) y resto (20%)
x_tr, x_resto, y_tr, y_resto = train_test_split(X, Y,
test_size=0.2,
random_state=23)
# Partición 2: validación (10%) y prueba (10%)
x_vl, x_ts, y_vl, y_ts = train_test_split(x_resto, y_resto,
test_size=0.5, random_state=12)
print(x_tr.shape, y_tr.shape)
print(x_vl.shape, y_vl.shape)
print(x_ts.shape, y_ts.shape)
Al ejecutar el código anterior obtenemos los siguientes tamaños para los sets obtenidos (entrenamiento, validación y prueba):
(331, 5) (331,)
(41, 5) (41,)
(42, 5) (42,)
Paso 3: escalamiento
Hagamos primero el escalamiento del set de entrenamiento (x_tr_s) y veamos algunas características de esta operación.
Comencemos importando el módulo MinMaxScaler de la librería Scikit-Learn:
from sklearn.preprocessing import MinMaxScaler
A continuación creamos una instancia del escalador y usamos fit_transform() para escalar el set de entrenamiento:
# Minmaxscaler
x_scaler = MinMaxScaler()
# fit_transform: Cálcular máximos/mínimos y escalar el set de entrenamiento
x_tr_s = x_scaler.fit_transform(x_tr)
E imprimamos en pantalla los valores mínimo y máximo encontrados en el set x_tr_
print('Características del escalador ajustado:')
print(x_scaler.data_min_, x_scaler.data_max_)
obteniendo:
Características del escalador ajustado:
[ 0. 506.1144 0. 24.93207 121.47353]
[ 43.8 2989.04654408 10. 25.01459 121.56627 ]
Estos valores mínimos y máximos de cada columna serán los usados por el escalador al momento de usar el método transform() cuando escalemos los datos de validación y prueba.
Ahora imprimamos en pantalla los valores mínimos y máximos de cada columna en el set de entrenamiento escalado:
print('Resultado del escalamiento sobre "x_tr":')
print(f' Mínimos: {x_tr_s.min(axis=0)}')
print(f' Máximos: {x_tr_s.max(axis=0)}')
con lo que obtenemos este resultado:
Resultado del escalamiento sobre "x_tr":
Mínimos: [0. 0. 0. 0. 0.]
Máximos: [1. 1. 1. 1. 1.]
Muy bien, los datos están escalados correctamente en el rango especificado (de 0 a 1).
A continuación hacemos lo mismo con la variable y_tr. Sin embargo, tenemos que hacer un ajuste adicional al momento de realizar el escalamiento. Imprimamos en pantalla el tamaño de este arreglo:
print(y_tr.shape)
obteniendo:
(331,)
Sin embargo, MinMaxScaler requiere que indiquemos de manera explícita que el tamaño es 331×1, así que tendremos que usar el método reshape para realizar este ajuste.
Teniendo en cuenta lo anterior, este sería el código para escalar los datos de salida del set de entrenamiento (y_tr):
y_tr_s = y_scaler.fit_transform(y_tr.reshape(-1,1))
Al igual que con el escalamiento de la variable x_tr, imprimamos en este caso los valores máximo y mínimo encontrados en la variable original (y_tr) así como el mínimo y máximo de la variable escalada (y_tr_s):
print('Características del escalador ajustado:')
print(y_scaler.data_min_, y_scaler.data_max_)
print('Resultado del escalamiento sobre "y_tr":')
print(f' Mínimos: {y_tr_s.min()}')
print(f' Máximos: {y_tr_s.max()}')
Y al ejecutar el código anterior podemos verificar que el escalamiento se ha realizado correctamente:
Características del escalador ajustado:
[7.6] [78.3]
Resultado del escalamiento sobre "y_tr":
Mínimos: 0.0
Máximos: 1.0
¡Muy bien! El siguiente paso es escalar los sets de validación y prueba. Tengamos en cuenta que en este caso debemos usar el método transform() para tomar los mínimos y máximos del set de entrenamiento como referencia durante este escalamiento.
Además, realizaremos el escalamiento únicamente de las variables de entrada (x_vl y x_ts) pues, a diferencia del entrenamiento, en este caso usaremos estas variables para generar predicciones con el modelo entrenado y por tanto se requiere únicamente contar con las entradas al modelo escaladas:
x_vl_s = x_scaler.transform(x_vl)
x_ts_s = x_scaler.transform(x_ts)
Imprimamos en pantalla los mínimos y máximos de cada variable escalada para cada uno de los sets de datos:
print('Set de validación: ')
print(f' {x_vl_s.min(axis=0)}, {x_vl_s.max(axis=0)}')
print('Set de prueba: ')
print(f' {x_ts_s.min(axis=0)}, {x_ts_s.max(axis=0)}')
Al ejecutar este bloque de código podemos verificar que los valores mínimos y máximos no son exactamente 0 y 1 respectivamente. Esto se debe a que el escalamiento de estos sets de validación y prueba se realizó tomando como referencia la información proveniente del set de entrenamiento:
Set de validación:
[0. 0.00268743 0. 0.01042172 0.11915031], [0.87442922 0.97522729 1. 0.72091614 0.82143627]
Set de prueba:
[0.02283105 0.00259141 0. 0.11488124 0.24088851], [0.94520548 0.99021929 0.9 0.7233398 0.86672418]
Paso 4: entrenar el modelo
En este caso entrenaremos un sencillo modelo de Regresión Lineal usando el módulo LinearRegression de Scikit-Learn:
# Ahora sí construir el modelo
from sklearn.linear_model import LinearRegression
# Crear el modelo (instancia)
lr = LinearRegression()
# Entrenar el modelo
lr.fit(x_tr_s, y_tr_s)
Paso 5: generar predicciones
Muy bien, teniendo el modelo entrenado, el único paso que nos resta es realizar las predicciones usando cualquiera de los dos sets disponibles (validación o prueba).
Para ello usaremos el método predict e introduciremos los dos sets de datos (validación y prueba) escalados:
# Predicciones con los sets de validación y prueba
y_vl_pred_s = lr.predict(x_vl_s)
y_ts_pred_s = lr.predict(x_ts_s)
En este caso las predicciones generadas por el modelo estarán escaladas en el rango de 0 a 1, pues el modelo fue entrenado precisamente con datos dentro de este rango (la variable y_tr_s):
print(y_vl_pred_s.min(), y_vl_pred_s.max())
print(y_ts_pred_s.min(), y_ts_pred_s.max())
con lo que obtenemos:
0.058985160240780314 0.6698053503819943
0.08189834491256845 0.6218937869913053
Así que en este caso debemos usar inverse_transform() para realizar el escalamiento inverso y obtener así las predicciones en la escala original:
y_vl_pred = y_scaler.inverse_transform(y_vl_pred_s)
y_ts_pred = y_scaler.inverse_transform(y_ts_pred_s)
print(y_vl_pred.min(), y_vl_pred.max())
print(y_ts_pred.min(), y_ts_pred.max())
Al imprimir en pantalla los valores mínimo y máximo de las predicciones en la escala original (obtenidas tras el escalamiento inverso) tendremos estos resultados:
11.770250829023169 54.955238272006994
13.39021298531859 51.56789074028528
Y en este punto ya hemos incorporado correctamente el escalamiento de los datos en todas las fases de desarrollo de este modelo de Machine Learning.
Es importante tener en cuenta que este último paso de escalamiento inverso no es necesario en caso de resolver un problema de clasificación. Esto se debe a que al clasificar los datos nuestras variables a predecir serán de tipo categórico y por tanto en este caso no aplica el concepto de escalamiento.
Enlaces de descarga set de datos y código fuente
Contenido exclusivo para suscriptores
Si eres suscriptor accede en este enlace ó suscríbete a la Academia Online y accede a todo el contenido (lecciones en video, código fuente, sets de datos y descargas) de todos los cursos, proyectos y tutoriales.
Conclusión
Acabamos de ver cómo realizar el escalamiento de datos usando la función minmaxscaler de Scikit-Learn, que permite escalar los datos a un rango definido por nosotros.
Este método puede ser usado cuando no hay demasiado sesgo en la distribución de los datos a escalar o cuando las variables a escalar no contienen outliers.
En próximos tutoriales veremos otras alternativas de escalamiento, como el uso de StandardScaler para estandarizar nuestros datos o RobustScaler para escalar datos pero en situaciones en las que tengamos variables con valores extremos u outliers.
