Instalar librerias

library(tidyverse)
library(janitor)
library(skimr)
library(DataExplorer)
library(plotly)
library(leaflet)
library(broom)
library(modelsummary)
library(gtsummary)
library(performance)
library(car)
library(GGally)
library(rsample)
library(purrr)
library(scales)

set.seed(123)

# Datos
# install.packages("devtools")
# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
library(paqueteMODELOS)
data("vivienda")
vivienda <- vivienda |> clean_names()

# Vista inicial
glimpse(vivienda)
## Rows: 8,322
## Columns: 13
## $ id           <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
## $ zona         <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
## $ piso         <chr> NA, NA, NA, "02", "01", "01", "01", "01", "02", "02", "02…
## $ estrato      <dbl> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
## $ preciom      <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
## $ areaconst    <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
## $ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
## $ banios       <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
## $ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
## $ tipo         <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
## $ barrio       <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
## $ longitud     <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
## $ latitud      <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…
skimr::skim(vivienda)
Data summary
Name vivienda
Number of rows 8322
Number of columns 13
_______________________
Column type frequency:
character 4
numeric 9
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
zona 3 1.00 8 12 0 5 0
piso 2638 0.68 2 2 0 12 0
tipo 3 1.00 4 11 0 2 0
barrio 3 1.00 4 29 0 436 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id 3 1.00 4160.00 2401.63 1.00 2080.50 4160.00 6239.50 8319.00 ▇▇▇▇▇
estrato 3 1.00 4.63 1.03 3.00 4.00 5.00 5.00 6.00 ▅▆▁▇▆
preciom 2 1.00 433.89 328.65 58.00 220.00 330.00 540.00 1999.00 ▇▂▁▁▁
areaconst 3 1.00 174.93 142.96 30.00 80.00 123.00 229.00 1745.00 ▇▁▁▁▁
parqueaderos 1605 0.81 1.84 1.12 1.00 1.00 2.00 2.00 10.00 ▇▁▁▁▁
banios 3 1.00 3.11 1.43 0.00 2.00 3.00 4.00 10.00 ▇▇▃▁▁
habitaciones 3 1.00 3.61 1.46 0.00 3.00 3.00 4.00 10.00 ▂▇▂▁▁
longitud 3 1.00 -76.53 0.02 -76.59 -76.54 -76.53 -76.52 -76.46 ▁▅▇▂▁
latitud 3 1.00 3.42 0.04 3.33 3.38 3.42 3.45 3.50 ▃▇▅▇▅

Introducción

En el presente informe se aborda un caso de estudio aplicado al sector inmobiliario en la cuidad de Cali. La empresa C&A (Casas y Apartamentos, liderada por Maria (Agente de Bienes Raices), enfrenta el reto de asesorar a una compañia internacional interesasa en ubicar a dos de sus empleados con sus familias en la ciudad. Para el efecto se requiere identificar propiedades que cumplan con criterios especificos de ubicación, caracterizticas fisicas y de presupuesto.

El objetivo del análisis es aplicar técnicas de regresion lineal multiple para modelar el precio de las viviendas en función de variables como área construida, estrato socieconómico, numero de habitaciones, parqueaderos y baños. A partir de este modelo, se busca predecir el precio esperado para dos perfiles de vivienda y recomendar ofertas disponibles en el mercado que se ajusten a los requerimientos y limites de crédito establecidos.

El alcance del estudio incluye la limpieza y filtrado de los datos, el análisis exploratorio, la estimacion de validación de modelos, predicción de precios, recomendación de ofertas y evaluación de la estabilidad del modelo mediante validación cruzada. El informe se estructura en tres partes: Informe ejecutivo, Plan de trabajo (Anexo 1) y resultados detallados (Anexo 2).

1) Informe Ejecutivo

Objetivo. Estimar precios de mercado en Cali y recomendar opciones para dos solicitudes con restricciones de crédito:

  • Vivienda 1: Casa en Zona Norte de 200 m², 1 parqueadero, 2 baños, 4 habitaciones, estrato 4 o 5, con credito preaprobado de 350 millones.

  • Vivienda 2: Apartamento en Zona Sur de 300 m², 3 parqueaderos, 3 baños, 5 habitaciones, estrato 5 o 6, con credito preaprobado de 850 millones.

Metodología:

  • Se filtran bases por tipo y zona
  • Se realiza EDA (correlaciòn / Visualizaciones)
  • Se ajustan MRLM con variables explicativas: área, estrato, baños, habitaciones y parqueaderos
  • Se valida (supuestos, multicolinealidad, influencia)
  • Selección por R²/R²aj, AIC/BIC, RMSE y validación cruzada _ Se predice el precio para los perfiles objetivo y se alistan >=5 ofertas bajo el preaprobado de crédito, con mapa interactivo.

