1.- BIBLIOTECAS

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.3     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(h2o)
## 
## ----------------------------------------------------------------------
## 
## Your next step is to start H2O:
##     > h2o.init()
## 
## For H2O package documentation, ask for help:
##     > ??h2o
## 
## After starting H2O, you can use the Web UI at http://localhost:54321
## For more information visit https://docs.h2o.ai
## 
## ----------------------------------------------------------------------
## 
## 
## Attaching package: 'h2o'
## 
## The following objects are masked from 'package:lubridate':
## 
##     day, hour, month, week, year
## 
## The following objects are masked from 'package:stats':
## 
##     cor, sd, var
## 
## The following objects are masked from 'package:base':
## 
##     %*%, %in%, &&, ||, apply, as.factor, as.numeric, colnames,
##     colnames<-, ifelse, is.character, is.factor, is.numeric, log,
##     log10, log1p, log2, round, signif, trunc
library(DALEX)
## Welcome to DALEX (version: 2.4.3).
## Find examples and detailed introduction at: http://ema.drwhy.ai/
## 
## 
## Attaching package: 'DALEX'
## 
## The following object is masked from 'package:dplyr':
## 
##     explain
library(DALEXtra)
library(iml)
library(skimr)
library(DataExplorer)
library(DataExplorer)
library(ggpubr)
library(univariateML)
library(recipes)
## 
## Attaching package: 'recipes'
## 
## The following object is masked from 'package:stringr':
## 
##     fixed
## 
## The following object is masked from 'package:stats':
## 
##     step

2.- DATOS

l set de datos rent, disponible en el paquete gamlss.data, contiene información sobre el precio del alquiler de 1969 viviendas situadas en Munich en el año 1993. Además del precio, incluye 9 variables adicionales:

R: precio del alquiler.

Fl: metros cuadrados de la vivienda.

A: año de construcción.

B: si tiene cuarto de baño (1) o no (0).

H: si tiene calefacción central (1) o no (0).

L: si la cocina está equipada (1) o no (0).

Sp: si la calidad del barrio donde está situada la vivienda es superior la media (1) o no (0).

Sm: si la calidad del barrio donde está situada la vivienda es inferior la media (1) o no (0).

loc: combinación de Sp y Sm indicando si la calidad del barrio donde está situada la vivienda es inferior (1), igual (2) o superior (3) a la media.

data("rent", package = "gamlss.data")
datos <- rent
str(datos)
## 'data.frame':    1969 obs. of  9 variables:
##  $ R  : num  693 422 737 732 1295 ...
##  $ Fl : num  50 54 70 50 55 59 46 94 93 65 ...
##  $ A  : num  1972 1972 1972 1972 1893 ...
##  $ Sp : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Sm : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ B  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ H  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 2 1 1 1 ...
##  $ L  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ loc: Factor w/ 3 levels "1","2","3": 2 2 2 2 2 2 2 2 2 2 ...
# Se descartan las variables SP y SM, ya que su información está recogida en la variable loc.
datos <- datos[, !(names(datos) %in% c("Sp", "Sm"))]
str(datos)
## 'data.frame':    1969 obs. of  7 variables:
##  $ R  : num  693 422 737 732 1295 ...
##  $ Fl : num  50 54 70 50 55 59 46 94 93 65 ...
##  $ A  : num  1972 1972 1972 1972 1893 ...
##  $ B  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ H  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 2 1 1 1 ...
##  $ L  : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ loc: Factor w/ 3 levels "1","2","3": 2 2 2 2 2 2 2 2 2 2 ...
# Se renombran las columnas para que sean más descriptivas.
colnames(datos) <- c("precio", "metros", "anyo", "banyo", "calefaccion", "cocina", "situacion")
str(datos)
## 'data.frame':    1969 obs. of  7 variables:
##  $ precio     : num  693 422 737 732 1295 ...
##  $ metros     : num  50 54 70 50 55 59 46 94 93 65 ...
##  $ anyo       : num  1972 1972 1972 1972 1893 ...
##  $ banyo      : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ calefaccion: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 2 1 1 1 ...
##  $ cocina     : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
##  $ situacion  : Factor w/ 3 levels "1","2","3": 2 2 2 2 2 2 2 2 2 2 ...

