knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)
if (!requireNamespace("paqueteMODELOS", quietly = TRUE)) {
try(remotes::install_github("centromagis/paqueteMODELOS", quiet = TRUE), silent = TRUE)
}
suppressPackageStartupMessages({
library(paqueteMODELOS)
library(dplyr)
library(stringr)
library(ggplot2)
library(plotly)
library(leaflet)
library(broom)
library(car)
library(lmtest)
})
## Warning: package 'GGally' was built under R version 4.4.3
## Warning: package 'ggplot2' was built under R version 4.4.3
## Warning: package 'knitr' was built under R version 4.4.3
## Warning: package 'dplyr' was built under R version 4.4.3
## Warning: package 'plotly' was built under R version 4.4.3
## Warning: package 'leaflet' was built under R version 4.4.3
## Warning: package 'car' was built under R version 4.4.3
## Warning: package 'carData' was built under R version 4.4.3
## Warning: package 'lmtest' was built under R version 4.4.3
## Warning: package 'zoo' was built under R version 4.4.3
Paso 1: Filtrar casas en zona norte
data("vivienda")
nm <- names(vivienda)
find_col <- function(patterns, nm) {
for (p in patterns) {
m <- nm[str_detect(nm, regex(p, ignore_case = TRUE))]
if (length(m) >= 1) return(m[1])
}
NA_character_
}
tipo_col <- find_col(c("tipo", "property_type"), nm)
zona_col <- find_col(c("zona", "sector", "region"), nm)
precio_col <- find_col(c("precio", "price", "valor"), nm)
area_col <- find_col(c("area", "area_construida", "m2"), nm)
estrato_col <- find_col(c("estrato", "stratum"), nm)
cuartos_col <- find_col(c("cuart", "habita", "rooms"), nm)
banos_col <- find_col(c("bañ", "bano", "bath"), nm)
parq_col <- find_col(c("parque", "parking"), nm)
lat_col <- find_col(c("lat", "latitude"), nm)
lon_col <- find_col(c("lon", "longitud"), nm)
base_norte_casas <- vivienda %>%
filter(str_detect(.data[[tipo_col]], regex("casa|house", ignore_case = TRUE)) &
str_detect(.data[[zona_col]], regex("norte|north", ignore_case = TRUE)))
head(base_norte_casas, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4057 Zona N… 02 6 750 445 NA 7 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
if (!is.na(lat_col) && !is.na(lon_col)) {
leaflet(data = base_norte_casas) %>%
addTiles() %>%
addCircleMarkers(
lng = base_norte_casas[[lon_col]],
lat = base_norte_casas[[lat_col]],
popup = paste("Precio:", base_norte_casas[[precio_col]]),
radius = 4, color = "blue", fillOpacity = 0.7
)
}
Paso 2: Análisis exploratorio
# Filtrar columnas válidas (no NA y presentes en la base)
eda_vars <- c(precio_col, area_col, estrato_col, cuartos_col, banos_col)
eda_vars <- eda_vars[!is.na(eda_vars) & eda_vars %in% names(base_norte_casas)]
# Validar que haya al menos 2 columnas para análisis
if (length(eda_vars) < 2) {
stop("No hay suficientes variables detectadas para el análisis exploratorio.")
}
# Crear el dataframe de análisis
eda_df <- base_norte_casas %>%
select(all_of(eda_vars)) %>%
na.omit()
# Matriz de dispersión interactiva
plot_ly(type = 'splom', data = eda_df,
dimensions = lapply(names(eda_df), function(colname) list(label = colname, values = ~ get(colname))),
marker = list(size = 4)) %>%
layout(title = "Matriz de dispersión interactiva")
# Gráfico precio vs área
if (!is.na(precio_col) && !is.na(area_col)) {
ggplotly(ggplot(base_norte_casas, aes(x = .data[[area_col]], y = .data[[precio_col]])) +
geom_point() + geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(title = "Precio vs Área Construida") + theme_minimal())
}
# Boxplot por estrato
if (!is.na(precio_col) && !is.na(estrato_col)) {
ggplotly(ggplot(base_norte_casas, aes(x = factor(.data[[estrato_col]]), y = .data[[precio_col]])) +
geom_boxplot(fill = "skyblue") + labs(title = "Precio por Estrato") + theme_minimal())
}
Paso 3: Modelo de regresión lineal múltiple
predictoras <- c(area_col, estrato_col, cuartos_col, parq_col, banos_col)
predictoras <- predictoras[!is.na(predictoras) & predictoras %in% names(base_norte_casas)]
formula_mod <- as.formula(paste0("`", precio_col, "` ~ ", paste0("`", predictoras, "`", collapse = " + ")))
modelo <- lm(formula_mod, data = base_norte_casas)
summary(modelo)
##
## Call:
## lm(formula = formula_mod, data = base_norte_casas)
##
## Residuals:
## Min 1Q Median 3Q Max
## -824.73 -80.43 -17.86 47.66 1005.36
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -246.78807 44.54968 -5.540 5.29e-08 ***
## areaconst 0.69459 0.05266 13.190 < 2e-16 ***
## estrato 87.80722 9.46502 9.277 < 2e-16 ***
## habitaciones 15.05430 4.86787 3.093 0.00211 **
## parqueaderos 27.33995 5.75390 4.752 2.76e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156.1 on 430 degrees of freedom
## (287 observations deleted due to missingness)
## Multiple R-squared: 0.5982, Adjusted R-squared: 0.5945
## F-statistic: 160.1 on 4 and 430 DF, p-value: < 2.2e-16
Paso 4: Validación de supuestos
res <- resid(modelo)
fitted <- fitted(modelo)
ggplot(data.frame(res, fitted), aes(x = fitted, y = res)) +
geom_point() + geom_hline(yintercept = 0, linetype = 2) + theme_minimal()

ggplot(data.frame(res), aes(sample = res)) +
stat_qq() + stat_qq_line() + theme_minimal()

shapiro.test(res)
##
## Shapiro-Wilk normality test
##
## data: res
## W = 0.85251, p-value < 2.2e-16
bptest(modelo)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 77.133, df = 4, p-value = 7.05e-16
durbinWatsonTest(modelo)
## lag Autocorrelation D-W Statistic p-value
## 1 0.1167176 1.764351 0.018
## Alternative hypothesis: rho != 0
Paso 5: Predicción para vivienda solicitada
nueva_vivienda <- data.frame(
`area_construida` = 120,
`estrato` = 4,
`habitaciones` = 3,
`parqueaderos` = 2,
`banos` = 2
)
names(nueva_vivienda) <- predictoras
predict(modelo, newdata = nueva_vivienda)
## 1
## 287.6349
Paso 6: Ofertas sugeridas (≤ 350 millones)
ofertas_potenciales <- base_norte_casas %>%
filter(.data[[precio_col]] <= 350000000)
head(ofertas_potenciales, 5)
## # A tibble: 5 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4057 Zona N… 02 6 750 445 NA 7 6
## 4 4460 Zona N… 02 4 625 355 3 5 5
## 5 6081 Zona N… 02 5 750 237 2 6 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
if (!is.na(lat_col) && !is.na(lon_col)) {
leaflet(data = ofertas_potenciales) %>%
addTiles() %>%
addCircleMarkers(
lng = ofertas_potenciales[[lon_col]],
lat = ofertas_potenciales[[lat_col]],
popup = paste("Precio:", ofertas_potenciales[[precio_col]]),
radius = 6, color = "orange", fillOpacity = 0.8
)
}