====== 08 - Reducción de la dimensionalidad 1: Métodos de filtro ======
En el aprendizaje automático, la selección de características importantes en los datos es una parte importante del ciclo completo.
El entrenamiento del modelo con características irrelevantes puede afectar el rendimiento del modelo. Para evitarlo, se utilizan métodos de **reducción de la dimensionalidad**.
Existen varios tipos:
* Métodos de filtro (**filter methods**): Selecciona características de manera independiente al algoritmo de ML que será utilizado. Realizan una clasificación de las características mediante una función que asigna una valor a cada una de ellas según su importancia para el modelo. Posteriormente, se eligen aquellas características más relevantes. Tienen un coste computacional bajo.\\ \\
* Métodos de envoltura (**wrapper methods**): Aplica un algoritmo de ML e itera haciendo todas las combinaciones de variables posibles (entre ellas y en número) para finalmente quedarse con aquella combinación que de mejor resultado en las métricas. Consume muchos recursos computacionales.\\ \\
* Métodos integrados (**embedded methods**): La selección de características más relevantes se hace durante la fase de entrenamiento, y esta selección depende de cada modelo. Tienen un coste computacional menor que los métodos de envoltura. Los veremos con detalle en el apartado **regularización**.\\ \\
* Métodos híbridos (**hybrid methods**): Combinan métodos de filtro y de envoltura. Obtienen mejores resultados que los métodos de filtro, pero no son tan rápidos. Por otra parte, tienen un coste computacional más bajo que los métodos de envoltura.\\ \\
===== Métodos de filtro =====
Dos categorías:
* Métodos de filtro **univariante**: Las características de clasifican según criterios específicos de forma individual. A continuación, se seleccionan las N características más relevantes. Se utilizan para eliminar características constantes (o cuasi constantes) de los datos, pero pueden seleccionar características redundantes porque la relación entre características individuales no se tiene en cuenta al tomar decisiones.\\ \\
* Métodos de filtro **multivariante**: Son capaces de eliminar características redundantes de los datos, ya que tienen en cuenta la relación mutua entre las características. Se pueden utilizar métodos de filtro multivariante para eliminar características duplicadas y correlacionadas de los datos.
Vamos a ver ejemplos de algunos métodos de filtro univariante y multivariante.
==== Métodos de filtro univariante ====
Para este ejemplo, utilizaremos un conjunto de datos de satisfacción de clientes del Santander.
Primero importamos las bibliotecas necesarias y cargamos los datos (cargamos sólo los primeros 40.000 datos):
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
# Configuración warnings
# ==============================================================================
import warnings
warnings.filterwarnings('ignore')
datos = pd.read_csv("santander.csv", nrows=40000)
datos.shape
(40000, 371)
Como vemos, el dataset contiene 40.000 datos con 371 variables (370 características y nuestra variable objetivo, que, en este caso, es la columna **TARGET**).
Antes de nada, es importante separar siempre los datos en entrenamiento y test para evitar el sobreajuste.
X = datos.drop(columns = 'TARGET', axis = 1)
y = datos['TARGET']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 0.7)
Para saber las características constantes (varianza 0), vamos a usar la función **VarianceThreshold** de la librería sklearn. Esta función, selecciona las características eliminando las que tienen una varianza por debajo de un umbral (en este caso, 0). Primero tenemos que crear el filtro, y después aplicarlo a nuestros datos de entrenamiento.
constant_filter = VarianceThreshold(threshold=0)
constant_filter.fit(X_train)
Para obtener todas las características que no son constantes, podemos usar la función **get_support()** del filtro que creamos.
len(X_train.columns[constant_filter.get_support()])
301
Como vemos, hay 301 características no constantes, con lo que tenemos 69 que sí lo son. Finalmente, para eliminar características constantes de los conjuntos de entrenamiento y prueba, podemos usar el método **transform()** del filtro.
X_train = constant_filter.transform(X_train)
X_test = constant_filter.transform(X_test)
X_train.shape, X_test.shape
((28000, 301), (12000, 301))
Si quisiéramos eliminar las características cuasi constantes, podemos variar el umbral del filtro:
constant_filter = VarianceThreshold(threshold=0.1)
constant_filter.fit(X_train)
len(X_train.columns[constant_filter.get_support()])
204
X_train = constant_filter.transform(X_train)
X_test = constant_filter.transform(X_test)
X_train.shape, X_test.shape
((28000, 204), (12000, 204))
No existe una regla sobre cuál debería ser el umbral para la varianza de las características cuasi constantes. Sin embargo, como regla general, se eliminan aquellas características que tengan valores similares en más del 99%.
==== Métodos de filtro multivariante ====
Vamos a ver un ejemplo de método de filtro multivariante, eliminando características que están altamente correlacionadas entre sí.
En este caso, vamos a utilizar el dataset wine de la librería sklearn. Importamos las librerías, cargamos los datos y separamos test y entrenamiento.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
# Configuración warnings
# ==============================================================================
import warnings
warnings.filterwarnings('ignore')
data = load_wine()
df = pd.DataFrame(data = data.data, columns = data.feature_names)
df['class'] = data.target
df.shape
(178, 14)
Nuestro conjunto de datos tiene 178 registros con 13 características (más nuestra variable objetivo, que es **class**).
X = df.drop(columns = 'class', axis = 1)
y = df['class']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size = 0.7)
Primero, sacamos la matriz de correlación de las características con el método **corr()** y mostramos el gráfico con un mapa de colores.
figure = plt.figure(figsize = (15, 10))
axes = figure.add_subplot()
corr_features = X_train.corr()
_ = sns.heatmap(corr_features, annot=True, cmap=plt.cm.coolwarm, axes = axes)
{{ :clase:ia:saa:6_mejorar_datos:output5.png?400 |}}
Lo siguiente será sacar las características que tienen una correlación mayor a un umbral dado. Para eso, definimos el umbral y volvemos a sacar la matriz de correlación, pero esta vez con los valores absolutos (un valor de correlación = -1 indica una alta correlación negativa, con lo que también deberíamos tenerlo en cuenta).
umbral_corr = 0.7
corr_matrix = X_train.corr().abs()
Para seleccionar las características que tienen una correlación mayor que nuestro umbral, vamos a usar un par de funciones de numpy:
* **ones()**: Crea un array de 1's de una dimensión dada.
* **triu()**: Devuelve una copia de un array con los elementos inferiores a la diagonal k-ésima igual a 0.
Por ejemplo, si queremos crear una matriz de 5x5 con los elementos del triángulo superior = 1 y el resto a 0:
np.triu(np.ones((5, 5)), k=1)
array([[0., 1., 1., 1., 1.],
[0., 0., 1., 1., 1.],
[0., 0., 0., 1., 1.],
[0., 0., 0., 0., 1.],
[0., 0., 0., 0., 0.]])
También podemos pasar los elementos a booleanos:
np.triu(np.ones((5, 5)), k=1).astype(np.bool)
array([[False, True, True, True, True],
[False, False, True, True, True],
[False, False, False, True, True],
[False, False, False, False, True],
[False, False, False, False, False]])
Ahora podemos combinar ambas matrices (la de correlación y la que hemos creado) con el método **where** de pandas para quedarnos sólo con los valores del triángulo superior de la matriz de correlación (el triángulo inferior contiene los mismos valores, con lo que no hace falta tenerlos en cuenta).
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool))
El método **where()** de pandas, reemplaza los valores de una variable donde la condición es Falso
Al final, tendremos una matriz parecida a ésta:
upper
{{ :clase:ia:saa:6_mejorar_datos:selection_041.png?400 |}}
Lo último será seleccionar aquellas columnas que contengan algún valor por encima del umbral dado:
to_drop = [column for column in upper.columns if any(upper[column] > umbral_corr)]
print(to_drop)
['flavanoids', 'od280/od315_of_diluted_wines']
Ahora ya podemos eliminar las características:
# Drop features
X_train = X_train.drop(columns = to_drop, axis=1)
X_test = X_test.drop(columns = to_drop, axis=1)
X_train.shape, X_test.shape
((124, 11), (54, 11))
En realidad, si un par de características están altamente correlacionadas, deberíamos borrar aquella que tenga una correlación más baja con nuestro target
No existe un umbral definido para borrar características correlacionadas. Si elegimos un umbral muy alto, podemos introducir información redundante al modelo, mientras que si elegimos un valor bajo, podemos perder información valiosa
===== Referencias =====
https://pharos.sh/aplicacion-de-metodos-de-filtrado-en-python-para-la-seleccion-de-caracteristicas/