Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.
Se espera que este análisis de datos proporcione ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.
A continuación se describen cada uno de los pasos que se desarrollaron en el análisis, desde la recopilación de la información hasta la interpretación de los resultados.
Recopilación y preprocesamiento de los datos: Además del uso de la base de datos recogida y proporcionada, también se trabajó con archivos tipo .shp para análisis georreferenciado de los datos; de esta última se tuvo en cuenta analizar con la comuna, que era una variable que no se encontraba en la base de datos inicial. Se realizó búsqueda de definiciones de Casa y Vivienda en Colombia, por esto, se reclasificó el tipo de vivienda tomando que toda vivienda que tuviera más de 3 pisos, sería Apartamento en vez de Casa. Las variables de piso y parqueaderos que se encontraba con más del 20% de valores faltantes, fueron imputados por la moda teniendo en cuenta las variables zona y tipo de vivienda. Finalmente, las viviendas que se encontraban con una zona errónea en el mapa, fueron reclasificadas teniendo en cuenta sus coordenadas.
Análisis Multivariados: Se realizaron los métodos de Análisis de Componentes Principales (ACP), Análisis de Conglomerados y Análisis de Componentes Múltiples (ACM) para obtener una comprensión del mercado inmobiliario. Se realizaron ajustes en el conjunto de datos para llevar a cabo cada uno de los análisis.
Discusión, conclusiones y recomendaciones. De acuerdo a los análisis desarrollados, se realiza la discusión detallada del resoultado por cada método y se plantean recomendaciones que proporcionen ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.
Se toma la base de datos que se encuentra en el paqueteMETODOS descargada del github del Centro de Magis. Luego de realizar un preprocesamiento de los datos y manejo a los datos faltantes existentes (Ver Anexos), se tiene finalmente la siguiente base de datos a trabajar.
# Conjunto de datos con todas las variables
head(df)
Está conformada por viviendas que poseen las características de zona del ciudad, el piso donde se encuentra ubicada, el estrato, el precio en millones de pesos, el área construida en metros cuadrados, el número de paqueaderos que posee la vivienda, número de baños y de habitaciones, el tipo de vivienda sea casa o apartamento, el barrio y coordenadas de latitud y longitud para su georreferenciación. Cada una de ellas viene identificado con un código único para reconocerla.
Analizando la variable zona, se construye un gráfico de barras para revisar comportamiento de la variable. En el gráfico se observa que las viviendas del estudio pertenecen a una gran mayoría a la Zona Sur (56.81%), seguido de la Zona Norte (23.08%) y donde sólamente el 1.49% de los apartamentos se encuentran en la Zona Centro.
# Estadísticas descriptivas: zona
tabla_zona <- df %>%
group_by(zona) %>%
tally(name = "n") %>%
mutate(porcentaje = round((n/sum(n)*100), 2))
library(ggplot2)
library(tidyverse)
library(hrbrthemes)
ggplot(tabla_zona, aes(x=zona, y=n, fill=zona)) +
geom_bar(stat="identity", color="black") +
scale_fill_hue(c = 40) +
ggtitle("Distribución de las zonas de viviendas") +
labs(x="Zona", y="frecuencia") +
ylim(c(0,6000)) +
geom_text(aes(label=paste0(n," ", "", "(", porcentaje, "%",")")),
vjust=-0.9,
color="black",
hjust=0.5,
position = position_dodge(0.9),
angle=0,
size=4.0
) +
theme(legend.position="none") +
theme_minimal()
Ahora, con los precios de las viviendas por zona geográfica puede evidenciarse que llegan a concentrarse en precios bajos, pero a su vez, cada una de ellas presentan viviendas con costos muy altos, incluyendo la zona Oriente y Centro, que a pesar de que la distribución de los precios es baja, hay viviendas con precios altos, lo que permite analizar qué variables están relacionadas a que el precio de vivienda en cada zona se alto o no.
De igual manera, se observa que los precios de las casas y apartamentos en las zonas Centro, Norte, Oeste y Oriente no se identifica diferencias significativas entre ellas y donde sí se tiene en la zona Sur, pues las casas llegan a tener un precio alto en comparación con los apartamentos.
# Estadísticas descriptivas: precio, tipo de vivienda y zona
library(dplyr)
library(ggplot2)
library(tidyverse)
library(hrbrthemes)
library(viridis)
df %>%
ggplot(aes(x=factor(zona), y=preciom, fill=factor(tipo_modif))) +
geom_boxplot() +
scale_fill_viridis(discrete=TRUE, alpha=0.6) +
geom_jitter(color="black", size=0.4, alpha=0.9) +
theme_ipsum()+
theme(legend.position="bottom",
plot.title=element_text(size=11)
) +
ggtitle("Precio del tipo de viviendas por zona en Cali") +
xlab("Zona") + ylab("Precio vivienda") + labs(fill="Tipo de vivienda")
Al graficar las variables de tipo, piso, estrato y precio en conjunto, se puede observar la relación que puede haber entre ellas. Se observa que la cantidad de apartamentos supera la de las casas en la ciudad, como también hay apartamentos ubicados hasta en el piso 12. El piso expone ser una variable que tiende a tener mucha variabilidad en el precio cuando la vivienda se encuentra entre los primeros pisos y se encuentra que la mayoría de las viviendas se encuentran en precios bajos.
library(GGally)
ggpairs(
df[, c("tipo_modif", "piso","preciom")],
columnLabels=c("Tipo", "Piso","Precio"),
upper=list(continous="density", combo="box_no_facet"),
lower=list(continous="points", combo="dot_no_facet")
)
Acompañando el análisis anterior, se calculan los indicadores para la variable de precio, donde se expone una media de $330 millones de pesos y donde el 75% de las viviendas tienen un precio menor a $540. Esta información corrobora lo analizado previamente en donde se especifica la mayoría de viviendas con precios bajos pues el máximo costo que se encuentra en una vivienda es de $1999 millones de pesos.
summary(df$preciom)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 58.0 220.0 330.0 433.9 540.0 1999.0
Con el fin de identificar características clave que puedan influir la variación de precios y oferta del mercado, se realiza un ACP.
Dado que el conjunto de datos a trabajar está conformada por variables cuantitativas y cualitativas, se agrupan sólamente las primeras para desarrollar el método. Por tanto, la nueva base de datos con variables como preciom, areaconst, parqueaderos, banios y habitaciones serán las tenidas en cuenta para su desarrollo. A continuación se expone la nueva tabla que incluye las variables mencionadas.
# Conjunto de datos para ACP
df_pca <- df[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones", "estrato")]
head(df_pca)
En el ACP se recomienda que las variables sean estandarizadas con el objetivo de que sean comparables, por esto se realiza la siguiente normalización observando que ahora los datos se mueven en un rango de valores distinto al inicial con la particularidad de tener media 0 y varianza 1.
# Estandarización de los datos
df_pca <- scale(df_pca[, 1:5])
Una vez escogida las variables cuantitativas y realizando su respectiva estandarización, se realiza el ACP sobre el conjunto de datos df_pca. Esto se usa para reducir la dimensionalidad del conjunto de datos mientras se retiene la mayor cantidad posible de variabilidad de los datos originales. Se toma la variable estrato como variable suplementaria en el análisis.
df_pca <- cbind(df_pca, estrato = df$estrato)
# Análisis de componentes principales
library(FactoMineR)
res.pca <- PCA(df_pca, quali.sup = "estrato")
Luego, se calculan las desviaciones estándar de los componentes principales que vienen definidas como las raíces cuadradas de los valores propios de la matriz de covarianza. Representan la magnitud de la variabilidad explicada por cada componente principal. Un componente principal con una desviación estándar alta explica más variabilidad de los datos originales que uno con una desviación estándar baja.
También se calcula la suma de los cuadrados de las desviaciones estándar, el cual indica la suma de los valores propios. Esta suma es igual al número de variables originales en el análisis (p), que en este caso deberían ser 5 (sin contar la variable de id). Los valores propios reflejan la cantidad de variabilidad explicada por cada componente principal. Si todos los componentes principales se consideran, la suma total de los valores propios será igual al número total de variables originales.
Por último, el porcentaje de la variabilidad total de los datos explicada por cada componente principal. Para cada componente, el valor propio (cuadrado de la desviación estándar) se divide por la suma total de los valores propios, y luego se multiplica por 100 para convertirlo en un porcentaje, permitiendo observar qué proporción de la variabilidad total del conjunto de datos es capturada por cada componente principal.
# Desviaciones estándar de los componentes principales
sdev <- sqrt(res.pca$eig[, 1])
sdev
## comp 1 comp 2 comp 3 comp 4 comp 5
## 1.7887897 0.9470205 0.6145791 0.5774628 0.4384209
# Valores propios
eigenvalues <- res.pca$eig[, 1]
eigenvalues
## comp 1 comp 2 comp 3 comp 4 comp 5
## 3.1997685 0.8968479 0.3777075 0.3334633 0.1922129
# Porcentaje de variabilidad explicada por componente
variance_explained <- res.pca$eig[, 2]
variance_explained
## comp 1 comp 2 comp 3 comp 4 comp 5
## 63.995370 17.936958 7.554149 6.669266 3.844258
Observando los resultados, se interpreta que en las desviaciones estándar la primera componente tiene un valor de 1.7887897, lo que indica que este componente explica una mayor cantidad de la variabilidad de los datos originales en comparación con los otros componentes. La suma de valores propios confirma que se están trabajando con cinco variables originales (p = 5). En cuanto al porcentaje de varianza explicada, la componente 1 explica por sí sola una gran parte de la información presente en los datos con un valor del 64%, seguido de la componente 2 con un 17.9% de la variabilidad de los datos.
Siguiendo lo anterior, las dos primeras componentes exponen ser suficientes para explicar los datos con una varianza acumulada de 81.9%. La salida previa se puede visualizar en el siguiente gráfico de variabilidad explicada.
# Gráfico de porcentaje de variabilidad explicada por componente
library(factoextra)
fviz_eig(res.pca, addlabels = TRUE)
La matriz de cargas muestra cómo cada variable original se combina para formar cada uno de los componentes principales conformada por los vectores propios. Al calcular esta matriz, se puede observar que la componente principal 1 (PC1) es una combinación de banios (0.87), areaconst (0.86), y preciom (0.84), dado que tienen los valores más altos, sugiriendo que este componente podría representar una medida general de tamaño o lujo de la propiedad. En cuanto a la componente principal 2 (PC2) se podría reflejar una dimensión donde las habitaciones (0.73) se opone a preciom (-0.36) y parqueaderos (-0.44). Esto podría sugerir una compensación entre el número de habitaciones y el costo o las facilidades como parqueaderos.
# Matriz de cargas de variables
res.pca$var$coord
## Dim.1 Dim.2 Dim.3 Dim.4 Dim.5
## preciom 0.8496444 -0.36251920 -0.1666703 -0.1708190 0.29954322
## areaconst 0.8616926 0.05225712 -0.4015429 0.2596009 -0.16163475
## parqueaderos 0.7683285 -0.44056070 0.3916520 0.2433919 -0.05428342
## banios 0.8712010 0.16578242 0.1015780 -0.3998664 -0.20811972
## habitaciones 0.6213204 0.73560816 0.1580582 0.1332619 0.17349479
Para conocer la contribución de cada una de las variables a cada una de las compoenentes, se realizan los gráficos de contribuciones. Dado que anteriormente se escogieron las dos primeras componentes para explicar los datos, se realizan las respectivas visualizaciones de ellas. Los baños, el área construida y el precio reflejan ser las variables que más contribuyen en la componente 1, donde sus valores soprepasan el promedio esperado. En el caso de la segunda componente, las variables de habitaciones y parqueadores son las que más están aportante a explicar esta segunda dimensión.
Estos resultados confirman lo expuesto en la matriz de cargas calculadas previamente.
# Contribución a la construcción de la variable a la componente
fviz_contrib(res.pca, choice = "var", axes=1)
fviz_contrib(res.pca, choice = "var", axes=2)
Al realizar el círculo de correlaciones se puede observar que las variables se encuentran posicionadas en una misma dirección (cuadrantes derecho), lo que indica que no existe una correlación negativamente en algunas de ellas y que a su vez las variables están correlacionadas positivamente entre sí. Analizando las proximidades presenten entre las variables, se destaca la cercanía entre la variable de parqueaderos y precio de la vivienda expone una correlación alta entre ellas, como también para el área construida y los baños y de igual manera se expone la no correlación lineal entre parqueaderos y habitaciones, donde su comportamiento se acerca a ser perpendicular. Finalmente, la contribución más alta viene dado por las habitaciones, al encontrarse mucho más cerca al perímetro del círculo de correlación.
# Círculo de correlación (nube de variables)
fviz_pca_var(res.pca,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE # Avoid text overlapping
)
Para analizar el comportamiento de cada una de las viviendas se realiza un gráfico de nube de individuos. En ella puede observarse como una gran cantidad de viviendas van presentando características similares al estar muy juntas entre ellas. Sin embargo, se destacan algunas que se alejan del común, como sucede con las viviendas 2926, 6933, 5179 y 1762, lo que indica que sus características no son compartidas por la mayoría del conjunto de datos y que presentan cualidades distintas.
# Nube de individuos
fviz_pca_ind(res.pca, col.ind = "blue")
Por último el gráfico biplot permite analizar que las viviendas el comportamiento de las viviendas ahora discriminado por el estrato. En el gráfico puede identificarse cómo aquellas viviendas más alejadas tienen estrato 5 y 6.
# Biplot
#fviz_pca_biplot(res.pca)
fviz_pca_biplot(res.pca,
habillage = "estrato",
col.var = "#034A94",
)
Revisando las características de estas viviendas se presenta su respectiva tabla descriptiva con las estadíisticas descriptivas generales del conjunto de datos. En ella se destaca que las cuatro observaciones presentan precios de vivienda muy por encima de la media general, con valores por encima de los $950, donde el mismo comportamiento se observa en el área construida y cantidad de parqueaderos. Lo anterior confirma lo que se vio previamente en el biplot, donde son estas variables las que se encuentran más cerca a las observaciones mencionadas. Siguiendo esto, las variables de baños y zonas muestra que estas viviendas no presentan características muy diferentes a la general, donde su promedio es cercano a lo esperado.
summary(df[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones")])
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 1.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.703 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## habitaciones
## Min. : 0.000
## 1st Qu.: 3.000
## Median : 3.000
## Mean : 3.605
## 3rd Qu.: 4.000
## Max. :10.000
df_pca_out <- rbind(df[2926, ], df[6933, ], df[5179, ], df[1762, ])
df_pca_out <- as.data.frame(df_pca_out)
rownames(df_pca_out) <- c("V2926", "V6933", "V5179", "V1762")
df_pca_out
Se realizará un análisis de clústering con base en el PCA calculado previamente, agregando de argumento, que el método determine el número de agrupamientos necesarios. Siguiendo los dendogramas generados, el método sugiere trabajar con 3 agrupamientos.
# Análisis de Clústering
res.hcpc <- HCPC(res.pca, graph = FALSE)
library(ggsci)
Cluster.Calificaciones <- HCPC(res.pca,nb.clust = -1)
Sin embargo, para comprobar esta cantidad, se realiza el gráfico del codo para determinar el número óptimo de agrupamientos. El gráfico demuestra que con la cantidad de clúster k=3, sigue habiendo una caída en el WSS significativa. No obstante, el análisis de PCA previo demostró en el gráfico de biplot que todas las variables se encontraban en la misma dirección hacia los cuadrantes derechos (primer y cuarto cuadrante) y por otro lado, en la nube de clúster puede observarse que el agrupamiento del clúster 3 se encuentra ubicado justo donde se ubican las variables, por lo que puede inferirse que los tres clúster realizados son coherentes con los análisis hechos.
k_values <- 2:10
ss <- numeric(length(k_values))
for (i in 1:length(k_values)) {
kmeans_model <- kmeans(df_pca, centers = k_values[i])
ss[i] <- kmeans_model$tot.withinss
}
# Gráfico del codo
plot(k_values, ss, type = "b", pch = 19, frame = FALSE,
xlab = "Número de clusters (K)", ylab = "Total within-clusters sum of squares (WSS)",
main = "Gráfico del codo")
De igual manera, esperando que quizás si se generan cuatro agrupamientos este último adicional pueda generar un corte dentro del clúster 3 siendo más específico y a su vez, dividir las viviendas por determinadas características, se realiza nuevamente el análisis de conglomerados pero ahora con 4 agrupamientos para identificar si se identifica mejora en el comportamiento de las agrupaciones.
# Análisis de Clústering con cuatro agrupaciones
res.HCPC2 <- HCPC(res.pca, nb.clust=4)
Los gráficos anteriores donde se tiene un clúster de más, dividió las viviendas en unas coordenadas en las que no se esperaba, pareciendo que creó una nueva agrupación con todas las viviendas con las características promedio generales, pues se encuentra alrededor del origen del cuadrante.
Por lo tanto, puede decirse que la agrupación no fue muy significativa y se opta por trabajar con las tres agrupaciones iniciales.
Dada las tres agrupaciones realizadas, se puede conocer qué viviendas son las que quedaron perteneciendo a cada clúster. En la siguiente tabla puede observarse, que entre los primeros 10 registros de viviendas, las viviendas 2, 3, 5, 6, 7 y 8 hacen parte del clúster 2.
head(res.hcpc$data.clust, 10)
Ahora, para conocer el comportamiento de las viviendas clasificadas en cada clúster se presenta la siguiente salida. El indicador de v.test, que compara la media de cada variable en el clúster con la media global, está explicando que en cada uno de las tres agrupaciones, las medias en cada una de ellas sí son muy diferentes a una media general, pues llegan a ser muy distintos de cero, a excepción del número de parqueaderos en el clúster 2, donde el v.test es cercano a la nulidad. De igual manera, se observa que todos los valores-p en cada una de las agrupaciones son significativas (< 0.05), lo que indica que sí existen diferencias estadísticamente significativas entre las medias de los clúster en comparación con el general.
res.hcpc$desc.var$quanti
## $`1`
## v.test Mean in category Overall mean sd in category Overall sd
## estrato -31.81742 4.3317604 4.633610e+00 0.9179002 1.0291603
## parqueaderos -48.07800 -0.4431622 -2.232057e-17 0.4128422 0.9999399
## habitaciones -53.58298 -0.4939047 -5.074009e-17 0.4803775 0.9999399
## preciom -58.82679 -0.5422398 -3.542222e-17 0.3617730 0.9999399
## areaconst -59.81342 -0.5513342 1.091881e-17 0.3125754 0.9999399
## banios -68.92504 -0.6353212 1.037623e-16 0.4686194 0.9999399
## p.value
## estrato 3.717172e-222
## parqueaderos 0.000000e+00
## habitaciones 0.000000e+00
## preciom 0.000000e+00
## areaconst 0.000000e+00
## banios 0.000000e+00
##
## $`2`
## v.test Mean in category Overall mean sd in category Overall sd
## habitaciones 43.19069 0.7171164 -5.074009e-17 1.1235776 0.9999399
## banios 42.10080 0.6990204 1.037623e-16 0.7194028 0.9999399
## areaconst 25.94833 0.4308330 1.091881e-17 0.6849839 0.9999399
## preciom 16.74692 0.2780574 -3.542222e-17 0.5893178 0.9999399
## estrato 12.47513 4.8467933 4.633610e+00 1.0572880 1.0291603
## parqueaderos 8.43524 0.1400545 -2.232057e-17 0.6405141 0.9999399
## p.value
## habitaciones 0.000000e+00
## banios 0.000000e+00
## areaconst 1.899032e-148
## preciom 5.963857e-63
## estrato 1.020418e-35
## parqueaderos 3.305244e-17
##
## $`3`
## v.test Mean in category Overall mean sd in category Overall sd
## preciom 67.86979 2.1115387 -3.542222e-17 1.0825560 0.9999399
## parqueaderos 63.17228 1.9653917 -2.232057e-17 1.4357182 0.9999399
## areaconst 55.92273 1.7398463 1.091881e-17 1.4470333 0.9999399
## banios 46.54623 1.4481283 1.037623e-16 0.9397198 0.9999399
## estrato 31.69572 5.6485310 4.633610e+00 0.6391945 1.0291603
## habitaciones 20.84025 0.6483738 -5.074009e-17 1.1088986 0.9999399
## p.value
## preciom 0.000000e+00
## parqueaderos 0.000000e+00
## areaconst 0.000000e+00
## banios 0.000000e+00
## estrato 1.779501e-220
## habitaciones 1.868662e-96
Y a nivel de las características de las viviendas por clúster, se observa que el mínimo del precio de la vivienda sí varía en cada agrupación con un $58 en el clúster 1 y $127 y $190 en los clúster 2 y 3, igual comportamiento que sucede con el área construida donde la mínima área es mayor en el clúster 2. Sin embargo, esto no se describe de la misma manera en las demás variables, pues los mínimos son similares. Con respecto a la mediana como mejor indicador estadístico en estos datos, sí se encuentran diferencias en cada uno de las agrupaciones y por variable, infieriendo así que las tres agrupaciones realizadas no tienen similitudes entre clústers.
library(dplyr)
# Extraer la columna de clusters
clusters <- res.hcpc$data.clust$clust
df_pca_cluster <- as.data.frame(cbind(df, clusters))
# Calcular las estadísticas descriptivas por cluster
df_pca_cluster %>%
group_by(clusters) %>%
summarise(
preciom_min = min(preciom, na.rm = TRUE),
preciom_median = median(preciom, na.rm = TRUE),
preciom_mean = mean(preciom, na.rm = TRUE),
preciom_var = var(preciom, na.rm = TRUE),
preciom_sd = sd(preciom, na.rm = TRUE),
preciom_max = max(preciom, na.rm = TRUE)
)
df_pca_cluster %>%
group_by(clusters) %>%
summarise(
areaconst_min = min(areaconst, na.rm = TRUE),
areaconst_median = median(areaconst, na.rm = TRUE),
areaconst_mean = mean(areaconst, na.rm = TRUE),
areaconst_var = var(areaconst, na.rm = TRUE),
areaconst_sd = sd(areaconst, na.rm = TRUE),
areaconst_max = max(areaconst, na.rm = TRUE),
)
df_pca_cluster %>%
group_by(clusters) %>%
summarise(
parqueaderos_min = min(parqueaderos, na.rm = TRUE),
parqueaderos_median = median(parqueaderos, na.rm = TRUE),
parqueaderos_mean = mean(parqueaderos, na.rm = TRUE),
parqueaderos_var = var(parqueaderos, na.rm = TRUE),
parqueaderos_sd = sd(parqueaderos, na.rm = TRUE),
parqueaderos_max = max(parqueaderos, na.rm = TRUE)
)
df_pca_cluster %>%
group_by(clusters) %>%
summarise(
banios_min = min(banios, na.rm = TRUE),
banios_median = median(banios, na.rm = TRUE),
banios_mean = mean(banios, na.rm = TRUE),
banios_var = var(banios, na.rm = TRUE),
banios_sd = sd(banios, na.rm = TRUE),
banios_max = max(banios, na.rm = TRUE)
)
df_pca_cluster %>%
group_by(clusters) %>%
summarise(
habitaciones_min = min(habitaciones, na.rm = TRUE),
habitaciones_median = median(habitaciones, na.rm = TRUE),
habitaciones_mean = mean(habitaciones, na.rm = TRUE),
habitaciones_var = var(habitaciones, na.rm = TRUE),
habitaciones_sd = sd(habitaciones, na.rm = TRUE),
habitaciones_max = max(habitaciones, na.rm = TRUE)
)
Para el análisis de los estratos en los clústers realizados, el gráfico georreferencial permite observar que a pesar de que en el análisis de conglomerados con base en un ACP las viviendas se encuentren agrupadas, la ubicación de las viviendas en cada una es muy variante. El clúster 1 que se caracteriza por precios bajos, menor tamaño y por tanto menor cantidad de baños, se encuentra por todas las zonas de la ciudad, y con más frecuencia en la comuna 17 de la zona sur, donde se destaca el clúster rojo en el mapa. En su mayoría la comuna 17 es reconocida por estratos altos, sin embargo, la aglomeración de viviendas en el mapa en esta zona es de estrato medio-bajo. Este mismo comportamiento lo tiene el clúster 2, caracterizado por tener condiciones medias de precio y áreas construidas. Finalmente el clúster 3, que se caracteriza por precios caros, áreas grandes, y con una cantidad de baños y parqueaderos superior a las demás agrupaciones mencionadas, se ubica en su mayoría entre las comunas 22 y comuna 2; donde la primera es en su mayoría una zona de estrato 6 y en la segunda, a pesar de que hayan barrios con recursos más bajos, sí hay existencia de viviendas con precios muy altos.
# Crear un gráfico para cada clúster
# Clúster 1
ggplot() +
geom_sf(data = mapa_cali, aes(fill = zona_com), color = "black", alpha = 0.3) +
geom_sf(data = sf_objeto %>% filter(clusters == 1), aes(color = as.factor(clusters)), size = 2, alpha = 0.6) +
scale_fill_manual(values = c("Zona 1" = "lightblue", "Zona 2" = "lightgreen", "Zona 3" = "lightpink")) +
scale_color_manual(values = c("1" = "red")) +
ggtitle("Distribución de Viviendas del Clúster 1") +
labs(color = "Clúster") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
# Clúster 2
ggplot() +
geom_sf(data = mapa_cali, aes(fill = zona_com), color = "black", alpha = 0.3) +
geom_sf(data = sf_objeto %>% filter(clusters == 2), aes(color = as.factor(clusters)), size = 2, alpha = 0.6) +
scale_fill_manual(values = c("Zona 1" = "lightblue", "Zona 2" = "lightgreen", "Zona 3" = "lightpink")) +
scale_color_manual(values = c("2" = "blue")) +
ggtitle("Distribución de Viviendas del Clúster 2") +
labs(color = "Clúster") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
# Clúster 3
ggplot() +
geom_sf(data = mapa_cali, aes(fill = zona_com), color = "black", alpha = 0.3) +
geom_sf(data = sf_objeto %>% filter(clusters == 3), aes(color = as.factor(clusters)), size = 2, alpha = 0.6) +
scale_fill_manual(values = c("Zona 1" = "lightblue", "Zona 2" = "lightgreen", "Zona 3" = "lightpink")) +
scale_color_manual(values = c("3" = "green")) +
ggtitle("Distribución de Viviendas del Clúster 3") +
labs(color = "Clúster") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
La zona, el tipo de vivienda y el estrato son las variables cualitativas aptas para realizar este análisis, al ser de naturaleza cualitativa y de pocas categorías cada una. Se agrega como variable suplementaria el precio para conocer su comportamiento con estas variables.
# Conjunto de datos para ACM
df_mca <- df[, c("tipo_modif", "zona", "estrato", "preciom")]
df_mca$estrato <- as.character(df_mca$estrato)
head(df_mca)
# Análisis de Componentes Múltiples
res.mca <- MCA(df_mca, graph = FALSE, quanti.sup = "preciom")
Al graficar el porcentaje de varianza explicada del conjunto de datos, se tiene que las dos primeras componentes tiener un valor acumulado de 38.1%, el cual es bajo debido a que se estpan analizando variables cualitativas. Si se incluyese la tercera componente, se tendría un porcentaje acumulado del 52.3%, sin embargo la cantidad de variables a analizar son tres y el objetivo es reducir la dimensión por lo que se escoje trabajar con dos componentes en el ACM.
# Gráfico de porcentaje de variabilidad explicada por componente
fviz_screeplot(res.mca, addlabels = TRUE, ylim = c(0, 25))
Luego, en el gráfico de variables se observa cómo las variables zona y estrato están altamente correlacionadas con la componente 1, al tener coordenadas muy altas en el eje x. En el caso de la componente 2, el tipo de vivienda es la que está muy correlacionada al eje, pues su coordenada está en valores muy bajos.
# Gráfico de variables
fviz_mca_var(res.mca, choice = "mca.cor",
repel = TRUE,
ggtheme = theme_minimal())
Al analizar el gráfico de las categorías de las variables, se tiene una cercanía entre las viviendas en la zona oriente y la de centro, lo que determina que ambas tienen perfiles muy similares entre ellas. Al igual que sucede con que la vivienda se encuentre ubicada en la zona oeste y que es de estrato 6. También se destaca que la zona sur de estrato 5 y 4 tienen perfiles muy diferentes a la zonas de centro y oriente al estar en cuadrantes opuestos.
fviz_mca_var(res.mca,
repel = TRUE, # Avoid text overlapping (slow)
ggtheme = theme_minimal())
Analizando las contribuciones de las categorías a las dimensiones, la zona de centro y oriente contribuyen en gran medida el eje positivo de la primera componente, y el tipo de vivienda de apartamento y la zona sur al eje negativa de esta misma. En cuanto a la segunda componente, las categorías que más contribuyen son la zona oeste y el estrato 4.
# Color by cos2 values: quality on the factor map
fviz_mca_var(res.mca, col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE, # Avoid text overlapping
ggtheme = theme_minimal())
En un biplot se puede analizar el comportamiento de las viviendas y de sus características, por tanto puede observarse que las viviendas no siguen un patrón homogéneo, sino que a su vez tienden a formarse en pequeños grupos alrededor del gráfico exponiendo que hay diferentes perfiles en las viviendas.
# Biplot
fviz_mca_biplot(res.mca,
ggtheme = theme_minimal()
)
Con el gráfico de círculo de correlación de la variable suplementaria precio, puede observarse que este vector no tiene una longitud tan larga o cerca al límite del círculo, por lo que esta variable no está bien representada en el análisis, pues tiene baja correlación en ambas componentes.
# Gráfico de la variable suplementaria precio
fviz_mca_var(res.mca, choice = "quanti.sup",
ggtheme = theme_minimal())
# Agregar los resultados de clustering al conjunto de datos original
#data_limpieza_df$cluster <- as.factor(res.HCPC2$clustering)
El desarrollo de cada una de las metodologías permitió conocer el comportamiento de las viviendas en distintos aspectos.
Se observó que las dos primeras componentes principales (PC1 y PC2) eran suficientes para explicar el 81.9% de la variabilidad total de los datos, donde:
PC1: se explica por un 64% de la variabilidad y está fuertemente influenciada por los baños, el área construida, y el precio. Esto sugiere que PC1 refleja una combinación de características relacionadas con el tamaño y lujo de la vivienda.
PC2: se explica por un 17.9% de la variabilidad y muestra una relación opuesta entre habitaciones y las variables precio y parqueaderos. Esto podría indicar una compensación entre el número de habitaciones y el costo o facilidades como los parqueaderos.
Entre los gráficos, el círculo de correlaciones destaca una fuerte correlación positiva entre las variables precio y parqueaderos, así como entre área y baños. En cambio, no se observó correlación lineal entre parqueaderos y habitaciones, mostrando un comportamiento cercano a la perpendicularidad en el gráfico. El biplot permitió observar cómo las viviendas con estratos 5 y 6 tienden a alejarse de las demás en términos de características, mostrando precios significativamente más altos, grandes áreas construidas y un mayor número de parqueaderos.
Por esto, el ACP permitió ver que las características clave que parecen influir en la variación de precios y la oferta en el mercado incluyen el tamaño de la propiedad, el número de baños, y el número de parqueaderos. Estas variables, especialmente en combinación con preciom, ayudan a diferenciar las propiedades de mayor lujo y costo dentro del mercado analizado.
El análisis de conglomerados realizado sobre las viviendas de la ciudad, utilizando un análisis de componentes principales, reveló tres agrupaciones principales. Estas agrupaciones reflejan características distintivas en términos de precio, tamaño, número de baños y parqueaderos, las cuales están relacionadas con la ubicación geográfica y el estrato socioeconómico de las viviendas.
Clúster 1: Viviendas de bajo costo y tamaño reducido. El primer clúster agrupa viviendas con precios más bajos, menores áreas construidas y menor cantidad de baños. Estas viviendas se encuentran distribuidas en varias zonas de la ciudad, pero con una concentración en la comuna 17, ubicada en la zona sur. A pesar de que la comuna 17 es reconocida por tener viviendas de estrato alto, la aglomeración de viviendas pertenecientes a este clúster sugiere la presencia de propiedades de estrato medio-bajo, lo que indica una oferta de viviendas más accesibles en términos de precio dentro de áreas típicamente más costosas.
Clúster 2: Viviendas de condiciones medias. El segundo clúster agrupa viviendas con condiciones medias en cuanto a precio y tamaño. Estas propiedades están distribuidas de manera similar al primer clúster, pero suelen tener precios y áreas construidas más elevadas. Este clúster representa una oferta inmobiliaria que satisface las necesidades de aquellos que buscan un equilibrio entre costo y espacio. Las viviendas de este grupo están también presentes en diversas zonas de la ciudad, incluyendo áreas que combinan tanto viviendas de estrato medio como de estrato alto.
Clúster 3: Viviendas de alto Costo y gran tamaño. El tercer clúster agrupa viviendas con precios altos, grandes áreas construidas, y un número superior de baños y parqueaderos. Estas viviendas se concentran principalmente en las comunas 22 y 2. La comuna 22 es conocida por ser una zona sur de estrato 6, con una fuerte presencia de viviendas de lujo, mientras que en la comuna 2, aunque hay barrios con recursos más bajos, existen viviendas con precios muy elevados. Este clúster destaca la oferta de viviendas de lujo en áreas exclusivas de la ciudad.
Relación entre las variables categóricas zona y estrato:
Se observa que las variables de zona y estrato están altamente correlacionadas con la primera componente del ACM. Esto indica que la ubicación geográfica y el nivel socioeconómico de las viviendas están fuertemente relacionados. Por ejemplo, las zonas centro y oriente tienden a agruparse en el gráfico, lo que sugiere que tienen perfiles similares en términos de oferta inmobiliaria. Por otro lado, la zona sur, asociada con estratos 4 y 5, presenta un perfil diferente al de las zonas centro y oriente, lo que podría reflejar una oferta diferenciada en términos de características de las viviendas y precios. Tipo de Vivienda:
El tipo de vivienda (Casa o Apartamento) muestra una fuerte correlación con la segunda componente del ACM. Esto significa que las características que distinguen entre casas y apartamentos influyen de manera significativa en la distribución de la oferta inmobiliaria. Los apartamentos parecen agruparse en zonas de estrato alto (como la zona norte), mientras que las casas están más dispersas, pero con una concentración notable en estratos medios y bajos en zonas como el oriente y el sur.
Identificación de patrones de comportamiento en la oferta:
Las zonas centro y oriente muestran perfiles similares, lo que sugiere que la oferta en estas áreas es homogénea en términos de estrato y tipo de vivienda. Esto podría indicar que estas zonas compiten directamente entre sí en el mercado. En contraste, la zona sur, asociada con estratos más bajos (4 y 5), se distingue claramente de las zonas centro y oriente, sugiriendo una oferta diferenciada que podría atraer a un segmento de mercado diferente.
Aunque el precio se incluyó como una variable suplementaria, el análisis muestra que su correlación con las componentes es baja. Esto sugiere que, aunque el precio es un factor importante en el mercado inmobiliario, no parece estar fuertemente alineado con las categorías de zona, estrato, y tipo de vivienda en las dos primeras dimensiones del ACM. Esto podría implicar que otros factores no considerados en el ACM, como el estado de la vivienda, la infraestructura cercana, o factores macroeconómicos, podrían estar influyendo en la fijación de precios.
El análisis de conglomerados y el análisis de correspondencia múltiple realizados proporcionan una comprensión detallada del comportamiento de las viviendas en función de sus características y ubicación. Los resultados indican que las viviendas se agrupan en tres principales clústeres, que reflejan distintos perfiles de mercado en términos de precio, área construida, número de baños y parqueaderos, así como la ubicación y estrato socioeconómico.
El clúster 1 agrupa viviendas de menor precio y tamaño, distribuidas en diversas zonas de la ciudad, pero especialmente en áreas con estrato medio-bajo. El clúster 2 incluye viviendas de precio y tamaño intermedios, mientras que el clúster 3 está compuesto por propiedades de alto precio, gran tamaño, y ubicadas predominantemente en zonas de estrato alto.
Por otro lado, el análisis de correspondencia múltiple reveló que la ubicación y el estrato están altamente correlacionados, influyendo significativamente en la estructura del mercado. Las zonas oriente y centro presentan perfiles similares, mientras que la zona sur muestra un perfil distinto, especialmente en los estratos 4 y 5. El tipo de vivienda también influye en la segmentación del mercado, con diferencias claras entre casas y apartamentos.
Entre las recomendaciones específicas para guiar las decisiones estratégicas se encuentran:
Desarrollar estrategias de marketing y ventas diferenciadas para cada uno de los tres clústeres identificados. Para el clúster 1, enfocar esfuerzos en promociones dirigidas a compradores de estratos medio-bajos, resaltando las ventajas de precio y accesibilidad. Para el clúster 3, enfatizar el lujo, exclusividad y la alta calidad de vida en zonas de estrato alto. También de considerar la creación de productos inmobiliarios específicos que atiendan a las características predominantes en cada clúster, como viviendas más compactas y asequibles en zonas de alta demanda de estrato medio-bajo.
Priorizar la inversión en la zona sur y oriente, especialmente en áreas donde se observe un alto grado de homogeneidad y fuerte demanda. El análisis indica que estas zonas tienen características demográficas y de mercado que pueden ser capitalizadas con una oferta adecuada. Además de explorar oportunidades para desarrollar viviendas de estrato medio-alto en la zona oeste, aprovechando la diferencia marcada que estas áreas presentan en comparación con el centro y oriente.
Ajustar los precios y características de las viviendas de acuerdo a la demanda en cada zona, usando los datos obtenidos para identificar las conjunto de características más valoradas por los compradores en cada clúster. En el caso de desarrollos futuros, enfocarse en aumentar la oferta de viviendas con características que son clave para cada clúster, como un mayor número de parqueaderos en el clúster 3, o una mayor flexibilidad en áreas construidas para el clúster 2.
Implementar un sistema de monitorización continua del mercado inmobiliario para actualizar las estrategias basadas en los cambios en la demanda y las dinámicas del mercado. Esto asegurará que la empresa mantenga su ventaja competitiva adaptándose rápidamente a las tendencias emergentes. Y finalmente, realizar análisis periódicos similares para identificar nuevos patrones de comportamiento o cambios en las preferencias de los consumidores, lo que permitirá ajustar las estrategias a tiempo.
A continuación se presenta un breve análisis exploratorio y preprocesamiento de los datos que fueron neecesarios para el desarrollo del ejercicio.
library(paqueteMODELOS)
data("vivienda")
head(vivienda)
library(dplyr)
data_limpieza = vivienda
#----------------------------------------------------------------------------------------------------
# Análsisis exploratorio
# Para conocer categorías y datos faltantes
#----------------------------------------------------------------------------------------------------
summarytools::freq(data_limpieza$zona, cumul = F)
summarytools::freq(data_limpieza$piso, cumul = F) ## para imputar NA´s 2638 (31.7%)
summarytools::freq(data_limpieza$estrato, cumul = F)
summarytools::freq(data_limpieza$preciom, cumul = F)
summarytools::freq(data_limpieza$areaconst, cumul = F)
summarytools::freq(data_limpieza$parqueaderos, cumul = F) ## para imputar NA´s 1605 (19.29%)
summarytools::freq(data_limpieza$banios, cumul = F)
summarytools::freq(data_limpieza$habitaciones, cumul = F)
summarytools::freq(data_limpieza$tipo, cumul = F)
summarytools::freq(data_limpieza$longitud, cumul = F)
summarytools::freq(data_limpieza$latitud, cumul = F)
#----------------------------------------------------------------------------------------------------
# Tipo
# Para definir como Apartamento aquellas casas que tienen más de 3 pisos
#----------------------------------------------------------------------------------------------------
data_limpieza$tipo_modif <- data_limpieza$tipo
casas_p4 <- data_limpieza %>% filter(tipo=="Casa", piso>3)
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="04")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="05")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="06")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="07")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="08")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="09")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="10")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="11")] <- "Apartamento"
data_limpieza$tipo_modif[which(data_limpieza$tipo=="Casa" & data_limpieza$piso=="12")] <- "Apartamento"
table(data_limpieza$piso, data_limpieza$tipo_modif)
#----------------------------------------------------------------------------------------------------
# Barrio
# Para estandarizar los nombres en caso que sean incongruentes
#----------------------------------------------------------------------------------------------------
library(stringdist) #función creada por la profesora J.P.
limpiar_barrios <- function(nombres) {
nombres <- tolower(trimws(iconv(nombres, from = "UTF-8", to = "ASCII//TRANSLIT")))
nombres <- gsub("[_ ]", "", nombres)
return(nombres)
}
data_limpieza$barrio2 <- limpiar_barrios(data_limpieza$barrio)
#----------------------------------------------------------------------------------------------------
# Georreferenciación
#----------------------------------------------------------------------------------------------------
#Visualización de la ubicación de las viviendas en Cali
library(ggplot2)
ggplot(data_limpieza, aes(longitud, latitud, colour=factor(zona))) +
geom_point() +
labs(title="Distribución de los barrios en Cali por zona") +
scale_colour_discrete(name ="Zona") +
theme_minimal()
#---------------------------------------------------------------
# Cambiar el formato a coordenadas de longitud y latitud
data_limpieza <- data_limpieza %>% filter(is.na(longitud)==FALSE, is.na(latitud)==FALSE)
data_limpieza <- as.data.frame(data_limpieza)
library(sp)
coordinates(data_limpieza) <- c("longitud", "latitud")
proj4string(data_limpieza) <- CRS("+proj=longlat +datum=WGS84")
library(sf)
sf_objeto <- st_as_sf(data_limpieza)
#---------------------------------------------------------------
mapa_cali <- st_read("C:/Users/Usuario/Documents/Universidad Javeriana/SEMESTRE 1/METODOS Y SIMULACION ESTADISTICA/UNIDAD 1/Comunas/bcs_lim_comunas.shp")
mapa_cali$zona_alcaldia <- c()
mapa_cali$zona_alcaldia[mapa_cali$NOMBRE %in% c("Comuna 2", "Comuna 4", "Comuna 5", "Comuna 6")] <- "Zona Norte"
mapa_cali$zona_alcaldia[mapa_cali$NOMBRE %in% c("Comuna 7", "Comuna 13", "Comuna 14",
"Comuna 15", "Comuna 16","Comuna 21")] <- "Zona Oriente"
mapa_cali$zona_alcaldia[mapa_cali$NOMBRE %in% c("Comuna 22", "Comuna 17")] <- "Zona Sur"
mapa_cali$zona_alcaldia[mapa_cali$NOMBRE %in% c("Comuna 18", "Comuna 19", "Comuna 20",
"Comuna 1")] <- "Zona Oeste"
mapa_cali$zona_alcaldia[mapa_cali$NOMBRE %in% c("Comuna 3", "Comuna 9", "Comuna 10",
"Comuna 11", "Comuna 12", "Comuna 8")] <- "Zona Centro"
# Visualización de mapa por zonas
ggplot() +
geom_sf(data = mapa_cali, aes(fill = zona_alcaldia)) +
geom_sf(data = sf_objeto, aes(color = zona)) +
scale_color_manual(values = c("blue", "red", "green", "orange", "purple")) +
scale_fill_manual(values = c("blue", "red", "green", "orange", "purple")) +
ggtitle("Distribución de viviendas por zona") +
guides(fill=guide_legend(title="Zona")) +
theme(plot.title = element_text(hjust = 0.5)) +
theme_minimal()
#---------------------------------------------------------------
# Limpieza de las coordenadas asignando la verdadera zona según ubicación de la vivienda
library(sf)
sf_objeto <- st_transform(sf_objeto, st_crs(mapa_cali))
out <- st_intersection(sf_objeto, mapa_cali)
ggplot() +
geom_sf(data = mapa_cali, aes(fill = zona_alcaldia)) +
geom_sf(data = out, aes(color = zona_alcaldia)) +
scale_color_manual(values = c("blue", "red", "green", "orange", "purple")) +
scale_fill_manual(values = c("blue", "red", "green", "orange", "purple")) +
ggtitle("Distribución de viviendas por zona ajustado") +
guides(fill=guide_legend(title="Zona")) +
theme(plot.title = element_text(hjust = 0.5)) +
theme_minimal()
#----------------------------------------------------------------------------------------------------
# Eliminación de datos faltantes
# Aquellas variables que tuvieron sólamente 3 valores faltantes
#----------------------------------------------------------------------------------------------------
data_limpieza_df <- as.data.frame(data_limpieza)
data_limpieza_df <- data_limpieza_df %>%
filter(
!is.na(zona),
!is.na(estrato),
!is.na(preciom),
!is.na(areaconst),
!is.na(banios),
!is.na(habitaciones),
!is.na(tipo),
!is.na(longitud),
!is.na(latitud)
)
#----------------------------------------------------------------------------------------------------
# Imputación de datos faltantes
# Variables como piso y parqueaderos que tienen más del 20% de valores faltantes
# Se realiza imputación por la moda teniendo en cuenta la comuna, estrato y tipo de vivienda
#----------------------------------------------------------------------------------------------------
library(dplyr)
library(modeest)
calcular_moda <- function(x) {
mfv(x, na_rm = TRUE)[1]
}
data_limpieza_df <- data_limpieza_df %>%
group_by(estrato, tipo) %>%
mutate(
piso = ifelse(is.na(piso), calcular_moda(piso), piso),
parqueaderos = ifelse(is.na(parqueaderos), calcular_moda(parqueaderos), parqueaderos)
) %>%
ungroup()
#----------------------------------------------------------------------------------------------------
# Base de datos final
#----------------------------------------------------------------------------------------------------
df <- data_limpieza_df %>% select(-tipo, -barrio)