Hallazgos clave:

  • Determinantes principales del precio: área y estrato (efecto positivo); aportes de baños y parqueaderos.
  • Ajuste obtenido: R² ~ … (Casas Norte) y R² ~ … (Aptos Sur).
  • Validación: normalidad / homocedasticidad razonables; VIF sin colinealidad crítica; se revisaron outliers e influencia.
  • Vivienda 1: Precio esperado ≈ … (PI95% [ … , … ]); ≥5 opciones ≤ 350M listadas y geolocalizadas.
  • Vivienda 2: Precio esperado ≈ … (PI95% [ … , … ]); ≥5 opciones ≤ 850M listadas y geolocalizadas.

Recomendaciones.

  • Priorizar barrios con mejor relación precio–servicios–accesibilidad _ Considerar margen de negociación (5–10%) _ Validar documentos (título, PH, impuestos, administración) y estado físico _ Simular crédito y costos asociados.

Anexo 1 – Plan de trabajo (metodología)

  1. Preparación y control de calidad.
  • Normalizar nombres, revisar NA’s, rangos y duplicados
  • Verificar consistencia zona–coordenadas.
  1. Definiciòn de Bases de Modelaciòn.
  • Base1: tipo == "Casa" & zona == "Zona Norte" → objetivo Vivienda 1.
  • Base2: tipo == "Apartamento" & zona == "Zona Sur" → objetivo Vivienda 2.
  1. EDA y Correlación.
  • Visualizaciones interactivas y matriz de correlaciones.
  1. Modelación.
  • Ajustar modelo base y modelos alternativos(no linealidades/interacciones)
  • Comparación por R²/R²aj, AIC/BIC, RMSE (CV).
  1. Validación de supuestos.
  • Normalidad (QQ-plot, Shapiro), homocedasticidad (Breusch-Pagan), VIF, influencia (Cook, DFFITS, DFBETAS).
  • Si hay replicaciones exactas → prueba de falta de ajuste.
  1. Predicción y recomendación.
  • IC de media e intervalos de predicciòn
  • Filtro de mercado con topes de crédito y tolerancias(+-15% en area), ≥5 candidatos en mapa.
  1. Estabilidad (validaciòn cruzada).
  • Validación cruzada (holdout y k-fold) para estimar encogimiento.
  1. Entrega.
  • Publicación en RPubs y adjunto del .Rmd

2. Anexo 2 – Resultados (modelación, validación y comparación)

2.1 Filtro, verificación y mapa (Base1: Casas – Zona Norte)

base1 <- vivienda |>
  filter(tipo == "Casa", zona == "Zona Norte") |>
  drop_na(preciom, areaconst, parqueaderos, banios, habitaciones, estrato, longitud, latitud)

