Resumen ejecutivo y metodología

Objetivo: apoyar la decisión de compra de 2 viviendas (V1: Casa Norte, V2: Apartamento Sur) usando Regresión Lineal Múltiple (MCO).
EDA con plotly Modelo RLM (MCO) Validación de supuestos Predicción + set de prueba Selección de 5 ofertas + mapa

Plan de trabajo (Anexo 1):
1) Filtrar ofertas por tipo y zona según solicitud.
2) Análisis exploratorio (precio vs área, estrato, baños, habitaciones, parqueaderos) con gráficos interactivos.
3) Estimar RLM: precio ~ área + estrato (categórica) + habitaciones + parqueaderos + baños.
4) Evaluar desempeño en set de prueba (RMSE, MAE, R²_test).
5) Validar supuestos (residuos, heterocedasticidad, normalidad, VIF).
6) Predecir precio de solicitud y proponer ≥5 ofertas bajo el crédito.
# install.packages("devtools") # solo 1 vez (NO ejecutar siempre)
# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE) # solo 1 vez

library(paqueteMODELOS)
data("vivienda")
base <- vivienda

# Vista rápida
dim(base)
[1] 8322   13
glimpse(base)
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…
base <- base %>%
  mutate(
    # Normalizar texto de zona/tipo (viene como "Zona Norte", etc.)
    zona_raw = as.character(zona),
    tipo_raw = as.character(tipo),
    zona = str_to_lower(zona_raw) %>%
      str_replace_all("zona\\s+", "") %>%
      str_trim() %>%
      str_to_title(),
    tipo = str_trim(tipo_raw) %>% str_to_title(),

    zona  = as.factor(zona),
    tipo  = as.factor(tipo),
    barrio = as.factor(barrio),
    piso   = as.factor(piso),

    # Estrato como categórica (factor ordenado)
    estrato = as.character(estrato),
    estrato = factor(estrato,
                     levels = sort(unique(as.numeric(estrato))),
                     ordered = TRUE),

    # Numéricas
    preciom = as.numeric(preciom),
    areaconst = as.numeric(areaconst),
    parqueaderos = as.numeric(parqueaderos),
    banios = as.numeric(banios),
    habitaciones = as.numeric(habitaciones),
    longitud = as.numeric(longitud),
    latitud = as.numeric(latitud)
  ) %>%
  filter(!is.na(longitud), !is.na(latitud))

# Verificación niveles clave para filtros
levels(base$zona)
[1] "Centro"  "Norte"   "Oeste"   "Oriente" "Sur"    
levels(base$tipo)
[1] "Apartamento" "Casa"       
levels(base$estrato)
[1] "3" "4" "5" "6"

1 Caso 1 — Vivienda 1 (Casa, Zona Norte, crédito 350M)

1.1 Paso 1) Filtro + evidencia + mapa

base1 <- base %>% filter(tipo == "Casa", zona == "Norte")

head(base1, 3)
# A tibble: 3 × 15
     id zona  piso  estrato preciom areaconst parqueaderos banios habitaciones
  <dbl> <fct> <fct> <ord>     <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
1  1209 Norte 02    5           320       150            2      4            6
2  1592 Norte 02    5           780       380            2      3            3
3  4057 Norte 02    6           750       445           NA      7            6
# ℹ 6 more variables: tipo <fct>, barrio <fct>, longitud <dbl>, latitud <dbl>,
#   zona_raw <chr>, tipo_raw <chr>
# Evidencia del filtro
table(base1$tipo)

Apartamento        Casa 
          0         722 
table(base1$zona)

 Centro   Norte   Oeste Oriente     Sur 
      0     722       0       0       0 
leaflet(base1) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng = ~longitud, lat = ~latitud,
    radius = 4,
    popup = ~paste("Barrio:", barrio,
                   "<br>Precio (M):", preciom,
                   "<br>Área:", areaconst,
                   "<br>Estrato:", estrato)
  )

Vivienda 1 — Validación geográfica (mapa)

Discusión: ¿todos los puntos están en la zona correspondiente?
  • Observación: Aunque el filtro confirma que los registros corresponden a Casa en Zona Norte, en el mapa se evidencia que la mayoría de los puntos se concentran en el sector norte, pero algunos aparecen dispersos hacia zonas más centrales y suroccidentales, fuera del patrón geográfico esperado para la Zona Norte.
  • Posibles causas: La variable “zona” puede responder a una clasificación administrativa o comercial (barrios limítrofes considerados como Norte) y/o existir imprecisiones en la georreferenciación (coordenadas aproximadas del barrio o registros atípicos en latitud/longitud).
  • Implicación: Estos puntos atípicos podrían introducir ruido espacial y afectar la relación ubicación–precio en el modelo. Se recomienda tratarlos como posibles outliers geográficos o validar su coherencia antes del modelamiento.

1.2 Paso 2) EDA con plotly + correlación (interpretación en cards)

# Correlación solo numéricas (estrato es factor)
vars_num1 <- base1 %>%
  select(preciom, areaconst, banios, habitaciones, parqueaderos) %>%
  drop_na()

