Preprocesamiento
Preprocesamiento
Preprocesamiento
En muchos casos los conjuntos de datos que recopilamos por ejemplo a través de una
encuesta en línea, presentan ausencia o inconsistencia de algunos datos siendo las causas de
esto muy diversas: Pueden ir desde incompletitud al momento de suministrar la información,
en el caso de una encuesta, o hasta por un error al momento de la captura de los datos. Este
hecho si no se atiende debidamente puede ocasionar errores o resultados no deseados en los
modelos de aprendizaje automático, de allí la importancia de esta etapa en los procesos de
aprendizaje automático.
En todos los algoritmos que vamos a comenzar a estudiar desde ahora, el modo de operar
después de efectivamente haber cargado el dataset, es el siguiente: 1) instanciamos un objeto
de alguna clase de interés con los argumentos respectivos; 2) con el objeto instanciado se
invoca al método fit(), el cual se ajusta con los datos del subconjunto de entrenamiento y
estima valores para los parámetros internos del modelo; 3) luego se llama al método
transform() con el cual se llevan a cabo las transformaciones requeridas sobre los datos de
entrada. Estas transformaciones pueden ser por ejemplo escalado de características,
imputación de valores ausentes, etc. Scikit-learn nos ofrece un método fit_transform() para
efectuar estas dos tareas de ajustar y transformar el objeto en un solo paso.
Cabe anotar, que el método fit() además de usarse con algoritmos de preprocesamiento
también se puede emplear con los algoritmos típicos de machine learning.
Con la biblioteca Scikit-learn esto puede ser logrado a través de la función train_test_split()
del submódulo model_selection.
import pandas as pd
df = pd.read_csv("precios_casas.csv")
X = df.iloc[:,1:].values
y = df.iloc[:,0].values
print(X)
La función train_test_split() recibe como parámetros los conjuntos X e y junto con el tamaño
del conjunto de prueba (test_size=0.2), es decir 20%, devolviendo los siguientes 4 arreglos de
numpy:
pruX: Conjunto de prueba con los atributos descriptivos: 920 filas y 16 columnas.
Puede usar un valor diferente para hacer la separación como 90%-10%, pero las más usadas
son 80%-20% y 70%-30%, en todo caso, se debe tener en cuenta siempre la cantidad de datos
disponibles, dado que por ejemplo en un dataset de millones de registros quizas un 99%-1%
podría ser una buena medida de separación.
Es muy común que después de recopilar datos para realizar una actividad de aprendizaje
automático nos demos cuenta que faltan algunos de ellos. Esta es una situación a la cual hay
que prestarle mucha atención debido a que la ausencia de datos puede provocar problemas
a los algoritmos utilizados, dado que estos últimos son muy susceptibles a los faltantes a tal
punto que les hacen producir resultados inesperados. La ausencia de un valor en Python se
representa con un NaN (Not a Number).
Para observar esta situación vamos a crear un conjunto de datos nuevo, ya que el de los
precios de las casas viene por defecto debidamente preprocesado y no tiene valores faltantes.
Esto lo puede comprobar escribiendo la siguiente instrucción, esta nos devuelve la suma de
valores ausentes por cada columna:
df.isnull().sum()
df = pd.DataFrame([
['1', 1, 30],
['2', 1, 32],
['3', 0]],
df.columns = ['codigo','credito','edad']
Como se puede apreciar falta un valor en la columna edad de la última fila. Para paliar esta
situación existen dos maneras posibles: La primera es eliminar del conjunto aquellas filas o
columnas donde se encuentren los valores ausentes, mediante la función dropna(), que en su
forma básica permite:
➢ Eliminar las columnas donde hay valores ausentes en alguna fila, así:
df.dropna(axis=1)
Aquí, axis=0 se refiere a filas y axis=1 a columnas.
la función dropna() maneja otros parámetros interesantes que permiten establecer cierto
control sobre el borrado de filas o columnas: how = {any, all}, con any borra si hay al menos
un valor NaN, en cambio con all hace el borrado si todos los valores son NaN; thresh=valor,
donde valor es un número que funciona como umbral con el cual se establece el número
máximo de NaN que debe superarse para realizar el borrado;
subset=[lista_nombres_columnas] que permite borrar las filas siempre y cuando las columnas
cuyos nombres se indican en la lista tengan NaN.
Sin embargo, este enfoque no se recomienda demasiado puesto que, se pueden perder datos
en demasía o datos que podrían resultar útiles para el entrenamiento del algoritmo de
aprendizaje automático. Por lo tanto, el mejor método en este sentido es el conocido como
imputación de valores ausentes, encontrándose accesible su implementación en Scikit-learn
a través de la clase SimpleImputer del submódulo sklearn.imputer. Al instanciar un objeto, en
el constructor se puede especificar una estrategia, como la de imputación por medias
(strategy=’mean’), la cual consiste en reemplazar el valor ausente con la media aritmética de
toda la columna. Otras opciones para strategy son ‘median’ para sustituir el valor que falta
por la mediana, ‘most-frequent’ para reemplazar por el valor más frecuente (la moda de los
datos) y ‘constant’ para cambiar por un valor fijo o constante.
Otro punto importante a considerar, es que hay filas que tienen columnas con algunos
caracteres especiales como por ejemplo símbolos de interrogación (?), entonces en estos
casos antes de realizar la tarea de imputación se recomienda primero reemplazar el simbolo
por un NaN mediante la función replace().
Una vez se haya realizado un reemplazo como el anterior se realiza la imputación utilizando
el siguiente fragmento de código:
from sklearn.impute import SimpleImputer
imp = imp.fit(df.values)
imp_datos = imp.transform(df.values)
Los algoritmos de aprendizaje automático están diseñados para operar con datos numéricos,
por lo cual se recomienda convertir a números todos aquellos datos categóricos. En este
sentido, algo estrictamente necesario es codificar la etiqueta de clase del conjunto de datos a
un valor numérico. Esto se consigue con la clase LabelEncoder. Miremos como hacer esto
tomando en consideración el siguiente dataframe:
df = pd.DataFrame([
)
df.columns = ['nombre','edad','color', 'etiqueta']
le_clase = LabelEncoder()
y = le_clase.fit_transform(df.etiqueta)
print(y)
Donde y sería un vector con los valores [0 1 0], que significa que “Clase 1” se codificó como 0
mientras que “Clase 2” como 1 resultado del ajuste y la transformación realizada con el
método fit_transform(). Desde luego, es posible obtener la representación original ['Clase 1'
'Clase 2' 'Clase 1'], mediante el método inverse_transform():
clase_inv = le_clase.inverse_transform(y)
print(clase_inv)
No obstante, la técnica anterior no es la más adecuada, ya que, si por ejemplo tenemos una
variable llamada profesión con los valores ingeniero, arquitecto y contador, codificada como
1,2 y 3 respectivamente, un algoritmo de aprendizaje podría inferir que contador es mayor a
arquitecto o que arquitecto es mayor que ingeniero, lo cual no es correcto puesto que no se
trata de una variable categórica ordinal.
Para mejorar esta situación hay otra técnica muy utilizada denominada one hot encoding (Una
codificación en caliente), con ella un atributo categórico nominal de n valores posibles
generará n columnas binarias, donde se coloca un 1 en la columna coincidente con el valor
que tiene la instancia para ese atributo y el resto de columnas referentes a ese mismo atributo
se colocan en 0. Por ejemplo, supongamos un conjunto de datos de vehículos con un atributo
color, con tres posibles valores: amarillo, azul y rojo. Entonces el atributo color=azul de una
instancia cualquiera se representaría como: 0,1,0.
Para que la codificación en caliente funcione la entrada recibida debe ser numérica, por lo que
usaremos la clase LabelEncoder vista hace un momento para realizar esta transformación.
Creamos un objeto de la mencionada clase y codificamos la columna color a un consecutivo
comenzando desde 0, cuyos valores los guardaremos en una nueva columna del dataframe
llamada color_cod.
le_color = LabelEncoder()
ohe_color = OneHotEncoder(categories='auto')
df['color_cod'] = le_color.fit_transform(df.color)
print(df)
print(df)
Finalmente podemos eliminar las columnas color y color_cod del dataframe porque
sencillamente ya no las necesitamos, mediante la siguiente sentencia:
Existe otra función llamada get_dummies() de la librería Pandas que también convierte
variables categóricas a variables indicadoras (o dummies) y es mucho más apropiada cuando
tienen una gran cantidad de valores.
d = {'M': 1, 'F':0}
df['genero'] = df['genero'].map(d)
df
1.5 ESCALAMIENTO DE CARACTERISTICAS
El escalamiento es otra de las tareas importantes dentro del preprocesado de datos, debido a
que la gran mayoría de los algoritmos de aprendizaje automático funcionan mejor si se tienen
las características de un conjunto de datos bajo la misma escala. Adicionalmente, es necesario
usar la misma escala en el conjunto de entrenamiento y de prueba a fin de no terminar
provocando un sesgo aleatorio en los datos.
Como ejemplo escalaremos el atributo precio del dataframe de los precios de las casas en un
rango entre 0 y 1, lo haremos usando la técnica de la normalización que le aplicaremos a la
variable por medio de la siguiente función:
𝑥𝑖 − 𝑥𝑚𝑖𝑛
𝑥𝑛𝑜𝑟𝑚𝑎𝑙𝑖𝑧𝑎𝑑𝑎 =
x𝑚𝑎𝑥 − x𝑚𝑖𝑛
Donde, 𝑥𝑖 corresponde a los precios de la columna price, 𝑥𝑚𝑖𝑛 es el valor más pequeño y x𝑚𝑎𝑥
es el valor más grande de la misma. La anterior ecuación la podemos programar en Python de
forma sencilla aprovechando la vectorización, característica para mejorar el rendimiento
computacional evitando en la medida de lo posible el uso de ciclos en la realización de
operaciones con vectores:
def normalizar(columna):
return x_norma
Sin embargo, no tendremos que preocuparnos por implementar la función anterior puesto
que ya está implementada en la clase MinMaxScaler de Scikit-learn. Considere el fragmento
de código siguiente:
import pandas as pd
df = pd.read_csv("precios_casas.csv")
esc = MinMaxScaler()
X_ent = esc.fit_transform(df.price.values.reshape(-1,1))
print(X_ent)
def estandarizar(columna):
return x_estandar
Pero, de igual forma como sucede con la normalización, Scikit-learn dispone de una clase
específica para estandarizar características, y es la clase StandardScaler. Retomemos el
ejemplo anterior para estandarizar la variable precio.
import pandas as pd
df = pd.read_csv("precios_casas.csv")
est = StandardScaler()
X_ent = est.fit_transform(df.price.values.reshape(-1,1))
print(X_ent)
Cabe mencionar que las técnicas de procesamiento explicadas anteriormente, tal y como se
expresó al principio necesitan recibir como entrada datos numéricos para operar.