1 Resumen ejecutivo

La empresa internacional solicita a C&A asesoría para la compra de dos viviendas en Cali, destinadas a reubicar a dos empleados con sus familias. Para responder con criterio técnico, se construyeron dos modelos de regresión lineal múltiple —uno para cada segmento de mercado solicitado— que estiman el precio de una vivienda en función de sus características físicas y socioeconómicas, a partir de la oferta real de los últimos tres meses.

Vivienda 1 — Casa en la Zona Norte (crédito preaprobado: $350 millones). El modelo de casas de la Zona Norte estima que una casa con las características solicitadas (200 m², 1 parqueadero, 2 baños, 4 habitaciones) tendría un precio esperado de aproximadamente $312 millones si es de estrato 4 y de $393 millones si es de estrato 5. La conclusión práctica es clara: con el crédito de $350 millones la solicitud es viable en estrato 4 (queda un margen cómodo), mientras que el estrato 5 supera el presupuesto en cerca de $43 millones. Se identificaron múltiples ofertas reales que satisfacen la solicitud dentro del presupuesto, presentadas más adelante en mapa.

Vivienda 2 — Apartamento en la Zona Sur (crédito preaprobado: $850 millones). El modelo de apartamentos de la Zona Sur estima que un apartamento con las características solicitadas (300 m², 3 parqueaderos, 3 baños, 5 habitaciones) tendría un precio esperado de aproximadamente $675 millones en estrato 5 y $736 millones en estrato 6. En ambos casos la solicitud es holgadamente viable con el crédito de $850 millones, dejando un margen de entre $114 y $175 millones que podría destinarse a mejores acabados, ubicación o gastos de la transacción.

Recomendación general para María. Ambas solicitudes son atendibles con los presupuestos disponibles. Para la Vivienda 1 conviene orientar la búsqueda hacia casas de estrato 4 en la Zona Norte para no exceder el crédito; para la Vivienda 2 hay amplio margen y se puede priorizar calidad y ubicación dentro de la Zona Sur. El detalle técnico (estimaciones, validaciones, comparación y ofertas concretas) se documenta en los anexos.


2 Anexo 1 — Plan de trabajo (metodología)

El análisis sigue la misma secuencia metodológica para cada una de las dos solicitudes, garantizando un tratamiento comparable:

  1. Filtro y depuración de la base. Se selecciona el segmento de mercado pertinente a cada solicitud (Casa/Zona Norte para la Vivienda 1; Apartamento/Zona Sur para la Vivienda 2), se verifican los registros mediante tablas y se representan geográficamente en un mapa, discutiendo la consistencia de la ubicación.
  2. Análisis exploratorio de datos (EDA). Se estudia la correlación entre el precio y las variables predictoras mediante gráficos interactivos (plotly).
  3. Estimación del modelo. Se ajusta un modelo de regresión lineal múltiple, preciom = f(areaconst, estrato, habitaciones, parqueaderos, banios), se interpretan los coeficientes significativos en contexto y se evalúa el ajuste global (R²).
  4. Validación de supuestos. Se verifican los supuestos sobre los errores (normalidad, varianza constante) y se sugieren medidas remediales sin aplicarlas.
  5. Estimación puntual. Se predice el precio de la vivienda solicitada con sus características específicas.
  6. Ofertas potenciales. Se filtran ofertas reales dentro del crédito preaprobado y se presentan en un mapa, discutiendo su pertinencia.

La variable respuesta es preciom (precio en millones de pesos). Los modelos se estiman por Mínimos Cuadrados Ordinarios con la función lm(), que descarta automáticamente los registros con datos faltantes en las variables del modelo.

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…

La base contiene 8322 registros y 13 variables. Nótese que parqueaderos y piso presentan datos faltantes; al estimar los modelos, R excluye los registros incompletos en las variables involucradas.


3 Anexo 2 — Desarrollo técnico

4 Caso 1: Vivienda 1 — Casa en la Zona Norte

4.1 Filtro y depuración de la base

Se filtran las ofertas de casas ubicadas en la Zona Norte, segmento correspondiente a la primera solicitud.

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

cat("Número de casas en la Zona Norte:", nrow(base1), "\n")
## Número de casas en la Zona Norte: 722
# Primeros 3 registros
base1 %>% select(zona, tipo, estrato, preciom, areaconst,
                 parqueaderos, banios, habitaciones, barrio) %>%
  head(3) %>%
  kable(caption = "Primeros 3 registros — Casas Zona Norte")
