ACTIVIDAD 2

Enunciado

Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.

Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.

Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:

Característica Vivienda 1 Vivienda 2
Tipo Casa Apartamento
Área construida 200 300
Parqueaderos 1 3
Baños 2 3
Habitaciones 4 5
Estrato 4 o 5 5 o 6
Zona Norte Sur
Crédito preaprobado 350 millones 850 millones

Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos) .

Punto 1

Cargar los paquetes necesarios

Se realiza un filtro a la base de datos, incluyendo únicamente las ofertas de base1 correspondientes a casas ubicadas en la zona norte de la ciudad. Se presentan los tres primeros registros de la base y se construyen tablas que permiten comprobar la validez de la consulta.

Cargar datos vivienda desde paquete MODELOS

data("vivienda")

# Normalizar tipos y valores
vivienda <- vivienda %>%
  mutate(
    zona = trimws(as.character(zona)),
    tipo = trimws(as.character(tipo)),
    barrio = trimws(as.character(barrio)),
    piso = ifelse(piso %in% c("", "NA"), NA, piso),
    zona = factor(zona),
    tipo = factor(tipo),
    barrio = factor(barrio)
  )

glimpse(vivienda)
|> Rows: 8,322
|> Columns: 13
|> $ id           <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
|> $ zona         <fct> Zona Oriente, Zona Oriente, Zona Oriente, Zona Sur, Zona …
|> $ 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         <fct> Casa, Casa, Casa, Casa, Apartamento, Apartamento, Apartam…
|> $ barrio       <fct> 20 de julio, 20 de julio, 20 de julio, 3 de julio, acopi,…
|> $ 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…
summary(vivienda$zona)
|>  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur         NA's 
|>          124         1920         1198          351         4726            3
summary(vivienda$tipo)
|> Apartamento        Casa        NA's 
|>        5100        3219           3

Filtro a la base de datos

Se realiza un filtro a la base de datos de vivienda, seleccionando únicamente los registros correspondientes a casas ubicadas en la zona norte de la ciudad. Como resultado, se obtiene la base base1, cuyas dimensiones se reportan en términos de número de filas y columnas. Finalmente, se presentan los tres primeros registros de esta base filtrada con el fin de ilustrar la estructura de la información.

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

cat("Dimensiones base1 (Casa, Zona Norte):", nrow(base1), "filas x", ncol(base1), "columnas\n")
|> Dimensiones base1 (Casa, Zona Norte): 722 filas x 13 columnas
base1 %>% head(3)
|> # A tibble: 3 × 13
|>      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
|>   <dbl> <fct>   <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 <fct>, barrio <fct>, longitud <dbl>, latitud <dbl>

Comprobación de resultados

Se construyen tablas de comprobación con el propósito de validar el filtro aplicado a la base base1. En primer lugar, la tabla de distribución por tipo de vivienda confirma que los 722 registros corresponden exclusivamente a casas. De igual forma, la tabla de distribución por zona muestra que la totalidad de las ofertas se encuentran en la zona norte de la ciudad, lo cual respalda la consistencia de la selección realizada. Finalmente, la tabla de estrato evidencia una mayor concentración de viviendas en los estratos 3 (235 registros) y 5 (271 registros), mientras que los estratos 4 (161 registros) y 6 (55 registros) presentan una menor participación relativa.

# Tablas de comprobación
tab_tipo_b1  <- base1 %>% count(tipo, name = "n")
tab_zona_b1  <- base1 %>% count(zona, name = "n")
tab_estr_b1  <- base1 %>% count(estrato, name = "n") %>% arrange(estrato)

tab_tipo_b1
|> # A tibble: 1 × 2
|>   tipo      n
|>   <fct> <int>
|> 1 Casa    722
tab_zona_b1
|> # A tibble: 1 × 2
|>   zona           n
|>   <fct>      <int>
|> 1 Zona Norte   722
tab_estr_b1
|> # A tibble: 4 × 2
|>   estrato     n
|>     <dbl> <int>
|> 1       3   235
|> 2       4   161
|> 3       5   271
|> 4       6    55