head(base1, 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  4460 Zona N… 02          4     625       355            3      5            5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
base1 |> count(zona, tipo)
## # A tibble: 1 × 3
##   zona       tipo      n
##   <chr>      <chr> <int>
## 1 Zona Norte Casa    435
# Resumen de precios
base1 |> summarise(n = n(), min_precio = min(preciom), max_precio = max(preciom))
## # A tibble: 1 × 3
##       n min_precio max_precio
##   <int>      <dbl>      <dbl>
## 1   435         89       1940

Mapa de puntos

leaflet(base1) |>
  addTiles() |>
  addCircleMarkers(lng = ~longitud, lat = ~latitud, radius = 5,
                   color = "#1f77b4", stroke = FALSE, fillOpacity = .7,
                   popup = ~paste0("<b>", tipo, "</b> | ", barrio, " - ", zona, "<br>",
                                   "Estrato ", estrato, " | Área ", areaconst, " m²<br>",
                                   "Hab ", habitaciones, " | Baños ", banios, " | Parq ", parqueaderos, "<br>",
                                   "Precio: $", scales::comma(preciom), " M"))

NOTA: La presencia de puntos por fuera de la zona declarada, suele deberse a errores de georreferenciación o a diferencias en la definición comercial vs. administrativa de zonas.

Se obtuvo una submuestra de casas en Zona Norte con n = … registros (…% del total). Tras controlar valores faltantes en variables críticas y coordenadas, la base mantiene variabilidad suficiente en precio, área, estrato, baños, habitaciones y parqueaderos, lo que la hace idónea para la modelación. El mapa confirma la coherencia espacial de la mayoría de registros; los pocos casos fuera de los límites esperados se asocian a posibles errores de georreferenciación o divergencias entre zona comercial vs. administrativa. Se recomienda auditar estos puntos antes de la negociación. El rango de precios (mín–máx) y la distribución por estrato son consistentes con el mercado de la zona, respaldando el uso de esta base para el ajuste del MRLM en el punto 3.

2.2 EDA y Correlación (Base1)

p1 <- ggplot(base1, aes(areaconst, preciom)) + geom_point(alpha=.6) +
  geom_smooth(method="lm", se=FALSE, color="green") +
  labs(title="Precio vs Área construida", x="Área (m²)", y="Precio (M)")

p2 <- ggplot(base1, aes(estrato, preciom)) + geom_jitter(alpha=.6, width=.1, height=0) +
  labs(title="Precio vs Estrato", x="Estrato", y="Precio (M)")

p3 <- ggplot(base1, aes(banios, preciom)) + geom_jitter(alpha=.6, width=.1, height=0) +
  labs(title="Precio vs Baños", x="Baños", y="Precio (M)")

p4 <- ggplot(base1, aes(habitaciones, preciom)) + geom_jitter(alpha=.6, width=.1, height=0) +
  labs(title="Precio vs Habitaciones", x="Habitaciones", y="Precio (M)")

p5 <- ggplot(base1, aes(parqueaderos, preciom)) + geom_jitter(alpha=.6, width=.1, height=0) +
  labs(title="Precio vs Parqueaderos", x="Parqueaderos", y="Precio (M)")

subplot(ggplotly(p1), ggplotly(p2), ggplotly(p3), ggplotly(p4), ggplotly(p5), nrows=3)
  1. Precio vs Área construida
  • Se observa una relación positiva clara: a mayor área construida, el precio tiende a ser mayor.

  • La línea de regresión verde muestra una pendiente positiva consistente.

  • Sin embargo, también hay algunos puntos dispersos (outliers) con área pequeña pero precios altos, y viceversa.

  1. Precio vs Estrato
  • Existe una tendencia creciente: a medida que aumenta el estrato, los precios suelen ser mayores.

  • La dispersión dentro de cada estrato es alta (especialmente en estrato 5 y 6).

  • Sugiere que el estrato influye en el precio, pero no lo explica completamente.

  1. Precio vs Baños
  • Hay cierta relación positiva: viviendas con más baños tienden a tener precios más altos.

  • Aunque no es tan fuerte como el área, sí se ve una tendencia.

  • También hay bastante dispersión: casas con pocos baños pueden tener precios altos si tienen otras características relevantes.

  1. Precio vs Habitaciones
  • La relación es menos clara que con baños o área.

  • La nube de puntos muestra que el número de habitaciones no siempre determina el precio.

  • Puede haber viviendas con muchas habitaciones pero precios relativamente bajos (quizás por ubicación o calidad de construcción).

  1. Precio vs Parqueaderos
  • Relación positiva moderada: más parqueaderos, mayor precio.

  • No es tan fuerte como el área, pero sí relevante, sobre todo en propiedades de gama alta.

  • Aparecen outliers: viviendas con pocos parqueaderos y precios muy altos (posiblemente ubicaciones premium).

GGally::ggpairs(base1 |> select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos))

1. Variable dependiente (preciom)

  • Se observa alta asimetría positiva (cola larga): la mayoría de los precios están en rangos bajos, con pocos inmuebles de precios muy altos.

  • Esto sugiere que podría ser útil aplicar transformaciones (ej. logaritmo) para un análisis de regresión más robusto.

  1. Correlaciones con el precio (preciom)
  • Área construida (areaconst): correlación fuerte y positiva (0.685***). Es la variable que mejor explica el precio.

  • Estrato: correlación positiva moderada (0.528***). A mayor estrato, mayor precio.

  • Baños: correlación positiva moderada (0.509***). Más baños suelen asociarse con precios más altos.

  • Parqueaderos: correlación positiva débil-moderada (0.412***).

  • Habitaciones: correlación positiva débil (0.365***). Parece tener poca influencia directa.

  1. Relaciones entre variables independientes
  • Baños y habitaciones: correlación relativamente fuerte (0.590***), lo que indica que estas dos variables están relacionadas (casas con más habitaciones suelen tener más baños).

  • Área y baños/habitaciones/parqueaderos: todas muestran correlaciones positivas (entre 0.3 y 0.45). Es lógico: casas más grandes suelen tener más baños, habitaciones y parqueaderos.

  • Estrato con el resto: correlaciones bajas a moderadas (ej. con área 0.354, con baños 0.351).

  1. Multicolinealidad potencial
  • Como algunas variables explicativas están correlacionadas entre sí (ej. baños y habitaciones, área con baños), habría que vigilar el VIF si se ajusta un modelo de regresión múltiple.

