C&A (Casas y Apartamentos) es una agencia inmobiliaria fundada en la ciudad de Cali por María Quintero, quien cuenta con más de 10 años de experiencia en el sector. La agencia dispone de un equipo de profesionales altamente calificados para asesorar en la compra de viviendas, tanto nuevas como usadas, para clientes nacionales e internacionales.
El objetivo de este informe es proporcionar recomendaciones claras para la adquisición de dos inmuebles en la ciudad de Cali para la compañía internacional “X”, considerando las características y condiciones específicas de cada propiedad.
Los resultados principales se presentan en el cuerpo ejecutivo del informe, mientras que los anexos incluyen todas las estimaciones, análisis, validaciones y comparaciones de modelos que respaldan dichas recomendaciones.
library(knitr)
library(kableExtra)
viviendas <- data.frame(
Característica = c("Tipo", "Área construida (m²)", "Parqueaderos", "Baños",
"Habitaciones", "Estrato", "Zona", "Crédito preaprobado"),
Vivienda_1 = c("Casa", 200, 1, 2, 4, "4–5", "Norte", "350 millones"),
Vivienda_2 = c("Apartamento", 300, 3, 3, 5, "5–6", "Sur", "850 millones"),
stringsAsFactors = FALSE
)
kable(viviendas, "html", caption = "<b>Tabla1. Solicitudes de vivienda</b>") %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(1:8, background = rep(c("#ECF0F1", "#FFFFFF"), 4)) %>%
column_spec(1, bold = TRUE, color = "#2C3E50")
| Característica | Vivienda_1 | Vivienda_2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| Área construida (m²) | 200 | 300 |
| Parqueaderos | 1 | 3 |
| Baños | 2 | 3 |
| Habitaciones | 4 | 5 |
| Estrato | 4–5 | 5–6 |
| Zona | Norte | Sur |
| Crédito preaprobado | 350 millones | 850 millones |
vivienda <- readRDS("vivienda_consolidada.rds")
norte_final <- readRDS("vivienda_norte_final.rds")
sur_final <- readRDS("vivienda_sur_final.rds")
viviendas_filtradas <- subset(vivienda,
areaconst >= 200 &
parqueaderos >= 1 &
banios >= 2 &
habitaciones >= 4 &
estrato %in% c(4,5) &
zona_nueva == "Norte" &
preciom <= 350 &
tipo == "Casa")
viviendas_filtradas2 <- subset(vivienda,
areaconst >= 300 &
parqueaderos >= 3 &
banios >= 3 &
habitaciones >= 5 &
estrato %in% c(5,6) &
zona_nueva == "Sur" &
preciom <= 850 &
tipo == "Apartamento")
resumen <- data.frame(
Solicitud = c("Norte_Casa", "Sur_Apartamento"),
n_ofertas = c(nrow(viviendas_filtradas),
nrow(viviendas_filtradas2)),
precio_promedio = c(mean(viviendas_filtradas$preciom),
mean(viviendas_filtradas2$preciom))
)
kable(resumen, "html",
caption = "<b>Tabla 2. Resumen de ofertas de vivienda</b>") %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(1:2, background = rep(c("#ECF0F1", "#FFFFFF"), 1)) %>%
column_spec(1, bold = TRUE, color = "#2C3E50")
| Solicitud | n_ofertas | precio_promedio |
|---|---|---|
| Norte_Casa | 30 | 329.4333 |
| Sur_Apartamento | 0 | NaN |
La Tabla 2 presenta un resumen del número de ofertas de vivienda y del precio promedio según los requerimientos del cliente en términos de área construida, zona, tipo de inmueble, habitaciones, baños y parqueaderos, cumpliendo con los criterios solicitados o superiores en cuanto a área, parqueaderos, habitaciones y baños.
Para la solicitud de casa en la zona Norte, con los requerimientos del cliente se evidencian 30 ofertas con un precio promedio de 329,5 millones, por debajo del crédito aprobado.
En cuanto a los requerimientos de apartamento en la zona Sur, no se encontraron ofertas que cumplan con todos los criterios del cliente. Las dos ofertas que sí cumplen los requisitos tienen un precio de 1.150 millones, muy por encima de la restricción presupuestal de 850 millones. Si no se considera el tipo de inmueble y se incluyen casas, el número de ofertas disponibles asciende a 47.
Para evaluar el presupuesto, se analizan los requerimientos tanto en el modelo general (Modelo 1)Anexo 16 como en el modelo específico para cada área (Modelo 2 y 3) . Esto permite identificar de manera más precisa qué viviendas cumplen con los criterios del cliente y cómo varía la disponibilidad y el precio promedio según las características particulares de cada zona o tipo de inmueble.
options(contrasts = c("contr.treatment", "contr.poly"))
vivienda <- readRDS("vivienda_consolidada.rds")
norte_final <- readRDS("vivienda_norte_final.rds")
sur_final <- readRDS("vivienda_sur_final.rds")
vivienda$estrato <- factor(vivienda$estrato)
vivienda$tipo1 <- factor(vivienda$tipo)
vivienda$estrato <- as.factor(vivienda$estrato)
modelo_global_log <- lm(
log(preciom) ~ log(areaconst) + estrato + banios +
habitaciones + parqueaderos + zona_nueva + tipo +
estrato:tipo + zona_nueva:tipo,
data = vivienda
)
viviendas_prueba <- data.frame(
id = paste0("Vivienda_", 1:4),
tipo = c("Casa", "Casa", "Apartamento", "Apartamento"),
areaconst = c(200, 200, 300, 300),
parqueaderos = c(1, 1, 3, 3),
banios = c(2, 2, 3, 3),
habitaciones = c(4, 4, 5, 5),
estrato = as.factor(c(4, 5, 5, 6)),
zona_nueva = as.factor(c("Norte", "Norte", "Sur", "Sur"))
)
pred_log_pi <- predict(modelo_global_log, newdata = viviendas_prueba, interval = "prediction", level = 0.95)
pred_pi <- exp(pred_log_pi)
tabla_global <- data.frame(
id = viviendas_prueba$id,
tipo = viviendas_prueba$tipo,
zona = viviendas_prueba$zona_nueva,
estrato = viviendas_prueba$estrato,
prediccion = round(exp(pred_log_pi[, "fit"]), 2),
lwr = round(exp(pred_log_pi[, "lwr"]), 2),
upr = round(exp(pred_log_pi[, "upr"]), 2)
)
kable(tabla_global, "html", caption = "<b>Tabla 3. Evaluación de solicitudes en el modelo global</b>") %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(1:nrow(tabla_global), background = rep(c("#ECF0F1", "#FFFFFF"), length.out = nrow(tabla_global))) %>%
column_spec(1:4, bold = TRUE, color = "#2C3E50")
| id | tipo | zona | estrato | prediccion | lwr | upr |
|---|---|---|---|---|---|---|
| Vivienda_1 | Casa | Norte | 4 | 295.93 | 184.67 | 474.22 |
| Vivienda_2 | Casa | Norte | 5 | 330.50 | 206.27 | 529.54 |
| Vivienda_3 | Apartamento | Sur | 5 | 569.85 | 355.74 | 912.80 |
| Vivienda_4 | Apartamento | Sur | 6 | 789.59 | 492.90 | 1264.85 |
Según el modelo global, los presupuestos planteados por los clientes son realistas dentro de los rangos estimados. Sin embargo, existe una probabilidad significativa de que el monto preaprobado no sea suficiente para adquirir el inmueble deseado.
Esto se evidencia en la solicitud 2 (Vivienda 4), un apartamento en la zona Sur, estrato 6, cuyas características coinciden con los requerimientos del cliente. La predicción central del precio es de 789,59 millones, pero su límite superior del intervalo de predicción alcanza los 1.264,85 millones, lo que refleja que, como se observa en la base de datos, los inmuebles con estas características pueden costar hasta 1.150 millones, superando el crédito aprobado.
norte_final <- readRDS("vivienda_norte_final.rds")
norte_final$estrato <- factor(norte_final$estrato)
norte_final$tipo <- factor(norte_final$tipo)
modelo_norte_r <- lm(log(preciom) ~ poly(areaconst,2) + estrato +
+ banios + parqueaderos+habitaciones,
data = norte_final)
viviendas_prueba_n <- data.frame(
id = paste0("Vivienda_", 1:2),
areaconst = c(200, 200),
parqueaderos = c(1, 1),
banios = c(2, 2),
habitaciones = c(4, 4),
estrato = as.factor(c(4, 5))
)
pred_log_pi_norte <- predict(modelo_norte_r, newdata = viviendas_prueba_n, interval = "prediction", level = 0.95)
pred_pi_norte <- exp(pred_log_pi_norte)
tabla_norte <- data.frame(
id = viviendas_prueba_n$id,
estrato = viviendas_prueba_n$estrato,
prediccion = round(exp(pred_log_pi_norte[, "fit"]), 2),
lwr = round(exp(pred_log_pi_norte[, "lwr"]), 2),
upr = round(exp(pred_log_pi_norte[, "upr"]), 2)
)
kable(tabla_norte, "html", caption = "<b>Tabla 4. Estimación de precios - Zona Norte</b>") %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(1:nrow(tabla_norte), background = rep(c("#ECF0F1", "#FFFFFF"), length.out = nrow(tabla_norte))) %>%
column_spec(1:2, bold = TRUE, color = "#2C3E50")
| id | estrato | prediccion | lwr | upr |
|---|---|---|---|---|
| Vivienda_1 | 4 | 327.73 | 202.45 | 530.53 |
| Vivienda_2 | 5 | 361.46 | 223.55 | 584.46 |
La Tabla 4 presenta la estimación de la solicitud para un inmueble tipo casa en la zona Norte, utilizando el Anexo 8, que se ajusta únicamente con datos de casas en esta zona. Al emplear intervalos de predicción al 95%, se observa que los rangos estimados se ajustan, en general, a la restricción presupuestaria del cliente. No obstante, el presupuesto podría resultar ligeramente insuficiente para un inmueble de estrato 5, donde la predicción puntual es de 361,46 millones y el límite superior alcanza los 584 millones.
sur_final <- readRDS("vivienda_sur_final.rds")
options(contrasts = c("contr.treatment", "contr.poly"))
sur_final$estrato <- factor(sur_final$estrato)
sur_final$tipo <- factor(sur_final$tipo)
modelo_sur2 <- lm(log(preciom) ~ log(areaconst) + estrato +
banios + habitaciones + parqueaderos,
data = sur_final)
viviendas_prueba_s <- data.frame(
id = paste0("Vivienda_", 1:2),
areaconst = c(300, 300),
parqueaderos = c(3, 3),
banios = c(3, 3),
habitaciones = c(5, 5),
estrato = as.factor(c(5, 6))
)
pred_log_pi_sur <- predict(modelo_sur2, newdata = viviendas_prueba_s, interval = "prediction", level = 0.95)
pred_pi_sur <- exp(pred_log_pi_sur)
tabla_sur <- data.frame(
id = viviendas_prueba_s$id,
estrato = viviendas_prueba_s$estrato,
prediccion = round(exp(pred_log_pi_sur[, "fit"]), 2),
lwr = round(exp(pred_log_pi_sur[, "lwr"]), 2),
upr = round(exp(pred_log_pi_sur[, "upr"]), 2)
)
kable(tabla_sur, "html", caption = "<b>Tabla 5. Estimación de precios – Zona Sur</b>") %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped", "hover", "condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(1:nrow(tabla_sur), background = rep(c("#ECF0F1", "#FFFFFF"), length.out = nrow(tabla_sur))) %>%
column_spec(1:2, bold = TRUE, color = "#2C3E50")
| id | estrato | prediccion | lwr | upr |
|---|---|---|---|---|
| Vivienda_1 | 5 | 664.35 | 452.88 | 974.56 |
| Vivienda_2 | 6 | 862.48 | 588.00 | 1265.10 |
Finalmente, la Tabla 5 presenta la estimación de la solicitud para un inmueble tipo apartamento en la zona Norte, utilizando el Modelo 2 Anexo 7, que se ajusta únicamente con datos de apartamentos en esta zona. Al emplear intervalos de predicción al 95%, se observa que los rangos estimados se ajustan, en general, a la restricción presupuestaria del cliente. No obstante, el presupuesto podría quedarse corto, y, como se evidenció, no existen ofertas de inmuebles con las características solicitadas dentro del crédito planteado.
Para responder la solicitud 1, correspondiente a una casa en la zona norte con ciertas características, se filtraron los inmuebles que cumplen con las condiciones solicitadas. Posteriormente, con base en el Modelo 3, se estimo el precio predicho de cada inmueble y se comparó con su precio de mercado.
La estrategia para encontrar las mejores alternativas fue la siguiente: se estableció un tope presupuestario de 400 millones, suponiendo cierta capacidad de negociación por parte del cliente cuyo presupuesto inicial es de 350 millones. A partir de este criterio se optó por dos tipos de alternativas.
En primer lugar, se seleccionaron tres ofertas extremas cuyo precio de mercado se encuentra muy por debajo del precio predicho por el modelo, lo que indica posibles inmuebles subvalorados y, por tanto, oportunidades potenciales para el cliente, al permitir adquirir una propiedad por un valor inferior al estimado por el modelo.
Adicionalmente, se incluyeron dos opciones más conservadoras, caracterizadas por residuos negativos más pequeños: una con un precio cercano al límite superior del presupuesto (aproximadamente 370 millones, considerando una posible negociación) y otra claramente por debajo (320 millones). Esta selección responde a que el modelo no captura algunas variables no observadas, como la antigüedad del inmueble o la calidad de los acabados. En este sentido, las alternativas conservadoras ofrecen mayor seguridad, ya que es posible que los precios considerablemente bajos de las tres primeras propiedades reflejen precisamente la ausencia de estos atributos adicionales.
viviendas_en <- subset(vivienda,
areaconst >= 200 &
parqueaderos >= 1 &
banios >= 2 &
habitaciones >= 4 &
estrato %in% c(4,5) &
zona_nueva == "Norte" &
tipo == "Casa")
############################################
viviendas_en <- subset(vivienda,
areaconst >= 200 &
parqueaderos >= 1 &
banios >= 2 &
habitaciones >= 4 &
estrato %in% c(4,5) &
zona_nueva == "Norte" &
tipo == "Casa")
############################################
pred_log <- predict(modelo_norte_r, newdata = viviendas_en)
pred_precio <- exp(pred_log)
residuos_real <- viviendas_en$preciom - pred_precio
residuo_pct <- 100 * residuos_real / pred_precio
viviendas_en$residuo <- round(residuos_real, 2)
viviendas_en$residuo_pct <- round(residuo_pct, 2)
viviendas_en$precio_predicho <- round(pred_precio, 2)
viviendas_en <- viviendas_en[, c("id", "estrato", "preciom", "precio_predicho", "residuo", "residuo_pct",
"areaconst", "parqueaderos", "banios", "habitaciones", "zona_nueva", "tipo", "barrio",
"longitud", "latitud")]
viviendas_400 <- subset(viviendas_en, preciom <= 400)
subset_gangas <- viviendas_400 %>%
dplyr::filter(id %in% c(1020, 1009, 3101, 1835, 1144))
tabla_gangas <- subset_gangas[, c("id", "estrato", "preciom", "precio_predicho","barrio")]
kable(tabla_gangas,
caption = "<b>Tabla 6 - Inmuebles Zona norte - casas</b>",
col.names = c("ID", "Estrato", "P de mercado (millones)", "Precio Predicho", "Barrio")) %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50")
| ID | Estrato | P de mercado (millones) | Precio Predicho | Barrio |
|---|---|---|---|---|
| 1009 | 5 | 250 | 417.29 | el bosque |
| 1020 | 4 | 230 | 390.02 | la merced |
| 1144 | 4 | 320 | 365.15 | la merced |
| 3101 | 5 | 340 | 548.13 | san vicente |
| 1835 | 4 | 370 | 393.50 | vipasa |
La tabla 6 presenta 5 ofertas de inmuebles a la solicitud 1 (Zona norte - Casa):
Inmueble ID 1009: El inmueble presenta un precio de 250 millones y un precio estimado mediante el modelo de 417.29 millones, lo que representa una diferencia de -167.29 millones en términos absolutos y de -40.09% en términos relativos. Cuenta con un área de 243 metros cuadrados, superior a los 200 metros solicitados por el cliente, 1 parqueadero, 4 baños y 5 habitaciones. Además, se localiza en el barrio El Bosque de la ciudad de Cali, correspondiente a estrato 5.
Inmueble ID 1020: El inmueble presenta un precio de 230 millones y un precio estimado mediante el modelo de 390.02 millones, lo que representa una diferencia de -160.02 millones en términos absolutos y -41.03% en términos relativos. Cuenta con un área de 250 metros cuadrados, superior a los 200 metros solicitados por el cliente, 2 parqueaderos, 3 baños y 5 habitaciones. Además, se localiza en el barrio La Merced de la ciudad de Cali, correspondiente a estrato 4.
Inmueble ID 1144: (Conservadora) El inmueble presenta un precio de 320 millones y un precio estimado mediante el modelo de 365.15 millones, lo que representa una diferencia de -45.15 millones en términos absolutos y -12.36% en términos relativos. Cuenta con un área de 200 metros cuadrados, cumpliendo con el mínimo solicitado por el cliente, 2 parqueaderos, 4 baños y 4 habitaciones. Además, se localiza en el barrio La Merced de la ciudad de Cali, correspondiente a estrato 4.
Inmueble ID 3101: El inmueble presenta un precio de 340 millones y un precio estimado mediante el modelo de 548.13 millones, lo que representa una diferencia de -208.13 millones en términos absolutos y -37.97% en términos relativos. Cuenta con un área de 355 metros cuadrados, considerablemente superior a los 200 metros solicitados por el cliente, 2 parqueaderos, 5 baños y 8 habitaciones. Además, se localiza en el barrio San Vicente de la ciudad de Cali, correspondiente a estrato 5.
Inmueble ID 1835: (Conservadora) El inmueble presenta un precio de 370 millones y un precio estimado mediante el modelo de 393.50 millones, lo que representa una diferencia de -23.50 millones en términos absolutos y -5.97% en términos relativos. Cuenta con un área de 264 metros cuadrados, superior a los 200 metros solicitados por el cliente, 1 parqueadero, 4 baños y 5 habitaciones. Además, se localiza en el barrio Vipasa de la ciudad de Cali, correspondiente a estrato 4. Es necesario negociar el precio.
subset_gangas$popup <- paste0(
"<b>ID:</b> ", subset_gangas$id,
"<br><b>Barrio:</b> ", subset_gangas$barrio,
"<br><b>Estrato:</b> ", subset_gangas$estrato,
"<br><b>Precio mercado:</b> ", subset_gangas$preciom, " millones",
"<br><b>Precio predicho:</b> ", round(subset_gangas$precio_predicho,2), " millones",
"<br><b>Área:</b> ", subset_gangas$areaconst, " m²",
"<br><b>Habitaciones:</b> ", subset_gangas$habitaciones,
"<br><b>Baños:</b> ", subset_gangas$banios
)
library(leaflet)
leaflet(subset_gangas) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
setView(lng = -76.53, lat = 3.45, zoom = 12) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 7,
color = "red",
fillOpacity = 0.8,
popup = ~popup
)
Para responder la solicitud 2, correspondiente a un apartamento en la zona Sur con ciertas características, se filtraron los inmuebles que cumplen con las condiciones solicitadas. Posteriormente, con base en el Modelo 2, se estimó el precio predicho de cada inmueble y se comparó con su precio de mercado. Dada la escasez de inmuebles con dichas características, se redujo en una unidad el requerimiento de habitaciones, parqueaderos y baños, con el fin de ampliar el conjunto de posibles alternativas.
Al evaluar y seleccionar los inmuebles más prometedores para el cliente, se eligieron tres opciones: dos cuyo precio de mercado se encuentra muy por debajo del precio predicho por el modelo, lo que sugiere posibles oportunidades de compra, y un inmueble cuyo valor de mercado se ubica alrededor de los 950 millones, superando en aproximadamente 100 millones el monto del credito aprobado. No obstante, este inmueble podría considerarse en caso de que el cliente logre negociar un aumento en el desembolso del crédito o una reducción en el precio con el vendedor o ambos al tiempo.
Dada la escasez de inmuebles con estas características bajo la restricción presupuestaria del cliente, las alternativas identificadas corresponden a ofertas relativamente agresivas, por lo que se recomienda realizar un peritaje de valuación antes de concretar cualquier decisión de compra.
viviendas_se <- subset(vivienda,
areaconst >= 280 &
parqueaderos >= 2 &
banios >= 2 &
habitaciones >= 4 &
estrato %in% c(5,6) &
zona_nueva == "Sur" &
tipo == "Apartamento")
pred_log <- predict(modelo_sur2, newdata = viviendas_se)
viviendas_se$precio_predicho <- exp(pred_log)
viviendas_se$residuo <- viviendas_se$preciom - viviendas_se$precio_predicho
viviendas_se$residuo_pct <- 100 * viviendas_se$residuo / viviendas_se$precio_predicho
viviendas_se_925 <- subset(viviendas_se, preciom <= 925)
subset_sur_gangas <- viviendas_se %>%
dplyr::filter(id %in% c(4952, 4394, 6275))
tabla_sur_gangas <- subset_sur_gangas[, c("id", "estrato", "preciom", "precio_predicho", "barrio")]
kable(tabla_sur_gangas,
caption = "<b>Tabla 7 - Inmuebles Zona Sur - apartamentos</b>",
col.names = c("ID", "Estrato", "P de mercado (millones)", "Precio Predicho", "Barrio")) %>%
kable_styling(full_width = FALSE, position = "center",
bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50")
| ID | Estrato | P de mercado (millones) | Precio Predicho | Barrio |
|---|---|---|---|---|
| 6275 | 6 | 950 | 1321.6079 | ciudad jardín |
| 4394 | 5 | 690 | 886.5940 | el ingenio |
| 4952 | 5 | 650 | 971.6003 | el ingenio |
Dado que la oferta de apartamentos en la zona sur resultó limitada, se amplió el análisis al mercado de apartamentos en la zona occidente Anexo 9 con el fin de identificar alternativas adicionales dentro del rango presupuestario del cliente.
viviendas_occidente <- subset(vivienda,
zona_nueva == "Occidente" &
tipo == "Apartamento")
viviendas_occidente$estrato <- as.factor(viviendas_occidente$estrato)
modelo_occ <- lm(log(preciom) ~ poly(areaconst,3) + habitaciones + banios + parqueaderos + estrato , data = viviendas_occidente)
viviendas_oc <- subset(vivienda,
areaconst >= 280 &
parqueaderos >= 3 &
banios >= 3 &
habitaciones >= 5 &
estrato %in% c(5,6) &
zona_nueva == "Occidente" &
tipo == "Apartamento")
pred_log <- predict(modelo_occ, newdata = viviendas_oc)
viviendas_oc$precio_predicho <- exp(pred_log)
viviendas_oc$residuo <- viviendas_oc$preciom - viviendas_oc$precio_predicho
viviendas_oc$residuo_pct <- 100 * viviendas_oc$residuo / viviendas_oc$precio_predicho
viviendas_oc_925 <- subset(viviendas_oc, preciom < 925)
tabla_gangas <- viviendas_oc_925[, c("id", "estrato", "preciom", "precio_predicho", "barrio")]
tabla_gangas$precio_predicho <- round(tabla_gangas$precio_predicho, 2)
tabla_gangas <- tabla_gangas[order(tabla_gangas$preciom), ]
kable(tabla_gangas,
caption = "<b>Tabla 8 - Apartamentos grandes en el occidente</b>",
col.names = c("ID", "Estrato", "P de mercado (millones)", "Precio Predicho", "Barrio")) %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped","hover","condensed")) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50")
| ID | Estrato | P de mercado (millones) | Precio Predicho | Barrio | |
|---|---|---|---|---|---|
| 6600 | 7512 | 5 | 670 | 751.49 | seminario |
| 3211 | 7182 | 5 | 730 | 1721.17 | guadalupe |
Inmueble ID 3211(occidente muy arriesgada): El apartamento presenta un precio de 730 millones y un precio estimado mediante el modelo de 1721.17 millones, lo que representa una diferencia de -991.17 millones en términos absolutos y -57.59% en términos relativos. Cuenta con un área de 573 metros cuadrados, superior a los 300 metros solicitados por el cliente, 3 parqueaderos, 8 baños y 5 habitaciones. Además, se localiza en el barrio Guadalupe de la ciudad de Cali, correspondiente a estrato 5. Este apartamento se localiza en la frontera de la zona occidente y sur por lo que cumple las demandas del cliente, no obstante dado su bajo precio comparado con predicho, es una opción demasiado arriesgada.
Inmueble ID 6600 (Occidente): El inmueble presenta un precio de 670 millones y un precio estimado mediante el modelo de 751.49 millones, lo que representa una diferencia de -81.49 millones en términos absolutos y -10.84% en términos relativos. Cuenta con un área de 300 metros cuadrados igual a los solicitados por el cliente, 3 parqueaderos, 5 baños y 6 habitaciones. Además, se localiza en el barrio Seminario de la ciudad de Cali, correspondiente a estrato 5.
Inmueble ID 6275: El inmueble presenta un precio de 950 millones y un precio estimado mediante el modelo de 1321.61 millones, lo que representa una diferencia de -371.61 millones en términos absolutos y -28.12% en términos relativos. Cuenta con un área de 330 metros cuadrados, superior a los 300 metros solicitados por el cliente, 4 parqueaderos, 6 baños y 4 habitaciones (Una menos que la solicitada por el cliente). Además, se localiza en el barrio Ciudad Jardín de la ciudad de Cali, correspondiente a estrato 6. Dado su valor, es necesario revisar si hay capacidad de negociación o aumento del cupo aprobado por el banco.
Inmueble ID 4394 : El inmueble presenta un precio de 690 millones y un precio estimado mediante el modelo de 886.59 millones, lo que representa una diferencia de -196.59 millones en términos absolutos y -22.17% en términos relativos. Cuenta con un área de 486 metros cuadrados, superior a los 300 metros solicitados por el cliente, 2 parqueaderos(-1 que lo solicitado por el cliente), 4 baños y 4 habitaciones (-1 solicitada). Además, se localiza en el barrio El Ingenio de la ciudad de Cali, correspondiente a estrato 5.
Inmueble ID 4952: El inmueble presenta un precio de 650 millones y un precio estimado mediante el modelo de 971.60 millones, lo que representa una diferencia de -321.60 millones en términos absolutos y -33.10% en términos relativos. Cuenta con un área de 600 metros cuadrados, el doble de la solicitud del cliente, 2 parqueaderos (-1 menos que lo solicitado), 4 baños y 5 habitaciones. Además, se localiza en el barrio El Ingenio de la ciudad de Cali, correspondiente a estrato 5.
viviendas_oc_925 <- rbind(viviendas_oc_925, subset_sur_gangas)
viviendas_oc_925$popup <- paste0(
"<b>ID:</b> ", viviendas_oc_925$id,
"<br><b>Barrio:</b> ", viviendas_oc_925$barrio,
"<br><b>Estrato:</b> ", viviendas_oc_925$estrato,
"<br><b>Precio mercado:</b> ", viviendas_oc_925$preciom, " millones",
"<br><b>Precio predicho:</b> ", round(viviendas_oc_925$precio_predicho,2), " millones",
"<br><b>Área:</b> ", viviendas_oc_925$areaconst, " m²",
"<br><b>Habitaciones:</b> ", viviendas_oc_925$habitaciones,
"<br><b>Baños:</b> ", viviendas_oc_925$banios
)
leaflet(viviendas_oc_925) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
setView(lng = -76.53, lat = 3.45, zoom = 12) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 7,
color = "red",
fillOpacity = 0.8,
popup = ~popup
)
En el apartado de recomendaciones se presentan al cliente las respuestas a las dos solicitudes.
Para la solicitud correspondiente a casas en la zona Norte, se proponen un total de cinco inmuebles que satisfacen las necesidades del cliente. De estos, tres representan una gran oportunidad de negocio según el modelo, dado que su precio se encuentra subvaluado. Además, se incluyen dos inmuebles con un perfil más conservador, que no implican riesgo para el negocio y cuyo precio se encuentra cercano al valor estimado por el modelo.
En cuanto a la solicitud número 2, correspondiente a apartamentos en la zona Sur, la decisión fue más compleja, por lo que se tuvo que relajar parcialmente las restricciones del cliente. Como resultado, tres inmuebles en la zona Sur presentan algunas características por debajo de las solicitadas; de ellos, dos se encuentran por debajo de la restricción presupuestaria del cliente y uno por encima.
Por tal motivo, se presentan al cliente dos ofertas adicionales en la zona Occidente, cercanas al polígono de la zona Sur, que se ajustan a sus preferencias y satisfacen las características solicitadas. Estos propiedades representan, segun los diferentes modelos, una excelente oportunidad de negocio, sin embargo, debido a su bajo precio, requieren medidas de validación adicionales. Se recomienda el acompañamiento de peritos de evaluación y la revisión de los antecedentes judiciales de todas las ofertas.
Adicionalmente, si el cliente desea una opción más conservadora, podría optar por buscar casas en dicho sector con las características que solicita, ya que existe una mayor oferta y estas se ajustan mejor a la restricción presupuestaria del cliente.
Dado que el modelo solo incluye variables como preciom, área construida, número de baños, habitaciones, parqueaderos, y variables categóricas como zona, barrio y estrato, no puede capturar de manera adecuada otros factores importantes que afectan el precio, como los acabados y la antigüedad de los inmuebles. Además, la ausencia de un componente espacial puede hacer que se ignore alguna variable geográfica que influya en los precios.
Adicionalmente, la variable zona no estaba correctamente clasificada, por lo que se procedió a reclasificar las viviendas siguiendo polígonos de zonas propuestos por el distrito Anexo 10. No obstante, esta distinción entre zonas (Sur, Occidente, etc.) es subjetiva y se desdibuja especialmente en las fronteras; para el cliente, la zona Sur podría abarcar ciertos sectores que no coinciden exactamente con la clasificación utilizada.
La base de inmuebles habitacionales de la agencia C&A contiene 8,319 observaciones de propiedades en la ciudad de Cali. Para el presente análisis, y dado que se busca ofrecer recomendaciones específicas con características y ubicaciones particulares, la base se filtró por zona y tipo de vivienda: para la solicitud 1 se seleccionaron únicamente casas ubicadas en la zona Norte, mientras que para la solicitud 2 se consideraron apartamentos ubicados en la zona Sur. Este filtrado permite que los modelos de predicción que se vayan a utilizar se ajusten a las condiciones de cada solicitud.
Despues del filtrado por tipo y zona, y tras establecer un poligono en el area basandose en una propuesta de distribución geográfica del distrito de la Alcaldía de Cali, se cuenta con 580 observaciones de casas en la zona norte. Para la zona sur y los apartamentos, se parte de la misma propuesta del distrito de Cali, construyendo un polígono aproximado en la zona sur, donde se contabilizan 1.999 inmuebles. (Esto luego construir una nueva variable zona usando las cordenadas longitud y latitud reportadas y asignando cada inmueble a su ubicación dentro de un poligono de zona)
Ilustración A1. Mapa de los inmuebles en la norte y casas.
library(paqueteMODELOS)
library(dplyr)
library(mice)
library(tidyr)
library(kableExtra)
library(ggplot2)
library(scales)
library(robustbase)
library(sf)
library(leaflet)
library(isotree)
norte_casa <- readRDS("vivienda_norte_casas.rds")
poligono_norte_clean <- st_read("norte.kml", quiet = TRUE) %>%
st_transform(4326) %>%
st_zm()
norte_casa_sf <- st_as_sf(
norte_casa,
coords = c("longitud", "latitud"),
crs = 4326,
remove = FALSE
) %>%
st_zm()
norte_final <- norte_casa_sf[
st_intersects(norte_casa_sf, poligono_norte_clean, sparse = FALSE)[, 1],
]
map_norte <- leaflet(data = norte_final) %>%
addTiles() %>%
addPolygons(
data = poligono_norte_clean,
color = "black",
weight = 2,
fillOpacity = 0.1,
group = "Límite Norte"
) %>%
addCircleMarkers(
radius = 4,
color = "blue",
stroke = FALSE,
fillOpacity = 0.7,
popup = ~paste0(
"<b>Barrio:</b> ", barrio, "<br>",
"<b>Precio:</b> ", preciom, "<br>",
"<b>Área:</b> ", areaconst
),
group = "Casas"
) %>%
addLegend(position = "bottomright",
colors = "blue",
labels = "Casas Norte (Validadas)")
map_norte
La Ilustración A1 muestra los inmuebles ubicados en la zona norte y de tipo casa. El polígono fue construido en Google Maps tomando como referencia los mapas distritales de la Alcaldía de Cali. Dado que se trata de una reconstrucción basada en cartografía previa de la Alcaldía y en Google Maps, su delimitación es aproximada y puede presentar imprecisiones sobre todo en los limites al norte donde hay expansiones de la ciudad.
Ilustración A2. Mapa de los inmuebles en la zona sur y de tipo apartamentos.
datos_sur <- readRDS("vivienda_sur_apartamentos.rds")
poligono_sur_clean <- st_read("sur.kml", quiet = TRUE) %>%
st_transform(4326) %>%
st_zm()
sur_apartamento_sf <- st_as_sf(datos_sur,
coords = c("longitud", "latitud"),
crs = 4326,
remove = FALSE) %>%
st_zm()
indices_sur_dentro <- st_intersects(sur_apartamento_sf, poligono_sur_clean, sparse = FALSE)
sur_final <- sur_apartamento_sf[indices_sur_dentro[, 1], ]
map_sur <- leaflet(data = sur_final) %>%
addTiles() %>%
addPolygons(data = poligono_sur_clean,
color = "red",
weight = 2,
fillOpacity = 0.1,
group = "Polígono Sur") %>%
addCircleMarkers(
radius = 4,
color = "green",
stroke = FALSE,
fillOpacity = 0.7,
popup = ~paste0("<b>Barrio:</b> ", barrio, "<br>",
"<b>Precio:</b> ", preciom, "<br>",
"<b>Estrato:</b> ", estrato),
group = "Apartamentos"
) %>%
addLegend(position = "bottomright",
colors = "green",
labels = "Apartamentos Sur (Limpios)")
map_sur
La Ilustración A2 muestra los inmuebles ubicados en la zona sur y de tipo apartamento. El polígono fue construido en Google Maps tomando como referencia los mapas distritales de la Alcaldía de Cali. Dado que se trata de una reconstrucción basada en cartografía previa de la Alcaldía y en Google Maps, su delimitación es aproximada y puede presentar imprecisiones sobre todo en los limites al sur donde hay expansiones de la ciudad.
library(here)
##################################################
vivienda1 <- readRDS("vivienda_modelo_final.rds")
norte_final1 <- readRDS("vivienda_norte_casas.rds")
sur_final1 <- readRDS("vivienda_sur_apartamentos.rds")
##################################################
resumen_df <- function(df, nombre) {
df %>%
summarise(
conjunto = nombre,
n_inmuebles = n(),
precio_promedio = mean(preciom, na.rm = TRUE),
area_promedio = mean(areaconst, na.rm = TRUE),
banios_promedio = mean(banios, na.rm = TRUE),
habitaciones_promedio = mean(habitaciones, na.rm = TRUE)
)
}
tabla_resumen <- bind_rows(
resumen_df(vivienda1, "Total"),
resumen_df(norte_final1, "Zona Norte - Casa"),
resumen_df(sur_final1, "Zona Sur - Apartamento")
)
tabla_resumen %>%
kable(
"html",
caption = "<b>Tabla A1. Resumen general de inmuebles</b>",
col.names = c(
"Conjunto",
"# Inmuebles",
"Precio Promedio (Millones)",
"Área Promedio",
"Baños Promedio",
"Habitaciones Promedio"
),
digits = 1
) %>%
kable_styling(
full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover", "condensed")
) %>%
row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") %>%
row_spec(
1:nrow(tabla_resumen),
background = rep(c("#ECF0F1", "#FFFFFF"), length.out = nrow(tabla_resumen))
) %>%
column_spec(1, bold = TRUE, color = "#2C3E50")
| Conjunto | # Inmuebles | Precio Promedio (Millones) | Área Promedio | Baños Promedio | Habitaciones Promedio |
|---|---|---|---|---|---|
| Total | 8319 | 433.9 | 174.9 | 3.1 | 3.6 |
| Zona Norte - Casa | 580 | 433.1 | 260.2 | 3.5 | 4.6 |
| Zona Sur - Apartamento | 1999 | 318.3 | 99.7 | 2.6 | 3.0 |
La tabla A1 presenta un resumen general de la población de inmuebles registrada, mostrando el número total de inmuebles (desglosado total o por zona y tipo según las solicitudes), el precio promedio de los inmuebles en millones, el área promedio en metros cuadrados, así como el promedio de baños, parqueaderos y habitaciones.
vivienda_long <- vivienda1 %>%
select(areaconst, banios, habitaciones, parqueaderos, estrato, preciom) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor") %>%
filter(!is.na(valor))
vivienda_long <- vivienda_long %>%
mutate(tipo_var = ifelse(variable %in% c("banios", "habitaciones", "parqueaderos", "estrato"),
"discreta", "continua"))
ggplot(vivienda_long, aes(x = valor)) +
geom_histogram(data = subset(vivienda_long, tipo_var == "continua"),
bins = 30, fill = "gray", color = "black") +
geom_histogram(data = subset(vivienda_long, tipo_var == "discreta"),
binwidth = 1, fill = "gray", color = "black") +
facet_wrap(~variable, scales = "free_x") +
scale_x_continuous(labels = comma) +
labs(title = "Ilustración A3. Histogramas variables (Base completa)",
x = "Valor",
y = "Frecuencia") +
theme_minimal()
La ilustración A3 presenta el histograma de algunas variables de la base de datos completa. Se observan distribuciones poco normales a través del diagnóstico gráfico de estas variables. Además, evidencia que la base de datos está limitada a los estratos 3, 4, 5 y 6.
vivienda_long <- sur_final1 %>%
select(areaconst, banios, habitaciones, parqueaderos, estrato, preciom) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor") %>%
filter(!is.na(valor))
vivienda_long <- vivienda_long %>%
mutate(tipo_var = ifelse(variable %in% c("banios", "habitaciones", "parqueaderos", "estrato"),
"discreta", "continua"))
ggplot(vivienda_long, aes(x = valor)) +
geom_histogram(data = subset(vivienda_long, tipo_var == "continua"),
bins = 30, fill = "gray", color = "black") +
geom_histogram(data = subset(vivienda_long, tipo_var == "discreta"),
binwidth = 1, fill = "gray", color = "black") +
facet_wrap(~variable, scales = "free_x") +
scale_x_continuous(labels = comma) +
labs(title = "Ilustración A4. Histogramas variables (Zona Sur - Apartamentos)",
x = "Valor",
y = "Frecuencia") +
theme_minimal()
vivienda_long <- norte_final1 %>%
select(areaconst, banios, habitaciones, parqueaderos, estrato, preciom) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor") %>%
filter(!is.na(valor))
vivienda_long <- vivienda_long %>%
mutate(tipo_var = ifelse(variable %in% c("banios", "habitaciones", "parqueaderos", "estrato"),
"discreta", "continua"))
ggplot(vivienda_long, aes(x = valor)) +
geom_histogram(data = subset(vivienda_long, tipo_var == "continua"),
bins = 30, fill = "gray", color = "black") +
geom_histogram(data = subset(vivienda_long, tipo_var == "discreta"),
binwidth = 1, fill = "gray", color = "black") +
facet_wrap(~variable, scales = "free_x") +
scale_x_continuous(labels = comma) +
labs(title = "Ilustración A5. Histogramas variables (Zona Norte - Casas)",
x = "Valor",
y = "Frecuencia") +
theme_minimal()
En las ilustraciones A4 y A5 se presentan los histogramas de las variables correspondientes a las bases filtradas: Zona Norte – Casa y Zona Sur – Apartamento. Visualmente, se evidencian algunas diferencias en las distribuciones de área construida y estrato; otras diferencias no son tan evidentes mediante un análisis gráfico. Es natural que el área construida sea menor en los apartamentos, ya que tienden a ser más pequeños que las casas.
library(plotly)
vivienda1$estrato <- as.factor(vivienda1$estrato)
vivienda1$zona_nueva <- as.factor(vivienda1$zona_nueva)
vivienda1$tipo <- as.factor(vivienda1$tipo)
fig <- plot_ly(vivienda1,
x = ~zona_nueva,
y = ~preciom,
color = ~zona_nueva,
type = "box",
boxpoints = "suspectedoutliers")
fig <- fig %>% layout(
title = "<b>Análisis Exploratorio Interactivo</b>",
yaxis = list(title = "Valor Cuantitativo"),
xaxis = list(title = "Categoría Seleccionada"),
updatemenus = list(
list(
y = 1.15, x = 0.2,
buttons = list(
list(method = "restyle",
args = list("x", list(vivienda1$zona_nueva)),
label = "Categoría: Zona"),
list(method = "restyle",
args = list("x", list(vivienda1$tipo)),
label = "Categoría: Tipo"),
list(method = "restyle",
args = list("x", list(vivienda1$estrato)),
label = "Categoría: Estrato")
)
),
list(
y = 1.15, x = 0.7,
buttons = list(
list(method = "restyle",
args = list("y", list(vivienda1$preciom)),
label = "Medida: Precio"),
list(method = "restyle",
args = list("y", list(vivienda1$areaconst)),
label = "Medida: Área"),
list(method = "restyle",
args = list("y", list(vivienda1$parqueaderos)),
label = "Medida: Parqueaderos"),
list(method = "restyle",
args = list("y", list(vivienda1$banios)),
label = "Medida: Baños"),
list(method = "restyle",
args = list("y", list(vivienda1$habitaciones)),
label = "Medida: Habitaciones")
)
)
)
)
fig
En esta etapa de limpieza se emplearán dos criterios. Primero, se eliminarán las observaciones con baños o habitaciones en cero, dado que estos valores no son realistas para inmuebles habitacionales. Luego, se revisarán los inmuebles con precios por metro cuadrado muy bajos o extremadamente altos. Se considera que nn el análisis de inmuebles, medidas univariadas como boxplots o boxplots ajustados por simetría, así como técnicas más avanzadas como isolation forest, pueden identificar valores atípicos que, aunque difieren del resto de las observaciones, representan propiedades válidas, por ejemplo, inmuebles de lujo, por lo que no se aplicarán estas técnicas, en su lugar se utilizará la distancia de Cook y la estabilidad de los coeficientes posmodelado para documentar y, si es necesario, eliminar observaciones problemáticas.
Solo se encontraron valores faltantes en dos variables: piso y parqueaderos. En la base global, el porcentaje de faltantes fue 31.44% para piso y 18.87% para parqueaderos. En la base zona sur – apartamentos, los porcentajes fueron 24.03% para piso y 14.83% para parqueaderos. En la zona norte – casas, los faltantes alcanzaron 42.28% para piso y 32.46% para parqueaderos.
Piso: La variable piso fue eliminada del análisis en los tres datasets, dado que presenta un alto porcentaje de valores faltantes y su imputación resulta compleja. No se puede determinar con certeza si los NA corresponden a pisos bajos (piso 1) o si fueron datos no registrados.
Parqueadero: En la variable parqueadero se evidencia un panorama complejo, que dificulta considerablemente su imputación. En primer lugar, la variable no presenta valores cero en su distribución; suponer que todos los inmuebles habitacionales cuentan con parqueadero sería un supuesto demasiado heroico y, por lo tanto, se descarta. Adicionalmente, en términos porcentuales, el número de NA disminuye conforme aumenta el estrato: el 52.5% de las viviendas de estrato 3 no tienen información de parqueaderos, el 22.5% de las observaciones de estrato 4 presentan NA, y solo el 8% y 5.67% para los estratos 5 y 6, respectivamente. Esto nos puede llevar a sospechar que la base contiene ceros que fueron registrados como NA.
La siguiente tarea consiste, por consiguiente, en codificar la variable y revisar, mediante un examen visual a través de un boxplot, si existen diferencias en el precio entre las observaciones que contienen NA en parqueaderos y las que no
vivienda1 <- vivienda1 %>%
filter(banios > 0, habitaciones > 0)
sur_final1 <- sur_final1 %>%
filter(banios > 0, habitaciones > 0)
norte_final1 <- norte_final1 %>%
filter(banios > 0, habitaciones > 0)
#####################################################
vivienda <- vivienda %>%
mutate(precio_m2 = preciom / areaconst)
sur_final1 <- sur_final1 %>%
mutate(precio_m2 = preciom / areaconst)
norte_final1 <- norte_final1 %>%
mutate(precio_m2 = preciom / areaconst)
##################################################
umbral_min <- 0.5
umbral_max <- 20
vivienda <- vivienda %>%
mutate(outlier_precio_m2 = precio_m2 < umbral_min | precio_m2 > umbral_max)
sur_final1 <- sur_final1 %>%
mutate(outlier_precio_m2 = precio_m2 < umbral_min | precio_m2 > umbral_max)
norte_final1 <- norte_final1 %>%
mutate(outlier_precio_m2 = precio_m2 < umbral_min | precio_m2 > umbral_max)
vivienda1 <- vivienda1 %>%
mutate(parqueadero_flag = ifelse(is.na(parqueaderos) | parqueaderos == 0, FALSE, TRUE))
# 2. Graficar usando el objeto correcto (vivienda1)
ggplot(vivienda1, aes(x = parqueadero_flag, y = preciom, fill = parqueadero_flag)) +
geom_boxplot(outlier.shape = NA, alpha = 0.6) +
scale_y_continuous(labels = comma, limits = c(0, 1500)) +
scale_fill_manual(values = c("#D3D3D3", "#4A4A4A"),
labels = c("Sin Parqueadero/NA", "Con Parqueadero")) +
labs(
title = "Ilustración A6. Comparación de Precios por Disponibilidad de Parqueadero",
subtitle = "",
x = "",
y = "Precio ",
fill = ""
) +
theme_minimal(base_size = 14) +
theme(legend.position = "bottom")
La ilustración A6 muestra el precio de los inmuebles agrupado según si cuentan con parqueaderos o no. Se observa que la mediana de las observaciones sin parqueaderos es menor que la de aquellas que sí los tienen, lo que refuerza la idea de que los NA podrían corresponder a ceros mal codificados
wilcox.test(preciom ~ parqueadero_flag, data = vivienda1)
##
## Wilcoxon rank sum test with continuity correction
##
## data: preciom by parqueadero_flag
## W = 2720160, p-value < 2.2e-16
## alternative hypothesis: true location shift is not equal to 0
El test de Wilcoxon (empleado tras evidenciar la no normalidad en la distribución del precio) indica que existe una diferencia estadísticamente significativa entre las medianas de los grupos, es decir, las medianas de los inmuebles con y sin parqueadero no son iguales (p-value < 2.2e-16).
options(scipen = 999)
vivienda1$parqueadero_flag <- as.factor(vivienda1$parqueadero_flag)
modelo_1 <- lm(log(preciom) ~ log(areaconst) + habitaciones + banios +
estrato + zona_nueva + parqueadero_flag,
data = vivienda1)
summary(modelo_1)
##
## Call:
## lm(formula = log(preciom) ~ log(areaconst) + habitaciones + banios +
## estrato + zona_nueva + parqueadero_flag, data = vivienda1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.4856 -0.1605 -0.0077 0.1566 1.5130
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.557754 0.028754 88.952 < 0.0000000000000002
## log(areaconst) 0.534836 0.007098 75.349 < 0.0000000000000002
## habitaciones -0.028498 0.003007 -9.476 < 0.0000000000000002
## banios 0.088581 0.003482 25.439 < 0.0000000000000002
## estrato4 0.262443 0.009805 26.765 < 0.0000000000000002
## estrato5 0.462550 0.010138 45.626 < 0.0000000000000002
## estrato6 0.818439 0.012341 66.321 < 0.0000000000000002
## zona_nuevaCentro Historico -0.004058 0.016894 -0.240 0.810198
## zona_nuevaNorte -0.013030 0.010312 -1.264 0.206436
## zona_nuevaOccidente 0.051122 0.010734 4.763 0.00000194
## zona_nuevaOriente -0.063362 0.017072 -3.711 0.000207
## zona_nuevaSur -0.023645 0.009879 -2.393 0.016717
## parqueadero_flagTRUE 0.078878 0.007943 9.930 < 0.0000000000000002
##
## (Intercept) ***
## log(areaconst) ***
## habitaciones ***
## banios ***
## estrato4 ***
## estrato5 ***
## estrato6 ***
## zona_nuevaCentro Historico
## zona_nuevaNorte
## zona_nuevaOccidente ***
## zona_nuevaOriente ***
## zona_nuevaSur *
## parqueadero_flagTRUE ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2541 on 8230 degrees of freedom
## Multiple R-squared: 0.8567, Adjusted R-squared: 0.8564
## F-statistic: 4099 on 12 and 8230 DF, p-value: < 0.00000000000000022
Al estimar un modelo auxiliar para evaluar el efecto de la ausencia de parqueadero, se encuentra que los inmuebles con parqueadero son, en promedio, 6.5 % más costosos que aquellos que no disponen de este atributo y significativo , ceteris paribus. Este resultado refuerza la hipótesis de que los valores faltantes (NA) en la variable de parqueaderos podrían corresponder a ceros mal codificados. No obstante, dado que no es posible asumir que todos los inmuebles poseen parqueadero (lo que invalidaría una imputación estándar), ni tampoco que todos los NA representan ceros, la estrategia metodológicamente más prudente, en ausencia de información auxiliar, consiste en estimar el modelo utilizando únicamente las observaciones con información observada en parqueaderos.
library(paqueteMODELOS)
library(dplyr)
library(mice)
library(dplyr)
library(tidyr)
library(kableExtra)
library(ggplot2)
library(scales)
library(robustbase)
library(sf)
library(leaflet)
library("isotree")
library(car)
library(corrplot)
vivienda <- readRDS("vivienda_consolidada.rds")
norte_final <- readRDS("vivienda_norte_final.rds")
sur_final <- readRDS("vivienda_sur_final.rds")
vars_ordinales_cont <- vivienda %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos)
matriz_kendall <- cor(vars_ordinales_cont, method = "kendall", use = "complete.obs")
corrplot(matriz_kendall,
method = "color",
type = "upper",
addCoef.col = "black",
tl.col = "black",
tl.srt = 45,
col = COL2('RdBu', 10),
diag = FALSE,
mar = c(0,0,2,0),
title = "Ilustración A7. Matriz de Asociación de Kendall: Viviendas")
La ilustración A7 presenta la matriz de asociación mediante el coeficiente de Kendall. Se observa que el área construida, el número de baños y los parqueaderos muestran una relación positiva moderada con el precio. El estrato también presenta una asociación de 0.56. Por otra parte, el número de habitaciones exhibe una asociación más débil, cercana a 0.32.
options(contrasts = c("contr.sum", "contr.poly"))
vivienda_anova <- vivienda %>%
mutate(
zona_nueva = as.factor(zona_nueva),
estrato = as.factor(estrato),
tipo = as.factor(tipo)
) %>%
filter(!is.na(zona_nueva), !is.na(estrato), !is.na(tipo))
modelo_lm <- lm(log(preciom) ~ zona_nueva * estrato * tipo, data = vivienda_anova)
anova_tipo3 <- Anova(modelo_lm, type = "III")
print(anova_tipo3)
## Anova Table (Type III tests)
##
## Response: log(preciom)
## Sum Sq Df F value Pr(>F)
## (Intercept) 93241 1 671047.3473 < 0.00000000000000022 ***
## zona_nueva 12 5 17.8644 < 0.00000000000000022 ***
## estrato 698 3 1674.6570 < 0.00000000000000022 ***
## tipo 218 1 1571.0941 < 0.00000000000000022 ***
## zona_nueva:estrato 13 15 6.0501 0.00000000000086313 ***
## zona_nueva:tipo 2 5 3.5464 0.003324 **
## estrato:tipo 9 3 21.8779 0.00000000000004167 ***
## zona_nueva:estrato:tipo 11 15 5.3215 0.00000000008622567 ***
## Residuals 1139 8195
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Con fines exploratorios, se realizó un ANOVA de tres vías (Type III) para evaluar el efecto conjunto de las variables cualitativas (Zona, estrato y t ipo de inmueble) sobre el precio del inmueble expresado en logaritmo. Los resultados evidencian que, ademas de los efectos principales, existen interacciones estadísticamente significativas entre las variables categoricas consideradas. Este hallazgo sugiere que el efecto de cada factor sobre el precio no es independiente, lo cual resulta relevante para la especificación de modelos predictivos más completos que incorporen tanto variables cualitativas como cuantitativas.
Para la construcción del modelo 1 se evaluaron diversas especificaciones alternativas. El modelo finalmente seleccionado incolpora log en el precio y en area costruida e interraciones entre las variables categoricas (estrato:tipo y zona_nueva:tipo). La selección del modelo se basó en el criterio de información bayesiano (BIC).
Este modelo tiene como propósito evaluar, en la base global de inmuebles, si las propuestas de crédito del cliente son realistas o, por el contrario, se encuentran por debajo del valor esperado para el tipo de inmueble.
options(contrasts = c("contr.treatment", "contr.poly"))
vivienda <- readRDS("vivienda_consolidada.rds")
norte_final <- readRDS("vivienda_norte_final.rds")
sur_final <- readRDS("vivienda_sur_final.rds")
vivienda$estrato <- factor(vivienda$estrato)
vivienda$tipo1 <- factor(vivienda$tipo)
vivienda$estrato <- as.factor(vivienda$estrato)
modelo_global_log <- lm(
log(preciom) ~ log(areaconst) + estrato + banios +
habitaciones + parqueaderos + zona_nueva + tipo +
estrato:tipo + zona_nueva:tipo,
data = vivienda
)
summary(modelo_global_log)
##
## Call:
## lm(formula = log(preciom) ~ log(areaconst) + estrato + banios +
## habitaciones + parqueaderos + zona_nueva + tipo + estrato:tipo +
## zona_nueva:tipo, data = vivienda)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.43768 -0.15138 -0.00069 0.14695 1.00422
##
## Coefficients:
## Estimate Std. Error t value
## (Intercept) 2.6165253 0.0384915 67.977
## log(areaconst) 0.5072903 0.0091823 55.247
## estrato4 0.3327848 0.0163320 20.376
## estrato5 0.5392265 0.0159665 33.772
## estrato6 0.8653710 0.0179361 48.247
## banios 0.0816945 0.0038622 21.153
## habitaciones -0.0349926 0.0033886 -10.327
## parqueaderos 0.0755018 0.0036328 20.784
## zona_nuevaCentro Historico 0.0096023 0.0245648 0.391
## zona_nuevaNorte 0.0426826 0.0159106 2.683
## zona_nuevaOccidente 0.0631634 0.0157358 4.014
## zona_nuevaOriente 0.0275696 0.0275290 1.001
## zona_nuevaSur -0.0004858 0.0148644 -0.033
## tipoCasa 0.1826782 0.0249657 7.317
## estrato4:tipoCasa -0.1479023 0.0235677 -6.276
## estrato5:tipoCasa -0.2438703 0.0226626 -10.761
## estrato6:tipoCasa -0.2932966 0.0247114 -11.869
## zona_nuevaCentro Historico:tipoCasa -0.0165329 0.0368788 -0.448
## zona_nuevaNorte:tipoCasa -0.1233524 0.0240558 -5.128
## zona_nuevaOccidente:tipoCasa -0.0509869 0.0246089 -2.072
## zona_nuevaOriente:tipoCasa -0.1110356 0.0401880 -2.763
## zona_nuevaSur:tipoCasa -0.0234636 0.0221058 -1.061
## Pr(>|t|)
## (Intercept) < 0.0000000000000002 ***
## log(areaconst) < 0.0000000000000002 ***
## estrato4 < 0.0000000000000002 ***
## estrato5 < 0.0000000000000002 ***
## estrato6 < 0.0000000000000002 ***
## banios < 0.0000000000000002 ***
## habitaciones < 0.0000000000000002 ***
## parqueaderos < 0.0000000000000002 ***
## zona_nuevaCentro Historico 0.69589
## zona_nuevaNorte 0.00732 **
## zona_nuevaOccidente 0.000060355639209 ***
## zona_nuevaOriente 0.31663
## zona_nuevaSur 0.97393
## tipoCasa 0.000000000000283 ***
## estrato4:tipoCasa 0.000000000370078 ***
## estrato5:tipoCasa < 0.0000000000000002 ***
## estrato6:tipoCasa < 0.0000000000000002 ***
## zona_nuevaCentro Historico:tipoCasa 0.65395
## zona_nuevaNorte:tipoCasa 0.000000301456701 ***
## zona_nuevaOccidente:tipoCasa 0.03831 *
## zona_nuevaOriente:tipoCasa 0.00574 **
## zona_nuevaSur:tipoCasa 0.28854
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.24 on 6666 degrees of freedom
## (1555 observations deleted due to missingness)
## Multiple R-squared: 0.8546, Adjusted R-squared: 0.8542
## F-statistic: 1866 on 21 and 6666 DF, p-value: < 0.00000000000000022
BIC(modelo_global_log)
## [1] 71.61843
Interpretación de los coeficientes.
log(areaconst): Por cada aumento del 1% en el área construida, el precio del inmueble aumenta aproximadamente un 0.51%, ceteris paribus. Esto evidencia rendimientos marginales decrecientes del área sobre el precio.
Cada baño adicional se asocia con un aumento promedio de cerca 8.5% en el precio (ceteris paribus).
Cada parqueadero adicional se asocia con un aumento promedio del 7.5% en el precio, manteniendo constantes las demás variables.
Habitaciones: Manteniendo las demas variables constantes (Especificamente el area construida en este caso), añadir más habitaciones reduce el precio un 3.49%.
Estrato (base: estrato 3)
Para el tipo de inmueble base (apartamento):
Estrato 4: ≈ 39.5% más costoso.
Estrato 5: ≈ 71.4% más costoso.
Estrato 6: ≈ 137.6% más costoso.
Dado que el modelo incluye interacciones con el tipo de inmueble, este efecto no es homogéneo entre apartamentos y casas.
Una Casa es en promedio 20.0% más costosa que un Apartamento, ceteris paribus.
Centro Histórico: sin efecto significativo. Norte: +4.3% respecto al Centro Occidente: +6.5% respecto al Centro (significativo) Nota: La base no incluye barrios como siloe. Oriente: No significativo. Sur: No significativo.
estrato4:tipoCasa: El coeficiente es negativo y significativo, lo que indica que el bonuns de precios frente a los apartamentos se reduce en este estrato. Mientras que en el estrato base las casas son aproximadamente 20.03% más costosas, en el estrato 4 esta diferencia se reduce a cerca del 3.5%, ceteris paribus.
estrato5:tipoCasa: Coeficiente negativo y significativo. Indica que, en el estrato 5, las casas son en promedio 5.9% más económicas que los apartamentos comparables.
estrato6:tipoCasa: Coeficiente negativo y significativo. Sugiere que, en el estrato 6, las casas son aproximadamente 10.5% más económicas que los apartamentos equivalentes.
La misma lógica aplica para las interacciones entre zona y tipo de inmueble, las cuales indican que el diferencial de precio entre casas y apartamentos varía según la localización.
El modelo explica ≈ 85.46% de la variabilidad de los precios de los inmuebles en la ciudad de Cali, eliminando 1.555 observaciones con valores faltantes (NA) en parqueaderos.
Para evaluar el supuesto de linealidad y justificar la inclusión del log en el area construida , se ajustó un Modelo Aditivo Generalizado (GAM). La Ilustración A8 presenta las funciones parciales de área y número de habitaciones, evidenciando una relación claramente no lineal con el logaritmo del precio de los inmuebles.
library(mgcv)
modelo_gam <- gam(log(preciom) ~ s(areaconst) + estrato + banios +
s(habitaciones) + parqueaderos + zona_nueva + tipo +
estrato:tipo,
data = vivienda)
par(mfrow = c(1, 2), oma = c(0, 0, 3, 0))
plot(modelo_gam, select = 1, shade = TRUE,
shade.col = "lightblue", main = "Área",
xlab = "Área Construida (m2)", ylab = "f(Área)")
plot(modelo_gam, select = 2, shade = TRUE,
shade.col = "mistyrose", main = "Habitaciones",
xlab = "Número de Habitaciones", ylab = "f(Habitaciones)")
mtext("Ilustración A8: Efectos parciales de Área y Habitaciones",
side = 3, line = 1, outer = TRUE, cex = 1.1, font = 2)
summary(modelo_gam)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## log(preciom) ~ s(areaconst) + estrato + banios + s(habitaciones) +
## parqueaderos + zona_nueva + tipo + estrato:tipo
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.066451 0.021296 237.906 < 0.0000000000000002
## estrato4 0.323384 0.016239 19.914 < 0.0000000000000002
## estrato5 0.514031 0.016618 30.931 < 0.0000000000000002
## estrato6 0.796253 0.019123 41.639 < 0.0000000000000002
## banios 0.080476 0.003861 20.844 < 0.0000000000000002
## parqueaderos 0.079508 0.003603 22.070 < 0.0000000000000002
## zona_nuevaCentro Historico -0.004455 0.018106 -0.246 0.805666
## zona_nuevaNorte -0.005483 0.011722 -0.468 0.640005
## zona_nuevaOccidente 0.036243 0.011762 3.081 0.002068
## zona_nuevaOriente -0.015808 0.019862 -0.796 0.426137
## zona_nuevaSur -0.011765 0.010920 -1.077 0.281373
## tipoCasa 0.081825 0.021953 3.727 0.000195
## estrato4:tipoCasa -0.133559 0.022837 -5.848 0.00000000519929
## estrato5:tipoCasa -0.214118 0.022590 -9.479 < 0.0000000000000002
## estrato6:tipoCasa -0.185593 0.025343 -7.323 0.00000000000027
##
## (Intercept) ***
## estrato4 ***
## estrato5 ***
## estrato6 ***
## banios ***
## parqueaderos ***
## zona_nuevaCentro Historico
## zona_nuevaNorte
## zona_nuevaOccidente **
## zona_nuevaOriente
## zona_nuevaSur
## tipoCasa ***
## estrato4:tipoCasa ***
## estrato5:tipoCasa ***
## estrato6:tipoCasa ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(areaconst) 8.450 8.902 358.25 <0.0000000000000002 ***
## s(habitaciones) 6.934 7.952 18.29 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.857 Deviance explained = 85.8%
## GCV = 0.0566 Scale est. = 0.056343 n = 6688
gvif_values <- vif(modelo_global_log)
print(gvif_values)
## GVIF Df GVIF^(1/(2*Df))
## log(areaconst) 4.076902 1 2.019134
## estrato 8.900710 3 1.439585
## banios 3.265088 1 1.806955
## habitaciones 2.419882 1 1.555597
## parqueaderos 1.923468 1 1.386892
## zona_nueva 17.493100 5 1.331333
## tipo 16.858351 1 4.105892
## estrato:tipo 31.090911 3 1.773259
## zona_nueva:tipo 77.975080 5 1.545950
La multicolinealidad del modelo se evaluó mediante el GVIF (Generalized Variance Inflation Factor), adecuado para variables categóricas y modelos con interacciones. Ninguna variable mostró valores que indiquen problemas graves de colinealidad.
Dado que el modelo cuenta con un número elevado de observaciones (6.668), se utilizó la prueba de Jarque-Bera para evaluar la normalidad de los residuos, acompañada de un Q-Q plot. Los resultados fueron:
library(tseries)
jarque.bera.test(residuals(modelo_global_log))
##
## Jarque Bera Test
##
## data: residuals(modelo_global_log)
## X-squared = 370.4, df = 2, p-value < 0.00000000000000022
qqnorm(residuals(modelo_global_log),
main = "Ilustración A9: Q-Q plot de residuos")
qqline(residuals(modelo_global_log), col = "red")
Tanto la ilustración A.9 como el valor p del estadístico indican que los errores del modelo no se distribuyen normalmente.
Se realizó un test de Breusch-Pagan para detectar heterocedasticidad. Los resultados muestran que la varianza de los errores no es constante, indicando que los residuos del modelo no cumplen el supuesto de homocedasticidad.
library(lmtest)
bptest(modelo_global_log)
##
## studentized Breusch-Pagan test
##
## data: modelo_global_log
## BP = 387.95, df = 21, p-value < 0.00000000000000022
Se realizó un test de Durbin-Watson para evaluar la autocorrelación de los residuos. Los resultados fueron:
library(lmtest)
dwtest(modelo_global_log)
##
## Durbin-Watson test
##
## data: modelo_global_log
## DW = 1.7368, p-value < 0.00000000000000022
## alternative hypothesis: true autocorrelation is greater than 0
Esto indica que los errores del modelo presentan autocorrelación positiva. Este comportamiento es esperado, dado que nuestra base de datos corresponde a inmuebles, y las ubicaciones cercanas tienden a influirse mutuamente, generando cierta relación entre las observaciones.
Finalmente, se presenta la Ilustración A10, que muestra los puntos influyentes mediante un gráfico de residuos studentizados versus apalancamiento. Este análisis se complementa con la presentación de un extracto (head) de los valores DFBETAS del modelo planteado.
par(mfrow = c(1, 1), mar = c(5, 5, 4, 2) + 0.1)
plot(modelo_global_log, which = 5,
main = "Ilustración A10: Análisis de Puntos Influyentes")
# Calcular DFBETAs
dfbetas_values <- dfbetas(modelo_global_log)
# máximo absoluto por observación
max_dfb <- apply(abs(dfbetas_values), 1, max)
# ordenar de mayor a menor
orden <- order(max_dfb, decreasing = TRUE)
# ver las observaciones más influyentes
head(dfbetas_values[orden, ])
## (Intercept) log(areaconst) estrato4 estrato5 estrato6
## 3118 0.411784950 -0.53691909 0.060983470 0.116348124 0.17895517
## 2843 0.390041403 -0.47575081 0.062744479 0.044434981 0.12759566
## 7451 0.353846338 -0.41646340 0.055608163 0.047771803 0.12101353
## 4357 -0.004205224 0.01303773 -0.002977588 0.006552142 0.02732087
## 7639 -0.084699266 0.13054898 -0.057714888 -0.025585596 -0.02270285
## 8152 0.130461715 -0.20678563 0.029593756 0.051467518 0.08149551
## banios habitaciones parqueaderos zona_nuevaCentro Historico
## 3118 0.07279852 0.37262922 0.10356202 0.0147675402
## 2843 0.15964264 0.15042801 0.15774181 0.0097296732
## 7451 0.10548967 0.09993363 0.15759154 0.0079896171
## 4357 0.09496760 0.02106171 -0.31559964 0.0003060009
## 7639 0.05437609 -0.06001454 -0.30462943 -0.0045269937
## 8152 -0.01276223 0.28116446 0.02807868 0.0081908370
## zona_nuevaNorte zona_nuevaOccidente zona_nuevaOriente zona_nuevaSur
## 3118 0.0118933406 0.027699216 -0.0047805550 -0.022924385
## 2843 0.0156310033 0.012624309 -0.0065697910 -0.075145326
## 7451 0.0123476105 0.012287661 -0.0063878114 -0.061674233
## 4357 -0.0001098773 0.004516732 0.0020335601 -0.003939588
## 7639 -0.0478865214 -0.006291313 0.0028798039 0.001297201
## 8152 0.0082117834 0.014618277 -0.0005564136 -0.007128989
## tipoCasa estrato4:tipoCasa estrato5:tipoCasa estrato6:tipoCasa
## 3118 -0.12946914 0.12570134 0.1127887979 0.09949496
## 2843 0.09555613 -0.02694371 0.0008260897 -0.04603402
## 7451 0.10114730 -0.02296756 -0.0025410422 -0.04391322
## 4357 -0.05720671 0.08840640 0.1100473637 0.11985844
## 7639 -0.04171674 0.03425822 0.0223454554 0.04728622
## 8152 0.03411965 -0.08540688 -0.0894371039 -0.08643458
## zona_nuevaCentro Historico:tipoCasa zona_nuevaNorte:tipoCasa
## 3118 0.082787347 0.116303810
## 2843 0.003797733 -0.007239883
## 7451 0.001698098 -0.009559874
## 4357 -0.019316688 -0.080177999
## 7639 -0.001833498 0.042241089
## 8152 0.030660429 0.035541426
## zona_nuevaOccidente:tipoCasa zona_nuevaOriente:tipoCasa
## 3118 0.096498565 0.096152448
## 2843 -0.009584797 0.007425993
## 7451 -0.010889332 0.005443285
## 4357 -0.029300373 -0.008044952
## 7639 0.004999952 -0.005690436
## 8152 0.032719276 0.302393072
## zona_nuevaSur:tipoCasa
## 3118 0.11165444
## 2843 0.02649240
## 7451 0.01501542
## 4357 -0.03381135
## 7639 0.00798422
## 8152 0.07173747
A partir del análisis de la Ilustración A10 (Residuos vs. Apalancamiento), se observa que, si bien existen algunas observaciones con apalancamiento (leverage) elevado (como los casos 8152,3118,2843); su número es reducido en relación con el tamaño muestral. Complementariamente, los estadísticos DFBETAS indican que la exclusión de estos puntos produciría cambios muy pequeños en los coeficientes, insuficientes para alterar las estimaciones globales. En consecuencia, no se consideró necesario eliminar observaciones del modelo y re entrenar el modelo.
Dado que los mismos supuestos no se cumplen en los cuatro modelos planteados, se proponen las mismas soluciones para los cuatro modelos. El análisis de los supuestos solo se aplica a aquellos que no se cumplen (normalidad, linealidad, homocedasticidad, presencia de outliers y autocorrelación) y no se considera la multicolinealidad.
Análisis de Linealidad y Especificación del Modelo
A través de la evaluación con un Modelo Aditivo Generalizado (GAM), se determinó que el modelo original no satisfacía el supuesto de linealidad, especialmente en la variable areaconst (área construida en metros cuadrados). La relación entre los metros cuadrados y el precio no es proporcional, sino que presentaba una curvatura que el modelo lineal no logra capturar.
Solución Aplicada: Para resolver este problema, se aplicó una transformación logarítmica a las variables. Al usar logaritmos, el modelo ahora interpreta los cambios en términos porcentuales - Elasticidad.
Análisis de Normalidad en los residuos
A pesar de esto, se identifican las siguientes posibles causas y posibles soluciones para abordar la falta de normalidad:
Transformaciones avanzadas: Una posible solución técnica es el uso de transformaciones de Box-Cox o Yeo-Johnson y con ello tratar de aproximar los residuos a una distribución normal de manera eficiente que con logaritmos.
Sesgo por variable omitida: La no normalidad suele ser un síntoma de variables no incluidas en el modelo, que el término de error está capturando. En este caso, factores como la antigüedad del inmueble o la autocorrelación espacial (vecindad) podrían estar generando sesgo en los residuos al no estar incorporados explícitamente en el modelo.
Alternativas de modelado: Una solución estructural seria utilizar Modelos Lineales Generalizados (GLM), que no requieren el supuesto de normalidad en los residuos.
Análisis Homocedasticidad
El test de Breusch-Pagan (BP = 387.95, p < 0.0001) rechaza la hipótesis nula de homocedasticidad, confirmando la presencia de heterocedasticidad en los residuos del modelo. Esto implica que la varianza del error no es constante a lo largo de todas las observaciones de la muestra.
Entre las posibles soluciones encontramos
Errores Estándar Robustos : Usar métodos como HC3 para que los errores estándar sean consistentes ante la presencia de heterocedasticidad.
El uso de Mínimos Cuadrados Ponderados (WLS): Asignandole un peso a cada observación en relación con su varianza.
Análisis de Autocorrelación
El test de Durbin-Watson (DW = 1.7368, p < 0.0001) indicando la presencia de autocorrelación positiva en los errores del modelo. En el contexto de este proyecto sobre el mercado inmobiliario de Cali, esto se traduce como autocorrelación espacial.
Es esperable en este tipo de modelos que los inmuebles cercanos compartan características similares que el modelo no logra capturar, como la seguridad, el acceso a servicios, la cercanía a centros culturales o de esparcimiento, etc.
Para evaluar esta autocorrelación espacial se utilizó el test de Moran, con los siguientes resultados:
library(spdep)
datos_completos <- vivienda[complete.cases(
vivienda[, c("preciom","areaconst","estrato","banios","habitaciones","parqueaderos","zona_nueva","tipo")]
), ]
residuos <- residuals(modelo_global_log)
coords <- cbind(datos_completos$longitud, datos_completos$latitud)
coords[,1] <- jitter(coords[,1], factor = 1e-5)
coords[,2] <- jitter(coords[,2], factor = 1e-5)
vecinos <- knn2nb(knearneigh(coords, k = 5))
pesos <- nb2listw(vecinos, style = "W")
moran.test(residuos, pesos)
##
## Moran I test under randomisation
##
## data: residuos
## weights: pesos
##
## Moran I statistic standard deviate = 17.774, p-value <
## 0.00000000000000022
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic Expectation Variance
## 0.12721204327 -0.00014954389 0.00005134664
Las soluciónes en este caso consisten en utilizar modelos espaciales, como SAR (Spatial Autoregressive Model) o SEM (Spatial Error Model), que permitan capturar las relaciones entre inmuebles cercanos.
Análisis de atipicos
Aunque el modelo detectó algunas observaciones atípicas, el examen mediante DBETA evidenció que estas no afectan significativamente los coeficientes debido al gran tamaño de la muestra de inmuebles.
Dado que muchas de estas observaciones son outliers representativos, es decir, reflejan casos reales del mercado inmobiliario, eliminarlas no resulta adecuado. En su lugar, se podrían emplear modelos robustos frente a outliers.
sur_final <- readRDS("vivienda_sur_final.rds")
options(contrasts = c("contr.treatment", "contr.poly"))
sur_final$estrato <- factor(sur_final$estrato)
sur_final$tipo <- factor(sur_final$tipo)
modelo_sur1 <- lm(log(preciom) ~ log(areaconst) + estrato +
banios + habitaciones + parqueaderos,
data = sur_final)
summary(modelo_sur1)
##
## Call:
## lm(formula = log(preciom) ~ log(areaconst) + estrato + banios +
## habitaciones + parqueaderos, data = sur_final)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.59856 -0.11055 0.01385 0.12743 0.61659
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.280421 0.088520 25.762 < 0.0000000000000002 ***
## log(areaconst) 0.608316 0.022442 27.107 < 0.0000000000000002 ***
## estrato4 0.254487 0.035050 7.261 0.000000000000585 ***
## estrato5 0.371276 0.035250 10.533 < 0.0000000000000002 ***
## estrato6 0.632286 0.038203 16.550 < 0.0000000000000002 ***
## banios 0.072668 0.008537 8.512 < 0.0000000000000002 ***
## habitaciones -0.036628 0.010034 -3.650 0.00027 ***
## parqueaderos 0.114180 0.010733 10.639 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1934 on 1688 degrees of freedom
## (295 observations deleted due to missingness)
## Multiple R-squared: 0.8453, Adjusted R-squared: 0.8447
## F-statistic: 1318 on 7 and 1688 DF, p-value: < 0.00000000000000022
BIC(modelo_sur1)
## [1] -701.5388
Interpretación de los coeficientes.
Área construida: log(areaconst): Por cada aumento del 1% en el área construida, el precio del inmueble aumenta aproximadamente un 0.60%, ceteris paribus. Esto evidencia rendimientos marginales decrecientes del área sobre el precio.
Estrato : (Base: Estrato 3)
Estrato 4: El precio de un apartamento en la zona sur aumenta en promedio un 28.9%.(ceteris paribus)
Estrato 5: El precio de un apartamento en la zona sur aumenta en promedio un 44.95%.(ceteris paribus)
Estrato 6: El precio de un apartamento en la zona sur aumenta en promedio un 88.1%.(ceteris paribus)
Parqueaderos: Cada parqueadero adicional incrementa el precio en promedio un 12%. Manteniendo constante las demás variables.
Baños: Cada baño adicional incrementa el precio en promedio un 7.5% al valor del inmueble. Manteniendo constantes las demás variables.
Habitaciones: Tiene un coeficiente negativo de -3.5%. Lo que indicaria que manteniendo constantes las demas variables los apartamentos del sur castigan tener más habitaciones, “efecto sardina”.
El modelo explica el 84.53% de la variabilidad de los precios de los apartamentos en el sur de cali, eliminando 295 observaciones con valores faltantes (NA) en parqueaderos.
Se procede de la misma forma con el modelo utilizando la base de apartamentos de la zona sur para evaluar la linealidad. Para ello, se emplea un modelo GAM con el fin de examinar las relaciones parciales. La Ilustración A11 muestra el efecto parcial del área construida y del número de habitaciones. Este gráfico sirve como guía para la inclusión del log en el área construida.Se observa la presencia de pocas observaciones con áreas grandes en el extremo derecho del gráfico de efecto parcial, lo que puede indicar observaciones problematicas.
# Aquí lo definiste como gam_sur
gam_sur <- mgcv::gam(log(preciom) ~ s(areaconst) + estrato +
s(banios, k = 3) + s(habitaciones, k = 3) + parqueaderos,
data = sur_final)
par(mfrow = c(1, 2), oma = c(0, 0, 3, 0))
plot(gam_sur, select = 1, shade = TRUE,
shade.col = "lightblue", main = "Area",
xlab = "Area Construida (m2)", ylab = "f(Area)")
plot(gam_sur, select = 2, shade = TRUE,
shade.col = "mistyrose", main = "Habitaciones",
xlab = "Número de Habitaciones", ylab = "f(Habitaciones)")
mtext("Ilustración A11: Efectos parciales de Área y Habitaciones",
side = 3, line = 1, outer = TRUE, cex = 1.1, font = 2)
summary(gam_sur)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## log(preciom) ~ s(areaconst) + estrato + s(banios, k = 3) + s(habitaciones,
## k = 3) + parqueaderos
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.19264 0.03679 141.140 < 0.0000000000000002 ***
## estrato4 0.21939 0.03398 6.456 0.00000000014 ***
## estrato5 0.31244 0.03478 8.982 < 0.0000000000000002 ***
## estrato6 0.57504 0.03697 15.552 < 0.0000000000000002 ***
## parqueaderos 0.10858 0.01048 10.359 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(areaconst) 8.819 8.989 119.81 <0.0000000000000002 ***
## s(banios) 1.859 1.980 34.19 <0.0000000000000002 ***
## s(habitaciones) 1.000 1.000 33.96 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.864 Deviance explained = 86.5%
## GCV = 0.033101 Scale est. = 0.032776 n = 1696
vif_values_sur <- vif(modelo_sur1)
print(vif_values_sur)
## GVIF Df GVIF^(1/(2*Df))
## log(areaconst) 3.352951 1 1.831107
## estrato 2.009355 3 1.123335
## banios 2.941057 1 1.714951
## habitaciones 1.463710 1 1.209839
## parqueaderos 2.219667 1 1.489855
Para evaluar la multicolinealidad se empleó el factor de inflación de la varianza generalizado (GVIF), debido a la presencia de términos con múltiples grados de libertad derivados del uso de polinomios. Los resultados obtenidos no evidencian problemas de multicolinealidad en el modelo.
Para evaluar la homocedasticidad de los residuos se aplicó la prueba de Breusch-Pagan. Los resultados se presentan a continuación e indican la presencia de heterocedasticidad en el modelo por lo que se rechaza la hipótesis nula de varianza constante.
bptest(modelo_sur1)
##
## studentized Breusch-Pagan test
##
## data: modelo_sur1
## BP = 272.58, df = 7, p-value < 0.00000000000000022
shapiro.test(residuals(modelo_sur1))
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_sur1)
## W = 0.9632, p-value < 0.00000000000000022
La normalidad de los errores se evaluó mediante la prueba de Shapiro-Wilk, dado que el número de observaciones del modelo es menor a 5.000. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de normalidad de los residuos.
shapiro.test(residuals(modelo_sur1))
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_sur1)
## W = 0.9632, p-value < 0.00000000000000022
La autocorrelación de los residuos se evaluó mediante la prueba de Durbin–Watson. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de ausencia de autocorrelación , evidenciando correlación positiva entre los errores. Este resultado es consistente con la naturaleza espacial implícita de los datos inmobiliarios.
dwtest(modelo_sur1)
##
## Durbin-Watson test
##
## data: modelo_sur1
## DW = 1.5697, p-value < 0.00000000000000022
## alternative hypothesis: true autocorrelation is greater than 0
par(mfrow = c(1, 1), mar = c(5, 5, 4, 2) + 0.1)
plot(modelo_sur1, which = 5,
main = "Ilustración A12: Análisis de Puntos Influyentes")
dfbetas_values <- dfbetas(modelo_sur1)
max_dfb <- apply(abs(dfbetas_values), 1, max)
orden <- order(max_dfb, decreasing = TRUE)
head(dfbetas_values[orden, ],5
)
## (Intercept) log(areaconst) estrato4 estrato5 estrato6 banios
## 745 1.80238729 -2.19774389 0.07184288 0.1576466 0.2967496 0.835279158
## 1678 1.72930002 -1.99670277 0.07233838 0.1749125 0.3060908 0.647883620
## 101 -0.04664826 -0.27827975 0.74583861 0.7666599 0.7270159 0.043512160
## 883 0.23158967 0.02846382 -0.55709316 -0.5504144 -0.5096137 -0.008410695
## 143 0.12758271 0.03923516 -0.50280463 -0.5076362 -0.4650176 -0.030529491
## habitaciones parqueaderos
## 745 0.8428441 0.905086321
## 1678 0.4998522 0.926067363
## 101 0.0809652 0.146316804
## 883 -0.1274939 0.005790936
## 143 0.1267732 -0.043519770
Si bien el análisis de influencia mediante la distancia de Cook y los DFBetas permitió identificar observaciones potencialmente influyentes, se optó por conservar el modelo con la muestra completa. Esta decisión se fundamenta en el objetivo del estudio, orientado a la predicción y a la identificación de inmuebles potencialmente oportunidades de negocio, donde los valores atípicos pueden representar oportunidades reales de mercado.
Para la elección del modelo especifico correspondiente a la Zona norte – Casas, se siguió el mismo criterio metodológico aplicado en los modelos anteriores. Inicialmente se estimaron diversas modelos y se seleccionaron aquellos con mejor desempeño según el (BIC) y la significancia estadística de los coeficientes.
En este caso, si bien el modelo con menor BIC no incluía la variable habitaciones, se decidió mantenerla en la especificación final debido a requerimientos explícitos del cliente y a su relevancia operativa para la etapa de predicción.
El modelo elegido se presenta a continuación:
sur_final <- readRDS("vivienda_sur_final.rds")
norte_final$estrato <- factor(norte_final$estrato)
norte_final$tipo <- factor(norte_final$tipo)
modelo_norte_r <- lm(log(preciom) ~ poly(areaconst,2) + estrato +
+ banios + parqueaderos+habitaciones,
data = norte_final)
summary(modelo_norte_r)
##
## Call:
## lm(formula = log(preciom) ~ poly(areaconst, 2) + estrato + +banios +
## parqueaderos + habitaciones, data = norte_final)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.60762 -0.14350 -0.00991 0.14028 0.99396
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.4828653 0.0554543 98.872 < 0.0000000000000002 ***
## poly(areaconst, 2)1 5.6693851 0.3859490 14.689 < 0.0000000000000002 ***
## poly(areaconst, 2)2 -2.3429431 0.3150768 -7.436 0.000000000000702 ***
## estrato4 0.2822718 0.0419708 6.725 0.000000000064883 ***
## estrato5 0.3802345 0.0398742 9.536 < 0.0000000000000002 ***
## estrato6 0.6518463 0.0611711 10.656 < 0.0000000000000002 ***
## banios 0.0303153 0.0133699 2.267 0.0239 *
## parqueaderos 0.0474763 0.0093590 5.073 0.000000615667876 ***
## habitaciones -0.0006902 0.0095592 -0.072 0.9425
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2427 on 378 degrees of freedom
## (185 observations deleted due to missingness)
## Multiple R-squared: 0.7548, Adjusted R-squared: 0.7496
## F-statistic: 145.4 on 8 and 378 DF, p-value: < 0.00000000000000022
BIC(modelo_norte_r)
## [1] 52.94886
Interpretación de los coeficientes.
Área construida: Los coeficientes de area construida significativos representan un efecto no lineal sobre el logaritmo del precio , se intuyen rendimientos decrecientes en el precio, es decir a medida que el area aumenta hasta cierto punto el precio no aumenta de la misma forma.
Estrato : (Base: Estrato 3)
Estrato 4: El precio de un apartamento en la zona sur aumenta en promedio cerca de un 32.6 %.(ceteris paribus)
Estrato 5: El precio de un apartamento en la zona sur aumenta en promedio un 46.25%.(ceteris paribus)
Estrato 6: El precio de un apartamento en la zona sur aumenta en promedio un 91.89%.(ceteris paribus)
Parqueaderos: Cada parqueadero adicional incrementa el precio en promedio un 4,8% Manteniendo constante las demás variables.
Baños: Cada baño adicional incrementa el precio en promedio un 3% al valor del inmueble. Manteniendo constantes las demás variables.
Habitaciones: El coeficiente no es significativo y su valor es muy pequeño, podemos suponer que todo el efecto es capturado por el area construida.
Se emplea un modelo GAM con el fin de examinar las relaciones parciales en la base de datos de casas en la zona norte. La Ilustración A13 muestra el efecto parcial del área construida sobre el precio en logaritmo.
gam_norte <- mgcv::gam(log(preciom) ~ s(areaconst) + estrato +
s(banios, k = 3) + s(parqueaderos, k = 3) +
s(habitaciones, k = 3),
data = norte_final)
par(mfrow = c(1, 1))
plot(gam_norte, select = 1, shade = TRUE, shade.col = "lightblue",
main = "Ilustración A13. Efecto Área",
xlab = "Área Construida (m2)", ylab = "f(área)")
summary(gam_norte)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## log(preciom) ~ s(areaconst) + estrato + s(banios, k = 3) + s(parqueaderos,
## k = 3) + s(habitaciones, k = 3)
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.78427 0.03458 167.275 < 0.0000000000000002 ***
## estrato4 0.24844 0.04309 5.765 0.0000000171659222 ***
## estrato5 0.34777 0.04156 8.369 0.0000000000000012 ***
## estrato6 0.61831 0.06156 10.044 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(areaconst) 8.258 8.846 26.624 < 0.0000000000000002 ***
## s(banios) 1.656 1.874 6.598 0.00869 **
## s(parqueaderos) 1.000 1.000 24.332 0.00000186 ***
## s(habitaciones) 1.356 1.577 0.413 0.72731
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.758 Deviance explained = 76.8%
## GCV = 0.059438 Scale est. = 0.056939 n = 387
La ilustracion A13 muestra el efecto parcial del área construida sobre el logaritmo del precio. Se observa que la relación no es completamente lineal, aunque los intervalos de confianza son bastante amplios, en parte debido a la escasez de datos en valores altos de área construida. Con base en la gráfica de efecto parcial, se intentó especificar en el modelo lineal un temrino polinomico de tercer grado poly(areaconst, 3), sin embargo, este no resulto significativo por lo que se opto por mantener unicamente un termino cuadratico para dicha variable.
Para evaluar la multicolinealidad se empleó el factor de inflación de la varianza generalizado (GVIF), debido a la presencia de términos con múltiples grados de libertad derivados del uso de polinomios . Los resultados obtenidos no evidencian problemas de multicolinealidad en el modelo.
vif_modelo_norte_r <- vif(modelo_norte_r)
print(vif_modelo_norte_r)
## GVIF Df GVIF^(1/(2*Df))
## poly(areaconst, 2) 1.863253 2 1.168336
## estrato 1.673526 3 1.089613
## banios 2.225686 1 1.491873
## parqueaderos 1.262914 1 1.123795
## habitaciones 1.711981 1 1.308427
La normalidad de los errores se evaluó mediante la prueba de Shapiro-Wilk, dado que el número de observaciones del modelo es menor a 5.000. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de normalidad de los residuos.
shapiro.test(residuals(modelo_norte_r))
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_norte_r)
## W = 0.98502, p-value = 0.0004918
Para evaluar la homocedasticidad de los residuos se aplicó la prueba de Breusch-Pagan. Los resultados se presentan a continuación e indican la presencia de heterocedasticidad en el modelo por lo que se rechaza la hipótesis nula de varianza constante.
bptest(modelo_norte_r)
##
## studentized Breusch-Pagan test
##
## data: modelo_norte_r
## BP = 17.391, df = 8, p-value = 0.02628
La autocorrelación de los residuos se evaluó mediante la prueba de Durbin–Watson. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de ausencia de autocorrelación , evidenciando correlación positiva entre los errores. Este resultado es consistente con la naturaleza espacial implícita de los datos inmobiliarios.
dwtest(modelo_norte_r)
##
## Durbin-Watson test
##
## data: modelo_norte_r
## DW = 1.8062, p-value = 0.02268
## alternative hypothesis: true autocorrelation is greater than 0
par(mfrow = c(1, 1), mar = c(5, 5, 4, 2) + 0.1)
plot(modelo_norte_r, which = 5,
main = "Ilustración A14: Análisis de Puntos Influyentes")
dfbetas_values <- dfbetas(modelo_norte_r)
max_dfb <- apply(abs(dfbetas_values), 1, max)
orden <- order(max_dfb, decreasing = TRUE)
head(dfbetas_values[orden, ],5
)
## (Intercept) poly(areaconst, 2)1 poly(areaconst, 2)2 estrato4
## 291 0.06374822 0.56471898 1.7757795 0.2164291960
## 488 0.01672890 -0.34643096 -0.7069744 0.0103074070
## 282 -0.16497360 -0.08148683 0.1144954 0.1757340534
## 348 0.42837867 0.23600911 -0.2635898 -0.4104875563
## 100 0.00160346 -0.36402145 -0.4834706 -0.0006337343
## estrato5 estrato6 banios parqueaderos habitaciones
## 291 0.21414653 0.38233092 0.13977443 0.13090097 -0.38852882
## 488 0.01918510 0.07686588 0.07283743 0.11566969 -0.20344230
## 282 0.23111560 0.15632432 0.18581723 -0.53378440 0.06792831
## 348 -0.49231809 -0.35119484 0.04039407 0.01798521 -0.20168534
## 100 -0.02586115 0.13441512 -0.13100228 -0.03078988 0.12507049
Dada la dificultad de encontrar inmuebles que se ajusten a los requerimientos específicos del cliente, se plantea un cuarto modelo que se ejecuta sobre la base de datos filtrada de apartamentos ubicados en el occidente de la ciudad. Se prueban diferentes especificaciones, buscando aquellas que presenten un mejor ajuste, medido a través del R cuadrado ajustado y el BIC. El modelo elegido incluye una transformación logarítmica en preciom y un polinomio cúbico en areaconst.
vivienda <- readRDS("vivienda_consolidada.rds")
viviendas_occidente <- subset(vivienda,
zona_nueva == "Occidente" &
tipo == "Apartamento")
viviendas_occidente$estrato <- as.factor(viviendas_occidente$estrato)
modelo_occ <- lm(log(preciom) ~ poly(areaconst,3,raw=TRUE) + habitaciones + banios + parqueaderos + estrato , data = viviendas_occidente)
summary(modelo_occ)
##
## Call:
## lm(formula = log(preciom) ~ poly(areaconst, 3, raw = TRUE) +
## habitaciones + banios + parqueaderos + estrato, data = viviendas_occidente)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.85772 -0.14942 0.01434 0.16020 0.71771
##
## Coefficients:
## Estimate Std. Error t value
## (Intercept) 4.072179239896 0.056412019657 72.186
## poly(areaconst, 3, raw = TRUE)1 0.014615408224 0.001002760901 14.575
## poly(areaconst, 3, raw = TRUE)2 -0.000038819588 0.000004007516 -9.687
## poly(areaconst, 3, raw = TRUE)3 0.000000035478 0.000000004895 7.248
## habitaciones -0.097994559414 0.011972858866 -8.185
## banios 0.091979724067 0.010927109091 8.418
## parqueaderos 0.144132105302 0.013504539909 10.673
## estrato4 0.197429280565 0.036470764821 5.413
## estrato5 0.396798694072 0.036425793248 10.893
## estrato6 0.594905700670 0.038754605271 15.351
## Pr(>|t|)
## (Intercept) < 0.0000000000000002 ***
## poly(areaconst, 3, raw = TRUE)1 < 0.0000000000000002 ***
## poly(areaconst, 3, raw = TRUE)2 < 0.0000000000000002 ***
## poly(areaconst, 3, raw = TRUE)3 0.000000000000783569 ***
## habitaciones 0.000000000000000732 ***
## banios < 0.0000000000000002 ***
## parqueaderos < 0.0000000000000002 ***
## estrato4 0.000000075555412218 ***
## estrato5 < 0.0000000000000002 ***
## estrato6 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2373 on 1126 degrees of freedom
## (126 observations deleted due to missingness)
## Multiple R-squared: 0.8807, Adjusted R-squared: 0.8798
## F-statistic: 923.7 on 9 and 1126 DF, p-value: < 0.00000000000000022
BIC(modelo_occ)
## [1] 22.96446
Interpretación de los coeficientes.
Área construida: El modelo indica que la relación entre el área y el precio no es una línea recta, sino una curva compleja . Todos los coeficientes son estadisticamente significativos. Inicialmente a medida que aumenta el area el precio aumenta, luego alcanza un punto donde disminuye este precio y finalmente vuelve a subir, quizas capturando el efecto de mansiones de lujo o inmuebles exclusivos.Ceteris paribus.
Habitaciones: Un aumento en un habitación reduce el preco en -9.33%, manteniendo las demas variables constantes (especificamente el area construida).
Baños : un baño adicional incremente al precio del inmueble en 9.63% manteniendo las demas variables constantes.
Parqueaderos: Cada parqueadero adicional incrementa el precio en 15.5% manteniendo las demas variables constantes.
estrato 4 (Base estrato 3): Pasar del estrato base al estrato 4 representa un aumento promedio en el precio del 21.8%. Ceteris paribus.
estrato5 (Base estrato 3): Pasar del estrato base al estrato 5 representa un aumento promedio en el precio del 48.7%. Ceteris paribus.
estrato6 (Base estrato 3): Pasar del estrato base al estrato 6 representa un aumento promedio en el precio del 81.2%. Ceteris paribus.
El modelo explica el 88.07% de la variabilidad de los precios de los apartamentos en el occidente de cali, eliminando 126 observaciones con valores faltantes (NA) en parqueaderos.
Se emplea un modelo GAM con el fin de examinar las relaciones parciales en la base de datos de apartamentos ubicados en el occidente de Cali. La Ilustración A15 muestra el efecto parcial del área construida sobre el logaritmo del precio. Esta ilustración sirve como guía para la inclusión de un polinomio de tercer grado en la variable área construida.
gam_occidente <- mgcv::gam(log(preciom) ~ s(areaconst) + estrato +
s(banios, k = 3) + s(parqueaderos, k = 3) +
s(habitaciones, k = 3),
data = viviendas_occidente)
par(mfrow = c(1, 1))
plot(gam_norte, select = 1, shade = TRUE, shade.col = "lightblue",
main = "Ilustración A15. Efecto Área",
xlab = "Área Construida (m2)", ylab = "f(área)")
summary(gam_occidente)
##
## Family: gaussian
## Link function: identity
##
## Formula:
## log(preciom) ~ s(areaconst) + estrato + s(banios, k = 3) + s(parqueaderos,
## k = 3) + s(habitaciones, k = 3)
##
## Parametric coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.68104 0.03537 160.616 < 0.0000000000000002 ***
## estrato4 0.18945 0.03655 5.183 0.000000259 ***
## estrato5 0.39136 0.03747 10.445 < 0.0000000000000002 ***
## estrato6 0.58236 0.03961 14.703 < 0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Approximate significance of smooth terms:
## edf Ref.df F p-value
## s(areaconst) 7.349 8.329 76.20 <0.0000000000000002 ***
## s(banios) 1.000 1.000 71.24 <0.0000000000000002 ***
## s(parqueaderos) 1.885 1.986 57.57 <0.0000000000000002 ***
## s(habitaciones) 1.401 1.640 37.45 <0.0000000000000002 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## R-sq.(adj) = 0.884 Deviance explained = 88.6%
## GCV = 0.054902 Scale est. = 0.054146 n = 1136
Para evaluar la multicolinealidad se empleó el factor de inflación de la varianza generalizado (GVIF), debido a la presencia de términos con múltiples grados de libertad derivados del uso de polinomios . Los resultados obtenidos no evidencian problemas de multicolinealidad en el modelo.
library(car)
vif_modelo_occ <- vif(modelo_occ)
print(vif_modelo_occ)
## GVIF Df GVIF^(1/(2*Df))
## poly(areaconst, 3, raw = TRUE) 5.475190 3 1.327598
## habitaciones 1.685651 1 1.298326
## banios 3.343650 1 1.828565
## parqueaderos 2.415136 1 1.554071
## estrato 2.102412 3 1.131843
La normalidad de los errores se evaluó mediante la prueba de Shapiro-Wilk, dado que el número de observaciones del modelo es menor a 5.000. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de normalidad de los residuos.
shapiro.test(residuals(modelo_occ))
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_occ)
## W = 0.99489, p-value = 0.0006765
Para evaluar la homocedasticidad de los residuos se aplicó la prueba de Breusch-Pagan. Los resultados se presentan a continuación e indican la presencia de heterocedasticidad en el modelo por lo que se rechaza la hipótesis nula de varianza constante.
bptest(modelo_occ)
##
## studentized Breusch-Pagan test
##
## data: modelo_occ
## BP = 105.65, df = 9, p-value < 0.00000000000000022
La autocorrelación de los residuos se evaluó mediante la prueba de Durbin–Watson. Los resultados que se presentan a continuación permiten rechazar la hipótesis nula de ausencia de autocorrelación , evidenciando correlación positiva entre los errores. Este resultado es consistente con la naturaleza espacial implícita de los datos inmobiliarios.
dwtest(modelo_occ)
##
## Durbin-Watson test
##
## data: modelo_occ
## DW = 1.6644, p-value = 0.000000005221
## alternative hypothesis: true autocorrelation is greater than 0
rownames(viviendas_occidente) <- viviendas_occidente$id
modelo_occ <- lm(formula = log(preciom) ~ poly(areaconst, 3, raw = TRUE) +
habitaciones + banios + parqueaderos + estrato,
data = viviendas_occidente)
plot(modelo_occ, which = 5, main = "Ilustración A16: Análisis de Puntos Influyentes")
dfbetas_values <- dfbetas(modelo_occ)
max_dfb <- apply(abs(dfbetas_values), 1, max)
orden <- order(max_dfb, decreasing = TRUE)
top_5_ids <- rownames(dfbetas_values)[orden][1:5]
casas_influyentes <- viviendas_occidente[top_5_ids, ]
tabla_impacto <- dfbetas_values[top_5_ids, ]
print(tabla_impacto)
## (Intercept) poly(areaconst, 3, raw = TRUE)1
## 7182 0.73658522 -1.12931216
## 7969 -0.19414700 0.33374514
## 6868 -0.06927709 0.01143141
## 7456 0.03722931 0.13069829
## 7923 -0.04836016 0.12918757
## poly(areaconst, 3, raw = TRUE)2 poly(areaconst, 3, raw = TRUE)3
## 7182 1.38884275 -1.63664188
## 7969 -0.38423597 0.44717116
## 6868 -0.02854361 0.04526114
## 7456 -0.13329744 0.12318373
## 7923 -0.12379029 0.12273224
## habitaciones banios parqueaderos estrato4 estrato5
## 7182 0.34339364 -0.27270059 0.362825694 0.160217290 0.253101637
## 7969 -0.05260616 -0.15978215 0.005997387 -0.034112308 -0.056607507
## 6868 -0.04175531 -0.12055966 -0.062388118 0.252207879 0.289493689
## 7456 -0.09065645 0.08997761 0.032398514 -0.248541852 -0.295304703
## 7923 0.02288770 -0.02019182 -0.293162622 -0.009445123 0.005107434
## estrato6
## 7182 0.34470220
## 7969 -0.08386902
## 6868 0.32452125
## 7456 -0.31303594
## 7923 0.02133007
Se realizó un análisis de influencia utilizando el indicador DFBETAS, con el objetivo de identificar observaciones que pudieran tener un impacto desproporcionado sobre los coeficientes estimados del modelo. Si bien se identificaron algunos casos con valores relativamente altos de DFBETAS(id = 7183;7669 y 8130), estos corresponden a un número reducido de observaciones dentro de una muestra amplia de más de 1110 registros por lo que se conservaron las observaciones, asi mismo estas observaciones pueden ser verdaderas oportunidades de negocio que vale la pena ser evaluadas.
El Anexo 10 muestra la configuración de los polígonos utilizados para la reclasificación de los inmuebles en nuevas zonas. Se identificó que un número considerable de inmuebles estaba incorrectamente clasificado. Además, se definió una nueva zona denominada “Centro Histórico”, con el objetivo de diferenciarla del área central convencional.