Mapa de los puntos

Para complementar el análisis, se genera un mapa interactivo con los datos de la base, utilizando únicamente las viviendas que cuentan con coordenadas de latitud y longitud. En este mapa se ubican puntos que representan cada oferta, y al hacer clic sobre ellos se despliega información adicional como el barrio de localización, el precio en millones, el área construida y el estrato socioeconómico. Gracias a esta representación espacial es posible observar de manera más clara cómo se distribuyen las viviendas y verificar si realmente corresponden a la zona norte de la ciudad, se aprecia que existen registros que se alejan de la zona esperada.

# Mapa de puntos
  leaflet(base1 %>% filter(!is.na(latitud), !is.na(longitud))) %>%
    addTiles() %>%
    addCircleMarkers(~longitud, ~latitud, radius = 4,
                     popup = ~paste0("<b>Barrio: </b>", barrio,
                                     "<br><b>Precio (M): </b>", preciom,
                                     "<br><b>Área:</b> ", areaconst, " m²",
                                     "<br><b>Estrato:</b> ", estrato))

Punto 2

Análisis de correlación

Para continuar con la exploración de los datos, se seleccionan algunas variables numéricas de la base base1 (precio, área construida, estrato, número de baños y número de habitaciones) con el fin de analizar sus relaciones. A partir de ellas se construye una matriz de correlaciones y se representa mediante un mapa de calor.

En los resultados se observa que el precio de la vivienda presenta una fuerte correlación positiva con el área construida (0.73) y con el estrato (0.61), lo que tiene sentido porque a mayor tamaño y mayor nivel socioeconómico, el precio tiende a aumentar. También aparece una relación moderada con el número de baños (0.52), aunque con las habitaciones la asociación es más débil (0.32). Por otro lado, el número de baños y habitaciones muestra una correlación relativamente alta entre sí (0.57), lo cual resulta lógico ya que viviendas con más cuartos suelen tener también más baños.

vars_num <- c("preciom","areaconst","estrato","banios","habitaciones")
eda1 <- base1 %>% select(all_of(vars_num)) %>% na.omit()

if (nrow(eda1) > 2) {
  cm <- cor(eda1, use = "complete.obs", method = "pearson")
  plot_ly(
    x = colnames(cm), y = rownames(cm), z = cm,
    type = "heatmap", colorscale = "Viridis"
  ) %>% layout(title = "Base1: Matriz de correlaciones")
} else {
  cat("⚠️ Base1 no tiene suficientes datos completos para correlaciones.\n")
}

Análisis grafico

Con el fin de visualizar de manera más clara las relaciones entre el precio y otras características de las viviendas de la base, se construyen gráficos de dispersión acompañados de una línea de tendencia ajustada con regresión lineal. En cada gráfico se compara el precio con una de las variables: área construida, estrato, número de baños y número de habitaciones.

En general, los resultados muestran que el precio aumenta a medida que crece el área construida, confirmando la fuerte relación positiva encontrada en la matriz de correlaciones. Algo similar ocurre con el estrato, donde se aprecia una tendencia ascendente del precio en los niveles socioeconómicos más altos. En el caso del número de baños, también se observa una correlación positiva, aunque menos pronunciada. Finalmente, la relación con el número de habitaciones resulta más débil: si bien hay una leve tendencia, el precio no crece de manera tan marcada como en las otras variables.

# Dispersión con tendencia (interactivo)
for (x in c("areaconst","estrato","banios","habitaciones")) {
  p <- ggplot(base1, aes_string(x = x, y = "preciom")) +
    geom_point(alpha = 0.5) +
    geom_smooth(method = "lm", se = TRUE, color = "red") +
    labs(title = paste("Precio vs", x, "(Base1)"),
         x = x, y = "Precio (millones)") +
    theme_minimal()
  print(ggplotly(p))
}

Punto 3

Regresión lineal múltiple

Para analizar cómo se relacionan de manera conjunta las características de las viviendas con su precio, se estima un modelo de regresión lineal múltiple en el que la variable dependiente es el precio (millones de pesos), y las variables explicativas son el área construida, el estrato socioeconómico, el número de habitaciones, el número de parqueaderos y el número de baños.