cor1 <- cor(vars_num1)
knitr::kable(round(cor1, 3), caption = "Caso 1: Matriz de correlación (variables numéricas)")
Caso 1: Matriz de correlación (variables numéricas)
preciom areaconst banios habitaciones parqueaderos
preciom 1.000 0.685 0.509 0.365 0.412
areaconst 0.685 1.000 0.457 0.421 0.307
banios 0.509 0.457 1.000 0.590 0.392
habitaciones 0.365 0.421 0.590 1.000 0.241
parqueaderos 0.412 0.307 0.392 0.241 1.000
plot_ly(base1, x = ~areaconst, y = ~preciom, type = "scatter", mode = "markers",
        text = ~paste("Barrio:", barrio, "<br>Estrato:", estrato)) %>%
  layout(title = "Caso 1: Precio vs Área construida (Casa - Norte)",
         xaxis = list(title = "Área construida"),
         yaxis = list(title = "Precio (millones)"))
plot_ly(base1, x = ~estrato, y = ~preciom, type = "box",
        boxpoints = "all", jitter = 0.25,
        text = ~paste("Barrio:", barrio, "<br>Área:", areaconst)) %>%
  layout(title = "Caso 1: Precio por Estrato (Casa - Norte)",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio (millones)"))
p2 <- plot_ly(base1, x=~banios, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Baños", xaxis=list(title="Baños"), yaxis=list(title="Precio (M)"))
p3 <- plot_ly(base1, x=~habitaciones, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Habitaciones", xaxis=list(title="Habitaciones"), yaxis=list(title="Precio (M)"))
p4 <- plot_ly(base1, x=~parqueaderos, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Parqueaderos", xaxis=list(title="Parqueaderos"), yaxis=list(title="Precio (M)"))

subplot(p2, p3, p4, nrows = 1, shareY = TRUE)

Vivienda 1 — Hallazgos EDA (plotly)

Análisis exploratorio de la relación entre el precio y las variables explicativas.
  • Área construida: Se observa una relación positiva y fuerte con el precio (correlación ≈ 0.685). A mayor área, el precio tiende a incrementarse, aunque con dispersión creciente en áreas grandes, lo que sugiere variabilidad en viviendas de alto valor.
  • Estrato: Existe una tendencia clara y creciente del precio a medida que aumenta el estrato socioeconómico. Las viviendas de estratos 5 y 6 presentan los niveles de precio más altos y mayor variabilidad, lo cual es coherente con el comportamiento del mercado inmobiliario.
  • Baños y habitaciones: Ambas variables muestran relaciones positivas moderadas con el precio (correlaciones ≈ 0.509 y 0.365 respectivamente). Esto indica que viviendas con más baños y habitaciones tienden a ser más costosas, aunque su impacto es menor que el del área construida.
  • Parqueaderos: Presenta una relación positiva moderada con el precio (correlación ≈ 0.412). Se observa que viviendas con más parqueaderos suelen ubicarse en rangos de precio más altos, aunque con cierta dispersión.
  • Outliers: Se identifican algunos valores atípicos en precios muy altos y áreas grandes. Estos pueden influir en el ajuste del modelo y deben considerarse durante la validación.

1.3 Paso 3) Modelo RLM (MCO) + interpretación + R² (tablas)

set.seed(123)
idx1 <- createDataPartition(base1$preciom, p = 0.8, list = FALSE)

train1 <- base1[idx1, ] %>% drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios)
test1  <- base1[-idx1, ] %>% drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios)

c(train = nrow(train1), test = nrow(test1))
train  test 
  353    82 
m1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train1)

# Tabla de coeficientes
knitr::kable(broom::tidy(m1), digits = 5, caption = "Caso 1: Coeficientes del modelo (MCO)")
Caso 1: Coeficientes del modelo (MCO)
term estimate std.error statistic p.value
(Intercept) 135.14855 30.31688 4.45786 0.00001
areaconst 0.70960 0.05932 11.96309 0.00000
estrato.L 211.79757 29.22093 7.24814 0.00000
estrato.Q 26.07203 22.36234 1.16589 0.24446
estrato.C 12.41235 16.65029 0.74547 0.45649
habitaciones 10.34466 6.35521 1.62774 0.10449
parqueaderos 21.99768 6.75095 3.25845 0.00123
banios 14.22075 8.46768 1.67942 0.09398
summary(m1)$r.squared
[1] 0.6055917
summary(m1)$adj.r.squared
[1] 0.5975892

Vivienda 1 — Interpretación del modelo (coeficientes)

Contexto: Casas en Zona Norte. El estrato se modela como variable categórica con contraste polinómico.
  • Área construida: Es la variable con mayor impacto. Por cada 1 m² adicional, el precio aumenta en aproximadamente 0.71 millones, manteniendo constantes las demás variables (p < 0.001).
  • Estrato: El componente lineal del estrato es altamente significativo (p < 0.001), indicando que el precio aumenta sistemáticamente al pasar a estratos superiores. Los componentes cuadrático y cúbico no son significativos, lo que sugiere una relación principalmente lineal entre estrato y precio.
  • Parqueaderos: Tiene un efecto positivo y significativo en el precio. Cada parqueadero adicional incrementa el valor de la vivienda en aproximadamente 22 millones (p = 0.001).
  • Baños y habitaciones: Presentan efectos positivos pero no estadísticamente significativos al 5% (p > 0.05). Esto indica que su impacto sobre el precio es menor cuando se controla por el área y el estrato.
  • Conclusión: El modelo confirma que el precio de las viviendas en la Zona Norte depende principalmente del tamaño (área construida), el estrato socioeconómico y la disponibilidad de parqueaderos, lo cual es coherente con la lógica del mercado inmobiliario.

