Feature engineering
Esto es la adición, tranformación o la eliminación de datos.
Code
pkgs = c ('dplyr' , 'ggplot2' , 'visdat' , 'caret' , 'recipes' , 'AmesHousing' , 'rsample' )
inst = lapply (pkgs, library, character.only = TRUE )
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
Loading required package: lattice
Attaching package: 'recipes'
The following object is masked from 'package:stats':
step
Target engineering
Para la regresión lineal se asume que los errores de predicción (y la variable respuesta) se distribuyan normal
Code
ames = make_ames ()
split = initial_split (ames, prop = 0.7 ,
strat = 'Sale_Price' )
ames_train = training (split)
ames_test = testing (split)
Por ejemplo la variable sale_price,
Code
ggplot (data = ames, aes (x = Sale_Price)) +
geom_histogram ()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Tenemos una distribución skewed a la derecha, no se distribuye normal, hacemos una transformación logartimica y obtenemos,
Code
ggplot (data = ames, aes (x = log (Sale_Price))) +
geom_histogram ()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Ahora parece distribuirse de una forma normal.
Para corregir sesgo a la derecha, se puede hacer lo siguiente:
Opción 1: normalizar con una transformación logartimica (como se hizo antes)
Code
respuesta_trans = log (ames$ Sale_Price)
Se puede usar un ‘plano’ para poder repalicarlo después,
Code
ames_recipe = recipe (Sale_Price ~ ., data = ames_train) %>%
step_log (all_outcomes ())
ames_recipe
── Recipe ──────────────────────────────────────────────────────────────────────
Number of variables by role
• Log transformation on: all_outcomes()
Si la variable respuesta tiene ceros o valores negativos no podemos hacer esta transformación, podemos usar log1p() (suma un 1 a todas las respuestas, funciona si la variable está entre - 0.99 y 0), si los valores son menores a -1 se usa la transformación Yeo-Johnson.
Code
Warning in log(-0.5): Se han producido NaNs
Code
Opcion 2: Usar la transformación de Box cox
\[
y(\lambda) =
\begin{cases}
\frac{Y^{\lambda}-1}{\lambda}, &\text{if } \lambda \neq 0 \\
\log(Y), & \text{if } \lambda = 0
\end{cases}
\] El valor óptimo de \(\lambda\) se estima de los datos de entrenamiento, este mismo lambda se aplica a los datos de entrenamiento y prueba
Si la variable respuesta es negativa se puede usar step_YeoJohnson().
Después del proceso se debería hacer la inversda para que los datos sean interpretables.
Code
y = forecast:: BoxCox (10 , lambda = 'auto' )
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
Code
[1] 9
attr(,"lambda")
[1] 1
Code
inv_box_cox = function (x, lambda) {
if (lambda == 0 ) exp (x) else (lambda * x + 1 )^ (1 / lambda)
}
inv_box_cox (y, lambda = 1 )
[1] 10
attr(,"lambda")
[1] 1
Manejando datos perdidos
Perdidos informativos: Implica una causa estrutural para los datos perdidos, se podrían reemplazar por una categoría en la variable.
Perdidos aleatorios: Valores perdidos independiente de la recolección de datos, se podrían borrar.
Visualizando los valores perdidos
Code
sum (is.na (AmesHousing:: ames_raw))
Code
AmesHousing:: ames_raw %>%
is.na () %>%
reshape2:: melt () %>%
ggplot (aes (Var2, Var1, fill = value)) +
geom_raster () +
coord_flip () +
scale_y_continuous (NULL , expand = c (0 , 0 )) +
scale_fill_grey (name = '' ,
labels = c ('Present' ,
'Missing' )) +
xlab ('Observación' ) +
theme (axis.text.y = element_text (size = 4 ))
La variable Garage_cars y Garage_area contiene el valor 0 mientras la variable Garage_xx tiene muchos NA, puede ser porque no había forma de identificar casas sin garages cuando se recolectaron los datos, estos son datos perdidos informativos, valdría la pena crear una categoría ‘None’ en esa variable.
Code
AmesHousing:: ames_raw %>%
filter (is.na (` Garage Type ` )) %>%
select (` Garage Type ` , ` Garage Cars ` , ` Garage Area ` )
# A tibble: 157 × 3
`Garage Type` `Garage Cars` `Garage Area`
<chr> <int> <int>
1 <NA> 0 0
2 <NA> 0 0
3 <NA> 0 0
4 <NA> 0 0
5 <NA> 0 0
6 <NA> 0 0
7 <NA> 0 0
8 <NA> 0 0
9 <NA> 0 0
10 <NA> 0 0
# ℹ 147 more rows
Code
vis_miss (AmesHousing:: ames_raw, cluster = TRUE )
Imputación
El proceso de reemplazar valores perdidos.
La imputación debe hacerse dentro del proceso de remuestreo.
Para imputar datos con la mediana:
Code
ames_recipe %>%
step_impute_median (Gr_Liv_Area)
── Recipe ──────────────────────────────────────────────────────────────────────
Number of variables by role
• Log transformation on: all_outcomes()
• Median imputation for: Gr_Liv_Area
Para imputar con la moda:
Code
ames_recipe %>%
step_impute_mode (Gr_Liv_Area)
── Recipe ──────────────────────────────────────────────────────────────────────
Number of variables by role
• Log transformation on: all_outcomes()
• Mode imputation for: Gr_Liv_Area
Se pueden usar estos pero no son los más eficientes por obvias razones, modelos mas complejos se pueden usar:
KNN: Identifica otras observaciones lo mas parecidas y con estos vecinos mas cercanos estima los valores perdidos.
El número de vecinos se puede cambiar, lo recomendable para imputación es 5 y 10, por defecto se toman 5
Code
ames_recipe %>%
step_impute_knn (all_predictors (), neighbors = 6 )
── Recipe ──────────────────────────────────────────────────────────────────────
Number of variables by role
• Log transformation on: all_outcomes()
• K-nearest neighbor imputation for: all_predictors()
Tree based: Se identifican las observaciones faltantes y la variable que lo contiene es tratada como la variable respuesta y se predice usando bagged decision trees.
Code
ames_recipe %>%
step_impute_bag (all_predictors ())
── Recipe ──────────────────────────────────────────────────────────────────────
Number of variables by role
• Log transformation on: all_outcomes()
• Bagged tree imputation for: all_predictors()
Filtrado de variables
Se deben eliminar las variables con varianza cero.
Se detecta cuando la variable tiene una fraccion de valores unicos sobre la muestra de menos de 10%
El radio de la frecuencia del valor mas prevalente a la frecuencia del segundo mas prevalente es grande, mas del 20%
Si ambas caracteristicas se cumplen es mejor quitar la variable por varianza cero.
Code
caret:: nearZeroVar (ames_train, saveMetrics = TRUE ) %>%
tibble:: rownames_to_column () %>%
filter (nzv)
rowname freqRatio percentUnique zeroVar nzv
1 Street 203.90000 0.09760859 FALSE TRUE
2 Alley 23.69136 0.14641288 FALSE TRUE
3 Land_Contour 21.64706 0.19521718 FALSE TRUE
4 Utilities 2047.00000 0.14641288 FALSE TRUE
5 Land_Slope 24.14815 0.14641288 FALSE TRUE
6 Condition_2 202.60000 0.34163006 FALSE TRUE
7 Roof_Matl 144.50000 0.34163006 FALSE TRUE
8 Bsmt_Cond 21.58824 0.29282577 FALSE TRUE
9 BsmtFin_Type_2 23.03947 0.34163006 FALSE TRUE
10 BsmtFin_SF_2 451.75000 9.80966325 FALSE TRUE
11 Heating 118.64706 0.29282577 FALSE TRUE
12 Low_Qual_Fin_SF 1009.50000 1.41532455 FALSE TRUE
13 Kitchen_AbvGr 23.93902 0.19521718 FALSE TRUE
14 Functional 38.04000 0.39043436 FALSE TRUE
15 Enclosed_Porch 90.73684 7.32064422 FALSE TRUE
16 Three_season_porch 1011.00000 1.22010737 FALSE TRUE
17 Screen_Porch 187.30000 4.92923377 FALSE TRUE
18 Pool_Area 2039.00000 0.53684724 FALSE TRUE
19 Pool_QC 509.75000 0.24402147 FALSE TRUE
20 Misc_Feature 30.40000 0.29282577 FALSE TRUE
21 Misc_Val 141.28571 1.31771596 FALSE TRUE
Aqui ninguna cumple las dos condiciones pero 20 variables están cerca de la varianza cero.