Los resultados muestran que el modelo es globalmente significativo y logra explicar aproximadamente el 60% de la variabilidad del precio (R² = 0.60). Esto indica que, aunque el modelo capta gran parte de la relación entre las variables, todavía queda un 40% que depende de otros factores no incluidos (como ubicación exacta, antigüedad de la vivienda o acabados).

En cuanto a los coeficientes, se observa que:

  • Área construida: tiene un efecto positivo y altamente significativo. Por cada metro cuadrado adicional, el precio aumenta en promedio 0.68 millones de pesos. Esto confirma la lógica de que a mayor tamaño, mayor valor de la vivienda.

  • Estrato: también es muy significativo. Un aumento de una unidad en el estrato se asocia con un incremento de aproximadamente 80 millones de pesos en el precio, lo cual refleja el peso que tiene el nivel socioeconómico en la valoración del inmueble.

  • Habitaciones: no resulta estadísticamente significativo (p = 0.177), lo que sugiere que, al controlar por área, estrato, parqueaderos y baños, el número de cuartos no aporta una diferencia clara en el precio.

  • Parqueaderos: sí es significativo. Cada parqueadero adicional se asocia con un aumento promedio de 24 millones de pesos, lo que resulta lógico considerando la demanda de parqueaderos en ciertas zonas de la ciudad.

  • Baños: también es significativo. Cada baño adicional incrementa el precio en aproximadamente 19 millones de pesos, coherente con la percepción de comodidad que generan más baños en una vivienda.

model_df1 <- base1 %>%
  select(preciom, areaconst, estrato, habitaciones, parqueaderos, banios) %>%
  na.omit()

mod1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
           data = model_df1)
summary(mod1)
|> 
|> Call:
|> lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
|>     banios, data = model_df1)
|> 
|> Residuals:
|>     Min      1Q  Median      3Q     Max 
|> -784.29  -77.56  -16.03   47.67  978.61 
|> 
|> Coefficients:
|>                Estimate Std. Error t value Pr(>|t|)    
|> (Intercept)  -238.17090   44.40551  -5.364 1.34e-07 ***
|> areaconst       0.67673    0.05281  12.814  < 2e-16 ***
|> estrato        80.63495    9.82632   8.206 2.70e-15 ***
|> habitaciones    7.64511    5.65873   1.351    0.177    
|> parqueaderos   24.00598    5.86889   4.090 5.14e-05 ***
|> banios         18.89938    7.48800   2.524    0.012 *  
|> ---
|> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
|> 
|> Residual standard error: 155.1 on 429 degrees of freedom
|> Multiple R-squared:  0.6041, Adjusted R-squared:  0.5995 
|> F-statistic: 130.9 on 5 and 429 DF,  p-value: < 2.2e-16

Punto 4

Validación de supuestos (Base1)- PARTE A

Después de estimar el modelo, se realizó un análisis de los residuos para verificar los supuestos de la regresión lineal múltiple. En el gráfico de residuos vs. valores ajustados, se observa cierta dispersión desigual y una tendencia ligera en la parte derecha, lo que podría sugerir problemas de heterocedasticidad. Esto se confirma con la prueba de Breusch-Pagan, que resulta altamente significativa (BP = 80.28, p < 0.001), indicando que la varianza de los residuos no es constante.

En el Q-Q plot, los residuos siguen una tendencia cercana a la normalidad, aunque presentan desviaciones leves en las colas, lo que sugiere que la normalidad se cumple de manera aproximada, pero no perfecta.

El gráfico Scale-Location muestra un patrón ascendente en los residuos estandarizados a medida que aumentan los valores ajustados, reforzando la presencia de heterocedasticidad.

En el gráfico de residuos vs leverage, se identifican algunos puntos influyentes (como las observaciones 239, 309 y 393), aunque no parecen comprometer de forma crítica el ajuste del modelo.

