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.
Los datos son obtenidos de la librería paqueteMETODOS de R y, analizados a través de la misma herramienta haciendo uso de diferentes paquetes que permiten realizar un análisis estadístico de acuerdo a la siguiente metodología.
library(paqueteMODELOS)
data("vivenda")
str(vivienda)
vivienda
## # A tibble: 8,322 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona … <NA> 3 250 70 1 3 6
## 2 1169 Zona … <NA> 3 320 120 1 2 3
## 3 1350 Zona … <NA> 3 350 220 2 2 4
## 4 5992 Zona … 02 4 400 280 3 5 3
## 5 1212 Zona … 01 5 260 90 1 2 3
## 6 1724 Zona … 01 5 240 87 1 3 3
## 7 2326 Zona … 01 4 220 52 2 2 3
## 8 4386 Zona … 01 5 310 137 2 3 4
## 9 1209 Zona … 02 5 320 150 2 4 6
## 10 1592 Zona … 02 5 780 380 2 3 3
## # ℹ 8,312 more rows
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
faltantes <- colSums(is.na(vivienda)) %>% as.data.frame()
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
library(naniar)
gg_miss_var(vivienda)
VIM::aggr(vivienda, cex.axis =1, cex.lab=1)
vivienda$parqueaderos[is.na(vivienda$parqueaderos)] <- 0
moda_piso <- Mode(vivienda$piso, na.rm = TRUE)
vivienda$piso[is.na(vivienda$piso)] <- moda_piso
vivienda <- na.omit(vivienda)
vivienda$tipo <- ifelse(vivienda$tipo=="Casa","CASA",ifelse(vivienda$tipo=="casa","CASA",vivienda$tipo))
vivienda$tipo <- ifelse(vivienda$tipo=="apto","APARTAMENTO",ifelse(vivienda$tipo=="Apartamento","APARTAMENTO",vivienda$tipo))
table(vivienda$tipo)
##
## APARTAMENTO CASA
## 5100 3219
vivienda$longitud <- ifelse(vivienda$longitud<=-90,vivienda$longitud/1000,vivienda$longitud)
vivienda$latitud <- ifelse(vivienda$latitud>=270,vivienda$latitud/1000,vivienda$latitud)
vivienda$preciom2 <- vivienda$preciom/vivienda$areaconst
faltantes <- colSums(is.na(vivienda)) %>% as.data.frame()
faltantes
## .
## id 0
## zona 0
## piso 0
## estrato 0
## preciom 0
## areaconst 0
## parqueaderos 0
## banios 0
## habitaciones 0
## tipo 0
## barrio 0
## longitud 0
## latitud 0
## preciom2 0
Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.
vivienda$piso <- as.numeric(vivienda$piso)
vivienda$estrato <- as.numeric(vivienda$estrato)
vivienda$parqueaderos <- as.numeric(vivienda$parqueaderos)
vivienda$banios <- as.numeric(vivienda$banios)
vivienda$habitaciones <- as.numeric(vivienda$habitaciones)
viviendaC = vivienda[,c(4:9)]
viviendaZ = scale(viviendaC)
head(viviendaZ)
## estrato preciom areaconst parqueaderos banios habitaciones
## [1,] -1.5872276 -0.5595498 -0.7339949 -0.3875522 -0.07793773 1.6406840
## [2,] -1.5872276 -0.3465670 -0.3842568 -0.3875522 -0.77811479 -0.4147626
## [3,] -1.5872276 -0.2552886 0.3152194 0.4168506 -0.77811479 0.2703863
## [4,] -0.6156201 -0.1031580 0.7349051 1.2212534 1.32241640 -0.4147626
## [5,] 0.3559875 -0.5291236 -0.5940997 -0.3875522 -0.77811479 -0.4147626
## [6,] 0.3559875 -0.5899759 -0.6150839 -0.3875522 -0.07793773 -0.4147626
prcomp(viviendaZ)
## Standard deviations (1, .., p=6):
## [1] 1.8480703 1.1222455 0.6779600 0.6592367 0.4909833 0.4357909
##
## Rotation (n x k) = (6 x 6):
## PC1 PC2 PC3 PC4 PC5 PC6
## estrato 0.3306904 -0.5816950 0.5551021 0.07749341 0.46143880 -0.1587620
## preciom 0.4781754 -0.1883533 -0.0359721 -0.36062553 -0.21967777 0.7458339
## areaconst 0.4417250 0.2388377 -0.2878930 -0.61783592 0.27859384 -0.4534506
## parqueaderos 0.4096354 -0.2607395 -0.6565763 0.57091588 0.04760414 -0.0700729
## banios 0.4668745 0.1921461 0.3633148 0.18500105 -0.67876421 -0.3437503
## habitaciones 0.2847775 0.6813261 0.2111757 0.34936005 0.44521273 0.2997240
Se evidencia que el primer componente principal explica el 56.9% de la variabilidad contenida en la base de datos y entre los dos primeros el 77.9%, lo cual indicaría que con solo una variable (CP1) que se obtiene mediante una combinación lineal de las variables se puede resumir gran parte de la variabilidad que contiene la base de datos.
res.pca <- prcomp(viviendaZ)
fviz_eig(res.pca, addlabels = TRUE)
Al analizar el círculo de correlaciones se evidencia que las variables “preciom” está considerablemente correlacionada con “parqueaderos”, lo que indica que a mayor número de parqueaderos mayor es el precio de la vivienda. Lo mismo se evidencia entre las variables “areaconst” y “banios”, lo que sugiere que las viviendas con mayor área construida tiene mayor cantidad de baños.
Por el contrario, se observa que las variables “habitaciones” y “estrato” tienen una baja correlación, dado que son perpendiculares. Sin embargo, ambas variables muestran altos niveles de contribución, es decir que, son importantes para explicar la variabilidad en los datos, siendo las variables son mayor relación con PC2.
fviz_pca_var(res.pca,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("#FF7F00", "#034D94"),
repel = TRUE # Avoid text overlapping
)
casos1 <- rbind(res.pca$x[5717,1:2],res.pca$x[1762,1:2]) # CP1
rownames(casos1) = c("5717","1762")
casos1 <- as.data.frame(casos1)
casos2 <- rbind(res.pca$x[2926,1:2], res.pca$x[8073,1:2]) # CP2
rownames(casos2) = c("2926","8073")
casos2 <- as.data.frame(casos2)
# Genera el gráfico
fviz_pca_ind(res.pca, col.ind = "#DEDEDE", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07")) +
geom_point(data = casos1, aes(x = PC1, y = PC2), color = "red", size = 3) +
geom_text(data = casos1, aes(x = PC1, y = PC2, label = rownames(casos1)), color = "red", vjust = -1) +
geom_point(data = casos2, aes(x = PC1, y = PC2), color = "blue", size = 3) +
geom_text(data = casos2, aes(x = PC1, y = PC2, label = rownames(casos2)), color = "blue", vjust = -1)
datos <- rbind(viviendaC[5717,],
viviendaC[1762,],
viviendaC[2926,],
viviendaC[8073,])
datos <- as.data.frame(datos)
rownames(datos) = c("Vivienda 5717","Vivienda 1762","Vivienda 2926","Vivienda 8073")
datos
## estrato preciom areaconst parqueaderos banios habitaciones
## Vivienda 5717 4 160 84 1 0 0
## Vivienda 1762 6 1800 1586 10 4 5
## Vivienda 2926 5 950 280 10 0 0
## Vivienda 8073 3 370 1440 1 4 10
La vivienda 5717 presenta un precio bajo $160M, mientras que la vivienda 1762 presenta un precio alto de $1800, evidenciando el sentido en que aumentan los valores de PC1. También se observa cómo el precio se relaciona con el número de parqueadero, así como el área construida con el número de baños.
Por otro lado, la vivienda 2926 es de estrato 5 y la vivienda 8073 de estrato 3, se evidencia que tienen un número de habitaciones de 0 y 10, respectivamente.
fviz_pca_biplot(res.pca,
repel = TRUE,
habillage = viviendaC$estrato,
col.var = "#034A94", # Variables color
col.ind = c("#DEDEDE", "#034A94") # Individuals color
)
La representación gráfica de los dos primeros componentes principales nos permite observar la relación existente entre las variables “areaconst” y “banios”, y “preciom” y “parqueaderos” formando un grupo que presenta un mayor efecto sobre PC1, mientras que las variables “habitaciones” y “estrato” presentan mayor efecto sobre PC2.
Los colores en dicha gráfica indican que a mayor estrato, mayor es el precio de la vivienda y el tamaño de la misma.
Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.
# Correlación
cor(viviendaC)
## estrato preciom areaconst parqueaderos banios
## estrato 1.00000000 0.6098066 0.2743233 0.5127889 0.4203218
## preciom 0.60980664 1.0000000 0.6873520 0.6397779 0.6691456
## areaconst 0.27432332 0.6873520 1.0000000 0.4823840 0.6484165
## parqueaderos 0.51278892 0.6397779 0.4823840 1.0000000 0.5231299
## banios 0.42032178 0.6691456 0.6484165 0.5231299 1.0000000
## habitaciones -0.07137615 0.2640912 0.5169129 0.1987555 0.5899064
## habitaciones
## estrato -0.07137615
## preciom 0.26409121
## areaconst 0.51691292
## parqueaderos 0.19875551
## banios 0.58990641
## habitaciones 1.00000000
corrplot(cor(viviendaC))
La matriz de correlación muestra que la variable que tiene menor correlación con el precio de la vivienda es el número de habitaciones, esto puede deberse a errores en la recolección de los datos o puede ser un comportamiento correcto, y no ser un factor determinante.
# PCA
res.pca <- PCA(viviendaC, graph = FALSE, scale.unit = TRUE )
# Distancia euclidiana
distancia <- dist(res.pca$ind$coord, method = "euclidean")
# Cluster
res.HCPC <- HCPC(res.pca, nb.clust=4)
wss <- sapply(1:10, function(k){kmeans(res.pca$ind$coord, k)$tot.withinss})
plot(1:10, wss, type="b", pch = 19, frame = FALSE,
xlab="Número de clusters K", ylab="Total within-clusters sum of squares")
# Clustering jerárquico
res.hclust <- hclust(distancia, method = "ward.D2")
# Calculamos el índice de Calinski-Harabasz para diferentes números de clusters
ch <- c()
for (k in 2:10) {
cluster_assignments <- cutree(res.hclust, k)
ch[k] <- cluster.stats(distancia, cluster_assignments)$ch
}
# Graficamos el índice de Calinski-Harabasz
plot(2:10, ch[2:10], type = "b", xlab = "Número de clusters", ylab = "Índice de Calinski-Harabasz")
De acuerdo con ambos métodos (método del codo e índice de Calinski-Harabasz) se puede deducir que k=4, es un óptimo número de Clúster.
res.HCPC$desc.var
##
## Link between the cluster variable and the quantitative variables
## ================================================================
## Eta2 P-value
## estrato 0.5638495 0
## preciom 0.6979368 0
## areaconst 0.5452858 0
## parqueaderos 0.5248949 0
## banios 0.5928349 0
## habitaciones 0.5778037 0
##
## Description of each cluster by quantitative variables
## =====================================================
## $`1`
## v.test Mean in category Overall mean sd in category Overall sd
## habitaciones -39.02301 2.8792762 3.605361 0.7522374 1.459449
## areaconst -46.64650 89.9194939 174.934938 49.0584275 142.955533
## parqueaderos -46.90204 0.7384789 1.481789 0.5654987 1.243084
## preciom -53.22682 210.8886062 433.904436 84.3116821 328.645270
## estrato -54.25919 3.9216850 4.633610 0.7300851 1.029160
## banios -60.40017 2.0115917 3.111311 0.6160892 1.428124
## p.value
## habitaciones 0
## areaconst 0
## parqueaderos 0
## preciom 0
## estrato 0
## banios 0
##
## $`2`
## v.test Mean in category Overall mean sd in category Overall sd
## estrato 46.084940 5.348264 4.633610 0.5944452 1.029160
## parqueaderos 13.818823 1.740625 1.481789 0.7103518 1.243084
## banios 10.688655 3.341319 3.111311 0.8837819 1.428124
## preciom 6.950902 468.325347 433.904436 176.0051906 328.645270
## areaconst -6.755470 160.383351 174.934938 71.9104638 142.955533
## habitaciones -11.801610 3.345833 3.605361 0.7590999 1.459449
## p.value
## estrato 0.000000e+00
## parqueaderos 1.962512e-43
## banios 1.150264e-26
## preciom 3.629584e-12
## areaconst 1.423729e-11
## habitaciones 3.829154e-32
##
## $`3`
## v.test Mean in category Overall mean sd in category Overall sd
## habitaciones 63.575009 6.550169 3.605361 1.571131 1.459449
## banios 30.749706 4.505073 3.111311 1.456520 1.428124
## areaconst 27.837912 301.239549 174.934938 131.002770 142.955533
## parqueaderos -7.410968 1.189402 1.481789 1.065065 1.243084
## estrato -22.573423 3.896280 4.633610 0.858972 1.029160
## p.value
## habitaciones 0.000000e+00
## banios 1.233900e-207
## areaconst 1.508815e-170
## parqueaderos 1.253805e-13
## estrato 7.907997e-113
##
## $`4`
## v.test Mean in category Overall mean sd in category Overall sd
## preciom 68.69075 1097.897537 433.904436 349.1186332 328.645270
## parqueaderos 57.74505 3.593103 1.481789 1.5747239 1.243084
## areaconst 54.02679 402.103419 174.934938 195.2082515 142.955533
## banios 46.69877 5.072906 3.111311 1.1888666 1.428124
## estrato 36.25382 5.731034 4.633610 0.5304264 1.029160
## habitaciones 16.14757 4.298522 3.605361 1.2674585 1.459449
## p.value
## preciom 0.000000e+00
## parqueaderos 0.000000e+00
## areaconst 0.000000e+00
## banios 0.000000e+00
## estrato 8.650420e-288
## habitaciones 1.181206e-58
La construcción de estos clústeres y su análisis permite conocer las características comunes bajo las cuales se pueden agrupar las viviendas, a fin de facilitar el proceso de venta de estas. Los clústeres obtenidos se analizan de acuerdo al valor v.test.
En el clúster 1 se tienen las Viviendas con valores bajos en las variables estudiadas, es decir, aquellas viviendas de menor precio y área construida, estrato más bajo, y menor número de baños, habitaciones y parqueaderos.
En el clúster 2 se encuentran aquellas viviendas con estratos altos, cuyo número de parqueaderos y baños es intermedio, pero tienen menor área construida y menor número de habitaciones.
En el clúster 3 se ubican las viviendas de bajos estratos que tienen mayor número de habitaciones y de baños, con un área construida intermedia, pero con bajo número de parqueaderos.
En el clúster 4 están las viviendas que tienen valores altos en las variables estudiadas, es decir, las viviendas de mayor precio y área construida, estrato alto, y mayor número de baños, habitaciones y parqueaderos.
Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.
viviendaC2 = vivienda[,c(2,5,10,11)]
hist(viviendaC2$preciom)
breaks <- seq(0, 2000, by = 250)
vivienda_AC <- viviendaC2
vivienda_AC$preciom <- NULL # Elimina la variable preciom
vivienda_AC$preciom_categorizado <- cut(viviendaC2$preciom, breaks = breaks, include.lowest = TRUE, labels = FALSE)
labels <- paste0("[", breaks[-length(breaks)], "-", breaks[-1], ")")
vivienda_AC$preciom_categorizado <- cut(viviendaC2$preciom, breaks = breaks, include.lowest = TRUE, labels = labels)
vivienda_AC$zona <- as.factor(vivienda_AC$zona)
vivienda_AC$tipo <- as.factor(vivienda_AC$tipo)
vivienda_AC$barrio <- as.factor(vivienda_AC$barrio)
GZ <- ggplot(vivienda_AC, aes(x=zona)) + geom_bar(fill= "blue") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
GT <- ggplot(vivienda_AC, aes(x=tipo)) + geom_bar(fill= "green")
GP <- ggplot(vivienda_AC, aes(x=preciom_categorizado)) + geom_bar(fill= "gray") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))
grid.arrange(GZ, GT, GP, ncol = 3)
tabla1 <- table(vivienda_AC$zona, vivienda_AC$tipo)
colnames(tabla1) <- c("APARTAMENTO", "CASA")
tabla1
##
## APARTAMENTO CASA
## Zona Centro 24 100
## Zona Norte 1198 722
## Zona Oeste 1029 169
## Zona Oriente 62 289
## Zona Sur 2787 1939
chisq.test(tabla1)
##
## Pearson's Chi-squared test
##
## data: tabla1
## X-squared = 690.93, df = 4, p-value < 2.2e-16
El resultado indica que se rechaza la hipótesis de independencia de las variables (p-value: 0.0000), indicando grado tipo de relación entre ellas.
resultados_ac_1 <- CA(tabla1)
fviz_screeplot(resultados_ac_1, addlabels = TRUE)+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
Los resultados indican que la primera componente resumen el 100%.
tabla2 <- table(vivienda_AC$preciom_categorizado, vivienda_AC$tipo)
colnames(tabla2) <- c("APARTAMENTO", "CASA" )
tabla2
##
## APARTAMENTO CASA
## [0-250) 2302 525
## [250-500) 1772 1462
## [500-750) 575 593
## [750-1000) 220 317
## [1000-1250) 112 123
## [1250-1500) 78 108
## [1500-1750) 22 57
## [1750-2000) 19 34
chisq.test(tabla2)
##
## Pearson's Chi-squared test
##
## data: tabla2
## X-squared = 805.48, df = 7, p-value < 2.2e-16
El resultado indica que se rechaza la hipótesis de independencia de las variables (p-value: 0.0000), indicando grado tipo de relación entre ellas.
resultados_ac_2 <- CA(tabla2)
fviz_screeplot(resultados_ac_2, addlabels = TRUE)+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?
Los resultados indican que la primera componente resumen el 100%.
tabla3 <- table(vivienda_AC$preciom_categorizado, vivienda_AC$zona)
colnames(tabla3) <- c("Zona Centro", "Zona Norte", "Zona Oeste", "Zona Oriente", "Zona Sur")
tabla3
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## [0-250) 49 805 98 228 1647
## [250-500) 64 776 400 119 1875
## [500-750) 8 219 323 3 615
## [750-1000) 2 80 151 0 304
## [1000-1250) 1 21 109 0 104
## [1250-1500) 0 12 73 1 100
## [1500-1750) 0 5 21 0 53
## [1750-2000) 0 2 23 0 28
chisq.test(tabla3)
## Warning in chisq.test(tabla3): Chi-squared approximation may be incorrect
##
## Pearson's Chi-squared test
##
## data: tabla3
## X-squared = 1090.2, df = 28, p-value < 2.2e-16
El resultado indica que se rechaza la hipótesis de independencia de las variables (p-value: 0.0000), indicando grado tipo de relación entre ellas.
resultados_ac_3 <- CA(tabla3)
El gráfico muestra que las viviendas con precios más altos están ubicadas en la zona oeste, las viviendas con precios medio-altos están en la zona Sur, las viviendas con precios medio-bajos están en la zona norte y las viviendas con precios bajos están en las zona centro y oriente.
fviz_screeplot(resultados_ac_3, addlabels = TRUE)+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
Los resultados indican que la primera componente resumen el 89.1% y los dos primeros componentes prepresentados en el plano factorial, mientras que los dos primeros ejes resumen un 97.2% de los datos.
Esta base de datos cuenta con variables numéricas que al ser transformadas permiten establecer correlaciones entre ellas, y realizar asociaciones con la variación de los precios de las viviendas, gracias al uso del ACP.
Por su parte, el análisis de conglomerados presenta una visión más amplia de lo que se observa con el ACP, dado que al definir los grupos de segmentación se logra identificar patrones en las características de las viviendas. La caracterización de las viviendas se puede traducir en oportunidades de negocio, e identificación de grupos foco.
El análisis de correspondencias presenta relaciones entre las variables categóricas que pueden no ser tan evidentes, lo cual es positivo al momento de establecer paramétros para la venta de viviendas.
No se incluyen anexos, dado que todo el código está en contenido en el desarrollo del trabajo.