Primeros 3 registros — Casas Zona Norte
zona tipo estrato preciom areaconst parqueaderos banios habitaciones barrio
Zona Norte Casa 5 320 150 2 4 6 acopi
Zona Norte Casa 5 780 380 2 3 3 acopi
Zona Norte Casa 6 750 445 NA 7 6 acopi
# Tablas que comprueban la consulta
cat("Verificación de tipo y zona (deben ser únicos):\n")
## Verificación de tipo y zona (deben ser únicos):
table(base1$tipo, base1$zona)
##       
##        Zona Norte
##   Casa        722
cat("\nDistribución por estrato:\n")
## 
## Distribución por estrato:
table(base1$estrato)
## 
##   3   4   5   6 
## 235 161 271  55

Las tablas confirman que el filtro es correcto: todos los registros son de tipo Casa y zona Zona Norte. Están presentes los cuatro estratos (3 a 6), con predominio de los estratos 5 (271 casas) y 3 (235); el estrato 6 es el menos frecuente (55). Los estratos 4 y 5, correspondientes a la solicitud, cuentan con oferta suficiente (161 y 271 casas respectivamente).

4.1.1 Mapa de las ofertas y consistencia geográfica

leaflet(base1) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud,
                   radius = 3, color = "#034D94", fillOpacity = 0.5,
                   popup = ~paste0("Barrio: ", barrio,
                                   "<br>Precio: $", preciom, " M",
                                   "<br>Estrato: ", estrato))
# Latitud de referencia aproximada del centro de Cali: ~3.44
# La Zona Norte debería concentrarse en latitudes altas (norte)
resumen_lat <- base1 %>%
  summarise(lat_min = round(min(latitud, na.rm = TRUE), 4),
            lat_media = round(mean(latitud, na.rm = TRUE), 4),
            lat_max = round(max(latitud, na.rm = TRUE), 4))
kable(resumen_lat, caption = "Rango de latitud — Casas 'Zona Norte'")
Rango de latitud — Casas ‘Zona Norte’
lat_min lat_media lat_max
3.3331 3.4602 3.4958
fuera <- base1 %>% filter(latitud < 3.43)
cat("Registros con latitud < 3.43 (posiblemente fuera del norte):",
    nrow(fuera), "de", nrow(base1),
    "(", round(100*nrow(fuera)/nrow(base1), 1), "%)\n")
## Registros con latitud < 3.43 (posiblemente fuera del norte): 107 de 722 ( 14.8 %)

Discusión — consistencia de la zona. Aunque todos los registros están etiquetados como “Zona Norte”, cerca del 15% presenta coordenadas de latitud por debajo de 3.43, es decir, ubicadas geográficamente hacia el centro-sur de la ciudad (algunos barrios como el gran limonar o el bosque aparecen con latitudes tan bajas como 3.33–3.41). Esto indica inconsistencias en la clasificación de zona de la base de datos, atribuibles a errores de digitación en el web scraping o a un criterio comercial de zonificación que no coincide exactamente con la geografía. Implicación práctica: conviene depurar estos registros o validar la zona con las coordenadas antes de tomar decisiones críticas, pues podrían sesgar el análisis del segmento.

4.2 Análisis exploratorio de datos (EDA)

Se estudia la relación entre el precio de la casa (preciom) y las variables predictoras: área construida, estrato, número de baños, número de habitaciones y número de parqueaderos.

vars1 <- c("preciom", "areaconst", "estrato", "banios", "habitaciones", "parqueaderos")
base1_num <- base1 %>% select(all_of(vars1)) %>% drop_na()

# Matriz de correlaciones
mat_cor1 <- round(cor(base1_num), 3)
kable(mat_cor1, caption = "Matriz de correlaciones — Casas Zona Norte")
Matriz de correlaciones — Casas Zona Norte
preciom areaconst estrato banios habitaciones parqueaderos
preciom 1.000 0.685 0.528 0.509 0.365 0.412
areaconst 0.685 1.000 0.354 0.457 0.421 0.307
estrato 0.528 0.354 1.000 0.351 0.058 0.261
banios 0.509 0.457 0.351 1.000 0.590 0.392
habitaciones 0.365 0.421 0.058 0.590 1.000 0.241
parqueaderos 0.412 0.307 0.261 0.392 0.241 1.000
# Correlación de cada predictora con el precio (gráfico interactivo)
cor_precio1 <- data.frame(
  variable = names(mat_cor1[,"preciom"]),
  correlacion = as.numeric(mat_cor1[,"preciom"])
) %>% filter(variable != "preciom") %>% arrange(correlacion)

p_cor1 <- plot_ly(cor_precio1, x = ~correlacion, y = ~reorder(variable, correlacion),
                  type = "bar", orientation = "h",
                  marker = list(color = "#034D94")) %>%
  layout(title = "Correlación de cada variable con el precio (Casas Norte)",
         xaxis = list(title = "Correlación con preciom"),
         yaxis = list(title = ""))