La prueba de Durbin-Watson (DW = 1.76, p = 0.005) sugiere la presencia de autocorrelación positiva en los residuos, aunque no de gran magnitud.

Finalmente, los valores de VIF (todos menores a 2) indican que no existe un problema relevante de multicolinealidad entre las variables independientes, por lo que la estimación de los coeficientes no está distorsionada por colinealidad excesiva.

Sugerencias para mejorar el modelo

  1. Corregir la heterocedasticidad:
  • Aplicar una transformación logarítmica al precio (log(preciom)) podría estabilizar la varianza de los residuos.

  • Alternativamente, utilizar una regresión robusta o estimadores con errores estándar robustos (como vcovHC en R) para ajustar la inferencia.

  1. Atender la autocorrelación de los residuos:

    • Incluir variables omitidas relacionadas con la localización o la antigüedad de la vivienda podría reducir la autocorrelación.

    • En caso de que la autocorrelación sea espacial (probable en datos inmobiliarios), considerar un modelo de regresión espacial.

  2. **Examinar los puntos influyentes**:
    • Revisar las observaciones 239, 309 y 393 para verificar si corresponden a datos atípicos o errores de registro.

    • Analizar si excluirlos o modelarlos aparte mejora el ajuste.

  3. Explorar interacciones o no linealidades:

    • Incluir términos cuadráticos para el área construida o interacciones entre estrato y área podría capturar mejor relaciones no lineales.
par(mfrow = c(2,2)); plot(mod1); par(mfrow = c(1,1))

PARTE B

res1 <- residuals(mod1)
fit1 <- fitted(mod1)

# Heterocedasticidad (Breusch-Pagan)
bp1 <- bptest(mod1)

# Independencia (Durbin-Watson) — interpretarlo con cuidado si no es serie temporal
dw1 <- dwtest(mod1)

# Multicolinealidad (VIF)
vif1 <- car::vif(mod1)

list(
  Breusch_Pagan = bp1,
  Durbin_Watson = dw1,
  VIF = vif1
)
|> $Breusch_Pagan
|> 
|>  studentized Breusch-Pagan test
|> 
|> data:  mod1
|> BP = 80.281, df = 5, p-value = 7.33e-16
|> 
|> 
|> $Durbin_Watson
|> 
|>  Durbin-Watson test
|> 
|> data:  mod1
|> DW = 1.7615, p-value = 0.005472
|> alternative hypothesis: true autocorrelation is greater than 0
|> 
|> 
|> $VIF
|>    areaconst      estrato habitaciones parqueaderos       banios 
|>     1.460998     1.307757     1.721015     1.226334     1.967421

Punto 5

Predicción para Vivienda 1(Casa, Zona Norte, área=200, estrato 4 o 5, habitaciones=4, parqueaderos=1, baños=2)

Con el modelo de regresión ajustado, se procede a realizar la predicción del precio para una vivienda con las características planteadas inicialmente: 200 m² de área construida, 4 habitaciones, 1 parqueadero y 2 baños, variando únicamente el estrato socioeconómico entre 4 y 5.

Los resultados de la predicción son los siguientes:

  • Para estrato 4, el modelo estima un precio promedio de 312 millones de pesos, con un intervalo de predicción del 95% que va aproximadamente de 6 a 618 millones.

  • Para estrato 5, el precio promedio aumenta a 393 millones de pesos, con un intervalo de predicción entre 86 y 699 millones.

En ambos casos, se observa que el intervalo de predicción es bastante amplio, lo cual refleja la incertidumbre inherente al modelo y la dispersión de los datos. Sin embargo, el efecto esperado es consistente: al pasar de estrato 4 a estrato 5, el precio se incrementa en cerca de 80 millones, coherente con la magnitud estimada para la variable estrato en el modelo previo.

nueva_v1_e4 <- data.frame(areaconst = 200, estrato = 4, habitaciones = 4, parqueaderos = 1, banios = 2)
nueva_v1_e5 <- data.frame(areaconst = 200, estrato = 5, habitaciones = 4, parqueaderos = 1, banios = 2)