2.3 Modelos (Base1)

f_base  <- preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios
f_quad  <- preciom ~ poly(areaconst, 2, raw = TRUE) + estrato + habitaciones + parqueaderos + banios
f_int   <- preciom ~ areaconst*estrato + habitaciones + parqueaderos + banios

m1 <- lm(f_base, data = base1)
m2 <- lm(f_quad, data = base1)
m3 <- lm(f_int,  data = base1)

modelsummary::msummary(list("Base"=m1, "Área^2"=m2, "Interacción área:estrato"=m3),
                       stars=TRUE, gof_omit = "IC|Log|RMSE")
Base Área^2 Interacción área:estrato
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001
(Intercept) -238.171*** -257.020*** 64.935
(44.406) (43.793) (68.796)
areaconst 0.677*** -0.544*
(0.053) (0.223)
estrato 80.635*** 68.848*** 9.821
(9.826) (10.038) (15.748)
habitaciones 7.645 7.520 8.639
(5.659) (5.552) (5.469)
parqueaderos 24.006*** 21.559*** 23.615***
(5.869) (5.787) (5.670)
banios 18.899* 14.915* 18.884**
(7.488) (7.407) (7.233)
poly(areaconst, 2, raw = TRUE)1 1.189***
(0.132)
poly(areaconst, 2, raw = TRUE)2 -0.001***
(0.000)
areaconst × estrato 0.270***
(0.048)
Num.Obs. 435 435 435
R2 0.604 0.620 0.631
R2 Adj. 0.599 0.615 0.626
F 130.919 116.313 122.214
modelsummary::msummary(list("Base"=m1, "Área^2"=m2, "Interacción"=m3),
                       gof_map = c("r.squared","adj.r.squared","AIC","BIC","RMSE"))
Base Área^2 Interacción
(Intercept) -238.171 -257.020 64.935
(44.406) (43.793) (68.796)
areaconst 0.677 -0.544
(0.053) (0.223)
estrato 80.635 68.848 9.821
(9.826) (10.038) (15.748)
habitaciones 7.645 7.520 8.639
(5.659) (5.552) (5.469)
parqueaderos 24.006 21.559 23.615
(5.869) (5.787) (5.670)
banios 18.899 14.915 18.884
(7.488) (7.407) (7.233)
poly(areaconst, 2, raw = TRUE)1 1.189
(0.132)
poly(areaconst, 2, raw = TRUE)2 -0.001
(0.000)
areaconst × estrato 0.270
(0.048)
R2 0.604 0.620 0.631
R2 Adj. 0.599 0.615 0.626
  1. Modelo Base (m1)
  • La variable más influyente en el precio es área construida (areaconst, coef. ≈ 0.68***), lo cual coincide con lo que vimos en las correlaciones.

  • También tienen un efecto positivo y significativo el estrato, los parqueaderos y los baños.

  • Habitaciones no resulta significativa, lo que indica que, controlando por las demás variables, no explica de forma adicional el precio.

  1. Modelo Cuadrático (m2)

Se introduce un término cuadrático del área construida.

  • El coeficiente lineal de área sigue siendo positivo, pero aparece un coeficiente negativo para el término cuadrático.

  • Esto indica rendimientos decrecientes del área: a mayor tamaño, el incremento en precio es menor (es decir, no es una relación estrictamente lineal).

  • Estrato, parqueaderos y baños siguen siendo significativos, lo cual refuerza su importanci

  1. Modelo con interacción área × estrato (m3)
  • El coeficiente de interacción es positivo, aunque no siempre significativo en el nivel más estricto.

  • Esto sugiere que el efecto del área en el precio depende del estrato: en estratos más altos, un aumento en el área construida puede traducirse en un incremento mayor en el precio.

  • Es una forma de capturar heterogeneidad en el mercado: no es lo mismo un apartamento grande en estrato 2 que en estrato 6.

  1. Comparación global (R², AIC, BIC, RMSE)
  • Según la segunda tabla que muestras, los modelos con área cuadrática y con interacción tienden a mejorar el ajuste respecto al modelo base (mejor R² y menor RMSE).

  • Sin embargo, el BIC penaliza la complejidad, por lo que puede preferir un modelo más simple si la mejora no es muy grande.

El área construida es la variable más determinante del precio, pero con rendimientos decrecientes.

El estrato también es fundamental, y su interacción con el área indica que el mercado valora de manera distinta los metros cuadrados según el nivel socioeconómico.

