El sector inmobiliario es aquel que abarca toda actividad sobre bienes inmuebles y uno de los pocos mercados transversales a la economía. Específicamente en las grandes ciudades dicho sector ha crecido significativamente en los últimos años debido a factores como el crecimiento de la población, la inversión extranjera y el desarrollo de nuevos proyectos de vivienda.
Por esto para la empresa inmobiliaria líder en este campo es importante realizar un análisis holístico de los datos que se tienen 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.
Aplicar un análisis integral y multidimensional de la base de
datos de la empresa inmobiliaria en cuestión para obtener una
comprensión del mercado inmobiliario urbano a través de tres técnicas de
análisis de datos como el análisis de componentes principales, el
análisis de conglomerados y el análisis de correspondencia.
Comenzaremos a partir de la base de datos suministrada nombrada
vivienda, comprendiendo la dimensión y estado inicial de esta. Luego,
realizaremos una revisión de los datos faltantes para realizar la
correcta corrección de estos.
Este apartado pretende realizar un entendimiento del estado inicial de la base de datos, la cual cuenta con 8322 registros y 13 variables.
dim(vivienda)
## [1] 8322 13
Un resumen inicial de la base de datos es el siguiente:
head(vivienda)
## # A tibble: 6 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona O… <NA> 3 250 70 1 3 6
## 2 1169 Zona O… <NA> 3 320 120 1 2 3
## 3 1350 Zona O… <NA> 3 350 220 2 2 4
## 4 5992 Zona S… 02 4 400 280 3 5 3
## 5 1212 Zona N… 01 5 260 90 1 2 3
## 6 1724 Zona N… 01 5 240 87 1 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
En donde se puede validar que efectivamente la base suministrada cuenta con 13 variables, de las cuales 3 de ellas son cualitativas y 10 cuantitativas. Adicionalmente esta vista nos muestra los 6 primeros valores que toma cada una de estas.
#str(vivienda)
summary(vivienda)
## id zona piso estrato
## Min. : 1 Length:8322 Length:8322 Min. :3.000
## 1st Qu.:2080 Class :character Class :character 1st Qu.:4.000
## Median :4160 Mode :character Mode :character Median :5.000
## Mean :4160 Mean :4.634
## 3rd Qu.:6240 3rd Qu.:5.000
## Max. :8319 Max. :6.000
## NA's :3 NA's :3
## 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 : 2.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.835 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
## NA's :2 NA's :3 NA's :1605 NA's :3
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8322 Length:8322 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.54
## Median : 3.000 Mode :character Mode :character Median :-76.53
## Mean : 3.605 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## NA's :3 NA's :3
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
## NA's :3
Este resumen muestra que:
Aquí se depurará la base de datos, usando las técnicas adecuadas para cada variable.
Para analizar más a fondo la información contenida en la base se
evalúa si esta cuenta con datos faltantes en alguna de las variables.
vivienda_faltantes <- colSums(is.na(vivienda)) %>%
as.data.frame()
vivienda_faltantes
## .
## id 3
## zona 3
## piso 2638
## estrato 3
## preciom 2
## areaconst 3
## parqueaderos 1605
## banios 3
## habitaciones 3
## tipo 3
## barrio 3
## longitud 3
## latitud 3
Y efectivamente la base contiene datos faltantes en todas sus variables como se muestra en el resumen anterior, siendo la variable piso y parqueaderos las más afectadas con 2638 y 1605 registros faltantes respectivamente.
md.pattern(vivienda, rotate.names = TRUE)
## preciom id zona estrato areaconst banios habitaciones tipo barrio longitud
## 4808 1 1 1 1 1 1 1 1 1 1
## 1909 1 1 1 1 1 1 1 1 1 1
## 876 1 1 1 1 1 1 1 1 1 1
## 726 1 1 1 1 1 1 1 1 1 1
## 1 1 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0
## 2 3 3 3 3 3 3 3 3 3
## latitud parqueaderos piso
## 4808 1 1 1 0
## 1909 1 1 0 1
## 876 1 0 1 1
## 726 1 0 0 2
## 1 0 0 0 12
## 2 0 0 0 13
## 3 1605 2638 4275
Del gráfico anterior se logra identificar que tres registros no cuentan con la mayoría de los datos, solo uno de estos registros cuenta con la variable preciom y es por esto que se decide eliminar estos tres registros ya que en comparación con la base total es una cantidad muy pequeña, por lo cual esta no se verá afectada. Quedando con una base de 8319 registros.
#Eliminar los registros sin variables
vivienda_sf <- vivienda[!is.na(vivienda$id),]
dim(vivienda_sf)
## [1] 8319 13
La variable piso y parqueaderos continuan con valores faltantes, siendo estos del 32% y 19% respectivamente.
vis_miss(vivienda_sf)
Es por esto que se procede a evaluar estas variables y definir la mejor forma de organizarlas.
#Completar valores faltantes
vivienda_sf$parqueaderos[is.na(vivienda_sf$parqueaderos)] <- 0
barplot(table(vivienda_sf$piso), main = "Distribución de la variable piso",
ylab = "Frecuencia", xlab = "Piso",col = "cadetblue",
border =par("fg"))
vivienda_sf$piso <- as.numeric(as.character(vivienda_sf$piso))
summary(vivienda_sf$piso)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.000 2.000 3.000 3.771 5.000 12.000 2635
El gráfico de barras nos muestra que la variable piso tiene una asimetria positiva hacia la derecha como lo muestra su cola. Igualmente el resumen estadistico indica una media de 3.7 y una mediana de 3, por lo que trabajaremos con la mediana ya que es el valor que más se ajusta al tipo de esta variable.
#Completar valores faltantes
vivienda_sf$piso[is.na(vivienda_sf$piso)] <- 3
Con la imputación realizada a estas variables podemos evidenciar que la base de datos ya no tiene datos faltantes, como lo certifica el siguiente mapa de valores faltantes:
gg_miss_var(vivienda_sf)
Otra validación importante es garantizar que no se cuente con registros duplicados en la base ya que esto podria modificar los resultados.
duplicados <- duplicated(vivienda_sf) #identificar registros duplicados
table(duplicados) #Conocer la cantidad de registros duplicados
## duplicados
## FALSE
## 8319
La base no cuenta con registros duplicados.
Y por último se procede a eliminar la variable id ya que es un identificador unico, por lo cual no aporta al analisis estadistico y también se eliminaran las variables latitud y longitud dado que no se hará ningun analisis espacial. Esto contribuye a reducir la dimensionalidad de la base de datos.
vivienda_sf <- vivienda_sf[,!names(vivienda_sf) %in% c("id","longitud","latitud")]
El Análisis de Componentes Principales (PCA) es una técnica estadística que reduce la cantidad de variables en un conjunto de datos, conservando la mayor cantidad de información posible. Transforma las variables originales en nuevas variables llamadas componentes principales, que son combinaciones lineales de las originales. Estos componentes capturan la mayor variabilidad posible en los datos. Se usa para simplificar el análisis y la visualización de datos complejos.
a. Convertir la variable piso en numérica.
#Volver la variable piso numerica
vivienda_sf$piso <- as.numeric(vivienda_sf$piso)
print(class(vivienda_sf$piso))
## [1] "numeric"
b. Crear una sub-base con las variables numéricas con las cuales se va a trabajar esta técnica (piso, precio, area construida, parqueaderos, baños y habitaciones).
# Seleccionar solo las variables numéricas
viviendanum <- data.frame (subset (vivienda_sf, select= c("piso", "preciom",
"areaconst","parqueaderos", "banios", "habitaciones")))
head(viviendanum)
## piso preciom areaconst parqueaderos banios habitaciones
## 1 3 250 70 1 3 6
## 2 3 320 120 1 2 3
## 3 3 350 220 2 2 4
## 4 2 400 280 3 5 3
## 5 1 260 90 1 2 3
## 6 1 240 87 1 3 3
c. Validar las correlaciones entre las variables numéricas
#matriz de correlaciones
matriz_cor <- cor(viviendanum, use = "complete.obs")
corrplot(matriz_cor, method = "color", type = "lower",
title = "Matriz de correlación", diag = FALSE,
number.cex = 0.5, number.digits = 2, addCoef.col = "gray4",
mar = c(0, 0, 2, 0))
Como lo muestra la matriz no se observa redundancia en los datos y la variable piso no presenta una correlación fuerte con las demás variables por lo cual se procede a eliminarla de la sub-base de variables numéricas.
viviendanum <- viviendanum[,!names(viviendanum) %in% "piso"]
d. Estandarizar las variables numéricas para evitar que las diferentes escalas afecten las estimaciones
#Normalizar variables numericas
viviendanumz <- scale(viviendanum)
head(viviendanumz)
## preciom areaconst parqueaderos banios habitaciones
## [1,] -0.5595498 -0.7339949 -0.3875522 -0.07793773 1.6406840
## [2,] -0.3465670 -0.3842568 -0.3875522 -0.77811479 -0.4147626
## [3,] -0.2552886 0.3152194 0.4168506 -0.77811479 0.2703863
## [4,] -0.1031580 0.7349051 1.2212534 1.32241640 -0.4147626
## [5,] -0.5291236 -0.5940997 -0.3875522 -0.77811479 -0.4147626
## [6,] -0.5899759 -0.6150839 -0.3875522 -0.07793773 -0.4147626
e. Elegir el número de componentes principales
#Elegir el número de componentes principales
res.pca <- prcomp(viviendanumz)
res.pca
## Standard deviations (1, .., p=5):
## [1] 1.7687244 0.9580196 0.6596223 0.5697744 0.4405313
##
## Rotation (n x k) = (5 x 5):
## PC1 PC2 PC3 PC4 PC5
## preciom 0.4786167 0.35962053 -0.3591123 0.2636141 0.66569141
## areaconst 0.4837887 -0.08525642 -0.5617850 -0.5381787 -0.39171573
## parqueaderos 0.4083175 0.52028538 0.6606172 -0.3457569 -0.08134506
## banios 0.4959071 -0.15655331 0.1211208 0.6948247 -0.48178371
## habitaciones 0.3524592 -0.75378949 0.3230334 -0.1963215 0.40580928
fviz_eig(res.pca, addlabels = TRUE, main="Gráfico de sedimentación",
barfill = "cadetblue", linecolor = "gray10")
Aquí el primer componente principal explica el 62.6% de la variabilidad contenida en la base de datos y entre los dos primeros está el 81% de los datos, lo cual indicaría que con solo una variable (CP1) que se obtiene mediante una combinación lineal de las variables se puede resumir un poco más de la variabilidad que contiene la base de datos.
Al analizar la matriz de componentes, destacamos que las variables “banios”, “areaconst” y “preciom” son las que más contribuyen a la primera componente, mientras que “habitaciones” y “parqueaderos” son las que más aportan a la segunda componente. Dado que la primera componente captura la mayor variabilidad de los datos, podemos afirmar que las variables “preciom”, “areaconst” y “banios” desempeñan un papel crucial en la definición del conjunto de datos.
#Grafico variables ACP Biplot
fviz_pca_var(res.pca,
col.var = "contrib",
gradient.cols = c("red1", "yellow3", "green3"),
repel = TRUE)
Con este gráfico identificamos en orden de mayor (verdes) a menor (rojas) la contribución de las variables: “habitaciones”, “preciom”, “banios”, “parqueaderos” y “areaconst”.
Se evidencia que sus magnitudes son muy similares, aunque la variable “areaconst” es un poquito más pequeña, lo cual indica que no tiene igual influencia en el análisis. También se observa que esta correlacionada (cercana) con la variable “banios”. Y la variable “preciom” también esta correlacionada con “parqueaderos”.
Esto indica que entre más metros cuadrados más baños tiene el inmueble y que las propiedades con precios altos tienden a tener más parqueaderos.
El Análisis de Conglomerados (Clustering) es una técnica que agrupa objetos similares en conjuntos llamados conglomerados o clústeres. Se usa para identificar estructuras o patrones en datos no etiquetados. Los objetos dentro de un mismo conglomerado son más parecidos entre sí que con los de otros conglomerados. Esta técnica se emplea para segmentar mercados, analizar patrones en datos biológicos, y descubrir grupos en grandes conjuntos de datos.
a. Determinar el número óptimo de clústeres con el método del codo
#Determinar el número óptimo de clústeres utilizando el método del codo
set.seed(123)
wcss <- sapply(1:10, function(k) kmeans(viviendanumz, centers = k)$tot.withinss)
## Warning: did not converge in 10 iterations
plot(1:10, wcss, type = "b", main = "Método del Codo", xlab = "K clusters", ylab = "Suma total de cuadrados")
A través de este método se analiza y concluye que k puede ser 2, ya que en este se da el primer punto de flexión en diferente dirección.
b. Determinar el número óptimo de clústeres con el método de la silueta
sil_width <- function(k){
km.res <- kmeans(viviendanumz, k, nstart = 10)
ss <- silhouette(km.res$cluster, dist(viviendanumz))
mean(ss[, 3])}
k.values <- 2:10
sil_values <- sapply(k.values, sil_width)
## Warning: did not converge in 10 iterations
#Graficar la siluetta
plot(k.values, sil_values, type = "b")
fviz_nbclust(viviendanumz, kmeans, method = "silhouette")
Y con este método se confirma que 2 es un número óptimo para los clusteres.
c. Validar la calidad del agrupamiento.
#revisar la calidad del agrupamiento (clustering) con K=2 cluster
dist_emp <- dist(viviendanumz, method = 'euclidean') #distancia euclidiana
hc_emp <- hclust(dist_emp, method = 'complete') #Cluster jerarquico con el método complete
cluster_assigments <- cutree(hc_emp, k = 2) #Determinamos a dónde pertenece cada observación
sil <- silhouette(cluster_assigments, dist(viviendanumz)) #Calcular el coeficiente de Silhouette
sil_avg <- mean(sil[,3])
cat("Coeficiente de Silhouette promedio k=2 : ", sil_avg) #Imprimir el coeficiente de Silhouette promedio
## Coeficiente de Silhouette promedio k=2 : 0.6448905
Con el coeficiente de Silhouette promedio = 0.64 con k=2 se puede considerar un clustering moderado. Lo que sugiere que, en general, las observaciones están bien agrupadas, pero puede haber algunos puntos que no están perfectamente asignados a un cluster. Aunque no es un valor excepcionalmente alto, es lo suficientemente bueno para sugerir que los clusters están razonablemente bien separados.
d. Los tamaños de los clusteres son los siguientes:
#Aplicar el algoritmo K-means con el número óptimo de clústeres
kmeans_result <- kmeans(viviendanumz, centers = 2, nstart = 5)
# Obtener el tamaño de cada clúster
tam_cluster <- table(kmeans_result$cluster)
print(tam_cluster)
##
## 1 2
## 5835 2484
e. Graficar los clusteres:
#Graficar y visualizar los cluster
fviz_cluster(kmeans_result,
data = viviendanumz,
palette = "jco",
geom = "point",
ellipse.type = "norm",
ggtheme = theme_minimal(),
main = "Resultado Clustering",
xlab = "Dim 1", ylab = "Dim 2",
labelsize = 10,
repel = TRUE)
Con los metodos aplicados se determino que el mercado de viviendas urbanas estudiado puede ser mejor comprendido y segmentado en dos grupos distintos de tamaños 2484 y 5835.
viviendanum = as.data.frame(viviendanum)
# asignamos los clusters al subset inicial numerico
vi_est= mutate_if(viviendanum, is.numeric, scale)
assigned_cluster <- viviendanum %>% mutate(cluster = as.factor (cluster_assigments))
# Gráfico de cajas y bigotes por variable
par(mfrow = c(2, 3))
# Gráfico de dispersión para precio de vivienda
plot(assigned_cluster$preciom ~ assigned_cluster$cluster,
xlab = "Cluster", ylab = "Precio",
main = "Cluster para precio de vivienda",
col = c("#B17AA0", "#7ECDBB"),
pch = 16)
# Gráfico de dispersión para área de vivienda
plot(assigned_cluster$areaconst ~ assigned_cluster$cluster,
xlab = "Cluster", ylab = "Área",
main = "Cluster para área de vivienda",
col = c("#B17AA0", "#7ECDBB"),
pch = 16)
# Gráfico de dispersión para habitaciones de vivienda
plot(assigned_cluster$habitaciones ~ assigned_cluster$cluster,
xlab = "Cluster", ylab = "Habitaciones",
main = "Cluster para habitaciones de vivienda",
col = c("#B17AA0", "#7ECDBB"),
pch = 16)
# Gráfico de dispersión para baños de vivienda
plot(assigned_cluster$banios ~ assigned_cluster$cluster,
xlab = "Cluster", ylab = "Baños",
main = "Cluster para baños de vivienda",
col = c("#B17AA0", "#7ECDBB"),
pch = 16)
# Gráfico de dispersión para parqueaderos de vivienda
plot(assigned_cluster$parqueaderos ~ assigned_cluster$cluster,
xlab = "Cluster", ylab = "Parqueaderos",
main = "Cluster para parqueaderos de vivienda",
col = c("#B17AA0", "#7ECDBB"),
pch = 16)
Los dos clusters representan segmentos claros dentro del mercado inmobiliario, con propiedades que comparten características similares dentro de cada grupo. El cluster 1 son inmuebles con menores precios por ende menor área construida, habitaciones y baños. En contraste el cluster 2 los inmuebles tienen mayores precios, áreas construidas, habitaciones y baños. Sin embargo, ambos clusters presentan una mediana similar en cuanto a la cantidad de parqueaderos.
El Análisis de Correspondencia (AC) es una técnica estadística que permite analizar y visualizar las relaciones entre dos variables categóricas en una tabla de contingencia. Este análisis es especialmente útil para entender cómo se asocian las categorías de una variable con las categorías de otra.
En la base de datos tenemos 4 variables de tipo categorico, las cuales son la zona, el estrato, el tipo y el barrio. De estas no analizaremos el barrio ya que tiene demasiadas categorias y se vuelve compleja de manejar.
Aqui haremos el análisis entre zona y estrato:
#se construye una tabla cruzada con zona y estrato 2 variables involucradas
tabla_ez <- table(vivienda_sf$estrato, vivienda_sf$zona)
tabla_ez
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## 3 105 572 54 340 382
## 4 14 407 84 8 1616
## 5 4 769 290 2 1685
## 6 1 172 770 1 1043
De esta tabla podemos concluir rapidamente que:
Para analizar a profundidad la asociación entre ambas variables, haremos una prueba Chi-cuadrado.
#Evaluación de la independencias de las 2 varibles
chisq.test(tabla_ez)
##
## Pearson's Chi-squared test
##
## data: tabla_ez
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
El resultado de la prueba de chi-cuadrado indica que hay una asociación significativa entre las variables “estrato” y “zona”.
El valor extremadamente bajo del valor p (p-value = 2.2e-16) indica que la asociación entre el estrato y la zona es altamente significativa. Esto significa que la distribución de los estratos varía significativamente según la zona en la que se encuentran las propiedades.
Se procede a realizar el análisis de correspondencia que consiste en estimar las coordenadas para cada uno de los niveles de ambas variables y representarlas en un plano cartesiano.
#Analisis de correspondencia del estrato y la zona
resultados_ac_ez <- CA(tabla_ez)
El gráfico nos permite establecer relaciones y validar las ya descritas inicialmente con la tabla de cruzada:
Para medir el grado de representatividad del proceso se calcula los valores de la varianza acumulada, utilizando para ellos los valores propios de la matriz de discrepancias
#Calculo de valores propios
valores_prop <-resultados_ac_ez$eig ; valores_prop
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.32215213 69.965515 69.96551
## dim 2 0.12745096 27.680002 97.64552
## dim 3 0.01084108 2.354483 100.00000
fviz_screeplot(resultados_ac_ez, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
Los resultados indican que la primera componente resume el 70% y los dos primeros ejes resumen un 97.7% de los datos.
Al visualizar la estructura de los datos en términos de componentes principales, se puede identificar las características clave que explican la mayor parte de la variación en los datos, lo que puede ser crucial para comprender el mercado inmobiliario y tomar decisiones estratégicas informadas. En este caso, contamos con diversas variables numéricas que permiten establecer correlaciones entre ellas encontrando que las variables más relevantes que debe tener en cuenta la empresa son el “preciom”, “areaconst” y “banios” ya que desempeñan un papel crucial en este.
En el análisis de conglomerados la identificación de dos clusters distintos indica diferencias significativas en las características de las propiedades y posiblemente en las dinámicas del mercado. Esto es valioso para la empresa inmobiliaria, ya que permite comprender mejor las necesidades de los clientes y adaptar estrategias de marketing y ventas de manera más efectiva.
Por último, en el análisis de correspondencia se encontró una asociación significativa entre el estrato y la zona, lo que sugiere que la distribución de los estratos varía significativamente según la zona en la que se encuentran las propiedades. También se resalta la importancia de esta técnica al mostrar relaciones entre variables categóricas que no son tan fáciles de entender pero que brindan mucha información al momento de analizar el conjunto de datos.
Utilizar el análisis de conglomerados realizado para adaptar estrategias comerciales específicas para cada cluster. Por ejemplo, los inmuebles con menor precio, menos número de baños y habitaciones potenciarlos a clientes solteros, estudiantes foráneos, entre otros.
El análisis permite identificar rápidamente tanto viviendas céntricas como excéntricas. Por lo tanto, enfocarse en un nicho de mercado específico con una estrategia clara puede marcar una diferencia significativa en la obtención de resultados de venta.
También las viviendas de estrato 4 como están cercanas a las de estrato 5 de la zona sur podrían evaluarse y si son similares ajustarse su precio para venderlas con un valor superior.
Y por último, pero no menos importante, continuar recolectando esta información para continuar sacándole provecho y analizar más estrategias para seguir mejorando.