pred_v1_e4 <- predict(mod1, nueva_v1_e4, interval = "prediction", level = 0.95)
pred_v1_e5 <- predict(mod1, nueva_v1_e5, interval = "prediction", level = 0.95)

pred_v1_e4
|>       fit      lwr      upr
|> 1 312.101 6.205196 617.9968
pred_v1_e5
|>        fit      lwr      upr
|> 1 392.7359 86.19637 699.2755

Punto 6

Ofertas para Vivienda 1 (≤ 350 M) + mapa (≥5 si hay)

PARTE A

cand_v1 <- vivienda %>%
  filter(
    tipo == "Casa",
    zona == "Zona Norte",
    !is.na(preciom), preciom <= 350,
    areaconst >= 200*0.8, areaconst <= 200*1.2,
    estrato %in% c(4,5),
    parqueaderos >= 1,
    banios >= 2,
    habitaciones >= 4
  ) %>%
  mutate(area_dev = abs(areaconst - 200)) %>%
  arrange(area_dev, preciom) %>%
  slice_head(n = 5)

cand_v1 %>% select(id, zona, barrio, preciom, areaconst, estrato, parqueaderos, banios, habitaciones)
|> # A tibble: 5 × 9
|>      id zona   barrio preciom areaconst estrato parqueaderos banios habitaciones
|>   <dbl> <fct>  <fct>    <dbl>     <dbl>   <dbl>        <dbl>  <dbl>        <dbl>
|> 1  1343 Zona … la fl…     320       200       5            2      4            4
|> 2  1144 Zona … la me…     320       200       4            2      4            4
|> 3  4210 Zona … el bo…     350       200       5            3      3            4
|> 4  4267 Zona … el bo…     335       202       5            1      4            5
|> 5  1887 Zona … vipasa     340       203       5            2      3            4

PARTE B

if (nrow(cand_v1) > 0 && all(c("latitud","longitud") %in% names(cand_v1))) {
  leaflet(cand_v1 %>% filter(!is.na(latitud), !is.na(longitud))) %>%
    addTiles() %>%
    addCircleMarkers(~longitud, ~latitud, radius = 5,
                     popup = ~paste0(
                       "<b>ID:</b> ", id,
                       "<br><b>Precio (M):</b> ", preciom,
                       "<br><b>Área:</b> ", areaconst, " m²",
                       "<br><b>Estrato:</b> ", estrato,
                       "<br><b>Habs:</b> ", habitaciones,
                       "<br><b>Baños:</b> ", banios,
                       "<br><b>Parq:</b> ", parqueaderos,
                       "<br><b>Barrio:</b> ", barrio
                     ))
} else {
  cat("⚠️ No se encontraron ≥5 ofertas que cumplan criterios o faltan coordenadas para mapear.\n")
}

Punto 7

Repetir 1–6 para Vivienda 2(Apartamento, Zona Sur, área=300, estrato 5 o 6, parqueaderos≥3, baños≥3, habitaciones≥5, ≤ 850 M)

7.1 Filtro base2 + verificación + mapa

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

cat("Dimensiones base2 (Apto, Zona Sur):", nrow(base2), "filas x", ncol(base2), "columnas\n")
|> Dimensiones base2 (Apto, Zona Sur): 2787 filas x 13 columnas
base2 %>% head(3)
|> # A tibble: 3 × 13
|>      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
|>   <dbl> <fct>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
|> 1  5098 Zona S… 05          4     290        96            1      2            3
|> 2   698 Zona S… 02          3      78        40            1      1            2
|> 3  8199 Zona S… <NA>        6     875       194            2      5            3
|> # ℹ 4 more variables: tipo <fct>, barrio <fct>, longitud <dbl>, latitud <dbl>
tab_tipo_b2 <- base2 %>% count(tipo, name = "n")
tab_zona_b2 <- base2 %>% count(zona, name = "n")
tab_estr_b2 <- base2 %>% count(estrato, name = "n") %>% arrange(estrato)