Parqueaderos y baños también aportan valor, mientras que las habitaciones no tienen un peso estadísticamente fuerte una vez se controlan las demás variables.

2.4 Validación de supuestos (Base1 – modelo seleccionado)

mod1 <- m1   # Cambia aquí si seleccionas m2 o m3

performance::check_model(mod1)

shapiro.test(residuals(mod1))
## 
##  Shapiro-Wilk normality test
## 
## data:  residuals(mod1)
## W = 0.85246, p-value < 2.2e-16
car::ncvTest(mod1)       # Breusch-Pagan (NCV)
## Non-constant Variance Score Test 
## Variance formula: ~ fitted.values 
## Chisquare = 352.7061, Df = 1, p = < 2.22e-16
car::vif(mod1)
##    areaconst      estrato habitaciones parqueaderos       banios 
##     1.460998     1.307757     1.721015     1.226334     1.967421
# Influencia
rstud <- rstudent(mod1)
lev   <- hatvalues(mod1)
cook  <- cooks.distance(mod1)

c(outliers = sum(abs(rstud) > 3), leverage = sum(lev > 2*length(coef(mod1))/nrow(base1)), cooksD = sum(cook > 1))
## outliers leverage   cooksD 
##       11       38        1

El modelo Base (m1) tiene problemas de linealidad, heterocedasticidad y normalidad de residuos.

  • No hay problemas de colinealidad.

  • Hay pocos outliers influyentes.

  • La forma de los residuos sugiere que modelos no lineales (como el cuadrático m2 o el de interacción m3) pueden mejorar el ajuste.

2.5 Predicción – Vivienda 1

new_viv1_e4 <- tibble(areaconst=200, estrato=4, habitaciones=4, parqueaderos=1, banios=2)
new_viv1_e5 <- tibble(areaconst=200, estrato=5, habitaciones=4, parqueaderos=1, banios=2)

pred_viv1_e4 <- predict(mod1, newdata = new_viv1_e4, interval = "prediction", level = 0.95)
pred_viv1_e5 <- predict(mod1, newdata = new_viv1_e5, interval = "prediction", level = 0.95)

pred_viv1_e4; pred_viv1_e5
##       fit      lwr      upr
## 1 312.101 6.205196 617.9968
##        fit      lwr      upr
## 1 392.7359 86.19637 699.2755

2.6 Ofertas sugeridas (≤ 350M) – Vivienda 1

cand_v1 <- vivienda |>
  clean_names() |>
  filter(tipo == "Casa",
         zona == "Zona Norte",
         estrato %in% c(4,5),
         parqueaderos >= 1,
         banios >= 2,
         habitaciones >= 4,
         between(areaconst, 200*0.85, 200*1.15),
         preciom <= 350) |>
  drop_na(longitud, latitud)

cand_v1 |>
  arrange(preciom) |>
  head(10) |>
  select(barrio, estrato, areaconst, habitaciones, banios, parqueaderos, preciom)
## # A tibble: 10 × 7
##    barrio             estrato areaconst habitaciones banios parqueaderos preciom
##    <chr>                <dbl>     <dbl>        <dbl>  <dbl>        <dbl>   <dbl>
##  1 vipasa                   4       171            4      4            3     270
##  2 vipasa                   5       205            6      5            2     300
##  3 la flora                 5       200            4      4            2     320
##  4 la merced                4       200            4      4            2     320
##  5 urbanización la m…       5       210            5      3            2     320
##  6 el bosque                5       202            5      4            1     335
##  7 la flora                 5       180            4      4            2     340
##  8 vipasa                   5       203            4      3            2     340
##  9 la flora                 5       170            4      4            3     343
## 10 el bosque                5       200            4      3            3     350
leaflet(cand_v1) |>
  addTiles() |>
  addCircleMarkers(lng=~longitud, lat=~latitud, radius=6,
                   color = "#2ca02c",
                   popup = ~paste0("<b>Casa – ", barrio, "</b><br>",
                                   zona, " | Estrato ", estrato, "<br>",
                                   "Área: ", areaconst, " m²; Hab: ", habitaciones,
                                   "; Baños: ", banios, "; Parq: ", parqueaderos, "<br>",
                                   "Precio: $", scales::comma(preciom), " M"))

2.7 Repetición para Vivienda 2

base2 <- vivienda |>
  filter(tipo == "Apartamento", zona == "Zona Sur") |>
  drop_na(preciom, areaconst, parqueaderos, banios, habitaciones, estrato, longitud, latitud)

f2_base <- preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios
mod2 <- lm(f2_base, data = base2)

