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.
El análisis sigue la misma secuencia metodológica para cada una de las dos solicitudes, garantizando un tratamiento comparable:
plotly).preciom = f(areaconst, estrato, habitaciones, parqueaderos, banios),
se interpretan los coeficientes significativos en contexto y se evalúa
el ajuste global (R²).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.
## 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.
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")| 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 |
## Verificación de tipo y zona (deben ser únicos):
##
## Zona Norte
## Casa 722
##
## Distribución por 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).
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'")| 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.
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")| 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_disp1Interpretació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.
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")| 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.
# 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.
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."))| 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.
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)")| 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.
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.
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")| 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 |
## Verificación de tipo y zona (deben ser únicos):
##
## Zona Sur
## Apartamento 2787
##
## Distribución por 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).
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'")| 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.
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")| 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.
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")| 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.
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.
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."))| 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.
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)")| 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.
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:
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.
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")| 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 |
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.