tab_tipo_b2
|> # A tibble: 1 × 2
|>   tipo            n
|>   <fct>       <int>
|> 1 Apartamento  2787
tab_zona_b2
|> # A tibble: 1 × 2
|>   zona         n
|>   <fct>    <int>
|> 1 Zona Sur  2787
tab_estr_b2
|> # A tibble: 4 × 2
|>   estrato     n
|>     <dbl> <int>
|> 1       3   201
|> 2       4  1091
|> 3       5  1033
|> 4       6   462
## PARTE B

if (all(c("latitud","longitud") %in% names(base2)) && nrow(base2) > 0) {
  leaflet(base2 %>% filter(!is.na(latitud), !is.na(longitud))) %>%
    addTiles() %>%
    addCircleMarkers(~longitud, ~latitud, radius = 4,
                     popup = ~paste0("<b>Barrio: </b>", barrio,
                                     "<br><b>Precio (M): </b>", preciom,
                                     "<br><b>Área:</b> ", areaconst, " m²",
                                     "<br><b>Estrato:</b> ", estrato))
} else {
  cat("⚠️ No hay coordenadas suficientes para mapear base2.\n")
}

7.2 NUEVA APLICANDO EDA (base2)

vars_num2 <- c("preciom","areaconst","estrato","banios","habitaciones")
eda2 <- base2 %>% select(all_of(vars_num2)) %>% na.omit()

if (nrow(eda2) > 2) {
  cm2 <- cor(eda2, use = "complete.obs", method = "pearson")
  plot_ly(x = colnames(cm2), y = rownames(cm2), z = cm2,
          type = "heatmap", colorscale = "Viridis") %>%
    layout(title = "Base2: Matriz de correlaciones")
} else {
  cat("⚠️ Base2 no tiene suficientes datos completos para correlaciones.\n")
}
for (x in c("areaconst","estrato","banios","habitaciones")) {
  p2 <- ggplot(base2, aes_string(x = x, y = "preciom")) +
    geom_point(alpha = 0.5) +
    geom_smooth(method = "lm", se = TRUE, color = "red") +
    labs(title = paste("Precio vs", x, "(Base2)"),
         x = x, y = "Precio (millones)") +
    theme_minimal()
  print(ggplotly(p2))
}

7.3 Modelo lineal (base2)

model_df2 <- base2 %>%
  select(preciom, areaconst, estrato, habitaciones, parqueaderos, banios) %>%
  na.omit()

mod2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
           data = model_df2)
summary(mod2)
|> 
|> Call:
|> lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
|>     banios, data = model_df2)
|> 
|> 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

7.4 Supuestos (base2)

par(mfrow = c(2,2)); plot(mod2); par(mfrow = c(1,1))

res2 <- residuals(mod2)
fit2 <- fitted(mod2)

bp2 <- bptest(mod2)
dw2 <- dwtest(mod2)
vif2 <- car::vif(mod2)

list(
  Breusch_Pagan = bp2,
  Durbin_Watson = dw2,
  VIF = vif2
)
|> $Breusch_Pagan
|> 
|>  studentized Breusch-Pagan test
|> 
|> data:  mod2
|> BP = 754.81, df = 5, p-value < 2.2e-16
|> 
|> 
|> $Durbin_Watson
|> 
|>  Durbin-Watson test
|> 
|> data:  mod2
|> DW = 1.5333, p-value < 2.2e-16
|> alternative hypothesis: true autocorrelation is greater than 0
|> 
|> 
|> $VIF
|>    areaconst      estrato habitaciones parqueaderos       banios 
|>     2.066518     1.545162     1.429280     1.737878     2.529494

7.5 Predicción Vivienda 2

nueva_v2_e5 <- data.frame(areaconst = 300, estrato = 5, habitaciones = 5, parqueaderos = 3, banios = 3)
nueva_v2_e6 <- data.frame(areaconst = 300, estrato = 6, habitaciones = 5, parqueaderos = 3, banios = 3)

pred_v2_e5 <- predict(mod2, nueva_v2_e5, interval = "prediction", level = 0.95)
pred_v2_e6 <- predict(mod2, nueva_v2_e6, interval = "prediction", level = 0.95)