Vivienda 1 — Ajuste del modelo (R²)

Evaluación de la capacidad explicativa del modelo en el conjunto de entrenamiento.
0.606
R² ajustado
0.598

El modelo explica aproximadamente el 60.6% de la variabilidad del precio de las viviendas en el conjunto de entrenamiento, lo cual representa un buen nivel de ajuste para datos del mercado inmobiliario. La diferencia mínima con el R² ajustado indica que el modelo no está sobreajustado y que las variables incluidas aportan información relevante.

1.4 Paso 4) Validación de supuestos (tablas + plots) + card

par(mfrow = c(2,2))
plot(m1)

par(mfrow = c(1,1))
bp1  <- bptest(m1)
sh1  <- shapiro.test(residuals(m1))
dw1  <- dwtest(m1)
vif1 <- car::vif(m1)

diag1 <- data.frame(
  BP_pvalue = bp1$p.value,
  Shapiro_pvalue = sh1$p.value,
  DW_pvalue = dw1$p.value,
  VIF_max = max(vif1)
)

knitr::kable(diag1, digits = 6, caption = "Caso 1: Diagnóstico de supuestos (resumen)")
Caso 1: Diagnóstico de supuestos (resumen)
BP_pvalue Shapiro_pvalue DW_pvalue VIF_max
BP 0 0 0.000138 3

Vivienda 1 — Supuestos del modelo (interpretación)

Evaluación formal y gráfica de los supuestos del modelo RLM (MCO).
Heterocedasticidad (BP p = 0.000) Normalidad (Shapiro p = 0.000) Independencia (DW p = 0.000138) Multicolinealidad (VIF máx = 3)

  • Heterocedasticidad: La prueba de Breusch-Pagan es significativa (p < 0.05), lo que indica que la varianza de los residuos no es constante. Esto también se observa en el gráfico Scale-Location, donde la dispersión aumenta con los valores ajustados.
  • Normalidad: La prueba de Shapiro-Wilk rechaza la normalidad de los residuos (p < 0.05). El gráfico Q-Q muestra desviaciones en las colas, posiblemente asociadas a valores atípicos en viviendas de alto precio.
  • Independencia: La prueba de Durbin-Watson resulta significativa (p < 0.05), lo que sugiere posible correlación en los residuos. Sin embargo, al tratarse de datos de corte transversal (no temporales), este resultado puede no ser crítico para la interpretación económica.
  • Multicolinealidad: El VIF máximo es 3, valor inferior al umbral crítico (5 o 10), por lo que no se evidencia colinealidad severa entre las variables explicativas.
  • Conclusión: Aunque el modelo presenta violaciones en normalidad y homocedasticidad, los coeficientes siguen siendo insesgados bajo MCO. Para mejorar la inferencia, podría considerarse una transformación logarítmica del precio o el uso de errores estándar robustos.

1.5 Indicadores en set de prueba (rúbrica) + card

pred_test1 <- predict(m1, newdata = test1)

RMSE1 <- RMSE(pred_test1, test1$preciom)
MAE1  <- MAE(pred_test1, test1$preciom)
R2t1  <- cor(pred_test1, test1$preciom)^2

perf1 <- data.frame(RMSE = RMSE1, MAE = MAE1, R2_test = R2t1)
knitr::kable(perf1, digits = 4, caption = "Caso 1: Desempeño en set de prueba")
Caso 1: Desempeño en set de prueba
RMSE MAE R2_test
148.5616 96.6495 0.613

Vivienda 1 — Desempeño del modelo (set de prueba)

Evaluación del poder predictivo del modelo en datos no utilizados durante el entrenamiento.
RMSE
148.56
MAE
96.65
R² (test)
0.613

  • RMSE: El error cuadrático medio de 148.56 millones indica que, en promedio, las predicciones del modelo se desvían alrededor de este valor respecto al precio real. Este nivel de error es razonable considerando la alta variabilidad del mercado inmobiliario.
  • MAE: El error absoluto medio de 96.65 millones muestra que el error típico de predicción es cercano a 100 millones, lo cual es aceptable para viviendas cuyo rango de precios es amplio.
  • R² en prueba: El modelo explica aproximadamente el 61.3% de la variabilidad del precio en datos nuevos, lo que evidencia una buena capacidad de generalización y un desempeño consistente con el observado en el entrenamiento.
  • Conclusión: El modelo presenta un buen equilibrio entre ajuste y capacidad predictiva, siendo adecuado para estimar el precio de viviendas en la Zona Norte con base en sus características estructurales.

1.6 Paso 5) Predicción solicitud Vivienda 1 (estrato 4–5)

lvl1 <- levels(train1$estrato)

req1_e4 <- data.frame(
  areaconst = 200,
  estrato = factor("4", levels = lvl1, ordered = TRUE),
  parqueaderos = 1,
  banios = 2,
  habitaciones = 4
)

req1_e5 <- data.frame(
  areaconst = 200,
  estrato = factor("5", levels = lvl1, ordered = TRUE),
  parqueaderos = 1,
  banios = 2,
  habitaciones = 4
)

pred_req1_e4 <- predict(m1, newdata = req1_e4, interval = "prediction")
pred_req1_e5 <- predict(m1, newdata = req1_e5, interval = "prediction")

