Nombre: Francisco Gonzalez
Carnet: 24002914
library(dplyr)
##
## 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
library(readr)
library(ggplot2)
library(corrplot)
## corrplot 0.95 loaded
library(tidyr)
# 1. Cargar los datos
df <- read_csv("train.csv")
## Rows: 14447 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ocean_proximity
## dbl (10): id, longitude, latitude, housing_median_age, total_rooms, total_be...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# 2. Descripción general del dataset
cat("Dimensiones del dataset:\n")
## Dimensiones del dataset:
dim(df)
## [1] 14447 11
cat("\nPrimeras filas:\n")
##
## Primeras filas:
print(head(df))
## # A tibble: 6 × 11
## id longitude latitude housing_median_age total_rooms total_bedrooms
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 9744 -122. 36.8 15 2191 358
## 2 13893 -116. 34.1 37 452 109
## 3 18277 -122. 37.3 35 1172 184
## 4 16176 -122. 37.7 52 126 24
## 5 8843 -118. 34.1 28 4001 1352
## 6 7653 -118. 33.8 28 2152 415
## # ℹ 5 more variables: population <dbl>, households <dbl>, median_income <dbl>,
## # median_house_value <dbl>, ocean_proximity <chr>
cat("\nResumen estadístico:\n")
##
## Resumen estadístico:
print(summary(df))
## id longitude latitude housing_median_age
## Min. : 1 Min. :-124.3 Min. :32.54 Min. : 1.00
## 1st Qu.: 5140 1st Qu.:-121.8 1st Qu.:33.93 1st Qu.:18.00
## Median :10210 Median :-118.5 Median :34.26 Median :29.00
## Mean :10275 Mean :-119.6 Mean :35.64 Mean :28.85
## 3rd Qu.:15449 3rd Qu.:-118.0 3rd Qu.:37.72 3rd Qu.:37.00
## Max. :20640 Max. :-114.3 Max. :41.95 Max. :52.00
##
## total_rooms total_bedrooms population households
## Min. : 2 Min. : 1.0 Min. : 6 Min. : 1.0
## 1st Qu.: 1444 1st Qu.: 295.0 1st Qu.: 786 1st Qu.: 280.0
## Median : 2121 Median : 433.0 Median : 1163 Median : 408.0
## Mean : 2635 Mean : 537.8 Mean : 1425 Mean : 500.1
## 3rd Qu.: 3138 3rd Qu.: 647.0 3rd Qu.: 1722 3rd Qu.: 604.5
## Max. :39320 Max. :6445.0 Max. :28566 Max. :6082.0
## NA's :137
## median_income median_house_value ocean_proximity
## Min. : 0.4999 Min. : 14999 Length:14447
## 1st Qu.: 2.5671 1st Qu.:119600 Class :character
## Median : 3.5350 Median :179700 Mode :character
## Mean : 3.8639 Mean :206874
## 3rd Qu.: 4.7229 3rd Qu.:264600
## Max. :15.0001 Max. :500001
##
cat("\nTipos de datos:\n")
##
## Tipos de datos:
str(df)
## spc_tbl_ [14,447 × 11] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:14447] 9744 13893 18277 16176 8843 ...
## $ longitude : num [1:14447] -122 -116 -122 -122 -118 ...
## $ latitude : num [1:14447] 36.8 34.1 37.3 37.7 34.1 ...
## $ housing_median_age: num [1:14447] 15 37 35 52 28 28 23 40 18 52 ...
## $ total_rooms : num [1:14447] 2191 452 1172 126 4001 ...
## $ total_bedrooms : num [1:14447] 358 109 184 24 1352 ...
## $ population : num [1:14447] 1150 184 512 37 1799 ...
## $ households : num [1:14447] 330 59 175 27 1220 429 1130 110 439 53 ...
## $ median_income : num [1:14447] 4.8 3.73 7.36 10.23 2.58 ...
## $ median_house_value: num [1:14447] 227500 65800 500001 225000 272900 ...
## $ ocean_proximity : chr [1:14447] "<1H OCEAN" "INLAND" "<1H OCEAN" "NEAR BAY" ...
## - attr(*, "spec")=
## .. cols(
## .. id = col_double(),
## .. longitude = col_double(),
## .. latitude = col_double(),
## .. housing_median_age = col_double(),
## .. total_rooms = col_double(),
## .. total_bedrooms = col_double(),
## .. population = col_double(),
## .. households = col_double(),
## .. median_income = col_double(),
## .. median_house_value = col_double(),
## .. ocean_proximity = col_character()
## .. )
## - attr(*, "problems")=<externalptr>
# 3. Revisión de valores nulos
cat("\nConteo de valores NA por variable:\n")
##
## Conteo de valores NA por variable:
print(colSums(is.na(df)))
## id longitude latitude housing_median_age
## 0 0 0 0
## total_rooms total_bedrooms population households
## 0 137 0 0
## median_income median_house_value ocean_proximity
## 0 0 0
# 4. Distribución de variables numéricas
num_vars <- df %>% select(where(is.numeric)) %>% select(-id)
for (col in names(num_vars)) {
print(
ggplot(df, aes_string(x = col)) +
geom_histogram(bins = 30, fill = "steelblue", color = "white") +
labs(title = paste("Histograma de", col), x = col, y = "Frecuencia")
)
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 137 rows containing non-finite outside the scale range
## (`stat_bin()`).
# 5. Distribución de variable categórica (ocean_proximity)
print(
ggplot(df, aes(x = ocean_proximity)) +
geom_bar(fill = "coral") +
labs(title = "Conteo por categoría: ocean_proximity", x = "ocean_proximity", y = "Frecuencia") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
)
# 6. Correlación entre variables numéricas
corr <- cor(num_vars, use = "complete.obs")
print(corrplot(corr, method = "color", tl.cex = 0.7, number.cex = 0.7))
## $corr
## longitude latitude housing_median_age total_rooms
## longitude 1.00000000 -0.925276861 -0.108724857 0.04308386
## latitude -0.92527686 1.000000000 0.009975766 -0.03415305
## housing_median_age -0.10872486 0.009975766 1.000000000 -0.36043960
## total_rooms 0.04308386 -0.034153051 -0.360439596 1.00000000
## total_bedrooms 0.06747626 -0.064580439 -0.322601658 0.93081258
## population 0.10004100 -0.106998172 -0.303472841 0.87057074
## households 0.05586774 -0.069709284 -0.305525485 0.92130168
## median_income -0.01342858 -0.081630247 -0.112757577 0.20178604
## median_house_value -0.04848349 -0.143927728 0.112052285 0.13692854
## total_bedrooms population households median_income
## longitude 0.0674762647 0.10004100 0.05586774 -0.0134285771
## latitude -0.0645804386 -0.10699817 -0.06970928 -0.0816302471
## housing_median_age -0.3226016579 -0.30347284 -0.30552549 -0.1127575767
## total_rooms 0.9308125825 0.87057074 0.92130168 0.2017860449
## total_bedrooms 1.0000000000 0.89006702 0.98176657 -0.0006637225
## population 0.8900670221 1.00000000 0.91589957 0.0120161690
## households 0.9817665656 0.91589957 1.00000000 0.0190061583
## median_income -0.0006637225 0.01201617 0.01900616 1.0000000000
## median_house_value 0.0564098147 -0.01988276 0.07063649 0.6876749992
## median_house_value
## longitude -0.04848349
## latitude -0.14392773
## housing_median_age 0.11205229
## total_rooms 0.13692854
## total_bedrooms 0.05640981
## population -0.01988276
## households 0.07063649
## median_income 0.68767500
## median_house_value 1.00000000
##
## $corrPos
## xName yName x y corr
## 1 longitude longitude 1 9 1.0000000000
## 2 longitude latitude 1 8 -0.9252768614
## 3 longitude housing_median_age 1 7 -0.1087248574
## 4 longitude total_rooms 1 6 0.0430838641
## 5 longitude total_bedrooms 1 5 0.0674762647
## 6 longitude population 1 4 0.1000409986
## 7 longitude households 1 3 0.0558677414
## 8 longitude median_income 1 2 -0.0134285771
## 9 longitude median_house_value 1 1 -0.0484834914
## 10 latitude longitude 2 9 -0.9252768614
## 11 latitude latitude 2 8 1.0000000000
## 12 latitude housing_median_age 2 7 0.0099757660
## 13 latitude total_rooms 2 6 -0.0341530507
## 14 latitude total_bedrooms 2 5 -0.0645804386
## 15 latitude population 2 4 -0.1069981718
## 16 latitude households 2 3 -0.0697092844
## 17 latitude median_income 2 2 -0.0816302471
## 18 latitude median_house_value 2 1 -0.1439277280
## 19 housing_median_age longitude 3 9 -0.1087248574
## 20 housing_median_age latitude 3 8 0.0099757660
## 21 housing_median_age housing_median_age 3 7 1.0000000000
## 22 housing_median_age total_rooms 3 6 -0.3604395957
## 23 housing_median_age total_bedrooms 3 5 -0.3226016579
## 24 housing_median_age population 3 4 -0.3034728414
## 25 housing_median_age households 3 3 -0.3055254853
## 26 housing_median_age median_income 3 2 -0.1127575767
## 27 housing_median_age median_house_value 3 1 0.1120522851
## 28 total_rooms longitude 4 9 0.0430838641
## 29 total_rooms latitude 4 8 -0.0341530507
## 30 total_rooms housing_median_age 4 7 -0.3604395957
## 31 total_rooms total_rooms 4 6 1.0000000000
## 32 total_rooms total_bedrooms 4 5 0.9308125825
## 33 total_rooms population 4 4 0.8705707393
## 34 total_rooms households 4 3 0.9213016832
## 35 total_rooms median_income 4 2 0.2017860449
## 36 total_rooms median_house_value 4 1 0.1369285409
## 37 total_bedrooms longitude 5 9 0.0674762647
## 38 total_bedrooms latitude 5 8 -0.0645804386
## 39 total_bedrooms housing_median_age 5 7 -0.3226016579
## 40 total_bedrooms total_rooms 5 6 0.9308125825
## 41 total_bedrooms total_bedrooms 5 5 1.0000000000
## 42 total_bedrooms population 5 4 0.8900670221
## 43 total_bedrooms households 5 3 0.9817665656
## 44 total_bedrooms median_income 5 2 -0.0006637225
## 45 total_bedrooms median_house_value 5 1 0.0564098147
## 46 population longitude 6 9 0.1000409986
## 47 population latitude 6 8 -0.1069981718
## 48 population housing_median_age 6 7 -0.3034728414
## 49 population total_rooms 6 6 0.8705707393
## 50 population total_bedrooms 6 5 0.8900670221
## 51 population population 6 4 1.0000000000
## 52 population households 6 3 0.9158995656
## 53 population median_income 6 2 0.0120161690
## 54 population median_house_value 6 1 -0.0198827622
## 55 households longitude 7 9 0.0558677414
## 56 households latitude 7 8 -0.0697092844
## 57 households housing_median_age 7 7 -0.3055254853
## 58 households total_rooms 7 6 0.9213016832
## 59 households total_bedrooms 7 5 0.9817665656
## 60 households population 7 4 0.9158995656
## 61 households households 7 3 1.0000000000
## 62 households median_income 7 2 0.0190061583
## 63 households median_house_value 7 1 0.0706364872
## 64 median_income longitude 8 9 -0.0134285771
## 65 median_income latitude 8 8 -0.0816302471
## 66 median_income housing_median_age 8 7 -0.1127575767
## 67 median_income total_rooms 8 6 0.2017860449
## 68 median_income total_bedrooms 8 5 -0.0006637225
## 69 median_income population 8 4 0.0120161690
## 70 median_income households 8 3 0.0190061583
## 71 median_income median_income 8 2 1.0000000000
## 72 median_income median_house_value 8 1 0.6876749992
## 73 median_house_value longitude 9 9 -0.0484834914
## 74 median_house_value latitude 9 8 -0.1439277280
## 75 median_house_value housing_median_age 9 7 0.1120522851
## 76 median_house_value total_rooms 9 6 0.1369285409
## 77 median_house_value total_bedrooms 9 5 0.0564098147
## 78 median_house_value population 9 4 -0.0198827622
## 79 median_house_value households 9 3 0.0706364872
## 80 median_house_value median_income 9 2 0.6876749992
## 81 median_house_value median_house_value 9 1 1.0000000000
##
## $arg
## $arg$type
## [1] "full"
# 7. Boxplots de precio de vivienda vs. proximidad al océano
print(
ggplot(df, aes(x = ocean_proximity, y = median_house_value)) +
geom_boxplot(fill = "lightgreen") +
labs(title = "Distribución de precio de vivienda por proximidad al océano",
x = "ocean_proximity", y = "median_house_value") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
)
# 8. Gráfico de dispersión: median_income vs. median_house_value
print(
ggplot(df, aes(x = median_income, y = median_house_value, color = ocean_proximity)) +
geom_point(alpha = 0.6) +
labs(title = "Ingreso medio vs. precio de vivienda",
x = "median_income", y = "median_house_value")
)
# 9. Mapa de localización: latitude vs longitude coloreado por precio de vivienda
library(ggplot2) # Por si no está cargado
library(ggmap) # Por si no está cargado
## ℹ Google's Terms of Service: <https://mapsplatform.google.com>
## Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service>
## OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
# Puedes usar el centro geográfico (centro de tus datos)
# Definir el centro de tus datos
centro <- c(lon = median(df$longitude), lat = median(df$latitude)+median(df$latitude)*.10)
ggmap::register_google(key = Sys.getenv("GOOGLE_API_KEY"))
ggmap::has_google_key()
## [1] TRUE
map <- get_googlemap(
center = centro,
zoom = 6,
size = c(640, 640),
scale = 1,
maptype = "terrain"
)
## ℹ <https://maps.googleapis.com/maps/api/staticmap?center=37.686,-118.5&zoom=6&size=640x640&scale=1&maptype=terrain&key=xxx-8Pzey8_I5kAHk>
# Crear un dataframe solo con las filas completas para las columnas relevantes
df_mapa <- df %>%
filter(!is.na(longitude), !is.na(latitude), !is.na(median_house_value))
# Usar este dataframe limpio en tu gráfico
ggmap(map) +
geom_point(
data = df_mapa,
aes(x = longitude, y = latitude, color = median_house_value),
alpha = 0.7, size = 2
) +
scale_color_viridis_c() +
labs(
title = "Valor medio de vivienda por ubicación geográfica en California",
x = "Longitud", y = "Latitud",
color = "Median House Value"
) +
theme(legend.position = "right")
1. Boxplot: Precio de vivienda según proximidad al océano
Los precios más altos de vivienda están en las
categorías ISLAND, NEAR BAY y
NEAR OCEAN.
La categoría INLAND tiene los precios más
bajos y además más dispersión (muchos valores bajos, algunos
altos como outliers).
Vivir cerca de la costa o la bahía está claramente asociado a un mayor valor de las viviendas.
2. Dispersión: Ingreso medio vs. precio de vivienda
Hay una fuerte correlación positiva: a mayor ingreso medio, mayor valor medio de la vivienda.
Existe un “tope” aparente en el precio de la vivienda (muchos puntos pegados en $500,000), lo que indica un capping (probablemente un límite superior en el dataset original).
Las viviendas cerca del océano y bahía suelen estar asociadas tanto a mayores ingresos como a mayores valores de vivienda.
3. Mapa geográfico: Precio de vivienda en California
Las áreas costeras, especialmente el área de la bahía de San Francisco y zonas de Los Ángeles, muestran valores de vivienda mucho más altos (zonas amarillas y verdes claros).
El interior del estado (INLAND) tiene predominantemente valores bajos (púrpuras y azules).
Patrón geográfico claro: el precio aumenta al acercarse al océano y áreas urbanas principales.
4. Matriz de correlación
La mayor correlación positiva es entre ingreso medio y valor de vivienda.
Otras variables con alta correlación son total_rooms, households, population, pero con menos impacto directo en el precio.
Latitude y longitude muestran cierta relación geográfica pero no directa con el precio (son proxies de ubicación).
Conclusiones generales
La variable más predictiva para el valor de la vivienda es el ingreso medio del área.
La proximidad al océano y bahías tiene un gran impacto positivo en el precio de la vivienda.
Existe un límite superior artificial en el valor de la vivienda, que puede distorsionar modelos predictivos si no se considera.
El patrón espacial es clave: las zonas urbanas costeras concentran los precios más altos.
Las variables relacionadas con tamaño de la vivienda y número de hogares/población tienen relación entre sí pero menor impacto directo en el precio.
library(tidymodels)
## ── Attaching packages ────────────────────────────────────── tidymodels 1.3.0 ──
## ✔ broom 1.0.8 ✔ rsample 1.3.0
## ✔ dials 1.4.0 ✔ tibble 3.2.1
## ✔ infer 1.0.8 ✔ tune 1.3.0
## ✔ modeldata 1.4.0 ✔ workflows 1.2.0
## ✔ parsnip 1.3.2 ✔ workflowsets 1.1.1
## ✔ purrr 1.0.4 ✔ yardstick 1.3.2
## ✔ recipes 1.3.0
## ── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
## ✖ purrr::discard() masks scales::discard()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ yardstick::spec() masks readr::spec()
## ✖ recipes::step() masks stats::step()
train <- df
# 2. Dividir en entrenamiento (80%) y prueba (20%)
set.seed(42)
split <- initial_split(train, prop = 0.8, strata = median_house_value)
train_split <- training(split)
test_split <- testing(split)
# 3. Preprocesamiento
receta <- recipe(median_house_value ~ ., data = train_split) %>%
update_role(id, new_role = "ID") %>%
step_impute_median(all_numeric_predictors()) %>% # 1. Imputar primero
step_impute_mode(all_nominal_predictors()) %>%
step_mutate_at(
all_numeric_predictors(), # 2. Winsorización
fn = ~scales::squish(., quantile(., 0.01, na.rm = TRUE), quantile(., 0.99))
) %>%
step_dummy(all_nominal_predictors()) %>% # 3. Dummies después
step_zv(all_predictors())
# 3. Preparar receta en train, aplicar en test
prep_receta <- prep(receta, training = train_split)
train_prep <- bake(prep_receta, new_data = train_split)
test_prep <- bake(prep_receta, new_data = test_split)
colSums(is.na(train_prep))
## id longitude
## 0 0
## latitude housing_median_age
## 0 0
## total_rooms total_bedrooms
## 0 0
## population households
## 0 0
## median_income median_house_value
## 0 0
## ocean_proximity_INLAND ocean_proximity_ISLAND
## 0 0
## ocean_proximity_NEAR.BAY ocean_proximity_NEAR.OCEAN
## 0 0
colSums(is.na(test_prep))
## id longitude
## 0 0
## latitude housing_median_age
## 0 0
## total_rooms total_bedrooms
## 0 0
## population households
## 0 0
## median_income median_house_value
## 0 0
## ocean_proximity_INLAND ocean_proximity_ISLAND
## 0 0
## ocean_proximity_NEAR.BAY ocean_proximity_NEAR.OCEAN
## 0 0
# 4. Especificación de modelos
# a) Regresión Lineal
modelo_lm <- linear_reg() %>% set_engine("lm")
# b) Árbol de Decisión
modelo_tree <- decision_tree() %>%
set_engine("rpart") %>%
set_mode("regression")
# c) Random Forest
modelo_rf <- rand_forest(trees = 500) %>%
set_engine("ranger", importance = "impurity") %>%
set_mode("regression")
# 5. Entrenamiento
# Regresión Lineal
ajuste_lm <- fit(modelo_lm, median_house_value ~ ., data = train_prep)
pred_lm <- predict(ajuste_lm, new_data = test_prep) %>%
bind_cols(test_prep)
rmse_lm <- yardstick::rmse(pred_lm, truth = median_house_value, estimate = .pred)
# Árbol de decisión
ajuste_tree <- fit(modelo_tree, median_house_value ~ ., data = train_prep)
pred_tree <- predict(ajuste_tree, new_data = test_prep) %>%
bind_cols(test_prep)
rmse_tree <- yardstick::rmse(pred_tree, truth = median_house_value, estimate = .pred)
# Random Forest
ajuste_rf <- fit(modelo_rf, median_house_value ~ ., data = train_prep)
pred_rf <- predict(ajuste_rf, new_data = test_prep) %>%
bind_cols(test_prep)
rmse_rf <- yardstick::rmse(pred_rf, truth = median_house_value, estimate = .pred)
# Comparar resultados
resultados <- tibble(
Modelo = c("Regresión Lineal", "Árbol de Decisión", "Random Forest"),
RMSE = c(rmse_lm$.estimate, rmse_tree$.estimate, rmse_rf$.estimate)
)
print(resultados)
## # A tibble: 3 × 2
## Modelo RMSE
## <chr> <dbl>
## 1 Regresión Lineal 67805.
## 2 Árbol de Decisión 73502.
## 3 Random Forest 47403.
evaluacion_grafica_regresion <- function(modelo, df_pred, target = "median_house_value", num_features = 15) {
library(ggplot2)
library(performance)
library(vip)
library(yardstick)
# library(DALEX) # Opcional, si tienes DALEX y quieres interpretación avanzada
# 1. Real vs Predicho
p1 <- ggplot(df_pred, aes_string(x = target, y = ".pred")) +
geom_point(alpha = 0.3, color = "blue") +
geom_abline(slope = 1, intercept = 0, color = "red") +
labs(title = "Real vs Predicho", x = "Valor real", y = "Valor predicho")
# 2. Residuos vs Predicción
df_pred$residuals <- df_pred$.pred - df_pred[[target]]
p2 <- ggplot(df_pred, aes(x = .pred, y = residuals)) +
geom_point(alpha = 0.3) +
geom_hline(yintercept = 0, color = "red") +
labs(title = "Residuos vs Predicción", x = "Predicción", y = "Residuo")
# 3. Histograma de residuos
p3 <- ggplot(df_pred, aes(x = residuals)) +
geom_histogram(bins = 30, fill = "darkgreen", alpha = 0.7) +
labs(title = "Distribución de residuos", x = "Residuo", y = "Frecuencia")
# 4. Métricas
met <- yardstick::metrics(df_pred, truth = !!sym(target), estimate = .pred)
print(met)
# 5. Importancia de variables (si aplica)
if("fit" %in% names(modelo)) {
print(vip::vip(modelo$fit, num_features = num_features) + ggtitle("Importancia de variables"))
} else {
message("No se puede calcular la importancia de variables para este tipo de modelo.")
}
# 6. Chequeo de regresión con performance (modelo lm auxiliar)
mod_check <- lm(as.formula(paste(target, "~ .pred")), data = df_pred)
performance::check_model(mod_check)
# 7. Mostrar los plots
print(p1)
print(p2)
print(p3)
invisible(list(
metricas = met,
p_real_vs_pred = p1,
p_residuos = p2,
p_hist_residuos = p3
))
}
evaluacion_grafica_regresion(ajuste_rf, pred_rf, target = "median_house_value")
##
## Attaching package: 'performance'
## The following objects are masked from 'package:yardstick':
##
## mae, rmse
##
## Attaching package: 'vip'
## The following object is masked from 'package:utils':
##
## vi
## # A tibble: 3 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 rmse standard 47403.
## 2 rsq standard 0.839
## 3 mae standard 32233.
Se compararon tres modelos principales para la predicción del valor de viviendas:
| Modelo | RMSE |
|---|---|
| Regresión Lineal | 67,805 |
| Árbol de Decisión | 73,502 |
| Random Forest | 47,403 |
El modelo Random Forest obtuvo el mejor desempeño con un RMSE de 47,403, significativamente menor que los otros dos modelos. Esto indica que tiene mayor capacidad para capturar las relaciones no lineales y complejas presentes en los datos.
La regresión lineal y el árbol de decisión presentan errores más elevados, lo que evidencia que la relación entre las variables explicativas y el valor de la vivienda no es completamente lineal ni puede ser explicada por reglas simples.
1. Importancia de Variables
La variable más importante es median_income, lo que
sugiere que el ingreso mediano de los hogares es el factor más
determinante para predecir el valor de la vivienda.
Otras variables relevantes son la proximidad al interior
(ocean_proximity_INLAND), la localización geográfica
(longitude y latitude) y, en menor medida,
características estructurales como total_rooms y
population.
2. Gráfico Real vs Predicho
Hay una fuerte correlación positiva entre los valores reales y predichos, lo que muestra que el modelo logra capturar correctamente la tendencia general de los datos.
Sin embargo, se observa cierta dispersión, especialmente en los valores altos, donde el modelo tiende a subestimar (los puntos se agrupan debajo de la línea roja).
3. Residuos vs Predicción
Los residuos están mayoritariamente dispersos alrededor de cero, lo que indica que el modelo no presenta sesgo sistemático importante.
Hay una mayor dispersión de residuos en los valores más altos de predicción, lo que sugiere que el modelo tiene mayor dificultad para predecir correctamente las viviendas de mayor valor.
4. Distribución de residuos
La distribución de los residuos tiene forma aproximadamente simétrica y centrada en cero, lo que es un buen indicio de que el modelo no está sistemáticamente sobreestimando ni subestimando el valor de las viviendas.
Existen algunos valores atípicos (outliers) en los extremos, lo cual es esperable en modelos aplicados a datos reales, especialmente con valores extremos de la variable objetivo.
modelo_xgb <- boost_tree(trees = 500, learn_rate = 0.05) %>%
set_engine("xgboost") %>%
set_mode("regression")
modelo_enet <- linear_reg(penalty = 0.1, mixture = 0.5) %>% # Cambia penalty/mixture para probar lasso/ridge
set_engine("glmnet")
modelo_svm <- svm_rbf(cost = 1, rbf_sigma = 0.1) %>%
set_engine("kernlab") %>%
set_mode("regression")
modelo_knn <- nearest_neighbor(neighbors = 10) %>%
set_engine("kknn") %>%
set_mode("regression")
# XGBoost
ajuste_xgb <- fit(modelo_xgb, median_house_value ~ ., data = train_prep)
pred_xgb <- predict(ajuste_xgb, new_data = test_prep) %>% bind_cols(test_prep)
rmse_xgb <- yardstick::rmse(pred_xgb, truth = median_house_value, estimate = .pred)
# Lasso/Ridge/Elastic Net (Elastic Net general)
ajuste_enet <- fit(modelo_enet, median_house_value ~ ., data = train_prep)
pred_enet <- predict(ajuste_enet, new_data = test_prep) %>% bind_cols(test_prep)
rmse_enet <- yardstick::rmse(pred_enet, truth = median_house_value, estimate = .pred)
# Support Vector Machine (SVM)
ajuste_svm <- fit(modelo_svm, median_house_value ~ ., data = train_prep)
pred_svm <- predict(ajuste_svm, new_data = test_prep) %>% bind_cols(test_prep)
rmse_svm <- yardstick::rmse(pred_svm, truth = median_house_value, estimate = .pred)
# KNN
ajuste_knn <- fit(modelo_knn, median_house_value ~ ., data = train_prep)
pred_knn <- predict(ajuste_knn, new_data = test_prep) %>% bind_cols(test_prep)
rmse_knn <- yardstick::rmse(pred_knn, truth = median_house_value, estimate = .pred)
# 4. Comparación de resultados
resultados <- tibble(
Modelo = c("XGBoost", "Elastic Net", "SVM", "KNN"),
RMSE = c(rmse_xgb$.estimate, rmse_enet$.estimate, rmse_svm$.estimate, rmse_knn$.estimate)
)
print(resultados)
## # A tibble: 4 × 2
## Modelo RMSE
## <chr> <dbl>
## 1 XGBoost 45280.
## 2 Elastic Net 67825.
## 3 SVM 53376.
## 4 KNN 56773.
# 5. Visualización real vs predicho para el mejor modelo (ejemplo con XGBoost)
evaluacion_grafica_regresion(ajuste_xgb, pred_xgb, target = "median_house_value")
## # A tibble: 3 × 3
## .metric .estimator .estimate
## <chr> <chr> <dbl>
## 1 rmse standard 45280.
## 2 rsq standard 0.848
## 3 mae standard 29820.
| Modelo | RMSE |
|---|---|
| XGBoost | 45,280 |
| Elastic Net | 67,825 |
| SVM | 53,376 |
| KNN | 56,773 |
XGBoost obtuvo el menor RMSE, superando a los demás modelos en capacidad predictiva. Es el modelo que mejor se ajusta a la estructura y complejidad de los datos, aprovechando su potencia para capturar relaciones no lineales y complejas.
Los modelos SVM y KNN también presentan resultados relativamente buenos, aunque no logran igualar el rendimiento de XGBoost.
Elastic Net, al igual que la regresión lineal, tiene un desempeño considerablemente inferior, evidenciando que la relación entre variables no es simplemente lineal ni puede ser explicada solo por regularización.
Conclusiones sobre los gráficos de XGBoost
1. Importancia de variables
La variable median_income sigue siendo la más
relevante por lejos, confirmando que el nivel de ingresos es el
principal factor en el valor de la vivienda.
La variable ocean_proximity_INLAND también es
significativa, seguida de aspectos geográficos (longitude,
latitude) y de identificación (id), aunque
estos últimos en menor medida.
Variables estructurales y otras categorías de proximidad al océano tienen un peso mucho menor.
2. Real vs Predicho
El gráfico muestra una buena correlación entre los valores reales y los predichos. La mayor parte de los puntos sigue la diagonal, lo que indica una predicción precisa en la mayoría de los casos.
Se observa dispersión, especialmente en los valores altos, donde hay una ligera tendencia del modelo a subestimar los precios más elevados.
3. Residuos vs Predicción
Los residuos se concentran cerca de cero en todo el rango de predicciones, lo cual es positivo y habla de un modelo bien ajustado.
Sin embargo, en los valores más altos de predicción, se ve mayor variabilidad y la presencia de algunos outliers, lo que indica que para las viviendas más caras el modelo puede cometer errores mayores.
No se observa un patrón claro de sesgo sistemático.
4. Distribución de residuos
La distribución es simétrica y centrada en cero, lo que indica que el modelo no tiende a sobrestimar ni subestimar sistemáticamente.
La presencia de algunos valores extremos es normal en problemas de este tipo y sugiere la existencia de datos atípicos o difíciles de predecir.
# 1. Cargar el test de Kaggle y prepararlo igual que test_prep_clean
test_kaggle <- readr::read_csv("test.csv")
## Rows: 6193 Columns: 10
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ocean_proximity
## dbl (9): id, longitude, latitude, housing_median_age, total_rooms, total_bed...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# 2. Preprocesar test_kaggle exactamente igual que tu test interno
test_kaggle_prep <- bake(prep_receta, new_data = test_kaggle) %>% drop_na()
# 3. Predecir con el modelo entrenado en train_prep
predicciones_kaggle <- predict(ajuste_xgb, new_data = test_kaggle_prep)
# 4. Recuperar el id de test original para el archivo de submission
# Asumiendo que 'id' NO fue eliminado en la preparación y sigue el mismo orden
# Recuperar los ids válidos (correspondientes a test_kaggle_prep)
ids_validos <- test_kaggle$id[as.numeric(rownames(test_kaggle_prep))]
# 5. Crear el archivo de salida
submission <- tibble(
id = ids_validos,
median_house_value = predicciones_kaggle$.pred
)
# 6. Guardar archivo CSV
readr::write_csv(submission, "submission.csv")