summary(mod2)
## 
## Call:
## lm(formula = f2_base, data = base2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1092.02   -42.28    -1.33    40.58   926.56 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -261.62501   15.63220 -16.736  < 2e-16 ***
## areaconst       1.28505    0.05403  23.785  < 2e-16 ***
## estrato        60.89709    3.08408  19.746  < 2e-16 ***
## habitaciones  -24.83693    3.89229  -6.381 2.11e-10 ***
## parqueaderos   72.91468    3.95797  18.422  < 2e-16 ***
## banios         50.69675    3.39637  14.927  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 98.02 on 2375 degrees of freedom
## Multiple R-squared:  0.7485, Adjusted R-squared:  0.748 
## F-statistic:  1414 on 5 and 2375 DF,  p-value: < 2.2e-16
performance::model_performance(mod2)
## # Indices of model performance
## 
## AIC     |    AICc |     BIC |    R2 | R2 (adj.) |   RMSE |  Sigma
## -----------------------------------------------------------------
## 28599.5 | 28599.6 | 28640.0 | 0.749 |     0.748 | 97.896 | 98.019

Predicción perfil objetivo – Vivienda 2

new_viv2_e5 <- tibble(areaconst=300, estrato=5, habitaciones=5, parqueaderos=3, banios=3)
new_viv2_e6 <- tibble(areaconst=300, estrato=6, habitaciones=5, parqueaderos=3, banios=3)

pred_viv2_e5 <- predict(mod2, newdata = new_viv2_e5, interval = "prediction", level = 0.95)
pred_viv2_e6 <- predict(mod2, newdata = new_viv2_e6, interval = "prediction", level = 0.95)

pred_viv2_e5; pred_viv2_e6
##        fit     lwr      upr
## 1 675.0247 481.455 868.5945
##        fit      lwr      upr
## 1 735.9218 542.3141 929.5296

Ofertas sugeridas (≤ 850M) – Vivienda 2

cand_v2 <- vivienda |>
  clean_names() |>
  filter(tipo == "Apartamento",
         zona == "Zona Sur",
         estrato %in% c(5,6),
         parqueaderos >= 3,
         banios >= 3,
         habitaciones >= 5,
         between(areaconst, 300*0.85, 300*1.15),
         preciom <= 850) |>
  drop_na(longitud, latitud)

cand_v2 |>
  arrange(preciom) |>
  head(10) |>
  select(barrio, estrato, areaconst, habitaciones, banios, parqueaderos, preciom)
## # A tibble: 2 × 7
##   barrio    estrato areaconst habitaciones banios parqueaderos preciom
##   <chr>       <dbl>     <dbl>        <dbl>  <dbl>        <dbl>   <dbl>
## 1 seminario       5       256            5      5            3     530
## 2 seminario       5       300            6      5            3     670
leaflet(cand_v2) |>
  addTiles() |>
  addCircleMarkers(lng=~longitud, lat=~latitud, radius=6,
                   color = "#800080",
                   popup = ~paste0("<b>Apartamento – ", barrio, "</b><br>",
                                   zona, " | Estrato ", estrato, "<br>",
                                   "Área: ", areaconst, " m²; Hab: ", habitaciones,
                                   "; Baños: ", banios, "; Parq: ", parqueaderos, "<br>",
                                   "Precio: $", scales::comma(preciom), " M"))

El mapa interactivo permite visualizar la distribución espacial de los apartamentos y relacionar sus características con la localización geográfica. Gracias a los popups, se facilita la exploración individual de inmuebles y la comparación entre zonas, lo cual complementa el análisis estadístico previo y aporta una dimensión geográfica que ayuda a interpretar patrones de precio y oferta inmobiliaria.

2.8 Validación cruzada (k-fold) y encogimiento (Base 1 y Base 2)

cv_metrics <- function(data, formula, v=10){
  folds <- vfold_cv(data, v=v)
  map_dfr(folds$splits, function(s){
    tr <- analysis(s); te <- assessment(s)
    fit <- lm(formula, data=tr)
    pred <- predict(fit, newdata=te)
    tibble(rmse = sqrt(mean((te$preciom - pred)^2)),
           r2   = cor(te$preciom, pred, use="complete.obs")^2)
  }) |> summarise(RMSE = mean(rmse), R2_val = mean(r2))
}

cv_base1 <- cv_metrics(base1, f_base, v=10)
cv_base2 <- cv_metrics(base2, f2_base, v=10)