pred_tbl1 <- rbind(
  data.frame(escenario = "Estrato 4", pred_req1_e4),
  data.frame(escenario = "Estrato 5", pred_req1_e5)
)

knitr::kable(pred_tbl1, digits = 2, caption = "Caso 1: Predicción (intervalo de predicción) para la solicitud")
Caso 1: Predicción (intervalo de predicción) para la solicitud
escenario fit lwr upr
1 Estrato 4 316.82 5.62 628.01
11 Estrato 5 394.88 84.34 705.43

Vivienda 1 — Predicción del precio y viabilidad financiera

Estimación del precio para la vivienda solicitada (Casa, Zona Norte, 200 m², 1 parqueadero, 2 baños, 4 habitaciones).
Precio estimado (Estrato 4)
316.82 M
Intervalo de predicción
[5.62 M — 628.01 M]
Precio estimado (Estrato 5)
394.88 M
Intervalo de predicción
[84.34 M — 705.43 M]

  • Viabilidad con crédito: La empresa cuenta con un crédito preaprobado de 350 millones. El precio estimado para estrato 4 (316.82 M) se encuentra dentro del presupuesto, por lo que la compra es financieramente viable.
  • Para estrato 5, el precio estimado es 394.88 M, lo cual supera el presupuesto disponible, por lo que sería necesario aumentar el financiamiento o considerar una negociación.
  • Interpretación del intervalo: Los intervalos de predicción son amplios debido a la variabilidad del mercado inmobiliario, sin embargo, la estimación puntual proporciona una referencia confiable para la toma de decisión.
  • Recomendación: En términos financieros, la mejor opción es buscar viviendas con características similares en estrato 4, donde la probabilidad de encontrar inmuebles dentro del presupuesto es mayor.

1.7 Paso 6) Ofertas potenciales (Top 5) + mapa

target_estratos1 <- c("4","5")

base1_scored <- base1 %>%
  drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios) %>%
  mutate(pred = predict(m1, newdata = .))

offers1 <- base1_scored %>%
  filter(preciom <= 350) %>%
  mutate(
    estrato_match = ifelse(as.character(estrato) %in% target_estratos1, 0, 1),
    dist = abs(areaconst - 200) +
      abs(habitaciones - 4) +
      abs(banios - 2) +
      abs(parqueaderos - 1) +
      estrato_match * 50
  ) %>%
  arrange(dist) %>%
  slice_head(n = 5)

knitr::kable(
  offers1 %>% select(preciom, pred, areaconst, estrato, habitaciones, banios, parqueaderos, barrio),
  digits = 2,
  caption = "Caso 1: Top 5 ofertas potenciales (precio <= 350M)"
)
Caso 1: Top 5 ofertas potenciales (precio <= 350M)
preciom pred areaconst estrato habitaciones banios parqueaderos barrio
350 453.10 200 5 4 3 3 el bosque
320 445.32 200 5 4 4 2 la flora
320 367.26 200 4 4 4 2 la merced
335 435.09 202 5 5 4 1 el bosque
350 429.35 203 5 5 2 2 el bosque
leaflet(offers1) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng=~longitud, lat=~latitud, radius=6,
    popup=~paste("Barrio:", barrio,
                 "<br>Precio:", preciom,
                 "<br>Pred:", round(pred,1),
                 "<br>Área:", areaconst,
                 "<br>Estrato:", estrato)
  )

Vivienda 1 — Recomendación de 5 ofertas potenciales

Selección de inmuebles que cumplen con las características solicitadas y un precio ≤ 350 millones.
  • Cumplimiento de condiciones: Todas las viviendas seleccionadas cumplen con el presupuesto máximo de 350 millones y presentan características similares a las requeridas (área ≈ 200 m², 4–5 habitaciones, 2–4 baños y ubicación en la Zona Norte).
  • Comparación con el modelo: Las predicciones del modelo para estas viviendas oscilan entre 367 M y 453 M, lo que indica que algunas propiedades se encuentran por debajo de su valor estimado, representando oportunidades de compra favorables.
  • Ubicación: Las ofertas se concentran en barrios como El Bosque, La Flora y La Merced, sectores reconocidos de la Zona Norte, lo que garantiza coherencia con la solicitud del cliente.
  • Mejor equilibrio precio–beneficio: Destacan especialmente las viviendas con precios de 320–335 millones, ya que presentan características similares a la solicitud pero con un valor inferior al promedio estimado por el modelo.
  • Recomendación: Se sugiere priorizar la evaluación de las viviendas ubicadas en El Bosque y La Flora, debido a su buena relación entre precio, características estructurales y ubicación estratégica dentro de la Zona Norte.

2 Caso 2 — Vivienda 2 (Apartamento, Zona Sur, crédito 850M)

2.1 Paso 1) Filtro + evidencia + mapa

base2 <- base %>% filter(tipo == "Apartamento", zona == "Sur")

head(base2, 3)
# A tibble: 3 × 15
     id zona  piso  estrato preciom areaconst parqueaderos banios habitaciones
  <dbl> <fct> <fct> <ord>     <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
1  5098 Sur   05    4           290        96            1      2            3
2   698 Sur   02    3            78        40            1      1            2
3  8199 Sur   <NA>  6           875       194            2      5            3
# ℹ 6 more variables: tipo <fct>, barrio <fct>, longitud <dbl>, latitud <dbl>,
#   zona_raw <chr>, tipo_raw <chr>
table(base2$tipo)