pred_v2_e5
|>        fit     lwr      upr
|> 1 675.0247 481.455 868.5945
pred_v2_e6
|>        fit      lwr      upr
|> 1 735.9218 542.3141 929.5296

7.6 Ofertas Vivienda 2 (≤ 850 M) + mapa

cand_v2 <- vivienda %>%
  filter(
    tipo == "Apartamento",
    zona == "Zona Sur",
    !is.na(preciom), preciom <= 850,
    areaconst >= 300*0.8, areaconst <= 300*1.2,
    estrato %in% c(5,6),
    parqueaderos >= 3,
    banios >= 3,
    habitaciones >= 5
  ) %>%
  mutate(area_dev = abs(areaconst - 300)) %>%
  arrange(area_dev, preciom) %>%
  slice_head(n = 5)

cand_v2 %>% 
  select(id, zona, barrio, preciom, areaconst, estrato, parqueaderos, banios, habitaciones)
|> # A tibble: 2 × 9
|>      id zona   barrio preciom areaconst estrato parqueaderos banios habitaciones
|>   <dbl> <fct>  <fct>    <dbl>     <dbl>   <dbl>        <dbl>  <dbl>        <dbl>
|> 1  7512 Zona … semin…     670       300       5            3      5            6
|> 2  8036 Zona … semin…     530       256       5            3      5            5

Mapa

if (nrow(cand_v2) > 0 && all(c("latitud","longitud") %in% names(cand_v2))) {
  leaflet(cand_v2 %>% filter(!is.na(latitud), !is.na(longitud))) %>%
    addTiles() %>%
    addCircleMarkers(~longitud, ~latitud, radius = 5,
                     popup = ~paste0(
                       "<b>ID:</b> ", id,
                       "<br><b>Precio (M):</b> ", preciom,
                       "<br><b>Área:</b> ", areaconst, " m²",
                       "<br><b>Estrato:</b> ", estrato,
                       "<br><b>Habs:</b> ", habitaciones,
                       "<br><b>Baños:</b> ", banios,
                       "<br><b>Parq:</b> ", parqueaderos,
                       "<br><b>Barrio:</b> ", barrio
                     ))
} else {
  cat("⚠️ No se encontraron ≥5 ofertas que cumplan criterios o faltan coordenadas para mapear.\n")
}

Siguiendo la misma metodología aplicada previamente, se construyó un modelo de regresión lineal múltiple para la segunda solicitud, correspondiente a la vivienda con crédito preaprobado de 850 millones de pesos.

En este caso, los resultados de diagnóstico evidencian algunos retos en la calidad del ajuste:

  • Heterocedasticidad: La prueba de Breusch-Pagan resulta altamente significativa (BP = 754.81, p < 0.001), indicando que la varianza de los residuos no es constante. Esto sugiere que el modelo podría estar sobre o subestimando la variabilidad del precio en ciertos rangos.

  • Autocorrelación: La prueba de Durbin-Watson (DW = 1.53, p < 0.001) confirma la presencia de autocorrelación positiva en los residuos, lo que implica que pueden existir patrones no capturados por las variables incluidas, posiblemente asociados a la localización geográfica o características omitidas del inmueble.

  • Multicolinealidad: Los valores de VIF se mantienen por debajo de 2.6, por lo que no se identifican problemas severos de colinealidad. Esto significa que las variables explicativas no están fuertemente correlacionadas entre sí y que los coeficientes estimados son relativamente estables.

COMENTARIOS

El análisis de las dos solicitudes evaluadas (350 millones y 850 millones) permitió evidenciar que, si bien el modelo de regresión lineal múltiple ofrece una aproximación inicial para la estimación del valor de las viviendas, presenta limitaciones que deben ser consideradas antes de utilizarlo como herramienta de apoyo para decisiones crediticias de alto impacto.

En ambos casos se identificaron signos de heterocedasticidad y autocorrelación positiva, lo que indica que el modelo no captura completamente la variabilidad ni las posibles dependencias espaciales o estructurales de los datos. Esto podría llevar a estimaciones sesgadas en ciertos segmentos del mercado, afectando la confiabilidad de la valoración.