p_cor1
# Dispersión interactiva: precio vs área construida (la relación más fuerte)
p_disp1 <- plot_ly(base1_num, x = ~areaconst, y = ~preciom,
                   type = "scatter", mode = "markers",
                   marker = list(color = "#FF7F00", opacity = 0.5),
                   text = ~paste("Área:", areaconst, "m²<br>Precio: $", preciom, "M")) %>%
  layout(title = "Precio vs Área construida (Casas Norte)",
         xaxis = list(title = "Área construida (m²)"),
         yaxis = list(title = "Precio (millones)"))
p_disp1

Interpretación EDA. La variable más correlacionada con el precio de las casas de la Zona Norte es el área construida (r ≈ 0.69), seguida del estrato (r ≈ 0.53) y el número de baños (r ≈ 0.51). Las habitaciones muestran la correlación más débil (r ≈ 0.37). El diagrama de dispersión confirma una relación lineal positiva entre área y precio: a mayor área construida, mayor precio, con dispersión creciente en las viviendas grandes. Las correlaciones entre predictoras son moderadas (todas < 0.6), lo que anticipa ausencia de multicolinealidad grave.

4.3 Estimación e interpretación del modelo

modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
              data = base1)
summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = base1)
## 
## 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
##   (287 observations deleted due to missingness)
## Multiple R-squared:  0.6041, Adjusted R-squared:  0.5995 
## F-statistic: 130.9 on 5 and 429 DF,  p-value: < 2.2e-16
kable(data.frame(VIF = round(vif(modelo1), 2)),
      caption = "Factores de inflación de varianza (VIF) — Modelo Casas Norte")
Factores de inflación de varianza (VIF) — Modelo Casas Norte
VIF
areaconst 1.46
estrato 1.31
habitaciones 1.72
parqueaderos 1.23
banios 1.97

Interpretación de los coeficientes (Casas Norte). El modelo es globalmente significativo (prueba F con p-valor < 0.001). Los coeficientes estadísticamente significativos (p < 0.05) se interpretan así, manteniendo las demás variables constantes:

  • Área construida (≈ +0.68): cada metro cuadrado adicional incrementa el precio esperado en cerca de $0.68 millones (≈ $680 mil). Es el efecto más robusto y coherente con el mercado.
  • Estrato (≈ +80.6): subir un nivel de estrato aumenta el precio esperado en unos $80.6 millones, reflejando el fuerte peso del nivel socioeconómico en la valoración.
  • Parqueaderos (≈ +24.0): cada parqueadero adicional añade unos $24 millones al precio.
  • Baños (≈ +18.9): cada baño adicional añade cerca de $18.9 millones.
  • Habitaciones: NO resulta significativa (p ≈ 0.18). Una vez consideradas el área, los baños y los parqueaderos, el número de habitaciones no aporta información adicional al precio. Esto es lógico: en casas del mismo tamaño, tener más cuartos (más pequeños) no necesariamente eleva el valor.

Todos los coeficientes significativos tienen signo positivo, lo cual es lógico y esperable: más área, mejor estrato y más comodidades encarecen la vivienda. Los VIF son todos inferiores a 2, confirmando que no hay problemas de multicolinealidad.

Ajuste del modelo (R²). El R² ≈ 0.60 indica que el modelo explica alrededor del 60% de la variabilidad del precio de las casas de la Zona Norte. Es un ajuste moderado: aceptable para un análisis exploratorio de mercado, pero deja un 40% sin explicar. Cómo mejorarlo: incorporar variables no incluidas (barrio o ubicación exacta, antigüedad, estado/acabados de la vivienda, estrato como factor en lugar de numérico), considerar transformaciones (p. ej. log(preciom)) para corregir la asimetría de los precios, o depurar las inconsistencias de zona detectadas en el filtro.

4.4 Validación de supuestos

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

par(mfrow = c(1, 1))
# Normalidad de los residuales (Shapiro-Wilk sobre una muestra si n grande)
res1 <- residuals(modelo1)
set.seed(123)
sw1 <- shapiro.test(if (length(res1) > 5000) sample(res1, 5000) else res1)
cat("Shapiro-Wilk (normalidad de residuales): W =", round(sw1$statistic, 4),
    ", p-value =", format(sw1$p.value, scientific = TRUE), "\n")
## Shapiro-Wilk (normalidad de residuales): W = 0.8525 , p-value = 8.766106e-20
# Homocedasticidad (Breusch-Pagan)
bp1 <- ncvTest(modelo1)
cat("Prueba de varianza no constante (Breusch-Pagan): p-value =",
    format(bp1$p, scientific = TRUE), "\n")
## Prueba de varianza no constante (Breusch-Pagan): p-value = 1.091092e-78