Apartamento        Casa 
       2787           0 
table(base2$zona)

 Centro   Norte   Oeste Oriente     Sur 
      0       0       0       0    2787 
leaflet(base2) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng = ~longitud, lat = ~latitud,
    radius = 4,
    popup = ~paste("Barrio:", barrio,
                   "<br>Precio (M):", preciom,
                   "<br>Área:", areaconst,
                   "<br>Estrato:", estrato)
  )

Vivienda 2 — Validación geográfica (mapa)

Discusión: ¿los puntos corresponden realmente a la Zona Sur?
  • Observación: El filtro confirma que los 2,787 registros corresponden a Apartamentos en la Zona Sur. En el mapa se observa una alta concentración de puntos en el sector sur de la ciudad, coherente con la clasificación realizada.
  • Distribución espacial: La mayor densidad se ubica en el corredor sur–suroccidental, con una dispersión leve hacia sectores contiguos, lo cual es consistente con los límites urbanos y barrios limítrofes.
  • Posibles inconsistencias: No se evidencian agrupaciones claras fuera del patrón esperado; sin embargo, podrían existir casos puntuales asociados a georreferenciación aproximada por barrio.
  • Implicación: La coherencia entre el filtro y la distribución espacial indica que la base es consistente para el modelamiento, reduciendo el riesgo de sesgo por mezcla de zonas.

2.2 Paso 2) EDA con plotly + correlación

vars_num2 <- base2 %>%
  select(preciom, areaconst, banios, habitaciones, parqueaderos) %>%
  drop_na()

cor2 <- cor(vars_num2)
knitr::kable(round(cor2, 3), caption = "Caso 2: Matriz de correlación (variables numéricas)")
Caso 2: Matriz de correlación (variables numéricas)
preciom areaconst banios habitaciones parqueaderos
preciom 1.000 0.741 0.711 0.296 0.693
areaconst 0.741 1.000 0.664 0.407 0.578
banios 0.711 0.664 1.000 0.520 0.556
habitaciones 0.296 0.407 0.520 1.000 0.237
parqueaderos 0.693 0.578 0.556 0.237 1.000
plot_ly(base2, x = ~areaconst, y = ~preciom, type = "scatter", mode = "markers",
        text = ~paste("Barrio:", barrio, "<br>Estrato:", estrato)) %>%
  layout(title = "Caso 2: Precio vs Área construida (Apartamento - Sur)",
         xaxis = list(title = "Área construida"),
         yaxis = list(title = "Precio (millones)"))
plot_ly(base2, x = ~estrato, y = ~preciom, type = "box",
        boxpoints = "all", jitter = 0.25,
        text = ~paste("Barrio:", barrio, "<br>Área:", areaconst)) %>%
  layout(title = "Caso 2: Precio por Estrato (Apartamento - Sur)",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio (millones)"))
q2 <- plot_ly(base2, x=~banios, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Baños", xaxis=list(title="Baños"), yaxis=list(title="Precio (M)"))
q3 <- plot_ly(base2, x=~habitaciones, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Habitaciones", xaxis=list(title="Habitaciones"), yaxis=list(title="Precio (M)"))
q4 <- plot_ly(base2, x=~parqueaderos, y=~preciom, type="scatter", mode="markers") %>%
  layout(title="Precio vs Parqueaderos", xaxis=list(title="Parqueaderos"), yaxis=list(title="Precio (M)"))

subplot(q2, q3, q4, nrows = 1, shareY = TRUE)

Vivienda 2 — Hallazgos EDA (plotly)

Análisis exploratorio para Apartamentos en la Zona Sur.
  • Área construida: Presenta una relación positiva fuerte con el precio (correlación ≈ 0.741). El gráfico muestra una tendencia claramente creciente: apartamentos de mayor tamaño tienden a alcanzar precios considerablemente más altos, aunque con mayor dispersión en áreas superiores.
  • Baños: Es una de las variables con mayor asociación al precio (correlación ≈ 0.711). Esto sugiere que en apartamentos del sur, el número de baños es un atributo altamente valorado por el mercado.
  • Parqueaderos: También muestra una relación positiva fuerte (correlación ≈ 0.693). A mayor número de parqueaderos, mayor tiende a ser el precio, lo cual es coherente en zonas residenciales de alto nivel.
  • Habitaciones: Presenta una relación positiva pero más moderada (correlación ≈ 0.296), indicando que el número de habitaciones influye en el precio, aunque menos que el área y los baños.
  • Estrato: El boxplot muestra una tendencia claramente creciente del precio a medida que aumenta el estrato (3 → 6). Los estratos altos presentan mayor mediana y mayor dispersión, reflejando mayor heterogeneidad en inmuebles de lujo.
  • Outliers: Se observan valores atípicos en precios superiores a 1,500 millones, lo que puede influir en el ajuste del modelo y sugiere la presencia de apartamentos de lujo dentro del conjunto de datos.

2.3 Paso 3) Modelo + coeficientes + R²

set.seed(123)
idx2 <- createDataPartition(base2$preciom, p = 0.8, list = FALSE)

train2 <- base2[idx2, ] %>% drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios)
test2  <- base2[-idx2, ] %>% drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios)