Por otra parte, la multicolinealidad no representa un problema crítico, ya que los VIF se mantienen en rangos aceptables, lo que garantiza estabilidad en las estimaciones de los coeficientes.

En síntesis, el modelo resulta útil como punto de partida, pero requiere ajustes metodológicos adicionales para mejorar su precisión y robustez. Se recomienda:

  1. Incorporar variables que representen mejor las características diferenciales de cada vivienda (ubicación geográfica, año de construcción, estado de conservación).

  2. Evaluar transformaciones en la variable dependiente para mitigar la heterocedasticidad.

  3. Considerar el uso de modelos robustos o espacialmente explícitos, especialmente cuando se analicen montos altos de crédito.

Resumen

Se evaluaron dos solicitudes de compra de vivienda en Cali para empleados de una compañía internacional. La primera solicitud corresponde a una casa en la zona norte con presupuesto de 350 millones. La segunda solicitud es para un apartamento en la zona sur con presupuesto de 850 millones.

1. Vivienda 1 – Casa, Zona Norte (Presupuesto: 350M)

• El análisis de mercado indica que una casa con las características solicitadas (200 m², estrato 4–5, 4 habitaciones, 2 baños, 1 parqueadero) tiene un valor estimado entre 312M y 393M. • Se identificaron cinco opciones viables en barrios como La Flora, Vipasa y El Bosque, todas en estrato 4–5 y dentro del rango de área solicitada.

• Recomendación: Las opciones disponibles permiten cumplir con el presupuesto de 350M, aunque en algunos casos el precio proyectado puede acercarse al límite superior. Se aconseja priorizar las ofertas en estrato 4, que se ajustan mejor al presupuesto y aún cumplen con las condiciones de área y número de habitaciones.

2. Vivienda 2 – Apartamento, Zona Sur (Presupuesto: 850M)

• El valor estimado para un apartamento de 300 m² en estratos 5–6 se encuentra en el rango de 675M a 735M, con intervalos que podrían llegar hasta 930M.

• Sin embargo, solo se encontraron 2 ofertas que cumplen plenamente las condiciones (área, estrato, número de habitaciones y parqueaderos). Ambas se ubican en el sector de Ciudad Jardín – Seminario, con precios entre 530M y 670M.

• Recomendación: ◦ Aunque hay menos opciones, estas alternativas se ajustan al presupuesto y superan en espacio y comodidades las condiciones mínimas solicitadas. ◦ Se sugiere considerar una negociación en estos proyectos, dado que ofrecen buen margen respecto al crédito aprobado.

Si se desea ampliar el portafolio de posibilidades, se recomienda flexibilizar el criterio de área (280–320 m²) o incluir apartamentos de estrato 4 alto en sectores consolidados del sur.

3. Observaciones Generales

• El análisis mostró que las viviendas en Cali presentan alta variabilidad en precios. Factores como ubicación exacta, acabados y estado de conservación influyen de forma significativa.

• Aunque los modelos estadísticos utilizados ofrecen un marco de referencia, existe un margen de incertidumbre amplio en las estimaciones. Esto implica que la decisión final debe apoyarse también en la evaluación cualitativa de cada inmueble.

• Se identificaron limitaciones en la información disponible: en el caso de los apartamentos del sur, la oferta es reducida, lo que obliga a manejar expectativas flexibles respecto a ubicación y características.

4. Conclusión

  • Para la Casa en Zona Norte (350M): hay disponibilidad de al menos cinco opciones viables y ajustadas al presupuesto, recomendando estrato 4 como la mejor relación calidad–precio.

  • Para el Apartamento en Zona Sur (850M): aunque hay solo dos opciones que cumplen estrictamente, ambas representan oportunidades atractivas, con posibilidad de negociación dentro del presupuesto aprobado.

• En ambos casos, se recomienda avanzar en visitas presenciales y negociaciones directas, complementando el análisis cuantitativo con la valoración cualitativa del entorno y estado de cada inmueble.