Interpretación de los supuestos (Casas Norte).

  • Normalidad: la prueba de Shapiro-Wilk arroja un p-valor muy pequeño (< 0.05), por lo que se rechaza la normalidad de los residuales. El gráfico Q-Q muestra desviaciones en las colas (algunas casas muy costosas se apartan de la recta).
  • Varianza constante: la prueba de Breusch-Pagan también da p-valor < 0.05, indicando heterocedasticidad (varianza no constante). El gráfico de residuales vs. valores ajustados muestra una forma de embudo: la dispersión crece con el precio estimado.
  • Independencia: se asume cumplida, según el alcance del curso.

Sugerencias (sin aplicar). Para corregir estos problemas se podría: (i) aplicar una transformación logarítmica a la variable respuesta (log(preciom)), que suele estabilizar la varianza y acercar los residuales a la normalidad cuando los precios son asimétricos; (ii) revisar y tratar las observaciones atípicas (casas de precio muy alto) identificadas en los diagnósticos; o (iii) considerar modelos más flexibles. No obstante, para fines de estimación de referencia del precio de mercado, el modelo sigue siendo informativo.

4.5 Estimación puntual — Vivienda 1

Se predice el precio de la casa solicitada: 200 m², 1 parqueadero, 2 baños, 4 habitaciones, en estrato 4 y 5 (la solicitud contempla ambos).

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

# Intervalo de confianza (respuesta media)
pred1_conf <- predict(modelo1, newdata = solicitud1, interval = "confidence")
# Intervalo de predicción (valor individual)
pred1_pred <- predict(modelo1, newdata = solicitud1, interval = "prediction")

tabla_pred1 <- data.frame(
  Estrato = c(4, 5),
  Precio_estimado = round(pred1_conf[, "fit"], 1),
  IC_media_inf = round(pred1_conf[, "lwr"], 1),
  IC_media_sup = round(pred1_conf[, "upr"], 1),
  IC_pred_inf = round(pred1_pred[, "lwr"], 1),
  IC_pred_sup = round(pred1_pred[, "upr"], 1)
)
kable(tabla_pred1,
      caption = "Predicción del precio — Vivienda 1 (Casa Norte)",
      col.names = c("Estrato", "Precio estimado ($M)",
                    "IC media inf.", "IC media sup.",
                    "IC pred. inf.", "IC pred. sup."))
Predicción del precio — Vivienda 1 (Casa Norte)
Estrato Precio estimado ($M) IC media inf. IC media sup. IC pred. inf. IC pred. sup.
4 312.1 287.2 337.0 6.2 618.0
5 392.7 360.9 424.6 86.2 699.3

Interpretación de la predicción (Vivienda 1). El modelo estima un precio esperado de ≈ $312 millones para una casa de estrato 4 y ≈ $393 millones para estrato 5, con las características solicitadas. Frente al crédito preaprobado de $350 millones:

  • Estrato 4 ($312 M): viable. Está por debajo del presupuesto, con margen de ≈ $38 millones.
  • Estrato 5 ($393 M): excede el presupuesto en ≈ $43 millones.

El intervalo de confianza (para el precio promedio de mercado) es estrecho, mientras que el intervalo de predicción (para una casa individual) es amplio, reflejando la variabilidad natural entre viviendas particulares. Recomendación: orientar la búsqueda hacia casas de estrato 4 para ajustarse al crédito, o negociar precio en estrato 5.

4.6 Ofertas potenciales — Vivienda 1 (crédito ≤ $350 millones)

ofertas1 <- base1 %>%
  filter(preciom <= 350, areaconst >= 150, banios >= 2, habitaciones >= 3) %>%
  arrange(desc(preciom))

cat("Ofertas reales que cumplen (≤$350M, área≥150, baños≥2, habitaciones≥3):",
    nrow(ofertas1), "\n")
## Ofertas reales que cumplen (≤$350M, área≥150, baños≥2, habitaciones≥3): 118
ofertas1 %>%
  select(barrio, estrato, preciom, areaconst, parqueaderos, banios, habitaciones) %>%
  head(6) %>%
  kable(caption = "Ofertas potenciales — Vivienda 1 (Casa Norte, ≤$350M)")
Ofertas potenciales — Vivienda 1 (Casa Norte, ≤$350M)
barrio estrato preciom areaconst parqueaderos banios habitaciones
el bosque 5 350 200 3 3 4
el bosque 5 350 300 3 5 6
el bosque 5 350 240 2 3 6
el bosque 5 350 203 2 2 5
la flora 5 350 264 2 3 4
la flora 5 350 160 2 3 3
top_ofertas1 <- ofertas1 %>% head(8)
leaflet(top_ofertas1) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud,
                   radius = 6, color = "#2ecc71", fillOpacity = 0.8,
                   popup = ~paste0("Barrio: ", barrio,
                                   "<br>Precio: $", preciom, " M",
                                   "<br>Área: ", areaconst, " m²",
                                   "<br>Estrato: ", estrato,
                                   "<br>Baños: ", banios,
                                   "<br>Habitaciones: ", habitaciones))