c(train = nrow(train2), test = nrow(test2))
train  test 
 1903   478 
m2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train2)

knitr::kable(broom::tidy(m2), digits = 5, caption = "Caso 2: Coeficientes del modelo (MCO)")
Caso 2: Coeficientes del modelo (MCO)
term estimate std.error statistic p.value
(Intercept) 49.98100 11.58695 4.31356 2e-05
areaconst 1.32811 0.05717 23.23277 0e+00
estrato.L 146.31605 8.60694 16.99977 0e+00
estrato.Q 63.79428 6.24117 10.22153 0e+00
estrato.C 33.11107 4.15630 7.96647 0e+00
habitaciones -17.46739 4.12780 -4.23165 2e-05
parqueaderos 56.97835 4.14184 13.75678 0e+00
banios 40.95771 3.66304 11.18133 0e+00
summary(m2)$r.squared
[1] 0.774101
summary(m2)$adj.r.squared
[1] 0.7732666

Vivienda 2 — Interpretación del modelo (coeficientes)

Modelo RLM (MCO) para Apartamentos en Zona Sur.
  • Área construida: Es la variable con mayor impacto estructural. Por cada 1 m² adicional, el precio aumenta en aproximadamente 1.33 millones (p < 0.001). Su efecto es más fuerte que en el caso de casas del norte.
  • Estrato: Todos los componentes (lineal, cuadrático y cúbico) son estadísticamente significativos (p < 0.001), indicando que la relación entre estrato y precio es no lineal y más pronunciada en estratos altos.
  • Parqueaderos: Tiene un impacto positivo fuerte. Cada parqueadero adicional incrementa el precio en aproximadamente 57 millones (p < 0.001), evidenciando alta valoración de este atributo en apartamentos del sur.
  • Baños: También es altamente significativo. Cada baño adicional incrementa el precio en aproximadamente 41 millones (p < 0.001).
  • Habitaciones: Presenta un coeficiente negativo (-17.47) y significativo. Esto sugiere que, controlando por área, un mayor número de habitaciones podría asociarse con menor valor por metro cuadrado, posiblemente reflejando redistribución del espacio.
  • Conclusión: El precio de apartamentos en Zona Sur depende principalmente del tamaño, estrato, parqueaderos y baños, con efectos más intensos que en el caso de casas del norte.

Vivienda 2 — Ajuste del modelo (R²)

Capacidad explicativa del modelo en el conjunto de entrenamiento.
0.774
R² ajustado
0.773

El modelo explica aproximadamente el 77.4% de la variabilidad del precio, lo cual representa un ajuste alto para datos inmobiliarios. La mínima diferencia entre R² y R² ajustado indica que el modelo está bien especificado y no presenta sobreajuste relevante.

2.4 Paso 4) Supuestos + diagnóstico

par(mfrow = c(2,2))
plot(m2)

par(mfrow = c(1,1))
bp2  <- bptest(m2)
sh2  <- shapiro.test(residuals(m2))
dw2  <- dwtest(m2)
vif2 <- car::vif(m2)

diag2 <- data.frame(
  BP_pvalue = bp2$p.value,
  Shapiro_pvalue = sh2$p.value,
  DW_pvalue = dw2$p.value,
  VIF_max = max(vif2)
)

