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.
Realizar un análisis integral y multidimensional de la base de datos de viviendas para obtener una comprensión del mercado inmobiliario en Cali aplicando diversas técnicas de análisis tales como: Análisis de Componentes principales, Análisis de Conglomerados y Análisis de Correspondencia.
Inicialmente se identifican registros cuyo ID no está definido, además de otras de sus características, por lo tanto se decide eliminar dichos registros
vivienda <- vivienda %>% filter(!is.na(id))
colSums(is.na(vivienda))
## id zona piso estrato preciom areaconst
## 0 0 2635 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 1602 0 0 0 0 0
## latitud
## 0
Observamos que los únicos valores en NA restantes son en las columnas de piso y parqueadero, asumiendo que estos valores no se completaron porque se asumía que la vivienda se encontraba a nivel del suelo en el caso del piso y no contaba con parqueadero en el caso de la respectiva columna, se reemplazan los valores por 1 para piso y 0 para parqueaderos.
vivienda$piso <- ifelse(is.na(vivienda$piso), 1, vivienda$piso)
vivienda$parqueaderos <- ifelse(is.na(vivienda$parqueaderos), 0, vivienda$parqueaderos)
vivienda <- na.omit(vivienda)
colSums(is.na(vivienda))
## id zona piso estrato preciom areaconst
## 0 0 0 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 0 0 0 0 0 0
## latitud
## 0
Se identifica que la variable piso es de tipo character, así que se transforma a variable numérica para poder aplicar análisis cuantivativos
Para el preciom realizamos un diagrama de caja
Podemos observar que entre 1000 y 2000 millones de pesos se encuentran varias viviendas, estos valores parecen representa una variación normal del precio de las viviendas y realmente no se trata de valores aislados, por lo tanto se mantendrán para el análisis.
A continuación realizamos un diagrama de caja para el área construida.
Se puede observar que la cantidad de datos por fuera del rango intercuartílico es significativo, por lo tanto se propone eliminar los registros que se encuentren alejados 1.5 veces este rango.
Q1 <- quantile(vivienda$areaconst, 0.25, na.rm = TRUE)
Q3 <- quantile(vivienda$areaconst, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
limite_inferior <- Q1 - 1.5 * IQR
limite_superior <- Q3 + 1.5 * IQR
# Filtrar las observaciones que no son outliers
vivienda <- subset(vivienda, areaconst >= limite_inferior & areaconst <= limite_superior)
# Verificar los resultados con un nuevo boxplot
boxplot(vivienda$areaconst, main = "Diagrama de caja sin valores atípicos",
ylab = "Área construida en metros cuadrados", col = "blue")
Realizamos un resumen de los datos agrupándolos por zona y calculando el promedio de sus características.
data_resumen <- vivienda %>%
group_by(zona) %>%
summarise(
`Precio` = round(mean(preciom, na.rm = TRUE)),
`Area(m^2)` = round(mean(areaconst, na.rm = TRUE)),
`Precio_x_metro_cuadrado` = round(mean(preciom, na.rm = TRUE) / mean(areaconst, na.rm = TRUE)),
Habitaciones = round(mean(habitaciones, na.rm = TRUE)),
Banios = round(mean(banios, na.rm = TRUE))
)
data_resumen <- data_resumen %>%
arrange(desc(Precio))
kable(data_resumen, "html") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
| zona | Precio | Area(m^2) | Precio_x_metro_cuadrado | Habitaciones | Banios |
|---|---|---|---|---|---|
| Zona Oeste | 649 | 178 | 4 | 3 | 3 |
| Zona Sur | 392 | 150 | 3 | 4 | 3 |
| Zona Norte | 323 | 142 | 2 | 3 | 3 |
| Zona Centro | 303 | 182 | 2 | 5 | 3 |
| Zona Oriente | 223 | 173 | 1 | 5 | 3 |
Posteriormente graficamos la distribución los tipos de vivienda por zona, esto nos permite evidenciar que el set de datos cuenta en su mayoría con departamentos y la mayoría de viviendas se encuentran ubicadas en la zona sur de la ciudad.
ggplot(vivienda, aes(x = zona, fill = tipo)) +
geom_bar() +
labs(title = "Distribución de Tipos de Vivienda por Zona",
x = "Zona",
y = "Cantidad de Viviendas",
fill = "Tipo de Vivienda") +
theme_minimal()
A continuación creamos una matriz de correlación entre las variables numéricas.
# Columnas numéricas sin incluir ID
# vivienda <- vivienda[, !names(vivienda) %in% c("id")]
variables_numericas <- vivienda[, sapply(vivienda, is.numeric)]
# Calcular la matriz de correlación
matriz_correlacion <- cor(variables_numericas, use = "complete.obs")
corrplot(matriz_correlacion, method = "color", type = "full",
tl.col = "black", tl.srt = 45,
addCoef.col = "black",
title = "Matriz de Correlación", mar = c(0, 0, 1, 0))
En el diagrama de correlación podemos darnos cuenta de que ninguna variable numérica se encuentra altamente correlacionada con las demás, por lo tanto no es necesario descargar ninguna.
El objetivo es reducir la dimensionalidad del conjunto de datos para identificar características clave que influyen en la variación de precios y la oferta del mercado inmobiliario.
Para dicho análisis, primero tomamos las 6 variables numéricas: “Estrato”, “Precio”, “Área”, “Parqueaderos”, “Baños” y “Habitaciones”, dejando por fuera “Piso” por ser una variable categórica, “longitud” y “latitud”.
Las variables seleccionadas también serán escaladas para un mejor funcionamiento del análisis por componentes principales debido a su sensibiliad a valores grandes.
variables_seleccionadas <- variables_numericas[, c("estrato", "preciom", "areaconst", "parqueaderos", "banios", "habitaciones")]
variables_numericas <- scale(variables_seleccionadas)
head(variables_numericas)
## estrato preciom areaconst parqueaderos banios habitaciones
## [1,] -1.5654629 -0.52834912 -0.8586851 -0.3568686 -0.001244741 1.8003312
## [2,] -1.5654629 -0.28907057 -0.3444747 -0.3568686 -0.761206950 -0.3782370
## [3,] -1.5654629 -0.18652262 0.6839461 0.5205993 -0.761206950 0.3479524
## [4,] -0.5906411 -0.01560937 1.3009986 1.3980672 1.518679676 -0.3782370
## [5,] 0.3841808 -0.49416647 -0.6530010 -0.3568686 -0.761206950 -0.3782370
## [6,] 0.3841808 -0.56253177 -0.6838536 -0.3568686 -0.001244741 -0.3782370
Realizamos el cálculo de los componentes principales y graficamos el porcentaje de la varianza explicada por cada componente:
pca <- PCA(variables_numericas, scale.unit = TRUE, ncp = 5, graph = FALSE)
# Gráfico de la varianza explicada por cada componente principal
fviz_eig(pca, addlabels = TRUE, ylim = c(0, 60))
Podemos observar que la primera componente principal explica el 56.9% de la varianza y en conjunto con la segunda explican aproximadamente el 78% de la varianza total.
A continuación graficamos la contribución de las variables al primer y segundo componente principal:
fviz_contrib(pca, choice = "var", axes = 1, top = 10)
fviz_contrib(pca, choice = "var", axes = 2, top = 10)
Esto permite ver qué variables son las más importantes en los primeros componentes, que son los que explican más varianza, el precio por metro cuadrado, la cantidad de baños y el área construida son las variables con mayor peso para el cálculo del primer componente principal. Mientras que el segundo es mayormente explicado por la cantidad de habitaciones y el estrato.
Gráfico de las variables en el plano de los dos primeros componentes principales
fviz_pca_var(pca, col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE)
En este gráfico, las variables que están más alineadas con un eje contribuyen más a ese componente, confirmando las suposiciones mencionadas en el anterior gráfico, por otro lado se puede apreciar que el número de habitaciones y el estrato socieconómico se encuentran ligeramente correlacionadas negativamente, mientras que el resto de variables que componen en su mayoría el primer componente se encuentran positivamente correlacionadas.
Posteriormente se realiza un gráfico de la distribución de los individuos para ver cómo las observaciones (viviendas) se distribuyen en el espacio de los componentes principales. Para el gráfico de los individuos se debe tomar una muestra más pequeña para que se puedan graficar correctamente.
viviendas_muestra <- sample_n(vivienda, 500)
variables_numericas_muestra <- viviendas_muestra[, sapply(viviendas_muestra, is.numeric)]
variables_seleccionadas_muestra <- variables_numericas_muestra[, c("estrato", "preciom", "areaconst", "parqueaderos", "banios", "habitaciones")]
variables_seleccionadas_muestra <- scale(variables_seleccionadas_muestra)
# Realizar el PCA
pca <- PCA(variables_seleccionadas_muestra, scale.unit = TRUE, ncp = 5, graph = FALSE)
# Visualizar los individuos en el plano de los primeros dos componentes principales
fviz_pca_ind(pca, col.ind = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE)
Inicialmente se seleccionan las variables numéricas relevantes para luego escalarlas, de esta forma evitar que las variables con mayores rangos de valores dominen el análisis. Tomamos nuevamente “Estrato”, “Precio por m cuadrado”, “Área construida”, “Parqueaderos”, “Bañios” y “Habitaciones”.
A continuación calculamos la matriz de distancia para con ella obtener el clustering jerárquico y graficar el dendograma.
# Calcular la matriz de distancia
distancia <- dist(variables_numericas, method = "euclidean")
# Realizar el clustering jerárquico
clustering_jerarquico <- hclust(distancia, method = "ward.D2")
# Visualizar el dendrograma
plot(clustering_jerarquico, labels = FALSE, hang = -1, main = "Dendrograma del Clustering Jerárquico")
El siguiente paso es identificar el número óptimo de clusters ya que o crucial en un análisis de conglomerados, porque ayuda a determinar cuántos grupos naturales existen en los datos. Existen varios métodos para hacerlo, el método del codo es uno de los métodos más utilizados para determinar el número óptimo de clusters. Consiste en graficar la suma de los errores cuadráticos dentro del cluster (inertia) para diferentes números de clusters y buscar el “codo” en la gráfica, que indica el punto donde añadir más clusters no mejora significativamente la agrupación.
set.seed(123) # Fijar semilla para reproducibilidad
fviz_nbclust(variables_numericas, kmeans, method = "wss") +
labs(subtitle = "Método del Codo para Determinar el Número de Clusters")
Una vez determinado el número óptimo de clusters, en este caso 2, procedemos realizar el clustering por el método k-means, esta es una técnica común que particiona los datos en k clusters, minimizando la variación dentro de los clusters.
Evaluamos la calidad del clustering con el índice de silueta, El índice de silueta mide cómo de similares son los objetos dentro de un cluster comparado con objetos de otros clusters. Un valor de silueta cercano a 1 indica que las observaciones están bien agrupadas.
fviz_silhouette(silhouette(kmeans_resultado$cluster, distancia))
## cluster size ave.sil.width
## 1 1 5132 0.51
## 2 2 2805 0.17
El valor reportado para la anchura media de la silueta es de 0.4 indicando que la calidad de clustering es moderadamente baja, lo que significa que los puntos no están claramente agrupados, sin embargo este indicador disminuye a medida que se aumenta la cantidad de clusters sugiriendo que 2 a 3 clusters pueden ser los más indicados.
# Visualizar los clusters en un plano 2D usando PCA
fviz_cluster(kmeans_resultado, data = variables_numericas, geom = "point",
ellipse.type = "convex",
palette = "jco", ggtheme = theme_minimal())
En la gráfica del cluster en 2D se puede evidenciar que a pesar de que ambos clusters se encuentran separados, el límite entre ellos no es muy claro, indicando que los individuos comparten características significativas.
Para este caso se decide por realizar el análisis teniendo en cuenta el tipo, zona y el estrado en lugar de barrio, de todas formas la zona ya nos indica una ubicación de la vivienda.
En una primera instancia creamos una tabla de contigencia para las variables selccionadas.
tabla_correspondencia <- ftable(vivienda$tipo, vivienda$zona, vivienda$estrato)
tabla_correspondencia
## 3 4 5 6
##
## Apartamento Zona Centro 14 7 3 0
## Zona Norte 337 246 497 116
## Zona Oeste 29 58 228 695
## Zona Oriente 58 2 1 1
## Zona Sur 201 1091 1028 460
## Casa Zona Centro 89 5 1 1
## Zona Norte 228 151 226 40
## Zona Oeste 24 23 40 43
## Zona Oriente 270 5 1 0
## Zona Sur 174 496 600 448
A continuación realizamos un análisis de correspondencia múltiple (MCA) ya que se trata de más de dos variables.
# Realizar el Análisis de Correspondencia Múltiple (MCA)
mca <- MCA(vivienda[, c("tipo", "zona", "estrato")], graph = FALSE)
# Resumen del análisis
summary(mca)
##
## Call:
## MCA(X = vivienda[, c("tipo", "zona", "estrato")], graph = FALSE)
##
##
## Eigenvalues
## Dim.1 Dim.2 Dim.3 Dim.4 Dim.5 Dim.6 Dim.7
## Variance 0.570 0.460 0.377 0.333 0.319 0.266 0.198
## % of var. 21.370 17.249 14.147 12.501 11.969 9.991 7.436
## Cumulative % of var. 21.370 38.619 52.767 65.268 77.237 87.228 94.663
## Dim.8
## Variance 0.142
## % of var. 5.337
## Cumulative % of var. 100.000
##
## Individuals (the 10 first)
## Dim.1 ctr cos2 Dim.2 ctr cos2 Dim.3 ctr
## 1 | 2.377 0.125 0.588 | 1.019 0.028 0.108 | 0.537 0.010
## 2 | 2.377 0.125 0.588 | 1.019 0.028 0.108 | 0.537 0.010
## 3 | 2.377 0.125 0.588 | 1.019 0.028 0.108 | 0.537 0.010
## 4 | 0.160 0.001 0.014 | -0.729 0.015 0.299 | 0.895 0.027
## 5 | -0.072 0.000 0.003 | -0.297 0.002 0.045 | -1.306 0.057
## 6 | -0.072 0.000 0.003 | -0.297 0.002 0.045 | -1.306 0.057
## 7 | -0.059 0.000 0.002 | -0.499 0.007 0.112 | -0.500 0.008
## 8 | -0.072 0.000 0.003 | -0.297 0.002 0.045 | -1.306 0.057
## 9 | 0.428 0.004 0.077 | -0.368 0.004 0.057 | -0.936 0.029
## 10 | 0.428 0.004 0.077 | -0.368 0.004 0.057 | -0.936 0.029
## cos2
## 1 0.030 |
## 2 0.030 |
## 3 0.030 |
## 4 0.450 |
## 5 0.867 |
## 6 0.867 |
## 7 0.112 |
## 8 0.867 |
## 9 0.370 |
## 10 0.370 |
##
## Categories (the 10 first)
## Dim.1 ctr cos2 v.test Dim.2 ctr cos2
## Apartamento | -0.409 6.240 0.296 -48.430 | 0.052 0.127 0.005
## Casa | 0.723 11.047 0.296 48.430 | -0.093 0.225 0.005
## Zona Centro | 2.672 6.313 0.110 29.490 | 1.100 1.326 0.019
## Zona Norte | 0.443 2.668 0.059 21.709 | -0.182 0.557 0.010
## Zona Oeste | -1.127 10.667 0.213 -41.110 | 1.732 31.212 0.503
## Zona Oriente | 2.994 22.329 0.399 56.251 | 1.494 6.888 0.099
## Zona Sur | -0.192 1.224 0.048 -19.579 | -0.506 10.514 0.335
## 3 | 1.665 29.101 0.606 69.366 | 0.671 5.858 0.099
## 4 | -0.168 0.435 0.010 -8.949 | -0.885 14.908 0.279
## 5 | -0.197 0.753 0.019 -12.358 | -0.474 5.376 0.111
## v.test Dim.3 ctr cos2 v.test
## Apartamento 6.211 | -0.246 3.421 0.107 -29.178 |
## Casa -6.211 | 0.436 6.057 0.107 29.178 |
## Zona Centro 12.143 | 1.179 1.856 0.021 13.012 |
## Zona Norte -8.915 | -1.393 39.768 0.586 -68.196 |
## Zona Oeste 63.178 | -0.057 0.041 0.001 -2.071 |
## Zona Oriente 28.069 | 0.780 2.291 0.027 14.661 |
## Zona Sur -51.549 | 0.494 12.241 0.320 50.375 |
## 3 27.961 | -0.226 0.812 0.011 -9.425 |
## 4 -47.053 | 0.719 11.985 0.184 38.206 |
## 5 -29.660 | -0.767 17.195 0.291 -48.037 |
##
## Categorical variables (eta2)
## Dim.1 Dim.2 Dim.3
## tipo | 0.296 0.005 0.107 |
## zona | 0.739 0.697 0.636 |
## estrato | 0.675 0.678 0.388 |
Con el propósito de poder interpretar estos resultados de una mejor manera, se realiza un gráfico biplot del análisis obtenido:
fviz_mca_biplot(mca, label = "var", repel = TRUE,
title = "Biplot del Análisis de Correspondencia Múltiple",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"))
A partir del análisis de correspondencia se puede identificar que “Zona Oriente” y “Zona Centro” comparten características similares en térmidos de tipos de vivienda y estrados, además el estrato más común en estas zonas es el 3.
Por otro lado, la “Zona Sur”, “Zona Norte” y “Zona Oeste” están más relacionadas con los estratos más altos, especialmente la “Zona Oeste” en la que predominaría teóricamente el estrado 6.
A partir de los análisis expuestos a través de todo el reporte, se puede concluir que:
Las variables relacionadas con el tamaño (área construida, cantidad de baños, cantidad de parqueaderos) y ubicación de las viviendas son las más importantes para explicar la variabilidad en los datos.
El gráfico de silueta indicó que la calidad del clustering es baja a moderada, con un valor promedio de silueta de 0.4, sugiriendo que las diferencias entre grupos de vivientas no son tan definidas, esto puede deberse a la complejidad de encontrar patrones claros que definan de forma clara y diferencial unas viviendas de otras.
Se identifica una clara relación entre las zonas, el tipo de vivienda y el estrato socieconómico, siendo las casas más comunes para la zona central y oriental en donde predomina el estrato 3, por otro lado los apartamentos son más comunes en el resto de zonas cuyos estratos más altos se encuentran más relacionados.