Discusión de ofertas (Vivienda 1). Existen numerosas casas en la Zona Norte por debajo de $350 millones que se acercan al perfil solicitado (área ≥ 150 m², al menos 2 baños y 3 habitaciones). Predominan en barrios como el bosque y la flora, en su mayoría de estrato 5 pero con precios ajustados al presupuesto. El mapa muestra su distribución geográfica. Recomendación para María: presentar a la empresa estas alternativas de estrato 5 a precio de $350 M (que ofrecen mejor nivel del que el modelo predeciría para ese presupuesto), o casas de estrato 4 con mayor holgura. Conviene verificar que las ofertas seleccionadas estén efectivamente en el norte geográfico, dada la inconsistencia de zona detectada.


5 Caso 2: Vivienda 2 — Apartamento en la Zona Sur

Se replica íntegramente la metodología del Caso 1, ahora para el segmento correspondiente a la segunda solicitud: apartamentos en la Zona Sur, con crédito preaprobado de $850 millones.

5.1 Filtro y depuración de la base

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

cat("Número de apartamentos en la Zona Sur:", nrow(base2), "\n")
## Número de apartamentos en la Zona Sur: 2787
base2 %>% select(zona, tipo, estrato, preciom, areaconst,
                 parqueaderos, banios, habitaciones, barrio) %>%
  head(3) %>%
  kable(caption = "Primeros 3 registros — Apartamentos Zona Sur")
Primeros 3 registros — Apartamentos Zona Sur
zona tipo estrato preciom areaconst parqueaderos banios habitaciones barrio
Zona Sur Apartamento 4 290 96 1 2 3 acopi
Zona Sur Apartamento 3 78 40 1 1 2 aguablanca
Zona Sur Apartamento 6 875 194 2 5 3 aguacatal
cat("Verificación de tipo y zona (deben ser únicos):\n")
## Verificación de tipo y zona (deben ser únicos):
table(base2$tipo, base2$zona)
##              
##               Zona Sur
##   Apartamento     2787
cat("\nDistribución por estrato:\n")
## 
## Distribución por estrato:
table(base2$estrato)
## 
##    3    4    5    6 
##  201 1091 1033  462

El filtro es correcto: todos los registros son Apartamento en la Zona Sur. Este segmento es mucho más numeroso que el de casas del norte y predominan los estratos 4 (1091) y 5 (1033), seguidos del estrato 6 (462) y el 3 (201). Los estratos 5 y 6, correspondientes a la solicitud, cuentan con amplia oferta (1033 y 462 apartamentos respectivamente).

5.1.1 Mapa de las ofertas y consistencia geográfica

leaflet(base2) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud,
                   radius = 2, color = "#e74c3c", fillOpacity = 0.4,
                   popup = ~paste0("Barrio: ", barrio,
                                   "<br>Precio: $", preciom, " M",
                                   "<br>Estrato: ", estrato))
resumen_lat2 <- base2 %>%
  summarise(lat_min = round(min(latitud, na.rm = TRUE), 4),
            lat_media = round(mean(latitud, na.rm = TRUE), 4),
            lat_max = round(max(latitud, na.rm = TRUE), 4))
kable(resumen_lat2, caption = "Rango de latitud — Apartamentos 'Zona Sur'")
Rango de latitud — Apartamentos ‘Zona Sur’
lat_min lat_media lat_max
3.3337 3.3895 3.4968
fuera2 <- base2 %>% filter(latitud > 3.45)
cat("Registros con latitud > 3.45 (posiblemente fuera del sur):",
    nrow(fuera2), "de", nrow(base2),
    "(", round(100*nrow(fuera2)/nrow(base2), 1), "%)\n")
## Registros con latitud > 3.45 (posiblemente fuera del sur): 125 de 2787 ( 4.5 %)

Discusión — consistencia de la zona. La clasificación de la Zona Sur es más consistente que la del norte: solo cerca del 4.5% de los apartamentos presenta latitudes por encima de 3.45 (hacia el norte). La latitud media (≈ 3.39) es coherente con una ubicación al sur del centro de la ciudad. Aun así, persisten algunos registros mal clasificados que convendría depurar. La mayor consistencia y el mayor tamaño de esta base hacen que el modelo del sur sea, en principio, más confiable.

5.2 Análisis exploratorio de datos (EDA)

vars2 <- c("preciom", "areaconst", "estrato", "banios", "habitaciones", "parqueaderos")
base2_num <- base2 %>% select(all_of(vars2)) %>% drop_na()