3.- EDA

skim(datos)
Data summary
Name datos
Number of rows 1969
Number of columns 7
_______________________
Column type frequency:
factor 4
numeric 3
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
banyo 0 1 FALSE 2 0: 1925, 1: 44
calefaccion 0 1 FALSE 2 0: 1580, 1: 389
cocina 0 1 FALSE 2 0: 1808, 1: 161
situacion 0 1 FALSE 3 2: 1247, 3: 550, 1: 172

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
precio 0 1 811.88 379.00 101.7 544.2 737.8 1022 3000 ▇▇▂▁▁
metros 0 1 67.73 20.86 30.0 52.0 67.0 82 120 ▆▇▇▅▂
anyo 0 1 1948.48 29.02 1890.0 1934.0 1957.0 1972 1988 ▃▁▃▇▇
head(datos)
##   precio metros anyo banyo calefaccion cocina situacion
## 1  693.3     50 1972     0           0      0         2
## 2  422.0     54 1972     0           0      0         2
## 3  736.6     70 1972     0           0      0         2
## 4  732.2     50 1972     0           0      0         2
## 5 1295.1     55 1893     0           0      0         2
## 6 1195.9     59 1893     0           0      0         2

4.- Datos ausentes

# Número de datos ausentes por variable
datos %>% map_dbl(.f = function(x){sum(is.na(x))})
##      precio      metros        anyo       banyo calefaccion      cocina 
##           0           0           0           0           0           0 
##   situacion 
##           0
plot_missing(
  data = datos,
  title = "Porcentaje de valores ausentes",
  ggtheme = theme_bw(),
  theme_config = list(legend.position = "bottom")
)

5.- Variable respuesta

p1 <- ggplot(data = datos, aes(x = precio)) +
      geom_density(fill = "steelblue", alpha = 0.8) +
      geom_rug(alpha = 0.1) +
      scale_x_continuous(labels = scales::comma) +
      labs(title = "Distribución original", x = "gastos_totales") +
      theme_bw()

p2 <- ggplot(data = datos, aes(x = sqrt(precio))) +
      geom_density(fill = "steelblue", alpha = 0.8) +
      geom_rug(alpha = 0.1) +
      scale_x_continuous(labels = scales::comma) +
      labs(title = "Transformación raíz cuadrada", x = "gastos_totales") +
      theme_bw()

p3 <- ggplot(data = datos, aes(x = log10(precio))) +
      geom_density(fill = "steelblue", alpha = 0.8) +
      geom_rug(alpha = 0.1) +
      scale_x_continuous(labels = scales::comma) +
      labs(title = "Transformación logarítmica") +
      theme_bw()

p4 <- ggplot(data = datos, aes(x = log(precio))) +
      geom_density(fill = "steelblue", alpha = 0.8) +
      geom_rug(alpha = 0.1) +
      scale_x_continuous(labels = scales::comma) +
      labs(title = "Transformación logarítmica") +
      theme_bw()

ggarrange(p1, p2, p3, p4, ncol = 1, align = "v")