knitr::kable(diag2, digits = 6, caption = "Caso 2: Diagnóstico de supuestos (resumen)")
Caso 2: Diagnóstico de supuestos (resumen)
BP_pvalue Shapiro_pvalue DW_pvalue VIF_max
BP 0 0 0 3
```{=html}

Vivienda 2 — Supuestos del modelo (interpretación)

Evaluación gráfica y formal de los supuestos del modelo RLM para apartamentos en Zona Sur.
Heterocedasticidad (BP p = 0.000) Normalidad (Shapiro p = 0.000) Independencia (DW p = 0.000) Multicolinealidad (VIF máx = 3)

  • Heterocedasticidad: La prueba de Breusch-Pagan es significativa (p < 0.05), indicando que la varianza de los residuos no es constante. El gráfico Scale-Location confirma que la dispersión aumenta a medida que crecen los valores ajustados, fenómeno común en precios inmobiliarios.
  • Normalidad: La prueba de Shapiro-Wilk rechaza la normalidad (p < 0.05). El gráfico Q-Q muestra desviaciones marcadas en las colas, especialmente en precios altos, evidenciando presencia de apartamentos de lujo que generan asimetría.
  • Independencia: La prueba de Durbin-Watson resulta significativa; sin embargo, dado que los datos son de corte transversal y no temporales, la autocorrelación no representa un problema crítico para la interpretación económica.
  • Multicolinealidad: El VIF máximo es 3, valor inferior a los umbrales críticos (5–10), lo que indica ausencia de colinealidad severa entre las variables explicativas.
  • Conclusión: Aunque existen violaciones en homocedasticidad y normalidad, el modelo mantiene buena capacidad explicativa (R² alto). Para mejorar la inferencia, podría considerarse una transformación logarítmica del precio o el uso de errores estándar robustos.

2.5 Indicadores en set de prueba (rúbrica)

pred_test2 <- predict(m2, newdata = test2)

RMSE2 <- RMSE(pred_test2, test2$preciom)
MAE2  <- MAE(pred_test2, test2$preciom)
R2t2  <- cor(pred_test2, test2$preciom)^2

perf2 <- data.frame(RMSE = RMSE2, MAE = MAE2, R2_test = R2t2)
knitr::kable(perf2, digits = 4, caption = "Caso 2: Desempeño en set de prueba")
Caso 2: Desempeño en set de prueba
RMSE MAE R2_test
91.3445 56.9182 0.7831

Vivienda 2 — Desempeño del modelo (set de prueba)

Evaluación del rendimiento predictivo en datos no utilizados durante el entrenamiento.
RMSE
91.34
MAE
56.92
R² (test)
0.783

  • RMSE: El error cuadrático medio de 91.34 millones indica que, en promedio, las predicciones se desvían en este orden de magnitud respecto al valor real. Este nivel de error es bajo considerando el rango amplio de precios observados.
  • MAE: El error absoluto medio de 56.92 millones muestra que el error típico de predicción es relativamente reducido frente al valor total de los inmuebles, lo que evidencia alta precisión.
  • R² en prueba: El modelo explica aproximadamente el 78.3% de la variabilidad del precio en datos nuevos, confirmando una excelente capacidad de generalización.
  • Conclusión: En comparación con el Caso 1, este modelo presenta mayor estabilidad y menor error de predicción, lo que sugiere que el mercado de apartamentos en Zona Sur es más estructurado y predecible.

2.6 Paso 5) Predicción solicitud Vivienda 2 (estrato 5–6)

lvl2 <- levels(train2$estrato)

req2_e5 <- data.frame(
  areaconst = 300,
  estrato = factor("5", levels = lvl2, ordered = TRUE),
  parqueaderos = 3,
  banios = 3,
  habitaciones = 5
)

req2_e6 <- data.frame(
  areaconst = 300,
  estrato = factor("6", levels = lvl2, ordered = TRUE),
  parqueaderos = 3,
  banios = 3,
  habitaciones = 5
)

pred_req2_e5 <- predict(m2, newdata = req2_e5, interval = "prediction")
pred_req2_e6 <- predict(m2, newdata = req2_e6, interval = "prediction")

pred_tbl2 <- rbind(
  data.frame(escenario = "Estrato 5", pred_req2_e5),
  data.frame(escenario = "Estrato 6", pred_req2_e6)
)

knitr::kable(pred_tbl2, digits = 2, caption = "Caso 2: Predicción (intervalo de predicción) para la solicitud")
Caso 2: Predicción (intervalo de predicción) para la solicitud
escenario fit lwr upr
1 Estrato 5 633.49 449.61 817.37
11 Estrato 6 792.34 608.38 976.29

Vivienda 2 — Predicción del precio y viabilidad financiera

Estimación para apartamento en Zona Sur (300 m², 3 parqueaderos, 3 baños, 5 habitaciones).
Precio estimado (Estrato 5)
633.49 M
Intervalo
[449.61 — 817.37] M
Precio estimado (Estrato 6)
792.34 M
Intervalo
[608.38 — 976.29] M

  • Viabilidad con crédito (850 M): Ambos escenarios se encuentran dentro del presupuesto aprobado.
  • Para estrato 5, el precio estimado (633.49 M) deja un margen amplio dentro del crédito disponible.
  • Para estrato 6, el valor estimado (792.34 M) también es financieramente viable, aunque con menor margen de maniobra.
  • Interpretación del intervalo: El rango de predicción es más estrecho que en el Caso 1, reflejando mayor estabilidad del modelo en apartamentos del sur.
  • Recomendación: Desde el punto de vista financiero, ambas opciones son factibles; sin embargo, el estrato 5 ofrece mayor holgura presupuestal, mientras que el estrato 6 puede implicar mayor valorización y exclusividad.

2.7 Paso 6) Ofertas potenciales (Top 5) + mapa

target_estratos2 <- c("5","6")

base2_scored <- base2 %>%
  drop_na(preciom, areaconst, estrato, habitaciones, parqueaderos, banios) %>%
  mutate(pred = predict(m2, newdata = .))

offers2 <- base2_scored %>%
  filter(preciom <= 850) %>%
  mutate(
    estrato_match = ifelse(as.character(estrato) %in% target_estratos2, 0, 1),
    dist = abs(areaconst - 300) +
      abs(habitaciones - 5) +
      abs(banios - 3) +
      abs(parqueaderos - 3) +
      estrato_match * 50
  ) %>%
  arrange(dist) %>%
  slice_head(n = 5)

knitr::kable(
  offers2 %>% select(preciom, pred, areaconst, estrato, habitaciones, banios, parqueaderos, barrio),
  digits = 2,
  caption = "Caso 2: Top 5 ofertas potenciales (precio <= 850M)"
)
Caso 2: Top 5 ofertas potenciales (precio <= 850M)
preciom pred areaconst estrato habitaciones banios parqueaderos barrio
670 697.94 300.00 5 6 5 3 seminario
410 629.03 295.55 5 4 4 2 cuarto de legua
490 602.98 288.00 5 4 5 1 cuarto de legua
520 661.50 320.00 5 4 4 2 cuarto de legua
650 625.23 275.00 5 5 5 2 ciudadela pasoancho
leaflet(offers2) %>%
  addProviderTiles("CartoDB.Positron") %>%
  addCircleMarkers(
    lng=~longitud, lat=~latitud, radius=6,
    popup=~paste("Barrio:", barrio,
                 "<br>Precio:", preciom,
                 "<br>Pred:", round(pred,1),
                 "<br>Área:", areaconst,
                 "<br>Estrato:", estrato)
  )

Vivienda 2 — Recomendación de 5 ofertas (≤ 850 M)

Filtrado: apartamentos Zona Sur, estrato 5, dentro del crédito aprobado.
Rango de precios reales
410 M — 670 M
Rango estimado por el modelo
602 M — 698 M

  • Todas las alternativas cumplen con el límite de 850 millones, por lo tanto son financieramente viables.
  • La oferta de 410 M (Cuarto de Legua) presenta una diferencia considerable frente al valor estimado por el modelo (≈ 629 M), lo que podría indicar una oportunidad de mercado o una posible subvaloración relativa.
  • Las viviendas entre 650 M y 670 M muestran coherencia entre precio real y predicción, lo que sugiere alineación con el comportamiento promedio del mercado.
  • El sector Cuarto de Legua concentra varias alternativas dentro del rango óptimo, lo cual puede indicar una zona con buena oferta relativa.
  • Desde la perspectiva riesgo–retorno, la propiedad de 410 M podría representar mayor potencial de valorización, mientras que las opciones entre 650–670 M implican menor desviación respecto al valor esperado.

Conclusión técnica: El modelo respalda la viabilidad de las cinco alternativas. Si el criterio principal es maximizar eficiencia financiera dentro del crédito aprobado, la opción de menor precio relativo ofrece mayor margen de valorización potencial. Si se prioriza estabilidad y coherencia con el mercado, las alternativas cercanas al valor estimado presentan menor riesgo de sobreprecio.

3. Conclusiones Generales

Modelo de Regresión Lineal Múltiple (MCO) – Evaluación integral y riesgos

1️⃣ Desempeño del modelo

  • Vivienda 1 (Casa – Norte): R² test ≈ 0.61 → capacidad explicativa moderada.
  • Vivienda 2 (Apartamento – Sur): R² test ≈ 0.78 → capacidad explicativa alta.
  • El modelo mostró mejor estabilidad predictiva en apartamentos del sur que en casas del norte.

2️⃣ Variables determinantes

  • El área construida es el principal determinante del precio en ambos casos.
  • El estrato tiene efecto positivo y significativo.
  • Los parqueaderos aportan valor adicional consistente.
  • En el Caso 2, los baños presentan mayor impacto relativo.

3️⃣ Validación de supuestos

  • Se detectó heterocedasticidad (BP p-value ≈ 0).
  • Los residuos no cumplen estrictamente normalidad (Shapiro p-value ≈ 0).
  • No hay multicolinealidad severa (VIF ≈ 3).
  • Existen observaciones influyentes, aunque no dominan el modelo.

4️⃣ Riesgos del modelo

  • Riesgo de heterocedasticidad: Puede afectar la eficiencia de los estimadores y la validez exacta de intervalos de confianza.
  • Riesgo de no normalidad: En muestras grandes el impacto es menor, pero puede afectar inferencias puntuales en submuestras pequeñas.
  • Riesgo de omisión de variables: El modelo no incluye variables como antigüedad del inmueble, acabados, seguridad del sector o dinámica macroeconómica.
  • Riesgo de extrapolación: Las predicciones fuera del rango observado (por ejemplo, áreas muy grandes) pueden ser menos confiables.
  • Riesgo de mercado dinámico: El modelo es estático y no incorpora cambios temporales en tasas de interés, inflación o shocks inmobiliarios.

5️⃣ Viabilidad financiera

  • Vivienda 1: viable dentro de 350 M, pero con mayor incertidumbre predictiva.
  • Vivienda 2: viable dentro de 850 M, con mayor precisión estadística.

6️⃣ Conclusión estratégica

El Modelo de Regresión Lineal Múltiple permite fundamentar la toma de decisiones con evidencia cuantitativa. Sin embargo, los resultados deben interpretarse considerando los riesgos estadísticos identificados. Desde un enfoque de gestión del riesgo, la Vivienda 2 presenta menor incertidumbre predictiva y mayor respaldo estadístico, mientras que la Vivienda 1 implica mayor variabilidad y amplitud en los intervalos de predicción.

Conclusión global: El modelo es una herramienta robusta para apoyar decisiones inmobiliarias, pero no sustituye el análisis cualitativo del mercado ni la evaluación integral del riesgo.

4. Referencias Bibliográficas

Fundamentos teóricos del Modelo de Regresión Lineal y validación de supuestos

  • Gujarati, D. N., & Porter, D. C. (2009). Econometría (5ª ed.). McGraw-Hill.
  • Wooldridge, J. M. (2016). Introductory Econometrics: A Modern Approach (6th ed.). Cengage Learning.
  • Kutner, M. H., Nachtsheim, C. J., Neter, J., & Li, W. (2005). Applied Linear Statistical Models (5th ed.). McGraw-Hill.
  • Breusch, T. S., & Pagan, A. R. (1979). A Simple Test for Heteroscedasticity and Random Coefficient Variation. Econometrica, 47(5), 1287-1294.
  • Shapiro, S. S., & Wilk, M. B. (1965). An analysis of variance test for normality (complete samples). Biometrika, 52(3-4), 591-611.
  • Montgomery, D. C., Peck, E. A., & Vining, G. G. (2012). Introduction to Linear Regression Analysis (5th ed.). Wiley.