mat_cor2 <- round(cor(base2_num), 3)
kable(mat_cor2, caption = "Matriz de correlaciones — Apartamentos Zona Sur")
Matriz de correlaciones — Apartamentos Zona Sur
preciom areaconst estrato banios habitaciones parqueaderos
preciom 1.000 0.741 0.650 0.711 0.296 0.693
areaconst 0.741 1.000 0.452 0.664 0.407 0.578
estrato 0.650 0.452 1.000 0.535 0.177 0.486
banios 0.711 0.664 0.535 1.000 0.520 0.556
habitaciones 0.296 0.407 0.177 0.520 1.000 0.237
parqueaderos 0.693 0.578 0.486 0.556 0.237 1.000
cor_precio2 <- data.frame(
  variable = names(mat_cor2[,"preciom"]),
  correlacion = as.numeric(mat_cor2[,"preciom"])
) %>% filter(variable != "preciom") %>% arrange(correlacion)

plot_ly(cor_precio2, x = ~correlacion, y = ~reorder(variable, correlacion),
        type = "bar", orientation = "h",
        marker = list(color = "#e74c3c")) %>%
  layout(title = "Correlación de cada variable con el precio (Aptos Sur)",
         xaxis = list(title = "Correlación con preciom"),
         yaxis = list(title = ""))
plot_ly(base2_num, x = ~areaconst, y = ~preciom,
        type = "scatter", mode = "markers",
        marker = list(color = "#034D94", opacity = 0.4),
        text = ~paste("Área:", areaconst, "m²<br>Precio: $", preciom, "M")) %>%
  layout(title = "Precio vs Área construida (Aptos Sur)",
         xaxis = list(title = "Área construida (m²)"),
         yaxis = list(title = "Precio (millones)"))

Interpretación EDA. En los apartamentos de la Zona Sur las correlaciones con el precio son más fuertes que en las casas del norte: área construida (r ≈ 0.74), baños (r ≈ 0.71), parqueaderos (r ≈ 0.69) y estrato (r ≈ 0.65) están todas fuertemente asociadas al precio. Llama la atención que las habitaciones tienen una correlación baja (r ≈ 0.30), mucho menor que las demás. Esto sugiere que, en apartamentos, el valor lo determinan más el tamaño y las comodidades (baños, parqueaderos) que la cantidad de cuartos. Las correlaciones entre predictoras son moderadas (la mayor, área–baños ≈ 0.66), por lo que no se anticipa multicolinealidad grave.

5.3 Estimación e interpretación del modelo

modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
              data = base2)
summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, 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
##   (406 observations deleted due to missingness)
## Multiple R-squared:  0.7485, Adjusted R-squared:  0.748 
## F-statistic:  1414 on 5 and 2375 DF,  p-value: < 2.2e-16
kable(data.frame(VIF = round(vif(modelo2), 2)),
      caption = "Factores de inflación de varianza (VIF) — Modelo Aptos Sur")
Factores de inflación de varianza (VIF) — Modelo Aptos Sur
VIF
areaconst 2.07
estrato 1.55
habitaciones 1.43
parqueaderos 1.74
banios 2.53

Interpretación de los coeficientes (Aptos Sur). El modelo es globalmente muy significativo (F con p-valor < 0.001) y todas las variables resultan significativas (p < 0.05). Manteniendo lo demás constante:

  • Área construida (≈ +1.29): cada m² adicional incrementa el precio esperado en cerca de $1.29 millones. El efecto por m² es casi el doble que en las casas del norte, reflejando el mayor valor del suelo/construcción en apartamentos del sur.
  • Estrato (≈ +60.9): subir un estrato añade unos $60.9 millones.
  • Parqueaderos (≈ +72.9): cada parqueadero adicional suma unos $72.9 millones — el factor de mayor impacto unitario, coherente con la escasez y el alto valor del parqueadero en apartamentos.
  • Baños (≈ +50.7): cada baño adicional añade unos $50.7 millones.
  • Habitaciones (≈ −24.8): coeficiente negativo y significativo. A igualdad de área, baños y parqueaderos, más habitaciones se asocia con un menor precio.

Discusión del signo negativo de habitaciones. Aunque a primera vista parece ilógico, tiene una explicación de mercado: para un apartamento del mismo tamaño, repartir el área en más habitaciones significa cuartos más pequeños y una distribución menos exclusiva; los apartamentos más costosos del sur tienden a ser amplios con pocas habitaciones grandes (mayor lujo por m²). El signo negativo captura este efecto una vez descontada el área. Es un buen ejemplo de por qué un coeficiente debe interpretarse manteniendo las demás variables constantes, y no de forma aislada. Los VIF son todos inferiores a 2.6, de modo que este signo no se debe a multicolinealidad, sino a un patrón real del segmento.