# Tabla de estadísticos principales
summary(datos$precio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   101.7   544.2   737.8   811.9  1022.0  3000.0

Algunos modelos de machine learning y aprendizaje estadístico requieren que la variable respuesta se distribuya de una forma determinada. Por ejemplo: para los modelos de regresión lineal (LM), la distribución tiene que ser de tipo normal. Para los modelos lineales generalizados (GLM), la distribución tiene que ser de la familia exponencial. Existen varios paquetes en R que permiten identificar a qué distribución se ajustan mejor los datos, uno de ellos es univariateML. Para conocer más sobre cómo identificar distribuciones consultar Ajuste de distribuciones con R.

comparacion_aic <- AIC(
                    mlbetapr(datos$precio),
                    mlexp(datos$precio),
                    mlinvgamma(datos$precio),
                    mlgamma(datos$precio),
                    mllnorm(datos$precio),
                    mlrayleigh(datos$precio),
                    mlinvgauss(datos$precio),
                    mlweibull(datos$precio),
                    mlinvweibull(datos$precio),
                    mllgamma(datos$precio)
                   )
comparacion_aic %>% rownames_to_column(var = "distribucion") %>% arrange(AIC)
##                  distribucion df      AIC
## 1       mlgamma(datos$precio)  2 28615.58
## 2       mllnorm(datos$precio)  2 28657.23
## 3    mlinvgauss(datos$precio)  2 28690.03
## 4     mlweibull(datos$precio)  2 28746.25
## 5    mlrayleigh(datos$precio)  1 28797.71
## 6      mlbetapr(datos$precio)  2 28867.94
## 7    mlinvgamma(datos$precio)  2 28869.14
## 8  mlinvweibull(datos$precio)  2 29245.12
## 9         mlexp(datos$precio)  1 30322.05
## 10     mllgamma(datos$precio)  2      Inf

6.- Variables continuas

plot_density(
  data = datos %>% select(-precio),
  ncol= 3,
  title = "Distribución variables continuas",
  ggtheme = theme_bw(),
  theme_config = list(
                  plot.title = element_text(size = 16, face = "bold"),
                  strip.text = element_text(colour = "black", size = 12, face = 2)
  )
)

custom_corr_plot <- function(variable1, variable2, df, alpha=0.3){
  p <- df %>%
       mutate(
         # Truco para que se ponga el título estilo facet
        title = paste(toupper(variable2), "vs", toupper(variable1))
       ) %>%
       ggplot(aes(x = !!sym(variable1), y = !!sym(variable2))) + 
       geom_point(alpha = alpha) +
       # Tendencia no lineal
       geom_smooth(se = FALSE, method = "gam", formula =  y ~ splines::bs(x, 3)) +
       # Tendencia lineal
       geom_smooth(se = FALSE, method = "lm", color = "firebrick") +
       facet_grid(. ~ title) +
       theme_bw() +
       theme(strip.text = element_text(colour = "black", size = 10, face = 2),
             axis.title = element_blank())
  return(p)
}
variables_continuas <- c("anyo", "metros")

plots <- map(
            .x = variables_continuas,
            .f = custom_corr_plot,
            variable2 = "precio",
            df = datos
         )

ggarrange(plotlist = plots, ncol = 2, nrow = 1) %>%
  annotate_figure(
    top = text_grob("Correlación con el precio de alquiler", face = "bold", size = 16,
                    x = 0.4)
  )
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'

plot_correlation(
  data     = datos,
  cor_args = list(use = "pairwise.complete.obs"),
  type     = "continuous",
  title    = "Matriz de correlación variables continuas",
  theme_config = list(legend.position = "none",
                      plot.title = element_text(size = 16, face = "bold"),
                      axis.title = element_blank(),
                      axis.text.x = element_text(angle = -45, hjust = +0.1)
                     )
)

GGally::ggscatmat(
  data = datos %>% select_if(is.numeric),
  alpha = 0.1) +
theme_bw() +
labs(title = "Correlación por pares") +
theme(
  plot.title = element_text(size = 16, face = "bold"),
  #axis.text = element_blank(),
  strip.text = element_text(colour = "black", size = 10, face = 2)
)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2

plot_bar( 
    datos, 
    ncol = 2, 
    title = "Número de observaciones por grupo", 
    ggtheme = theme_bw(), 
    theme_config = list( 
                      plot.title = element_text(size = 16, face = "bold"), 
                      strip.text = element_text(colour = "black", size = 12, face = 2),
                      legend.position = "none" 
                      ) 
)

custom_box_plot <- function(variable1, variable2, df, alpha=0.3){
  p <- df %>%
       mutate(
         # Truco para que se ponga el título estilo facet
        title = paste(toupper(variable2), "vs", toupper(variable1))
       ) %>%
       ggplot(aes(x = !!sym(variable1), y = !!sym(variable2))) + 
       geom_violin(alpha = alpha) +
       geom_boxplot(width = 0.1, outlier.shape = NA) +
       facet_grid(. ~ title) +
       theme_bw() +
       theme(strip.text = element_text(colour = "black", size = 10, face = 2),
             axis.title = element_blank())
  return(p)
}
variables_cualitativas <- c("banyo", "calefaccion", "cocina", "situacion")

plots <- map(
            .x = variables_cualitativas,
            .f = custom_box_plot,
            variable2 = "precio",
            df = datos
         )

ggarrange(plotlist = plots, ncol = 2, nrow = 2) %>%
  annotate_figure(
    top = text_grob("Correlación con precio", face = "bold", size = 16,
                    x = 0.28)
  )

Si alguno de los niveles de una variable cualitativa tiene muy pocas observaciones en comparación a los otros niveles, puede ocurrir que, durante la validación cruzada o bootstrapping, algunas particiones no contengan ninguna observación de dicha clase (varianza cero), lo que puede dar lugar a errores. En este caso hay que tener precaución con la variable banyo.

table(datos$banyo)
## 
##    0    1 
## 1925   44

7.- Modelos

# Creación de un cluster local con todos los cores disponibles.
h2o.init(ip = "localhost",
         # -1 indica que se empleen todos los cores disponibles.
         nthreads = -1,
         # Máxima memoria disponible para el cluster.
         max_mem_size = "6g")
##  Connection successful!
## 
## R is connected to the H2O cluster: 
##     H2O cluster uptime:         27 minutes 37 seconds 
##     H2O cluster timezone:       Europe/Paris 
##     H2O data parsing timezone:  UTC 
##     H2O cluster version:        3.42.0.2 
##     H2O cluster version age:    4 months and 27 days 
##     H2O cluster name:           H2O_started_from_R_Usuario_hwc583 
##     H2O cluster total nodes:    1 
##     H2O cluster total memory:   4.81 GB 
##     H2O cluster total cores:    20 
##     H2O cluster allowed cores:  20 
##     H2O cluster healthy:        TRUE 
##     H2O Connection ip:          localhost 
##     H2O Connection port:        54321 
##     H2O Connection proxy:       NA 
##     H2O Internal Security:      FALSE 
##     R Version:                  R version 4.3.2 (2023-10-31 ucrt)
## Warning in h2o.clusterInfo(): 
## Your H2O cluster version is (4 months and 27 days) old. There may be a newer version available.
## Please download and install the latest version from: https://h2o-release.s3.amazonaws.com/h2o/latest_stable.html
# Se eliminan los datos del cluster por si ya había sido iniciado.
h2o.removeAll()
h2o.no_progress()
datos_h2o <- as.h2o(x = datos, destination_frame = "datos_h2o")

7.1.- TRAIN Y TEST SPLIT

set.seed(123)
particiones <- h2o.splitFrame(data=datos_h2o, ratios = c(0.8), seed = 123)
datos_train_h2o <- h2o.assign(data = particiones[[1]], key = "datos_train_h2o")
datos_test_h2o <- h2o.assign(data = particiones[[2]], key = "datos_test_h2o")
datos_train <- as.data.frame(datos_train_h2o)
datos_test <- as.data.frame(datos_test_h2o)
summary(datos_train$precio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   101.7   544.7   741.6   813.9  1026.5  3000.0
summary(datos_test$precio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   127.1   537.8   718.6   804.0  1000.0  2764.5
summary(datos_train$banyo)/nrow(datos_train)*100
##         0         1 
## 97.963081  2.036919
summary(datos_test$banyo)/nrow(datos_test)*100
##         0         1 
## 96.984925  3.015075

7.2.- MODELO GLM

Entrenamiento

# Se define la variable respuesta y los predictores.
var_respuesta <- "precio"
# Para este modelo se emplean todos los predictores disponibles.
predictores <- setdiff(h2o.colnames(datos_train_h2o), var_respuesta)

Optimización de hiperparámetros

# Valores de alpha que se van a comparar.
hiperparametros <- list(alpha = seq(0,1,0.1))
grid_glm <- h2o.grid(
    # Algoritmo y parámetros
    algorithm      = "glm",
    family         = "gamma",
    # Variable respuesta y predictores
    y              = var_respuesta,
    x              = predictores, 
    # Datos de entrenamiento
    training_frame = datos_train_h2o,
    # Preprocesado
    standardize    = TRUE,
    missing_values_handling = "Skip",
    ignore_const_cols = TRUE,
    # Hiperparámetros
    hyper_params    = hiperparametros,
    # Tipo de búsqueda
    search_criteria = list(strategy = "Cartesian"),
    lambda_search   = TRUE,
    # Selección automática del solver adecuado
    solver          = "AUTO",
    # Estrategia de validación para seleccionar el mejor modelo
    seed            = 123,
    nfolds          = 3,
    keep_cross_validation_predictions = TRUE,
    grid_id         = "grid_glm"
)
## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 12!
## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!

## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 71!
## Warning in h2o.getGrid(grid_id = grid_id): Adding alpha array to hyperparameter
## runs slower with gridsearch. This is due to the fact that the algo has to run
## initialization for every alpha value. Setting the alpha array as a model
## parameter will skip the initialization and run faster overall.
# Se muestran los modelos ordenados de mayor a menor rmse
resultados_grid_glm <- h2o.getGrid(
                         grid_id = "grid_glm",
                         sort_by = "rmse",
                         decreasing = FALSE
                       )
## Warning in h2o.getGrid(grid_id = "grid_glm", sort_by = "rmse", decreasing =
## FALSE): Adding alpha array to hyperparameter runs slower with gridsearch. This
## is due to the fact that the algo has to run initialization for every alpha
## value. Setting the alpha array as a model parameter will skip the
## initialization and run faster overall.
as.data.frame(resultados_grid_glm@summary_table)
##                  alpha         model_ids     rmse
## 1                  0.1  grid_glm_model_2 310.2497
## 2                  0.2  grid_glm_model_3 310.2498
## 3  0.30000000000000004  grid_glm_model_4 310.2498
## 4                  0.4  grid_glm_model_5 310.2499
## 5                  0.5  grid_glm_model_6 310.2499
## 6   0.6000000000000001  grid_glm_model_7 310.2499
## 7   0.7000000000000001  grid_glm_model_8 310.2499
## 8                  0.8  grid_glm_model_9 310.2499
## 9                  0.9 grid_glm_model_10 310.2499
## 10                 1.0 grid_glm_model_11 310.2499
## 11                 0.0  grid_glm_model_1 310.3075
# Se reentrena el modelo con los mejores hiperparámetros
mejores_hiperparam <- h2o.getModel(resultados_grid_glm@model_ids[[1]])@parameters

modelo_glm <- h2o.glm(
    # Variable respuesta y predictores
    y              = var_respuesta,
    x              = predictores,
    # Datos de entrenamiento
    training_frame = datos_train_h2o,
    # Preprocesado
    standardize    = TRUE,
    missing_values_handling = "Skip",
    ignore_const_cols = TRUE,
    # Hiperparámetros
    alpha = mejores_hiperparam$alpha,
    lambda_search   = TRUE,
    # Selección automática del solver adecuado
    solver          = "AUTO",
    # Estrategia de validación (para comparar con otros modelos)
    seed            = 123,
    nfolds          = 10,
    keep_cross_validation_predictions = TRUE,
    model_id        = "modelo_glm"
)
## Warning in doTryCatch(return(expr), name, parentenv, handler): Reached maximum
## number of iterations 3!

Influencia de predictores

h2o.varimp(modelo_glm)
## Variable Importances: 
##         variable relative_importance scaled_importance percentage
## 1         metros            0.010826          1.000000   1.000000
## 2    situacion.1            0.000000          0.000000   0.000000
## 3    situacion.2            0.000000          0.000000   0.000000
## 4    situacion.3            0.000000          0.000000   0.000000
## 5  calefaccion.0            0.000000          0.000000   0.000000
## 6  calefaccion.1            0.000000          0.000000   0.000000
## 7       cocina.0            0.000000          0.000000   0.000000
## 8       cocina.1            0.000000          0.000000   0.000000
## 9        banyo.0            0.000000          0.000000   0.000000
## 10       banyo.1            0.000000          0.000000   0.000000
## 11          anyo            0.000000          0.000000   0.000000
h2o.varimp_plot(modelo_glm)

Diagnosis de los residuos

explainer_glm <- DALEXtra::explain_h2o(
                  model = modelo_glm,
                  data  = datos_train[, predictores],
                  y     = datos_train[[var_respuesta]],
                  label = "modelo_glm"
                )
## Preparation of a new explainer is initiated
##   -> model label       :  modelo_glm 
##   -> data              :  1571  rows  6  cols 
##   -> target variable   :  1571  values 
##   -> predict function  :  yhat.H2ORegressionModel  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package h2o , ver. 3.42.0.2 , task regression (  default  ) 
##   -> predicted values  :  numerical, min =  813.8608 , mean =  813.8803 , max =  813.9076  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  -712.1723 , mean =  2.96012e-13 , max =  2186.092  
##   A new explainer has been created!
plot(model_performance(explainer_glm))

predicciones_train <- h2o.predict(
                        modelo_glm,
                        newdata = datos_train_h2o
                      )
predicciones_train <- h2o.cbind(
                       datos_train_h2o["precio"],
                       predicciones_train
                      )
predicciones_train <- as.data.frame(predicciones_train)
predicciones_train <- predicciones_train %>%
                      mutate(
                        residuo = predict - precio
                      )
p1 <- ggplot(predicciones_train, aes(x = precio, y  = predict)) +
      geom_point(alpha = 0.1) +
      geom_smooth(method = "gam", color = "red", size = 1, se = TRUE) +
      labs(title = "Predicciones vs valor real") +
      theme_bw()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
p2 <- ggplot(predicciones_train, aes(1:nrow(predicciones_train), y  = residuo)) +
      geom_point(alpha = 0.1) +
      geom_hline(yintercept = 0, color = "red", size = 1) +
      labs(title = "Residuos del modelo") +
      theme_bw()
p3 <- ggplot(predicciones_train, aes(x  = residuo)) +
      geom_density() +
      geom_rug() +
      labs(title = "Residuos del modelo") +
      theme_bw()

p4 <- ggplot(predicciones_train, aes(sample  = predict)) +
      stat_qq() +
      stat_qq_line(color = "red", size = 1) +
      labs(title = "QQ-plot residuos del modelo") +
      theme_bw()

ggpubr::ggarrange(p1, p2, p3, p4, ncol = 2, nrow = 2) %>%
ggpubr::annotate_figure(
  top = ggpubr::text_grob("Diagnóstico residuos entrenamiento",
                          color = "black", face = "bold", size = 14)
)
## `geom_smooth()` using formula = 'y ~ s(x, bs = "cs")'

predicciones_test <- h2o.predict(
                        object  = modelo_glm,
                        newdata = datos_test_h2o
                     )
predicciones_test     <- as.vector(predicciones_test)
datos_test$prediccion <- predicciones_test

Error de test

rmse_test_glm <- MLmetrics::RMSE(
                    y_pred = datos_test$precio,
                    y_true = datos_test$prediccion
                 )
paste("Error de test (rmse) del modelo GLM:", rmse_test_glm)
## [1] "Error de test (rmse) del modelo GLM: 378.70675549663"

Escritura del modelo

# Se guarda el modelo en el directorio actual en la carpeta modelos
h2o.saveModel(object = modelo_glm, path = "./modelos", force = TRUE)
## [1] "C:\\Users\\Usuario\\OneDrive\\Documentos\\modelos\\modelo_glm"
file.rename(file.path("./modelos", modelo_glm@model_id), "./modelos/modelo_glm.h2o")
## Warning in file.rename(file.path("./modelos", modelo_glm@model_id),
## "./modelos/modelo_glm.h2o"): cannot rename file './modelos/modelo_glm' to
## './modelos/modelo_glm.h2o', reason 'El sistema no puede encontrar la ruta
## especificada'
## [1] FALSE