Por : Oscar Mauricio Montaño Bolaños
Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:
Tabla 1. Predicciones solicitadas:
Para dar respuesta a la solicitud, se divide el set de datos en dos subdataset así:
pal <- colorFactor(
palette = "Blues",
domain = df_base1$estrato
)
leaflet(df_base1) %>%
addTiles() %>%
setView(lng = -76.53, lat = 3.45, zoom = 12) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = ~sqrt(preciom) / 10,
color = ~pal(estrato),
fillOpacity = 0.7,
popup = ~paste(
"<b>Precio:</b>", preciom, "<br>",
"<b>Estrato:</b>", estrato
)
) %>%
addLegend(
"bottomright",
pal = pal,
values = ~estrato,
title = "Estrato"
) %>%
addControl(
html = "<div style='text-align:center;
font-size:14px;
padding:5px;'>
<b>Figura 1.</b> Mapa de viviendas Base 1 (zona norte, tipo: casas)
</div>",
position = "bottomleft"
)
De acuerdo con la Figura 1, se observa que las viviendas analizadas no se concentran exclusivamente en la zona norte, sino que también se encuentran distribuidas en sectores del centro y sur de la ciudad. Dado que el objetivo del estudio es analizar el comportamiento del mercado inmobiliario en la zona norte, se decidió aplicar un análisis de conglomerados basado en las coordenadas geográficas (latitud y longitud), con el fin de identificar y aislar los agrupamientos espaciales correspondientes a dicho sector.
df_norte <- df_base1 %>%
filter(!is.na(latitud), !is.na(longitud))
coords <- df_norte %>%
select(latitud, longitud)
coords_scaled <- scale(coords)
#nrow(coords) # total de registros
#nrow(distinct(coords))
wss <- sapply(1:10, function(k){
kmeans(coords_scaled, centers = k, nstart = 25)$tot.withinss
})
#### Gráfico de codo kmeans####
# plot(1:10, wss, type = "b",
# xlab = "Número de cluster",
# ylab = "WSS",
# main = "Método del codo")
set.seed(123)
k3 <- kmeans(coords_scaled, centers = 3, nstart = 25)
df_norte$cluster <- as.factor(k3$cluster)
###Gráfico e cluster###
ggplot(df_norte, aes(x = longitud, y = latitud, color = cluster)) +
geom_point(alpha = 0.7, size = 2.5) +
coord_fixed() +
scale_y_continuous(
breaks = seq(min(df_norte$latitud),
max(df_norte$latitud),
by = 0.01)
) +
labs(
x = "Longitud",
y = "Latitud",
caption = "Figura 2. Cluster espacial - Casas - Zona Norte de Cali"
) +
theme_minimal(base_size = 15) +
theme(
plot.caption = element_text(
hjust = 0.5, # centrar
size = 14,
face = "bold"
)
)
De acuerdo con la figura 2, se decide filrar los cluster 1 y 3, que espacialmente se situan en la zona norte.
| Base_de_datos | Registros | Variables |
|---|---|---|
| Base 1 | 722 | 13 |
| Base Norte (Cluster 1 y 3) | 615 | 14 |
De acuerdo con la tabla 2, el dataset de la zona norte, se redujo en un 14.82 % (107 registros) después de filtrar los cluster 1 y 3.
La Figura 3 muestra una relación positiva entre el precio de la vivienda y el área construida, indicando que, en términos generales, el valor del inmueble tiende a incrementarse a medida que aumenta su tamaño. Se observa además una alta concentración de viviendas con áreas construidas inferiores a 500 m², en las cuales se encuentran representados los distintos estratos socioeconómicos. Asimismo, se identifican algunas observaciones con áreas construidas considerablemente superiores al promedio de la muestra, cuyos precios no necesariamente siguen la tendencia general, lo cual podría indicar la presencia de valores atípicos o propiedades con características particulares.
En la Figura 4 se observa que la mayor concentración de viviendas se encuentra entre 2 y 6 baños, dentro de los cuales se presenta una mezcla de estratos socioeconómicos. Asimismo, se aprecia una tendencia general en la que el precio de la vivienda tiende a aumentar a medida que aumenta el número de baños, lo cual es consistente con la idea de que un mayor número de baños suele asociarse con viviendas de mayor tamaño y mejores características. Sin embargo, también se evidencia una alta dispersión de precios dentro de cada categoría de baños, lo que sugiere que el número de baños por sí solo no determina completamente el precio del inmueble, sino que interactúa con otros factores como el área construida, el estrato socioeconómico y la ubicación específica de la vivienda. Finalmente, se identifican algunos casos con un número elevado de baños (superior a 7), que corresponden a viviendas con precios considerablemente más altos, lo que podría indicar propiedades de mayor tamaño o con características particulares dentro del mercado inmobiliario.
La figura 5 evidencia que la mayor concentración de viviendas se encuentra entre 1 y 4 parqueaderos, donde se presentan diferentes niveles de precios y una mezcla de estratos socioeconómicos. En general, se aprecia una tendencia positiva entre el número de parqueaderos y el precio de la vivienda, lo que sugiere que las propiedades con mayor disponibilidad de estacionamiento suelen tener un mayor valor en el mercado. No obstante, también se evidencia una alta dispersión de precios dentro de cada categoría de parqueaderos, lo cual indica que esta variable por sí sola no determina completamente el precio del inmueble. Asimismo, se identifican algunos casos con un número elevado de parqueaderos, que corresponden a viviendas con precios considerablemente altos, posiblemente asociadas a propiedades de mayor tamaño o ubicadas en estratos socioeconómicos más altos.
Debido a que la variable precio no presenta una distribución normal, de acuerdo con la actividad 1, se utilizó la correlación de Spearman, la cual es una medida no paramétrica basada en rangos que permite evaluar relaciones monótonas entre variables sin asumir normalidad.
df_num <- df_cluster_13 %>%
select(where(is.numeric), -latitud, -longitud,-id, -estrato)
par(mar = c(5, 4, 2, 2)) # ajusta márgenes el tercer 2 ajusta la altura
corPlot(df_num,
method = "spearman",
cex.axis = 1.2, # tamaño etiquetas ejes
cex = 1.2, # tamaño números dentro
las = 2,
main = "")
title("Matriz de correlaciones, Casas Zona Norte", cex.main = 1.2)
La matriz de correlaciones muestra que el precio de la vivienda presenta una moderada correlación positiva con el área construida (0.74), lo que sugiere que el tamaño del inmueble es uno de los principales determinantes de su valor. Asimismo, variables como el número de baños, parqueaderos y habitaciones presentan correlaciones moderadas con el precio, lo que indica que también influyen en su variación. Adicionalmente, se observa una correlación relativamente alta entre el número de baños y habitaciones (0.63), lo cual sugiere que estas características tienden a aumentar conjuntamente en viviendas de mayor tamaño.
NA_cluster_B1 <- df_cluster_13 %>%
summarise(across(everything(), ~sum(is.na(.)))) %>%
tidyr::pivot_longer(
cols = everything(),
names_to = "variable",
values_to = "Num_Faltantes"
) %>%
arrange(desc("Num_Faltantes"))
NA_cluster_B1 %>%
knitr::kable("html", booktabs = TRUE,
caption = "<span style='font-size:13pt; font-weight:bold;'>Tabla 3. Resumen de valores faltantes Base 1</span>") %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover")) %>%
column_spec(1, bold = TRUE, width = "8cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| variable | Num_Faltantes |
|---|---|
| id | 0 |
| zona | 0 |
| piso | 287 |
| estrato | 0 |
| preciom | 0 |
| areaconst | 0 |
| parqueaderos | 216 |
| banios | 0 |
| habitaciones | 0 |
| tipo | 0 |
| barrio | 0 |
| longitud | 0 |
| latitud | 0 |
| cluster | 0 |
De acuerdo con la tabla 3, se decide eliminar los registros con faltantes en la columna parqueaderos (216 filas). Además, los registros de la columna estrato se convierten a tipo factor para incluir dentro del modelo.
#### Convirtiendo estrato a factor ####
df_cluster_13$estrato <- as.factor(df_cluster_13$estrato)
#### Filtrando NA de parqueaderos####
df_filtrado_clus <- df_cluster_13%>%
filter( !is.na(parqueaderos))
Tabla 4. Coeficientes del modelo de regresión inicial.
modelo_norte <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos,
data = df_filtrado_clus)
summary(modelo_norte)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = df_filtrado_clus)
##
## Residuals:
## Min 1Q Median 3Q Max
## -793.99 -69.68 -13.44 42.46 987.31
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.76263 28.72798 0.340 0.73417
## areaconst 0.68907 0.05629 12.241 < 2e-16 ***
## estrato4 76.81706 26.20122 2.932 0.00357 **
## estrato5 140.98187 24.30102 5.801 1.36e-08 ***
## estrato6 258.71578 40.01470 6.466 3.01e-10 ***
## banios 20.93522 8.44258 2.480 0.01357 *
## habitaciones 5.29806 6.08711 0.870 0.38463
## parqueaderos 25.23501 6.10429 4.134 4.37e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156.9 on 391 degrees of freedom
## Multiple R-squared: 0.6067, Adjusted R-squared: 0.5996
## F-statistic: 86.15 on 7 and 391 DF, p-value: < 2.2e-16
De acuerdo con los resutados de la tabla 4, se observa que la variable habitaciones no es significativa, presuntamente porque ya se incluye en la variable areaconstruida, por lo que se decide excluirla de este.
Tabla 5. Coeficientes del modelo de regresión sin variable habitaciones.
modelo_final <- lm(preciom ~ areaconst + estrato + banios + parqueaderos,
data = df_filtrado_clus)
summary(modelo_final)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + parqueaderos,
## data = df_filtrado_clus)
##
## Residuals:
## Min 1Q Median 3Q Max
## -786.63 -71.23 -16.02 43.51 993.87
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 21.66372 25.25740 0.858 0.391572
## areaconst 0.70137 0.05447 12.876 < 2e-16 ***
## estrato4 71.16793 25.37675 2.804 0.005291 **
## estrato5 134.31166 23.05384 5.826 1.19e-08 ***
## estrato6 250.37625 38.83859 6.447 3.37e-10 ***
## banios 24.90798 7.10014 3.508 0.000504 ***
## parqueaderos 25.36073 6.10069 4.157 3.96e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156.9 on 392 degrees of freedom
## Multiple R-squared: 0.6059, Adjusted R-squared: 0.5999
## F-statistic: 100.4 on 6 and 392 DF, p-value: < 2.2e-16
La tabla 5 exhibe el modelo que excluye la variable habitaciones, debido a que no resultó estadísticamente significativa en el modelo inicial. Esta decisión permite obtener un modelo más parsimonioso, en el cual todas las variables explicativas presentan significancia estadística y contribuyen a explicar el precio de la vivienda. El modelo resultante presenta un coeficiente de determinación cercano al 60 %, lo cual indica una capacidad explicativa moderada. Posteriormente, se realizó un análisis de diagnóstico para identificar observaciones influyentes mediante la distancia de Cook, con el fin de evaluar su impacto en la estabilidad de las estimaciones del modelo.
par(mfrow = c(2,2))
plot(modelo_final)
Figura 6. Evaluación gráfica de los supuestos del modelo de regresión.
De acuerdo con la figura 6, se observa lo siguiente:
Residuals vs Fitted: Evalúa linealidad y homoscedasticidad (varianza constante de los errores). Se observa dispersión creciente a medida que aumentan los valores ajustados. La varianza de los errores aumenta para viviendas más caras.
Q-Q Plot: Evalúa normalidad de los resíduos. Los residuos no siguen perfectamente una distribución normal, especialmente en los extremos.´
Scale-Location: También evalúa homoscedasticidad. La variabilidad de los residuos aumenta con el precio estimado. Esto confirma la presencia de heterocedasticidad. En mercado inmobiliario es normal porque las viviendas caras tienen mayor dispersión de precios.
Residuals vs Leverage: Evalúa observaciones influyentes en el modelo. Puntos fuera de las líneas de Cook, altamente influyentes. Hay algunas observaciones destacadas: 359, 218 y 280.
De acuerdo con el análisis gráfco de los supuestos de normalidad, se decide evaluar el modelo sin los valores influyentes.
Tabla 6. Coeficientes del modelo de regresión eliminando valores influyentes
cook <- cooks.distance(modelo_final)
n <- nrow(df_filtrado_clus)
threshold <- 4/n
#which(cook > threshold)
df_sin_outliers_final <- df_filtrado_clus[cook <= threshold, ]
modelo_sin_out_fin <- lm(preciom ~ areaconst + estrato + banios + parqueaderos,
data = df_sin_outliers_final)
summary(modelo_sin_out_fin)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + parqueaderos,
## data = df_sin_outliers_final)
##
## Residuals:
## Min 1Q Median 3Q Max
## -267.02 -61.15 -11.24 39.71 492.84
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 51.97875 17.38857 2.989 0.002983 **
## areaconst 0.70433 0.04621 15.240 < 2e-16 ***
## estrato4 65.15945 17.03003 3.826 0.000153 ***
## estrato5 104.83132 15.81997 6.627 1.21e-10 ***
## estrato6 214.04573 28.73034 7.450 6.61e-13 ***
## banios 21.85299 5.13065 4.259 2.60e-05 ***
## parqueaderos 19.13707 4.49980 4.253 2.67e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 103.1 on 371 degrees of freedom
## Multiple R-squared: 0.6957, Adjusted R-squared: 0.6908
## F-statistic: 141.4 on 6 and 371 DF, p-value: < 2.2e-16
| Registros | Variables |
|---|---|
| 378 | 14 |
La tabla 6 evidencia que el modelo explica aproximadamente 69.6 % de la variabilidad del precio de la vivienda. El R² ajustado (69.08 %) indica que el modelo mantiene buena capacidad explicativa después de penalizar por número de variables.
Interpretación de los coeficientes:
Área construida: Cada metro cuadrado adicional aumenta el precio aproximadamente en 0.70 millones.
Estrato: Se interpreta respecto al estrato base (3).
Estrato 4 , incremento en 65 millones.
Estrato 5, incremento en 105 millones.
Estrato 6, incremento en 214 millones.
Esto refleja el efecto de la ubicación y nivel socioeconómico del sector.
Número de baños: Cada baño adicional aumenta el precio aproximadamente en 22 millones.
Parqueaderos: Cada parqueadero adicional aumenta el precio aproximadamente en 19 millones.
Tras identificar observaciones influyentes mediante la distancia de Cook, se estimó nuevamente el modelo excluyendo dichos valores. El modelo resultante presenta un coeficiente de determinación de 0.696, lo que indica que aproximadamente el 69.6 % de la variabilidad del precio de la vivienda es explicada por las variables incluidas. Asimismo, se observa una reducción importante en el error estándar residual, lo que sugiere una mejora en la capacidad predictiva del modelo. Todas las variables explicativas resultan estadísticamente significativas y presentan efectos positivos sobre el precio de la vivienda.
par(mfrow = c(2,2))
plot(modelo_sin_out_fin)
Figura 7. Evaluación gráfica de los supuestos del modelo eliminando valores influyentes mediante distancia Cook´s .
La figura 7 muestra una mejora en el comportamiento de los residuos. Los residuos se distribuyen de forma aproximadamente aleatoria alrededor de cero, la normalidad se cumple razonablemente y no se identifican observaciones con influencia excesiva sobre el modelo. Aunque persiste una ligera evidencia de heterocedasticidad, esta no parece ser suficientemente severa como para comprometer la validez general del modelo.
En la tabla 7 se observa el numéro de filas resultante del dataset base 1 resultante de eliminar los valores influyentes mediante las distancias de Cook’s.
nueva_vivienda <- data.frame(
areaconst = 200,
estrato = factor(c(4,5), levels = levels(df_sin_outliers_final$estrato)),
banios = 2,
parqueaderos = 1
)
pred <- predict(modelo_sin_out_fin,
newdata = nueva_vivienda,
interval = "confidence",
level = 0.95)
resultado <- cbind(nueva_vivienda, pred)
kable(resultado,
caption = "Tabla 8. Predicción del precio de la vivienda con intervalos de confianza al 95%")
| areaconst | estrato | banios | parqueaderos | fit | lwr | upr |
|---|---|---|---|---|---|---|
| 200 | 4 | 2 | 1 | 320.8473 | 293.9622 | 347.7324 |
| 200 | 5 | 2 | 1 | 360.5191 | 337.7998 | 383.2385 |
La tabla 7 presenta las predicciones del precio de una vivienda con 200 m² de área construida, dos baños y un parqueadero para los estratos 4 y 5. Los resultados muestran un aumento significativo en el precio estimado al pasar de estrato 4 a estrato 5, con intervalos de confianza al 95 % que reflejan la incertidumbre asociada al modelo.
#width ancho, height, alto
plot_ly(resultado,
x = ~factor(estrato),
y = ~fit,
type = "scatter",
mode = "markers",
marker = list(size = 10, color = "blue"),
error_y = list(
type = "data",
symmetric = FALSE,
array = resultado$upr - resultado$fit,
arrayminus = resultado$fit - resultado$lwr,
thickness = 2,
width = 6
),
text = ~paste(
"Estrato:", estrato,
"<br>Precio estimado:", round(fit,1),
"<br>Límite inferior:", round(lwr,1),
"<br>Límite superior:", round(upr,1)
),
hoverinfo = "text") %>%
layout(
title = "Predicción del precio de la vivienda",
xaxis = list(
title = "Estrato"
),
yaxis = list(
title = "Precio estimado (millones de pesos)",
tickmode = "linear",
tick0 = 250,
dtick = 10
),
shapes = list(
list(
type = "line",
x0 = -0.5,
x1 = 1.5,
y0 = 350,
y1 = 350,
line = list(
color = "red",
dash = "dash",
width = 2
)
)
),
annotations = list(
list(
x = 1,
y = 355,
text = "Crédito preaprobado = 350 millones",
showarrow = FALSE,
font = list(color = "red")
),
list(
x = 0.5,
y = -0.25,
text = "Figura 8. Predicción del precio de vivienda vs presupuesto aprobado",
showarrow = FALSE,
xref = "paper",
yref = "paper",
font = list(size = 12)
)
),
margin = list(
b = 120 # espacio inferior para la referencia
)
)
La Figura 8 muestra la predicción del precio de una vivienda con 200 m² de área construida, dos baños y un parqueadero para los estratos 4 y 5. El punto representa el precio estimado por el modelo, mientras que las barras verticales indican el intervalo de confianza al 95%. La línea roja corresponde al crédito hipotecario preaprobado de 350 millones de pesos. Los resultados sugieren que una vivienda en estrato 4 se encuentra dentro del rango financiable, mientras que en estrato 5 el precio estimado tiende a superar dicho límite, lo que podría restringir la capacidad de compra.
df_base2 <- df%>%
filter(tipo == 'Apartamento' & zona == 'Zona Sur')
pal <- colorFactor(
palette = "Blues",
domain = df_base2$estrato
)
leaflet(df_base2) %>%
addTiles() %>%
setView(lng = -76.53, lat = 3.45, zoom = 12) %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = ~sqrt(preciom) / 10,
color = ~pal(estrato),
fillOpacity = 0.7,
popup = ~paste(
"<b>Precio:</b>", preciom, "<br>",
"<b>Estrato:</b>", estrato
)
) %>%
addLegend(
"bottomright",
pal = pal,
values = ~estrato,
title = "Estrato"
) %>%
addControl(
html = "<div style='text-align:center;
font-size:14px;
padding:5px;'>
<b>Figura 9.</b> Mapa de viviendas Base 2 (zona sur, tipo: apartamento)
</div>",
position = "bottomleft"
)
De acuerdo con la Figura 9, se observa que las viviendas analizadas no se concentran exclusivamente en la zona sur, sino que también se encuentran distribuidas en sectores del centro y norte de la ciudad. Dado que el objetivo del estudio es analizar el comportamiento del mercado inmobiliario en la zona sur, se decidió aplicar un análisis de conglomerados basado en las coordenadas geográficas (latitud y longitud), con el fin de identificar y aislar los agrupamientos espaciales correspondientes a dicho sector.
coords <- df_base2 %>%
select(latitud, longitud)
coords_scaled <- scale(coords)
#nrow(coords) # total de registros
#nrow(distinct(coords))
wss <- sapply(1:10, function(k){
kmeans(coords_scaled, centers = k, nstart = 25)$tot.withinss
})
#### Gráfico de codo kmeans####
# plot(1:10, wss, type = "b",
# xlab = "Número de cluster",
# ylab = "WSS",
# main = "Método del codo")
set.seed(123)
k3 <- kmeans(coords_scaled, centers = 3, nstart = 25)
df_base2$cluster <- as.factor(k3$cluster)
###Gráfico e cluster###
ggplot(df_base2, aes(x = longitud, y = latitud, color = cluster)) +
geom_point(alpha = 0.7, size = 2.5) +
coord_fixed() +
scale_y_continuous(
breaks = seq(min(df_base2$latitud),
max(df_base2$latitud),
by = 0.01)
) +
labs(
x = "Longitud",
y = "Latitud",
caption = "Figura 10. Cluster espacial -Apatamentos- Zona Sur de Cali"
) +
theme_minimal(base_size = 15) +
theme(
plot.caption = element_text(
hjust = 0.5, # centrar
size = 14,
face = "bold"
)
)
# Promedio cluster
centroides_cluster <- df_base2 %>%
group_by(cluster) %>%
summarise(
latitud_prom = round(mean(latitud),2),
longitud_prom = round(mean(longitud),2),
n = n()
)
centroides_cluster%>%
knitr::kable("html", booktabs = TRUE,
caption = "<span style='font-size:13pt; font-weight:bold;'>Tabla 9. Promedio de latitud y longitud por cluster</span>") %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover")) %>%
column_spec(1, bold = TRUE, width = "8cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| cluster | latitud_prom | longitud_prom | n |
|---|---|---|---|
| 1 | 3.41 | -76.54 | 995 |
| 2 | 3.44 | -76.50 | 216 |
| 3 | 3.37 | -76.53 | 1576 |
A partir del análisis espacial de los clusters obtenidos mediante coordenadas geográficas (figura 10), se identificó que el cluster 3 presenta las menores latitudes promedio (tabla 9), lo que indica una localización más hacia el sur de la ciudad de Cali. Por lo tanto, este grupo fue seleccionado para representar la oferta inmobiliaria de la zona sur.
df_cluster_3 <- df_base2 %>%
filter(cluster == '3')
tabla_dim <- data.frame(
Base_de_datos = c("Base 2", "Base Sur (Cluster 3)"),
Registros = c(nrow(df_base2), nrow(df_cluster_3)),
Variables = c(ncol(df_base2), ncol(df_cluster_3))
)
tabla_dim %>%
knitr::kable("html", booktabs = TRUE,
caption = "<span style='font-size:13pt; font-weight:bold;'>Tabla 10. Dimensiones de las bases de datos utilizadas en el análisis Zona Sur.</span>") %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover")) %>%
column_spec(1, bold = TRUE, width = "8cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| Base_de_datos | Registros | Variables |
|---|---|---|
| Base 2 | 2787 | 14 |
| Base Sur (Cluster 3) | 1576 | 14 |
De acuerdo con la tabla 10, el dataset de la zona sur, se redujo en un 43.45 % (1211 registros) después de filtrar el cluster 3.
La figura 11 muestra una relación positiva entre el precio y el área construida de los apartamentos en la zona sur de Cali. A medida que aumenta el tamaño de la vivienda, el precio tiende a incrementarse. Asimismo, se observa una clara segmentación por estrato socioeconómico, donde los estratos más altos concentran los precios más elevados y presentan mayor dispersión.
La gráfica 12 muestra una relación positiva entre el precio de los apartamentos y el número de baños en la zona sur de Cali. A medida que aumenta el número de baños, el precio tiende a incrementarse. Además, se observa una clara diferenciación por estrato socioeconómico, donde los estratos más altos concentran los precios más elevados y presentan mayor dispersión en los valores de las viviendas.
En la figura 13 se observa la relación entre el precio de los apartamentos y el número de parqueaderos en la zona sur de Cali. Debido a que se trata principalmente de apartamentos, el número de parqueaderos tiende a ser menor en comparación con la Base 1, la cual incluía viviendas con mayor área construida. Asimismo, se identifica una mayor concentración de observaciones en apartamentos con dos parqueaderos, lo que sugiere que esta es la configuración más común dentro de la oferta analizada. A medida que aumenta el número de parqueaderos (tres y cuatro), se observa una mayor dispersión en los precios, lo que indica una mayor variabilidad en las características de estas viviendas. Finalmente, el estrato 6 predomina en todos los niveles de parqueaderos, concentrando la mayor cantidad de observaciones y los precios más altos dentro del conjunto de datos.
Debido a que la variable precio no presenta una distribución normal, de acuerdo con la actividad 1, se utilizó la correlación de Spearman, la cual es una medida no paramétrica basada en rangos que permite evaluar relaciones monótonas entre variables sin asumir normalidad.
df_num <- df_cluster_3 %>%
select(where(is.numeric), -latitud, -longitud,-id, -estrato)
par(mar = c(7, 4, 2, 2)) # más espacio abajo
corPlot(df_num,
method = "spearman",
cex.axis = 1.2, # tamaño etiquetas ejes
cex = 1.2, # tamaño números dentro
las = 2,
main = "")
title("Matriz de correlaciones, Apartamentos Zona Sur",
cex.main = 1.2)
En términos generales, la matriz de correlaciones evidencia que el precio de los apartamentos en la zona sur de Cali está fuertemente asociado con el área construida, el número de baños y los parqueaderos, mientras que el número de habitaciones presenta una relación moderada. Asimismo, las variables estructurales del inmueble muestran correlaciones positivas entre sí, lo que refleja que las viviendas de mayor tamaño suelen contar con más instalaciones.
NA_cluster_B2 <- df_cluster_3 %>%
summarise(across(everything(), ~sum(is.na(.)))) %>%
tidyr::pivot_longer(
cols = everything(),
names_to = "variable",
values_to = "Num_Faltantes"
) %>%
arrange(desc("Num_Faltantes"))
NA_cluster_B2 %>%
knitr::kable("html", booktabs = TRUE,
caption = "<span style='font-size:13pt; font-weight:bold;'>Tabla 11. Resumen de valores faltantes Base 2</span>") %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover")) %>%
column_spec(1, bold = TRUE, width = "8cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| variable | Num_Faltantes |
|---|---|
| id | 0 |
| zona | 0 |
| piso | 362 |
| estrato | 0 |
| preciom | 0 |
| areaconst | 0 |
| parqueaderos | 214 |
| banios | 0 |
| habitaciones | 0 |
| tipo | 0 |
| barrio | 0 |
| longitud | 0 |
| latitud | 0 |
| cluster | 0 |
De acuerdo con la tabla 11, se decide eliminar los registros con faltantes en la columna parqueaderos (214 filas). Además, los registros de la columna estrato se convierten a tipo factor para incluir dentro del modelo.
#### Convirtiendo estrato a factor ####
df_cluster_3$estrato <- as.factor(df_cluster_3$estrato)
#### Filtrando NA de parqueaderos####
df_filtrado_clus_sur<- df_cluster_3%>%
filter( !is.na(parqueaderos))
Tabla 12. Coeficientes del modelo de regresión inicial.
modelo_sur <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos,
data = df_filtrado_clus_sur)
summary(modelo_sur)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = df_filtrado_clus_sur)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1197.96 -37.87 -0.81 35.53 857.49
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -56.58417 24.12590 -2.345 0.0192 *
## areaconst 1.44075 0.06918 20.827 < 2e-16 ***
## estrato4 6.32490 20.21550 0.313 0.7544
## estrato5 17.62439 20.33764 0.867 0.3863
## estrato6 123.89606 21.73157 5.701 1.46e-08 ***
## banios 51.99665 4.49269 11.574 < 2e-16 ***
## habitaciones -14.65719 5.44545 -2.692 0.0072 **
## parqueaderos 81.12027 5.94392 13.648 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 96.99 on 1354 degrees of freedom
## Multiple R-squared: 0.807, Adjusted R-squared: 0.806
## F-statistic: 808.7 on 7 and 1354 DF, p-value: < 2.2e-16
Los resultados del modelo (tabla 12) muestran que el 80.7 % de la variabilidad del precio de los apartamentos es explicada por las variables incluidas en el modelo, lo que indica un buen ajuste del modelo para análisis inmobiliarios. Los coeficientes representan el cambio esperado en el precio cuando la variable aumenta una unidad, manteniendo las demás constantes.
Por cada metro cuadrado adicional, el precio del apartamento aumenta aproximadamente 1.44 millones de pesos, manteniendo constantes las demás variables.
Los apartamentos de estrato 6 tienen en promedio 123.9 millones más que los de estrato 3, manteniendo constantes las demás variables. Los demás estratos no son significativos dentro del modelo, por lo que se mantiene la variable estrato debido a su relevancia teórica en el mercado inmobiliario.
Cada baño adicional incrementa el precio del apartamento aproximadamente en 52 millones.
Manteniendo constante el área y otras variables, un mayor número de habitaciones se asocia con una disminución del precio. Esto puede parecer contraintuitivo, pero suele ocurrir porque: al dividir el espacio en más habitaciones, cada habitación tiende a ser más pequeña.
Cada parqueadero adicional incrementa el precio del apartamento aproximadamente en 81 millones.
En síntesis, el modelo muestra que el precio de los apartamentos en la zona sur de Cali está fuertemente determinado por características estructurales del inmueble, particularmente el área construida, el número de baños y los parqueaderos. Asimismo, el estrato socioeconómico, especialmente el estrato 6, presenta un efecto significativo en el incremento del precio.
vif_mod <- car::vif(modelo_sur)
vif_mod%>%
knitr::kable("html", booktabs = TRUE,
caption = "<span style='font-size:13pt; font-weight:bold;'>Tabla 13. Resumen de valores faltantes Base 2</span>") %>%
kable_styling(full_width = FALSE,
position = "center",
bootstrap_options = c("striped", "hover")) %>%
column_spec(1, bold = TRUE, width = "8cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| GVIF | Df | GVIF^(1/(2*Df)) | |
|---|---|---|---|
| areaconst | 2.182637 | 1 | 1.477375 |
| estrato | 1.989581 | 3 | 1.121485 |
| banios | 2.806950 | 1 | 1.675396 |
| habitaciones | 1.396102 | 1 | 1.181568 |
| parqueaderos | 2.309081 | 1 | 1.519566 |
Ya que todas las variables son significativas dentro del modelo, se evaluó la posible presencia de multicolinealidad mediante el Factor de Inflación de la Varianza (VIF). Los resultados (tabla 13) muestran valores inferiores a 2 para todas las variables explicativas, lo que indica ausencia de multicolinealidad significativa en el modelo. En consecuencia, las estimaciones obtenidas pueden considerarse estables y confiables para la interpretación de los efectos de las variables sobre el precio de los apartamentos.
Aclaraciones de los resultados de la tabla 13.
GVIF: Generalized Variance Inflation Factor (usado cuando hay variables categóricas con varios niveles, como estrato).
Df: grados de libertad asociados a la variable.
GVIF^(1/(2*Df)): versión ajustada del GVIF que permite comparar con el VIF tradicional.
par(mfrow = c(2,2))
plot(modelo_sur)
Figura 14. Evaluación gráfica de los supuestos del modelo de regresión, apartamentos zona sur
De la figura 14 se puede indicar lo siguiente:
Residuals vs Fitted: Evalúa linealidad y homoscedasticidad (varianza constante de los errores). Se observa dispersión creciente a medida que aumentan los valores ajustados. La varianza de los errores aumenta para viviendas más caras.
Q-Q Plot: Evalúa normalidad de los resíduos. Los residuos no siguen perfectamente una distribución normal, especialmente en los extremos.´
Scale-Location: También evalúa homoscedasticidad. La variabilidad de los residuos aumenta con el precio estimado. Esto confirma la presencia de heterocedasticidad. En mercado inmobiliario es normal porque las viviendas caras tienen mayor dispersión de precios.
Residuals vs Leverage: Evalúa observaciones influyentes en el modelo. Puntos fuera de las líneas de Cook, altamente influyentes. Hay algunas observaciones destacadas: 1099 y 477.
De acuerdo con el análisis gráfico de los supuestos de normalidad, se decide evaluar el modelo sin los valores influyentes.
Tabla 14. Coeficientes del modelo de regresión eliminando valores influyentes, apartamentos-zona sur
cook <- cooks.distance(modelo_sur)
n <- nrow(df_filtrado_clus_sur)
threshold <- 4/n
#which(cook > threshold)
df_sin_outliers_sur <- df_filtrado_clus_sur[cook <= threshold, ]
modelo_sin_out_sur <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos,
data = df_sin_outliers_sur)
summary(modelo_sin_out_sur)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = df_sin_outliers_sur)
##
## Residuals:
## Min 1Q Median 3Q Max
## -227.470 -28.420 1.328 29.756 214.878
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -48.68012 14.82356 -3.284 0.00105 **
## areaconst 2.16019 0.07362 29.344 < 2e-16 ***
## estrato4 14.73622 12.42632 1.186 0.23588
## estrato5 29.16831 12.51889 2.330 0.01996 *
## estrato6 117.29950 13.43538 8.731 < 2e-16 ***
## banios 37.27385 2.88953 12.900 < 2e-16 ***
## habitaciones -16.25431 3.30989 -4.911 1.02e-06 ***
## parqueaderos 49.99932 3.99197 12.525 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 56.04 on 1298 degrees of freedom
## Multiple R-squared: 0.9025, Adjusted R-squared: 0.902
## F-statistic: 1717 on 7 and 1298 DF, p-value: < 2.2e-16
| Registros | Variables |
|---|---|
| 1306 | 14 |
La tabla 14 evidencia que el modelo explica aproximadamente 90 % de la variabilidad del precio de la vivienda. El R² ajustado (90 %) indica que el modelo tiene un nivel de explicación muy alto para modelos inmobiliarios.
El error promedio del modelo es aproximadamente de 56 millones.
Interpretación de los coeficientes:
Área construida: Cada metro cuadrado adicional aumenta el precio aproximadamente en 2.16 millones.
Estrato: Se interpreta respecto al estrato base (3).
El precio de estrato 4 no difiere significativamente del estrato base.
Los apartamentos en estrato 5 cuestan en promedio 29 millones más que los del estrato base.
Los apartamentos en estrato 6 cuestan en promedio 117 millones más que los del estrato base.
Esto refleja el efecto del nivel socioeconómico del sector.
Cada baño adicional aumenta el precio del apartamento aproximadamente en 37 millones.
A mayor número de habitaciones, el precio disminuye en promedio 16 millones. La relación negativa de habitaciones ocurre porque existe una interacción implícita con el área construida. Ejemplo: dos apartamentos de 100 m², uno con dos habitaciones (espacio más amplio y por tanto más costoso), el otro con cuatro habitaciones (menos espacio, menor confort).
Parqueaderos: Cada parqueadero adicional aumenta el precio aproximadamente en 50 millones.
nueva_vivienda <- data.frame(
areaconst = 300,
estrato = factor(c(5,6), levels = levels(df_sin_outliers_sur$estrato)),
banios = 3,
habitaciones = 5,
parqueaderos = 3
)
pred <- predict(modelo_sin_out_sur,
newdata = nueva_vivienda,
interval = "confidence",
level = 0.95)
resultado <- cbind(nueva_vivienda, pred)
kable(resultado,
caption = "Tabla 16. Predicción del precio de la vivienda-Apartamentos-zona sur")
| areaconst | estrato | banios | habitaciones | parqueaderos | fit | lwr | upr |
|---|---|---|---|---|---|---|---|
| 300 | 5 | 3 | 5 | 3 | 809.0920 | 782.8320 | 835.3519 |
| 300 | 6 | 3 | 5 | 3 | 897.2232 | 872.3601 | 922.0862 |
#width ancho, height, alto
plot_ly(resultado,
x = ~factor(estrato),
y = ~fit,
type = "scatter",
mode = "markers",
marker = list(size = 10, color = "blue"),
error_y = list(
type = "data",
symmetric = FALSE,
array = resultado$upr - resultado$fit,
arrayminus = resultado$fit - resultado$lwr,
thickness = 2,
width = 6
),
text = ~paste(
"Estrato:", estrato,
"<br>Precio estimado:", round(fit,1),
"<br>Límite inferior:", round(lwr,1),
"<br>Límite superior:", round(upr,1)
),
hoverinfo = "text") %>%
layout(
title = "Predicción del precio de la vivienda",
xaxis = list(
title = "Estrato"
),
yaxis = list(
title = "Precio estimado (millones de pesos)",
tickmode = "linear",
tick0 = 750,
dtick = 25
),
shapes = list(
list(
type = "line",
x0 = -0.5,
x1 = 1.5,
y0 = 850,
y1 = 850,
line = list(
color = "red",
dash = "dash",
width = 2
)
)
),
annotations = list(
list(
x = 1,
y = 860,
text = "Crédito aprobado = 850 millones",
showarrow = FALSE,
font = list(color = "red")
),
list(
x = 0.5,
y = -0.25,
text = "Figura 15. Predicción del precio de vivienda vs presupuesto aprobado - Apartamentos zona sur",
showarrow = FALSE,
xref = "paper",
yref = "paper",
font = list(size = 12)
)
),
margin = list(
b = 120
)
)
De acuerdo con la tabla 16 y la figura 15 la predicción de un apartamento con las características analizadas tendría un precio estimado de aproximadamente 809 millones de pesos en estrato 5 y 897 millones en estrato 6. Al comparar estos valores con el crédito aprobado de 850 millones, se observa que el apartamento en estrato 5 se encuentra dentro del rango estimado por el modelo, mientras que el apartamento en estrato 6 supera el presupuesto disponible incluso considerando el límite inferior del intervalo de confianza. Esto sugiere que, bajo las condiciones analizadas, la adquisición de una vivienda en estrato 5 sería más consistente con la capacidad de financiamiento considerada.
El análisis realizado permitió identificar los principales factores que influyen en el precio de la vivienda mediante la aplicación de modelos de regresión lineal múltiple. A partir del análisis exploratorio, la evaluación de correlaciones y la verificación de los supuestos del modelo, se determinó que variables como el área construida, el estrato socioeconómico, el número de baños y el número de parqueaderos presentan una relación significativa con el precio de los inmuebles.
En el caso de las viviendas tipo casa ubicadas en la zona norte, el modelo ajustado permitió explicar una proporción importante de la variabilidad del precio de la vivienda. La eliminación de observaciones influyentes mediante la distancia de Cook permitió mejorar el ajuste del modelo y obtener estimaciones más estables de los coeficientes. Con base en el escenario de predicción planteado para una vivienda de aproximadamente 200 m², con 2 baños y 1 parqueadero, el modelo estimó un precio promedio cercano a 320 millones de pesos para estrato 4 y alrededor de 360 millones de pesos para estrato 5. Al comparar estos valores con un crédito preaprobado de 350 millones de pesos, se observa que una vivienda en estrato 4 se encuentra dentro del rango estimado por el modelo, mientras que una vivienda en estrato 5 podría superar el presupuesto dependiendo del comportamiento del mercado, lo que sugiere que el estrato 4 representa una alternativa más consistente con la capacidad de financiamiento planteada.
En el segundo escenario, correspondiente a apartamentos ubicados en la zona sur, el modelo presentó una alta capacidad explicativa, evidenciada por un coeficiente de determinación cercano al 90 %, lo que indica un ajuste considerablemente bueno del modelo para este segmento del mercado. Para un apartamento con 300 m², 3 baños, 5 habitaciones y 3 parqueaderos, el modelo predijo un precio aproximado de 809 millones de pesos en estrato 5 y cerca de 897 millones de pesos en estrato 6. Al comparar estas estimaciones con un presupuesto aprobado de 850 millones de pesos, se observa que el apartamento en estrato 5 se encuentra dentro del rango estimado por el modelo, mientras que el apartamento en estrato 6 excede el presupuesto disponible incluso considerando el límite inferior del intervalo de confianza.
En términos generales, los resultados obtenidos evidencian que el estrato socioeconómico y el área construida son los factores con mayor influencia en la determinación del precio de la vivienda, lo cual es consistente con la dinámica del mercado inmobiliario. Asimismo, el uso de modelos de regresión permitió no solo explicar el comportamiento del precio de los inmuebles, sino también generar escenarios de predicción útiles para apoyar la toma de decisiones de compra en función del presupuesto disponible.
Finalmente, este estudio demuestra que las técnicas de modelación estadística constituyen una herramienta valiosa para analizar el mercado inmobiliario, permitiendo identificar patrones en los precios de las viviendas y estimar rangos de valor esperados bajo diferentes características del inmueble.