Ajuste del modelo (R²). El R² ≈ 0.75 indica que el modelo explica el 75% de la variabilidad del precio de los apartamentos del sur — un ajuste bueno, superior al del modelo de casas (60%), gracias al mayor tamaño de muestra y a la mayor consistencia del segmento. Cómo mejorarlo: añadir ubicación fina (barrio), antigüedad y acabados, o transformar la respuesta (log(preciom)) para el 25% de varianza no explicada.

5.4 Validación de supuestos

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

par(mfrow = c(1, 1))
res2 <- residuals(modelo2)
set.seed(123)
sw2 <- shapiro.test(if (length(res2) > 5000) sample(res2, 5000) else res2)
cat("Shapiro-Wilk (normalidad de residuales): W =", round(sw2$statistic, 4),
    ", p-value =", format(sw2$p.value, scientific = TRUE), "\n")
## Shapiro-Wilk (normalidad de residuales): W = 0.7912 , p-value = 4.837179e-48
bp2 <- ncvTest(modelo2)
cat("Prueba de varianza no constante (Breusch-Pagan): p-value =",
    format(bp2$p, scientific = TRUE), "\n")
## Prueba de varianza no constante (Breusch-Pagan): p-value = 0e+00

Interpretación de los supuestos (Aptos Sur). Al igual que en el modelo de casas, se rechazan los supuestos de normalidad y de varianza constante (ambos p-valores < 0.05). El gráfico Q-Q evidencia colas pesadas (apartamentos de precio muy alto) y el gráfico de residuales vs. ajustados muestra heterocedasticidad en forma de embudo. La independencia se asume cumplida.

Sugerencias (sin aplicar). La medida remedial más recomendable es una transformación logarítmica de la respuesta (log(preciom)), habitual en datos de precios inmobiliarios y que suele corregir simultáneamente la no normalidad y la heterocedasticidad; adicionalmente, el tratamiento de las observaciones atípicas de precio elevado (apartamentos de lujo) mejoraría el ajuste. Pese a estas desviaciones, el modelo es válido como referencia de precio de mercado para orientar la asesoría.

5.5 Estimación puntual — Vivienda 2

Se predice el precio del apartamento solicitado: 300 m², 3 parqueaderos, 3 baños, 5 habitaciones, en estrato 5 y 6.

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

pred2_conf <- predict(modelo2, newdata = solicitud2, interval = "confidence")
pred2_pred <- predict(modelo2, newdata = solicitud2, interval = "prediction")

tabla_pred2 <- data.frame(
  Estrato = c(5, 6),
  Precio_estimado = round(pred2_conf[, "fit"], 1),
  IC_media_inf = round(pred2_conf[, "lwr"], 1),
  IC_media_sup = round(pred2_conf[, "upr"], 1),
  IC_pred_inf = round(pred2_pred[, "lwr"], 1),
  IC_pred_sup = round(pred2_pred[, "upr"], 1)
)
kable(tabla_pred2,
      caption = "Predicción del precio — Vivienda 2 (Apartamento Sur)",
      col.names = c("Estrato", "Precio estimado ($M)",
                    "IC media inf.", "IC media sup.",
                    "IC pred. inf.", "IC pred. sup."))
Predicción del precio — Vivienda 2 (Apartamento Sur)
Estrato Precio estimado ($M) IC media inf. IC media sup. IC pred. inf. IC pred. sup.
5 675.0 652.1 697.9 481.5 868.6
6 735.9 712.7 759.1 542.3 929.5

Interpretación de la predicción (Vivienda 2). El modelo estima un precio esperado de ≈ $675 millones para un apartamento de estrato 5 y ≈ $736 millones para estrato 6, con las características solicitadas. Frente al crédito preaprobado de $850 millones:

  • Estrato 5 ($675 M): holgadamente viable, con margen de ≈ $175 millones.
  • Estrato 6 ($736 M): también viable, con margen de ≈ $114 millones.

En ambos casos la solicitud se atiende cómodamente dentro del presupuesto. El margen disponible permite priorizar mejor ubicación, acabados o cubrir gastos de la transacción. Recomendación: dado el amplio margen, se puede apuntar a apartamentos de estrato 6 de alta calidad sin comprometer el crédito.

5.6 Ofertas potenciales — Vivienda 2 (crédito ≤ $850 millones)

ofertas2 <- base2 %>%
  filter(preciom <= 850, areaconst >= 200, banios >= 3,
         habitaciones >= 4, estrato >= 5) %>%
  arrange(desc(preciom))

cat("Ofertas reales que cumplen (≤$850M, área≥200, baños≥3, habitaciones≥4, estrato≥5):",
    nrow(ofertas2), "\n")
