Capítulo 2 Feature Engineering
2.1 Regresión polinomial
Es común encontrar en la literatura este tema junto con la regresión múltiple, sin embargo, el alcance de esta transformación va a más allá de la regresión lineal, por lo que estudiaremos este tema como parte de la ingeniería de variables y no como un modelo lineal exclusivamente.
El objetivo de la transformación polinomial dentro de la ingeniería de variables es crear nuevas variables que puedan explicar la relación entre la variable de respuesta y explicativa a través de un polinomio de grado k.
En el siguiente gráfico se representa mediante una línea roja a la regresión lineal y mediante una curva azul al polinomio que relaciona a las variables independiente y dependiente.
Es evidente que existe un mejor ajuste cuando se considera un polinomio de grado k en vez de la componente lineal. Este método ofrece mayor flexibilidad para el ajuste de un modelo. Será importante mediar posteriormente entre el sesgo y la varianza de forma que podamos tener un mejor ajuste sin caer en el sobreajuste. La fórmula que expresa la relación entre variable de respuesta y explicativas es ahora:
\[Y_i \sim X_1 + X_1^2\]
Es importante mencionar que cuando se ajusta un modelo polinomial de segundo orden, se deben mantener ambas variables en el model (la original y la cuadrática). Cuando se tenga un modelo polinomial de grado k, se deberán conservar los k elementos que componen el polinomio:
\[Y\sim X_1+X_1^2 + ... + X_1^k\]
Estas transformaciones son posibles realizarlas a múltiples variables que conforman el conjunto de datos que sirve de insumo para el modelo. A través de los pasos secuenciales en las recetas podemos integrar esta tarea a través de la función step_poly( )
. Veamos un ejemplo:
library(tidymodels)
= 100
n set.seed(12345)
<- tibble(x = seq(-10, 10, lengt=n)) %>%
original_data mutate(y = x^3 + rnorm(n, mean = 0, sd = 100))
<- recipe(y ~ x, data = original_data) %>%
poly_recipe step_poly(x, degree = 3) %>%
prep()
juice(poly_recipe)
## # A tibble: 100 × 4
## y x_poly_1 x_poly_2 x_poly_3
## <dbl> <dbl> <dbl> <dbl>
## 1 -941. -0.171 0.217 -0.249
## 2 -870. -0.168 0.204 -0.219
## 3 -895. -0.165 0.191 -0.190
## 4 -874. -0.161 0.178 -0.163
## 5 -716. -0.158 0.166 -0.137
## 6 -908. -0.154 0.154 -0.113
## 7 -616. -0.151 0.142 -0.0904
## 8 -661. -0.147 0.131 -0.0690
## 9 -618. -0.144 0.119 -0.0489
## 10 -640. -0.140 0.108 -0.0302
## # … with 90 more rows
juice(poly_recipe) %>% lm(y ~ x_poly_1 + x_poly_2 + x_poly_3, data = .)
##
## Call:
## lm(formula = y ~ x_poly_1 + x_poly_2 + x_poly_3, data = .)
##
## Coefficients:
## (Intercept) x_poly_1 x_poly_2 x_poly_3
## 24.52 3643.95 -34.12 1541.84
juice(poly_recipe) %>%
mutate(
x = seq(-10, 10, lengt=n),
y_est = 24.52 + 3643.95 * x_poly_1 -34.12 * x_poly_2 + 1541.84 * x_poly_3) %>%
ggplot(aes(x = x, y = y)) +
geom_point() +
geom_smooth(aes(y = y_est)) +
ggtitle("Regresión polinomial")
En el ejemplo anterior puede mostrarse que es a través de la ponderación de los términos polinomiales que se logra estimar a la variable de respuesta Y.
Este mismo procedimiento puede usarse en la receta de la ingeniería de datos para realizar la predicción de una variable de respuesta mediante cualquier otro algoritmo predictivo.
2.2 Análisis de Componentes Principales
El análisis PCA (por sus siglas en inglés) es una técnica de reducción de dimensión útil tanto para el proceso de análisis exploratorio, el inferencial y predictivo. Es una técnica ampliamente usada en muchos estudios, pues permite sintetizar la información relevante y desechar aquello que no aporta tanto. Es particularmente útil en el caso de conjuntos de datos “amplios” en donde las variables están correlacionadas entre sí y donde se tienen muchas variables para cada observación.
En los conjuntos de datos donde hay muchas variables presentes, no es fácil trazar los datos en su formato original, lo que dificulta tener una idea de las tendencias presentes en ellos. PCA permite ver la estructura general de los datos, identificando qué observaciones son similares entre sí y cuáles son diferentes. Esto puede permitirnos identificar grupos de muestras que son similares y determinar qué variables hacen a un grupo diferente de otro.
La idea detrás de esta técnica es la siguiente:
- Se desean crear nuevas variables llamadas Componentes Principales, las cuales son creadas como combinación lineal (transformación lineal) de las variables originales, por lo que cada una de las variables nuevas contiene parcialmente información de todas las variables originales.
\[Z_1 = a_{11}X_1 +a_{12}X_2 + ... + a_{1p}X_p\] \[Z_2 = a_{21}X_1 +a_{22}X_2 + ... + a_{2p}X_p\] \[...\] \[Z_p = a_{p1}X_1 +a_{p2}X_2 + ... + a_{pp}X_p\]
- Se desea que la primer componente principal capture la mayor varianza posible de todo el conjunto de datos.
\[\forall i \in 2,...,p \quad Var(Z_1)>Var(Z_i)\]
- La segunda componente principal deberá SER INDEPENDIENTE de la primera y deberá abarcar la mayor varianza posible del restante. Esta condición se debe cumplir para toda componente i, de tal forma que las nuevas componentes creadas son independientes entre sí y acumulan la mayor proporción de varianza en las primeras de ellas, dejando la mínima proporción de varianza a las últimas componentes.
\[Z_1 \perp\!\!\!\perp Z_2 \quad \& \quad Var(Z_1)>Var(Z_2)>Var(Z_i)\]
- El punto anterior permite desechar unas cuantas componentes (las últimas) sin perder mucha varianza.
¡¡ RECORDAR !!
A través de CPA se logra retener la mayor cantidad de varianza útil pero usando menos componentes que el número de variables originales.
Para que este proceso sea efectivo, debe existir ALTA correlación entre las variables originales.
Cuando muchas variables se correlacionan entre sí, todas contribuirán fuertemente al mismo componente principal. Cada componente principal suma un cierto porcentaje de la variación total en el conjunto de datos. Cuando sus variables iniciales estén fuertemente correlacionadas entre sí y podrá aproximar la mayor parte de la complejidad de su conjunto de datos con solo unos pocos componentes principales.
Agregar componentes adicionales hace que la estimación del conjunto de datos total sea más precisa, pero también más difícil de manejar.
2.2.1 Eigenvalores y eigenvectores
Los vectores propios y los valores propios vienen en pares: cada vector propio tiene un valor propio correspondiente. Los vectores propios son la ponderación que permite crear la combinación lineal de las variables para conformar cada componente principal, mientras que el valor propio es la varianza asociada a cada componente principal. Desde un punto de vista geométrico, el eigenvector es la dirección del vector determinado por la componente principal y el eigenvalor es la magnitud de dicho vector.
El valor propio de una componente es la varianza de este.
La suma acumulada de los primeros \(j\) eigenvalores representa la varianza acumulada de las primeras \(j\) componentes principales
El número de valores propios y vectores propios que existe es igual al número de dimensiones que tiene el conjunto de datos.
2.2.2 Implementación en R
library(sf)
library(magrittr)
library(tidymodels)
<- st_read('data/IMEF_2010.dbf', quiet = TRUE)
indice_marg glimpse(indice_marg)
## Rows: 32
## Columns: 16
## $ CVE_ENT <chr> "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "1…
## $ AÑO <int> 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 20…
## $ POB_TOT <int> 1184996, 3155070, 637026, 822441, 2748391, 650555, 4796580, 34…
## $ ANALF <dbl> 3.274040, 2.600783, 3.234464, 8.370643, 2.645050, 5.157943, 17…
## $ SPRIM <dbl> 14.754823, 12.987567, 14.273833, 22.541207, 12.168029, 18.4761…
## $ OVSDE <dbl> 1.0649743, 0.4322072, 0.9436751, 6.4196750, 1.0916308, 0.68577…
## $ OVSEE <dbl> 0.62347891, 0.94517891, 2.84464884, 2.59080046, 0.53707721, 0.…
## $ OVSAE <dbl> 0.9854257, 3.5616214, 7.0865085, 9.7378176, 1.3908497, 1.17060…
## $ VHAC <dbl> 30.33066, 29.05839, 31.73806, 45.96720, 30.26891, 31.32052, 53…
## $ OVPT <dbl> 1.761813, 3.398537, 5.814081, 4.500699, 1.423701, 4.691477, 15…
## $ PL_5000 <dbl> 25.1626166, 10.3491523, 15.6188287, 30.8755279, 12.1486353, 14…
## $ PO2SM <dbl> 33.64880, 21.86970, 23.29986, 45.51076, 30.04270, 32.04402, 69…
## $ IM <dbl> -0.91086057, -1.14014880, -0.68128749, 0.43357139, -1.14000448…
## $ GM <chr> "Bajo", "Muy bajo", "Bajo", "Alto", "Muy bajo", "Bajo", "Muy a…
## $ LUGAR <int> 28, 30, 23, 10, 29, 26, 2, 21, 32, 15, 14, 1, 6, 27, 22, 8, 19…
## $ NOM_ENT <chr> "Aguascalientes", "Baja California", "Baja California Sur", "C…
%>% dplyr::count(GM, sort = TRUE) indice_marg
## GM n
## 1 Medio 9
## 2 Alto 8
## 3 Bajo 8
## 4 Muy bajo 4
## 5 Muy alto 3
<- recipe(IM ~ ., data = indice_marg) %>%
pca_recipe update_role(NOM_ENT, GM, new_role = "id") %>%
step_normalize(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM) %>%
step_pca(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM, num_comp=9, res="res") %>%
step_rm(LUGAR, AÑO, POB_TOT) %>%
prep()
juice(pca_recipe)
## # A tibble: 32 × 13
## CVE_ENT GM NOM_ENT IM PC1 PC2 PC3 PC4 PC5 PC6
## <fct> <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 01 Bajo Aguasc… -0.911 -2.34 -0.227 0.372 0.492 0.264 0.0764
## 2 02 Muy b… Baja C… -1.14 -2.93 0.595 -0.0597 -0.492 0.291 -0.0508
## 3 03 Bajo Baja C… -0.681 -1.75 1.37 -0.683 -0.400 -0.304 0.160
## 4 04 Alto Campec… 0.434 1.12 -0.819 -0.151 -0.271 -0.929 0.178
## 5 05 Muy b… Coahui… -1.14 -2.93 -0.144 0.157 -0.133 0.0419 -0.0786
## 6 06 Bajo Colima -0.779 -2.00 0.0316 0.552 -0.136 0.320 -0.729
## 7 07 Muy a… Chiapas 2.32 5.96 0.132 1.36 -0.0122 -0.673 -0.471
## 8 08 Bajo Chihua… -0.520 -1.34 1.05 -1.27 0.633 -0.646 -0.387
## 9 09 Muy b… Distri… -1.48 -3.81 0.110 0.159 -0.453 0.205 -0.355
## 10 10 Medio Durango 0.0525 0.135 0.675 -1.50 0.929 -0.448 0.146
## # … with 22 more rows, and 3 more variables: PC7 <dbl>, PC8 <dbl>, PC9 <dbl>
Veamos los pasos de esta receta:
Primero, debemos decirle a la receta qué datos se usan para predecir la variable de respuesta.
Se actualiza el rol de las variables nombre de entidad y grado de marginación con la función
NOM_ENT
, ya que es una variable que queremos mantener por conveniencia como identificador de filas, pero no son un predictor ni variable de respuesta.Necesitamos centrar y escalar los predictores numéricos, porque estamos a punto de implementar PCA.
Finalmente, usamos
step_pca()
para realizar el análisis de componentes principales.La función
prep()
es la que realiza toda la preparación de la receta.
Una vez que hayamos hecho eso, podremos explorar los resultados del PCA. Comencemos por ver cómo resultó el PCA. Podemos ordenar los resultados mediante la función tidy()
, incluido el paso de PCA, que es el segundo paso. Luego hagamos una visualización para ver cómo se ven los componentes.
A continuación se muestran la desviación estándar, porcentaje de varianza y porcentaje de varianza acumulada que aporta cada componente principal.
summary(pca_recipe$steps[[2]]$res)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 2.572 0.82085 0.7920 0.64640 0.52101 0.44069 0.31797
## Proportion of Variance 0.735 0.07487 0.0697 0.04643 0.03016 0.02158 0.01123
## Cumulative Proportion 0.735 0.80990 0.8796 0.92603 0.95619 0.97777 0.98900
## PC8 PC9
## Standard deviation 0.25660 0.18201
## Proportion of Variance 0.00732 0.00368
## Cumulative Proportion 0.99632 1.00000
Podemos observar que en la primera componente principal, las \(9\) variables que utilizó el Consejo Nacional de Población para obtener el Índice de Marginación 2010 aportan de manera positiva en el primer componente principal.
library(tidytext)
<- tidy(pca_recipe, 2)
tidied_pca
%>%
tidied_pca filter(component %in% paste0("PC", 1:4)) %>%
group_by(component) %>%
top_n(9, abs(value)) %>%
ungroup() %>%
mutate(terms = reorder_within(terms, abs(value), component)) %>%
ggplot(aes(abs(value), terms, fill = value > 0)) +
geom_col() +
facet_wrap(~component, scales = "free_y") +
scale_y_reordered() +
labs(
x = "Absolute value of contribution",
y = NULL, fill = "Positive?"
+
)theme_minimal()
Notamos que las \(9\) variables aportan entre el \(25\%\) y el \(35\%\) a la primera componente principal.
2.2.3 Representación gráfica
library(ggrepel)
juice(pca_recipe) %>%
mutate(GM = factor(GM, levels = c("Muy alto", "Alto", "Medio", "Bajo", "Muy bajo")),
ordered = T) %>%
ggplot(aes(PC1, PC2, label = NOM_ENT)) +
geom_point(aes(color = GM), alpha = 0.7, size = 2) +
geom_text_repel() +
ggtitle("Grado de marginación de entidades")
Finalmente, podemos observar como (de izquierda a derecha) los estados con grado de marginación Muy bajo, Bajo, Medio, Alto y Muy Alto respectivamente.
juice(pca_recipe) %>%
ggplot(aes(x = IM, y = PC1)) +
geom_smooth(method = "lm") +
geom_point(size = 2) +
ggtitle("Comparación: Índice Marginación Vs PCA CP1")
## `geom_smooth()` using formula 'y ~ x'
2.2.4 ¿Cuántas componentes retener?
Existe en la literatura basta información sobre el número de componentes a retener en un análisis de PCA. El siguiente gráfico lleva por nombre gráfico de codo y muestra el porcentaje de varianza explicado por cada componente principal.
library(factoextra)
library(FactoMineR)
<- indice_marg %>%
res.pca select(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM) %>%
as.data.frame() %>%
set_rownames(indice_marg$NOM_ENT) %>%
PCA(graph=FALSE)
fviz_eig(res.pca, addlabels=TRUE, ylim=c(0, 100))
El grafico anterior muestra que hay una diferencia muy grande entre la varianza retenida por la 1er componente principal y el resto de las variables. Dependiendo del objetivo del analisis podra elegirse el numero adecuado de componentes a retener, no obstante, la literatura sugiere retener 1 o 2 componentes principales.
Regresando al tema de feature engineering, es posible realizar el proceso de componentes principales y elegir una de las dos opciones siguientes:
Especificar el número de componentes a retener
Indicar el porcentaje de varianza a alcanzar
La segunda opción elegirá tantas componentes como sean necesarias hasta alcanzar el hiperparámetro mínimo indicado. A continuación se ejemplifica:
Caso 1:
<- recipe(IM ~ ., data = indice_marg) %>%
pca_recipe update_role(NOM_ENT, GM, new_role = "id") %>%
step_normalize(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM) %>%
step_pca(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM,num_comp=2) %>%
step_rm(LUGAR, AÑO, POB_TOT) %>%
prep()
juice(pca_recipe)
## # A tibble: 32 × 6
## CVE_ENT GM NOM_ENT IM PC1 PC2
## <fct> <fct> <fct> <dbl> <dbl> <dbl>
## 1 01 Bajo Aguascalientes -0.911 -2.34 -0.227
## 2 02 Muy bajo Baja California -1.14 -2.93 0.595
## 3 03 Bajo Baja California Sur -0.681 -1.75 1.37
## 4 04 Alto Campeche 0.434 1.12 -0.819
## 5 05 Muy bajo Coahuila de Zaragoza -1.14 -2.93 -0.144
## 6 06 Bajo Colima -0.779 -2.00 0.0316
## 7 07 Muy alto Chiapas 2.32 5.96 0.132
## 8 08 Bajo Chihuahua -0.520 -1.34 1.05
## 9 09 Muy bajo Distrito Federal -1.48 -3.81 0.110
## 10 10 Medio Durango 0.0525 0.135 0.675
## # … with 22 more rows
Caso 2:
<- recipe(IM ~ ., data = indice_marg) %>%
pca_recipe update_role(NOM_ENT, GM, new_role = "id") %>%
step_normalize(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM) %>%
step_pca(ANALF, SPRIM, OVSDE, OVSEE, OVSAE, VHAC, OVPT, PL_5000, PO2SM,threshold=0.90) %>%
step_rm(LUGAR, AÑO, POB_TOT) %>%
prep()
juice(pca_recipe)
## # A tibble: 32 × 8
## CVE_ENT GM NOM_ENT IM PC1 PC2 PC3 PC4
## <fct> <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 01 Bajo Aguascalientes -0.911 -2.34 -0.227 0.372 0.492
## 2 02 Muy bajo Baja California -1.14 -2.93 0.595 -0.0597 -0.492
## 3 03 Bajo Baja California Sur -0.681 -1.75 1.37 -0.683 -0.400
## 4 04 Alto Campeche 0.434 1.12 -0.819 -0.151 -0.271
## 5 05 Muy bajo Coahuila de Zaragoza -1.14 -2.93 -0.144 0.157 -0.133
## 6 06 Bajo Colima -0.779 -2.00 0.0316 0.552 -0.136
## 7 07 Muy alto Chiapas 2.32 5.96 0.132 1.36 -0.0122
## 8 08 Bajo Chihuahua -0.520 -1.34 1.05 -1.27 0.633
## 9 09 Muy bajo Distrito Federal -1.48 -3.81 0.110 0.159 -0.453
## 10 10 Medio Durango 0.0525 0.135 0.675 -1.50 0.929
## # … with 22 more rows
Así es como usaremos el análisis de componentes principales para mejorar la estructura de variables que sirven de input para cualquiera de los modelos posteriores. Continuaremos con un paso más de pre-procesamiento antes de comenzar a aprender nuevos modelos.
2.3 Imputación KNN
Antes de aprender el uso de la función de imputación, recordaremos brevemente como funciona el algoritmo de K-Nearest-Neighbor (KNN)
KNN es un algoritmo de aprendizaje supervisado que podemos usar tanto para regresión como clasificación. Es un algoritmo fácil de interpretar y que permite ser flexible en el balance entre sesgo y varianza (dependiendo de los hiper-parámetros seleccionados).
El algoritmo de K vecinos más cercanos realiza comparaciones entre un nuevo elemento y las observaciones anteriores que ya cuentan con etiqueta. La esencia de este algoritmo está en etiquetar a un nuevo elemento de manera similar a como están etiquetados aquellos K elementos que más se le parecen. Veremos este proceso para cada uno de los posibles casos:
2.3.1 Ventajas y limitaciones del Clasificador KNN
Ventajas:
- KNN no hace ninguna suposición subyacente sobre los datos.
- Con la adición de más puntos de datos, el clasificador evoluciona constantemente y es capaz de adaptarse rápidamente a los cambios en el conjunto de datos de entrada.
- Le da al usuario la flexibilidad de elegir la métrica de medida de distancia.
Desventajas:
- KNN es muy sensible a los valores atípicos.
- No funciona con datos faltantes.
- A medida que crece el conjunto de datos, la clasificación se vuelve más lenta.
- Existe la llamada maldición de la dimensionalidad.
Este algoritmo es altamente usado para imputación de datos faltantes, ¿tiene lógica, cierto?, con recipes podemos aplicar un paso con la función: step_impute_knn antes llamada step_knnimpute(), podemos observar la documentación de la función en el siguiente enlace.
2.3.2 Implementación en R
Veámos cómo implementar la imputación por KNN para datos faltantes en R:
step_impute_knn(
recipe,### Variables a imputar
..., neighbors = 5,
impute_with = imp_vars(all_predictors()),
id = rand_id("impute_knn")
)
Acerca de los parámetros:
neighbors: Número de vecinos
impute_with: Una llamada a imp_vars para especificar qué variables se usan para imputar las variables. Si una columna se incluye en ambas listas para ser imputada y para ser un predictor de imputación, se eliminará de esta última y no se usará para imputarse a sí misma.
id: Una cadena de caracteres que es exclusiva de este paso para identificarlo.
La función utiliza el conjunto de entrenamiento para imputar cualquier otro conjunto de datos. La única función de distancia disponible es la distancia de Gower, que se puede utilizar para combinaciones de datos nominales y numéricos.
Acerca de Gower El coeficiente de similitud de Gower propuesto en 1971 permite la manipulación simultánea de variables cuantitativas y cualitativas en una base de datos, mediante la aplicación de este coeficiente se logra hallar la similitud entre individuos a los cuales se les han medido una serie de características en común. Una similaridad alta, es decir cercana a 1, indicara gran homogeneidad entre los individuos; por el contrario, una similaridad cercana a cero indica que los individuos son diferentes
Vamos a utilizar los datos de biomass de la libreria modeldata que contiene un conjunto de datos donde diferentes combustibles de biomasa se caracterizan por la cantidad de ciertas moléculas (carbono, hidrógeno, oxígeno, nitrógeno y azufre) y el poder calorífico superior correspondiente (HHV). En esta base hemos retirado valores aleatoriamente sobre dos variables para realizar el ejercicio.
library(recipes)
library(modeldata)
library(DataExplorer)
data(biomass)
<- as_tibble(biomass)
biomass_te_whole
# induce some missing data at random
set.seed(19735)
<- sample(1:nrow(biomass_te_whole), 75)
carb_missing <- sample(1:nrow(biomass_te_whole), 75)
nitro_missing
$carbon[carb_missing] <- NA
biomass_te_whole$nitrogen[nitro_missing] <- NA
biomass_te_whole"carb_imputed"] <- "No"
biomass_te_whole[$carb_imputed[carb_missing] <- "Yes"
biomass_te_whole"nitro_imputed"] <- "No"
biomass_te_whole[$nitro_imputed[nitro_missing] <- "Yes"
biomass_te_whole
<- biomass_te_whole %>% filter( dataset == 'Training')
biomass_tr <- biomass_te_whole %>% filter( dataset == "Testing")
biomass_te
biomass_te_whole[nitro_missing,]
## # A tibble: 75 × 10
## sample dataset carbon hydrogen oxygen nitrogen sulfur HHV carb_imputed
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 Red Alder … Traini… 49.6 6.06 43.8 NA 0.07 19.3 No
## 2 Cereals, H… Traini… 44.8 5 42.5 NA 0.13 18.4 No
## 3 Sewage Slu… Traini… 28.3 4.07 17.5 NA 1.25 12.1 No
## 4 Palm Fibre Testing 47.5 6.01 36.4 NA 0.3 19.2 No
## 5 Rice Straw Traini… 35.7 4.62 39.1 NA 0 14.8 No
## 6 Prune Pits Traini… 49.3 6.59 41.8 NA 0.07 20.1 No
## 7 Ryegrass S… Traini… 46.7 5.8 41.9 NA 0.2 18.5 No
## 8 Peanut Hul… Traini… 45.8 5.46 39.6 NA 0.12 18.6 No
## 9 Cereals, H… Traini… NA 4.9 43.1 NA 0.12 18.2 Yes
## 10 Erco Char Traini… 65.9 2.57 12.8 NA 0.1 24.2 No
## # … with 65 more rows, and 1 more variable: nitro_imputed <chr>
%>% DataExplorer::plot_missing( title = "Train" ) biomass_tr
%>% DataExplorer::plot_missing( title = "Test" ) biomass_te
En la primera opción vamos a aplicar el paso de immputación indicando qué columnas quieren ser imputadas y con cuáles variables queremos que se haga el proceso.
<- recipe(
recipe_esp ~ carbon + hydrogen + oxygen + nitrogen + sulfur,
HHV data = biomass_tr) %>%
step_impute_knn(
carbon, nitrogen,impute_with= imp_vars(hydrogen, oxygen),
neighbors = 3) %>%
prep()
<- bake(recipe_esp, biomass_tr)
imputed_esp_train <- bake(recipe_esp, biomass_te) ## Probando la receta con un test
imputed_esp_test
%>% DataExplorer::plot_missing( title = "Imputacion Train") imputed_esp_train
%>% DataExplorer::plot_missing(title = "Imputacion Test") imputed_esp_test
Sin embargo, siempre podemos pedirle al modelo que haga la imputación de todas las variables que tengan nulos, con toda la información de las demás variables disponibles (no nulas).
<- recipe(
recipe ~ carbon + hydrogen + oxygen + nitrogen + sulfur,
HHV data = biomass_tr) %>%
step_impute_knn(all_predictors(), neighbors = 3) %>%
prep()
<- bake(recipe, biomass_te) %>%
imputed bind_cols(biomass_te %>% select(carb_imputed, nitro_imputed)) # prueba con test
%>% DataExplorer::plot_missing() imputed
%>%
biomass filter(dataset == 'Testing') %>%
select(nitrogen) %>%
bind_cols(imputed[,c(4, 8)]) %>%
ggplot(aes(x = `nitrogen...2`, y = `nitrogen...1`, color = nitro_imputed)) +
geom_point() +
ggtitle("Comparación de datos reales vs imputados (Nitrógeno)") +
xlab("Imputado") +
ylab("Real")
%>%
biomass filter(dataset == 'Testing') %>%
select(carbon) %>%
bind_cols(imputed[,c(1, 7)]) %>%
ggplot(aes(x = `carbon...2`, y = `carbon...1`, color = carb_imputed)) +
geom_point() +
ggtitle("Comparación de datos reales vs imputados (Carbono)") +
xlab("Imputado") +
ylab("Real")
2.4 Ejercicios
Cada equipo estará a cargo de desarrollar una receta de feature engineering utilizando los pasos vistos en el curso pasado y el actual.
Tendrán 15 días para probar distintas estrategias que mejoren las predicciones del precio de ventas.
Se deberá entregar y explicar el código creado por el equipo. Este código servirá para los ejercicios de optimización de modelos en los siguientes capítulos.