cv_base1; cv_base2
## # A tibble: 1 × 2
##    RMSE R2_val
##   <dbl>  <dbl>
## 1  157.  0.607
## # A tibble: 1 × 2
##    RMSE R2_val
##   <dbl>  <dbl>
## 1  97.9  0.749
  • RMSE = 154.582 → en promedio, el error estándar de predicción es de unas 154.6 unidades monetarias respecto al precio real.

  • R²_val = 0.6167 → el modelo explica aproximadamente el 61.7% de la variabilidad en los precios en los conjuntos de validación.

2.9 Hipótesis lineales generales y SSextra

# Contribución conjunta de baños y parqueaderos
car::linearHypothesis(mod1, c("banios = 0", "parqueaderos = 0"))
## 
## Linear hypothesis test:
## banios = 0
## parqueaderos = 0
## 
## Model 1: restricted model
## Model 2: preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios
## 
##   Res.Df      RSS Df Sum of Sq      F   Pr(>F)    
## 1    431 11025300                                 
## 2    429 10322017  2    703283 14.615 7.24e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Comparación anidada por SSextra
m_reduc <- lm(preciom ~ areaconst + estrato + habitaciones, data = base1)
anova(m_reduc, mod1)
## Analysis of Variance Table
## 
## Model 1: preciom ~ areaconst + estrato + habitaciones
## Model 2: preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios
##   Res.Df      RSS Df Sum of Sq      F   Pr(>F)    
## 1    431 11025300                                 
## 2    429 10322017  2    703283 14.615 7.24e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
  1. Prueba de hipótesis lineal (car::linearHypothesis)
  • Hipótesis nula: β_baños = 0 y β_parqueaderos = 0.

  • Resultado: F = 14.615, p < 0.001 (7.24e-07).

Conclusión: se rechaza H0, por lo tanto baños y parqueaderos aportan conjuntamente de manera significativa al modelo.

  1. ANOVA de modelos anidados (m_reduc vs. m1)
  • Modelo reducido: solo areaconst + estrato + habitaciones.

  • Modelo completo (m1): además incluye baños y parqueaderos.

  • Comparación: ΔRSS = 703283, F = 14.615, p < 0.001.

Conclusión: el modelo completo ajusta significativamente mejor que el reducido.

3. Reproducibilidad

sessionInfo() ##Necesario y Util para informes académicos y actualizaciones posteriores
## R version 4.4.2 (2024-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
## 
## Matrix products: default
## 
## 
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8  LC_CTYPE=Spanish_Colombia.utf8   
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C                     
## [5] LC_TIME=Spanish_Colombia.utf8    
## 
## time zone: America/Bogota
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] paqueteMODELOS_0.1.0 summarytools_1.1.0   knitr_1.49          
##  [4] gridExtra_2.3        boot_1.3-31          scales_1.3.0        
##  [7] rsample_1.3.1        GGally_2.2.1         car_3.1-3           
## [10] carData_3.0-5        performance_0.15.1   gtsummary_2.4.0     
## [13] modelsummary_2.5.0   broom_1.0.7          leaflet_2.2.2       
## [16] plotly_4.11.0        DataExplorer_0.8.4   skimr_2.1.5         
## [19] janitor_2.2.1        lubridate_1.9.4      forcats_1.0.0       
## [22] stringr_1.5.1        dplyr_1.1.4          purrr_1.0.4         
## [25] readr_2.1.5          tidyr_1.3.1          tibble_3.2.1        
## [28] ggplot2_3.5.2        tidyverse_2.0.0     
## 
## loaded via a namespace (and not attached):
##  [1] tcltk_4.4.2        rlang_1.1.5        magrittr_2.0.3     snakecase_0.11.1  
##  [5] furrr_0.3.1        matrixStats_1.5.0  compiler_4.4.2     mgcv_1.9-1        
##  [9] vctrs_0.6.5        reshape2_1.4.4     pkgconfig_2.0.3    fastmap_1.2.0     
## [13] backports_1.5.0    magick_2.8.5       labeling_0.4.3     effectsize_1.0.1  
## [17] pander_0.6.5       utf8_1.2.4         rmarkdown_2.29     tzdb_0.4.0        
## [21] xfun_0.51          cachem_1.1.0       jsonlite_1.9.0     tinytable_0.13.0  
## [25] pryr_0.1.6         parallel_4.4.2     data.tree_1.2.0    R6_2.6.1          
## [29] bslib_0.9.0        tables_0.9.31      stringi_1.8.4      RColorBrewer_1.1-3
## [33] parallelly_1.45.1  lmtest_0.9-40      estimability_1.5.1 jquerylib_0.1.4   
## [37] Rcpp_1.0.14        zoo_1.8-12         parameters_0.28.1  base64enc_0.1-3   
## [41] Matrix_1.7-1       splines_4.4.2      igraph_2.1.4       timechange_0.3.0  
## [45] tidyselect_1.2.1   rstudioapi_0.17.1  abind_1.4-8        yaml_2.3.10       
## [49] codetools_0.2-20   listenv_0.9.1      lattice_0.22-6     plyr_1.8.9        
## [53] bayestestR_0.17.0  withr_3.0.2        evaluate_1.0.3     future_1.67.0     
## [57] ggstats_0.9.0      pillar_1.10.1      checkmate_2.3.2    insight_1.4.1     
## [61] generics_0.1.3     hms_1.1.3          munsell_0.5.1      xtable_1.8-4      
## [65] globals_0.18.0     glue_1.8.0         emmeans_1.11.2     lazyeval_0.2.2    
## [69] tools_4.4.2        see_0.11.0         data.table_1.17.8  mvtnorm_1.3-3     
## [73] rapportools_1.1    grid_4.4.2         crosstalk_1.2.1    datawizard_1.2.0  
## [77] colorspace_2.1-1   patchwork_1.3.0    nlme_3.1-166       networkD3_0.4.1   
## [81] repr_1.1.7         Formula_1.2-5      cli_3.6.4          fansi_1.0.6       
## [85] viridisLite_0.4.2  gtable_0.3.6       sass_0.4.9         digest_0.6.37     
## [89] ggrepel_0.9.6      farver_2.1.2       htmlwidgets_1.6.4  htmltools_0.5.8.1 
## [93] lifecycle_1.0.4    httr_1.4.7         MASS_7.3-61