## Ofertas reales que cumplen (≤$850M, área≥200, baños≥3, habitaciones≥4, estrato≥5): 28
ofertas2 %>%
  select(barrio, estrato, preciom, areaconst, parqueaderos, banios, habitaciones) %>%
  head(6) %>%
  kable(caption = "Ofertas potenciales — Vivienda 2 (Apto Sur, ≤$850M)")
Ofertas potenciales — Vivienda 2 (Apto Sur, ≤$850M)
barrio estrato preciom areaconst parqueaderos banios habitaciones
pance 6 750 200.00 3 5 4
guadalupe 5 730 573.00 3 8 5
el ingenio 6 700 250.00 2 4 5
el ingenio 5 690 486.00 2 4 4
seminario 5 670 300.00 3 5 6
quintas de don 6 660 224.41 2 5 4
top_ofertas2 <- ofertas2 %>% head(8)
leaflet(top_ofertas2) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud,
                   radius = 6, color = "#9b59b6", fillOpacity = 0.8,
                   popup = ~paste0("Barrio: ", barrio,
                                   "<br>Precio: $", preciom, " M",
                                   "<br>Área: ", areaconst, " m²",
                                   "<br>Estrato: ", estrato,
                                   "<br>Baños: ", banios,
                                   "<br>Habitaciones: ", habitaciones))

Discusión de ofertas (Vivienda 2). Se identifican varias ofertas de apartamentos amplios (área ≥ 200 m²), de estrato 5 y 6, con al menos 3 baños y 4 habitaciones, todas por debajo de $850 millones, en barrios del sur como pance, el ingenio, ciudadela pasoancho y seminario. El mapa muestra su ubicación. Dado el amplio margen presupuestal, María puede ofrecer a la empresa alternativas de alto nivel (estrato 6, buena área y comodidades) sin agotar el crédito, reservando parte para acabados o costos de cierre.


6 Conclusiones y recomendaciones

Sobre las dos solicitudes. El análisis de regresión lineal múltiple, aplicado por separado a cada segmento de mercado, permite responder con criterio técnico a la empresa internacional:

  • Vivienda 1 (Casa, Zona Norte, crédito $350 M): precio estimado de $312 M en estrato 4 y $393 M en estrato 5. La solicitud es viable en estrato 4; el estrato 5 excede ligeramente el presupuesto. Existen numerosas ofertas reales dentro del crédito.
  • Vivienda 2 (Apartamento, Zona Sur, crédito $850 M): precio estimado de $675 M en estrato 5 y $736 M en estrato 6. La solicitud es holgadamente viable en ambos estratos, con margen suficiente para priorizar calidad y ubicación.

Sobre los modelos. El área construida y el estrato son los determinantes más consistentes del precio en ambos segmentos. El modelo de apartamentos del sur (R² ≈ 0.75) ajusta mejor que el de casas del norte (R² ≈ 0.60), gracias a un mayor tamaño de muestra y una clasificación de zona más consistente. En ninguno de los dos modelos hay multicolinealidad (VIF < 3), aunque ambos incumplen los supuestos de normalidad y homocedasticidad, lo que sugiere aplicar una transformación logarítmica de la respuesta en un análisis posterior.

Recomendaciones para María.

  1. Para la Vivienda 1, orientar la búsqueda a casas de estrato 4 en la Zona Norte, o negociar precio en ofertas de estrato 5 a $350 M que ofrecen mejor relación valor-presupuesto.
  2. Para la Vivienda 2, aprovechar el amplio margen para presentar apartamentos de estrato 6 de alta calidad en el sur.
  3. Depurar las inconsistencias de zona detectadas (especialmente en el norte, ≈15% de registros mal ubicados) antes de decisiones críticas, validando la zona con las coordenadas geográficas.
  4. En un análisis futuro, enriquecer los modelos con variables de ubicación fina (barrio), antigüedad y acabados, y aplicar transformaciones para mejorar el cumplimiento de los supuestos.

7 Anexos técnicos

7.1 Comparación de modelos

comp <- bind_rows(
  glance(modelo1) %>% mutate(Modelo = "Casas Norte"),
  glance(modelo2) %>% mutate(Modelo = "Aptos Sur")
) %>% select(Modelo, r.squared, adj.r.squared, sigma, statistic, AIC, BIC, nobs)
kable(comp, digits = 2, caption = "Comparación de los dos modelos estimados")
Comparación de los dos modelos estimados
Modelo r.squared adj.r.squared sigma statistic AIC BIC nobs
Casas Norte 0.60 0.60 155.11 130.92 5630.86 5659.39 435
Aptos Sur 0.75 0.75 98.02 1413.80 28599.54 28639.96 2381

7.2 Fuente de datos

Los datos provienen de la base vivienda del paquete paqueteMODELOS, con información de propiedades residenciales en Cali obtenida mediante web scraping de la plataforma OLX, correspondiente a los últimos tres meses de oferta.