Machine learning R

Author

Mateo Vega

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 ──────────────────────────────────────────────────────────────────────
── Inputs 
Number of variables by role
outcome:    1
predictor: 80
── Operations 
• 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
log(-0.5)
Warning in log(-0.5): Se han producido NaNs
[1] NaN
Code
log1p(-0.5)
[1] -0.6931472
  • 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
y
[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))
[1] 13997
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 ──────────────────────────────────────────────────────────────────────
── Inputs 
Number of variables by role
outcome:    1
predictor: 80
── Operations 
• 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 ──────────────────────────────────────────────────────────────────────
── Inputs 
Number of variables by role
outcome:    1
predictor: 80
── Operations 
• 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 ──────────────────────────────────────────────────────────────────────
── Inputs 
Number of variables by role
outcome:    1
predictor: 80
── Operations 
• 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 ──────────────────────────────────────────────────────────────────────
── Inputs 
Number of variables by role
outcome:    1
predictor: 80
── Operations 
• 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.