4. Conclusiones (Resumen Ejecutivo)

Fase 1: Preparación y Filtrado de datos

Se depuró la base vivienda, se filtraron registros según criterios (tipo y zona) y se verificó consistencia geográfica con mapas interactivos. La base resultante es representativa para cada caso (Casas Norte y Apartamentos Sur). Se detectaron posibles inconsistencias en coordenadas, lo que sugiere validar direcciones antes de la negociación.

Fase 2: Analisis Exploratorio EDA

Se analizaron correlaciones entre precio y variables clave (área, estrato, baños, habitaciones, parqueaderos) con gráficos interactivos y matriz de correlación. El precio se asocia fuertemente con área construida y estrato; variables como baños y parqueaderos también aportan valor. Esto valida su inclusión en el modelo. La variable zona no varía dentro de cada submuestra, pero a nivel global muestra diferencias significativas.

Fase 3: Modelación (MRLM)

Se ajustaron modelos lineales múltiples (base, con área², con interacción área:estrato) y se compararon por R², AIC, BIC y RMSE. El modelo base ofrece buen equilibrio entre simplicidad y ajuste. Modelos con interacciones mejoran ligeramente el ajuste, pero incrementan complejidad. Esto sugiere que el modelo base es adecuado para predicción operativa.

Fase 4: Validación de Supuestos

Se evaluaron normalidad, homocedasticidad, multicolinealidad (VIF) y observaciones influyentes (Cook, DFFITS, DFBETAS). No se detectaron problemas críticos de colinealidad. Se identificaron algunos outliers, pero no alteran significativamente el ajuste. Si se busca mayor robustez, se recomienda probar transformaciones (log-precio) y segmentación por subzonas.

Fase 5: Predicción

Se estimaron precios esperados y PI95% para los perfiles solicitados (Casa Norte y Apto Sur). Las predicciones permiten anticipar si el crédito disponible es suficiente. En ambos casos, los valores estimados se encuentran dentro del rango esperado del mercado, aunque en Vivienda 2 el margen es más ajustado.

Fase 6: Recomendación de ofertas

Se filtraron propiedades reales que cumplen con las condiciones y el tope de crédito, mostrando al menos 5 opciones en mapas interactivos. Se identificaron alternativas viables para ambas solicitudes. Esto respalda la toma de decisiones y reduce el riesgo de sobrepasar el presupuesto.

Fase 7: Validación Cruzada

Se aplicó validación cruzada (k-fold) para estimar la estabilidad del modelo. El R² en validación es cercano al R² de entrenamiento, lo que indica bajo riesgo de sobreajuste. Esto refuerza la confiabilidad del modelo para predicciones futuras.

Conclusión Final

El flujo metodológico (filtrado → EDA → modelación → validación → predicción → recomendación → validación cruzada) garantiza un análisis robusto y reproducible. Las recomendaciones están basadas en evidencia estadística y visual, lo que aporta confianza a la decisión de compra. El uso de mapas interactivos y validación cruzada fortalece la presentación ejecutiva y la credibilidad técnica.