1. Problema

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.

2. Revisión de la Data

library(paqueteMODELOS)
## Loading required package: boot
## Loading required package: broom
## Loading required package: GGally
## Loading required package: ggplot2
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Loading required package: gridExtra
## Loading required package: knitr
## Loading required package: summarytools
## Warning in fun(libname, pkgname): couldn't connect to display ":0"
## system might not have X11 capabilities; in case of errors when using dfSummary(), set st_options(use.x11 = FALSE)
library(cluster)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(tidyr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(naniar)
vivienda<-data.frame(vivienda)
dim(vivienda)
## [1] 8322   13
names(vivienda)
##  [1] "id"           "zona"         "piso"         "estrato"      "preciom"     
##  [6] "areaconst"    "parqueaderos" "banios"       "habitaciones" "tipo"        
## [11] "barrio"       "longitud"     "latitud"
names(vivienda)<-c("Id", "Zona", "Piso", "Estrato", "PrecioM2",
                             "AreaConstruida", "NParqueaderos", "NBaños","NHabitaciones",
                             "TipoVivienda", "Barrio", "Longitud", "Latitud")

El conjunto de datos con el que se está trabajando está compuesto por 8322 filas y 13 columnas, lo que se traduce en 8322 observaciones (viviendas) con 13 variables para cada una de ellas.

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      
##     PrecioM2      AreaConstruida   NParqueaderos        NBaños      
##  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       
##  NHabitaciones    TipoVivienda          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

3. Revisión de faltantes

Se procede a validar el número de faltantes por cada variable

faltantes <- colSums(is.na(vivienda)) %>% # dataframe de faltantes por variable
  as.data.frame()
faltantes
##                   .
## Id                3
## Zona              3
## Piso           2638
## Estrato           3
## PrecioM2          2
## AreaConstruida    3
## NParqueaderos  1605
## NBaños            3
## NHabitaciones     3
## TipoVivienda      3
## Barrio            3
## Longitud          3
## Latitud           3

Las variables Piso y la Variable Número de parqueaderos son las que representan el mayor número de faltantes con 32% y un 19% respectivamente

missing_data <- vivienda %>%
  summarise_all(~ mean(is.na(.)) * 100) %>%
  gather(key = "variable", value = "percent_missing")
#  gráfico
ggplot(missing_data, aes(x = reorder(variable, -percent_missing), y = percent_missing, fill = percent_missing)) +
  geom_bar(stat = "identity", show.legend = FALSE) +  
  geom_text(aes(label = paste0(round(percent_missing, 1), "%")), 
            vjust = -0.5, size = 3, color = "black") + 
  scale_fill_gradient(low = "lightblue", high = "red") + 
  labs(title = "Porcentaje de valores faltantes por variable",
       x = "Variable",
       y = "Porcentaje de valores faltantes") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10)) 

4. Tratamiento de valores faltantes

Se procede con la variable Piso para tratar los valores faltantes con la mediana,para ello se toma la decisión de dividir los faltantes por la variable tipo de vivienda, teniendo en cuenta que las casas y apartamentos tienen una estructura de pisos diferente.

vivienda$Piso<- as.numeric(
  as.factor(vivienda$Piso))

# valor variable cuando es apartamento
piso_aparta <- subset(vivienda, TipoVivienda == "Apartamento" & is.na(Piso))
dim(piso_aparta)
## [1] 1381   13
piso_aparta2 <- subset(vivienda, TipoVivienda == "Apartamento" & !is.na(Piso))
dim(piso_aparta2)
## [1] 3719   13
mediana_piso_apartamento <- median(piso_aparta2$Piso, na.rm = TRUE)
vivienda$Piso[vivienda$TipoVivienda == "Apartamento" & is.na(vivienda$Piso)] <-mediana_piso_apartamento
#valor variable cuando es casa
piso_casa <- subset(vivienda, TipoVivienda == "Casa" & is.na(Piso))
dim(piso_casa)
## [1] 1254   13
piso_casa2 <- subset(vivienda, TipoVivienda == "Casa" & !is.na(Piso))
dim(piso_casa2)
## [1] 1965   13
mediana_piso_casa <- median(piso_casa2$Piso, na.rm = TRUE)
vivienda$Piso[vivienda$TipoVivienda == "Casa" & is.na(vivienda$Piso)] <-mediana_piso_casa

Los faltantes correspondientes a la variable Parqueadero se aborda, asumiendo que los valores nulos indican que no se tiene parquedero por lo que se imputan con valor 0

vivienda$NParqueaderos[is.na(vivienda$NParqueaderos)] <- 0

5. Validación faltantes despues de tratar las variables Tipo de vivienda y Número de parqueaderos.

faltantes <- colSums(is.na(vivienda)) %>% # dataframe de faltantes por variable
  as.data.frame()
faltantes
##                .
## Id             3
## Zona           3
## Piso           3
## Estrato        3
## PrecioM2       2
## AreaConstruida 3
## NParqueaderos  0
## NBaños         3
## NHabitaciones  3
## TipoVivienda   3
## Barrio         3
## Longitud       3
## Latitud        3
gg_miss_var(vivienda)

vivienda_final<-na.omit(vivienda)
gg_miss_var(vivienda_final)

dim(vivienda_final)
## [1] 8319   13

Despues del tratamiento de datos faltantes, el conjunto de datos cuenta con 8319 registros.

6. Estandarización de variables numéricas

vivienda_final_scaled <- vivienda_final %>%
  select(Piso, PrecioM2, AreaConstruida, NParqueaderos, NBaños, NHabitaciones, Longitud, Latitud) %>%
  mutate_all(scale)

Matriz de correlación

library(corrplot)
## corrplot 0.95 loaded
cor_matrix <- cor(vivienda_final_scaled)

# Matriz de correlación
corrplot(cor_matrix, method = "color", col = colorRampPalette(c("blue", "white", "red"))(200),
         addCoef.col = "black", 
         tl.col = "black", tl.srt = 45,
         number.cex = 0.7, 
         cl.pos = "r") 

7. Desarrollo de componentes principales

res.pca <- prcomp(vivienda_final_scaled)
fviz_eig(res.pca, addlabels = TRUE)

# visualización en el plano

fviz_pca_var(res.pca,
             col.var = "contrib", # Color by contributions to the PC
             gradient.cols = c("#FF7F00",  "#034D94"),
             repel = TRUE     # Avoid text overlapping
)

8. Análisis de conglomerados

Para el análisis de conglomerado se selecciona el algoritmo de kmeans y se procede con el metodo del codo para evaluar el número optimo de centroides.

set.seed(123)  # Asegura resultados reproducibles
wss <- vector()  # Lista vacía para guardar los resultados

for (k in 1:10) {
  kmeans_model <- kmeans(vivienda_final_scaled, centers = k, nstart = 25)
  wss[k] <- kmeans_model$tot.withinss  # Suma de errores
}

# Graficar el método del codo
plot(1:10, wss, type = "b", pch = 19, frame = FALSE,
     xlab = "Número de Clusters (K)", ylab = "Suma de errores dentro del cluster",
     main = "Método del Codo para determinar K")

De acuerdo a la grafica del Codo se procede a tener 6 centroides o clusters

#Kmean con 6 centroides
set.seed(123)
km_result <- kmeans(vivienda_final_scaled, centers = 6, nstart = 25)

# Agregar la clasificación de clusters al dataset original
dfkmean <- vivienda_final_scaled
dfkmean <- as.data.frame(dfkmean) 
dfkmean$cluster <- as.factor(km_result$cluster)

Revisión de los clusters en relación al precio por metro cuadrado y el area construida

ggplot(dfkmean, aes(x = PrecioM2, y = AreaConstruida, color = cluster)) +
  geom_point() +
  labs(title = "Resultados de K-means")

Revisión de los clusters en relación al precio por metro cuadrado y el número de habitaciones

ggplot(dfkmean, aes(x = PrecioM2, y =NHabitaciones , color = cluster)) +
  geom_point() +
  labs(title = "Resultados de K-means")

9. Análisis de correspondencia

Variables zona-estrato

library(FactoMineR)
library(gridExtra)
tabla <- table(vivienda_final$Zona, vivienda_final$Estrato)
colnames(tabla) <- c("Estrato3", "Estrato4", "Estrato5", "Estrato6" )
tabla
##               
##                Estrato3 Estrato4 Estrato5 Estrato6
##   Zona Centro       105       14        4        1
##   Zona Norte        572      407      769      172
##   Zona Oeste         54       84      290      770
##   Zona Oriente      340        8        2        1
##   Zona Sur          382     1616     1685     1043
chisq.test(tabla)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
resultados_ac <- CA(tabla)

Revisión varianza

valores_prop <-resultados_ac$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, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
  ylab("Porcentaje de varianza explicado") + xlab("Ejes")

Revisión variables zona-tipo vivienda

tabla2 <- table(vivienda_final$Zona, vivienda_final$TipoVivienda)
colnames(tabla2) <- c("Apartamento","Casa")
tabla2
##               
##                Apartamento Casa
##   Zona Centro           24  100
##   Zona Norte          1198  722
##   Zona Oeste          1029  169
##   Zona Oriente          62  289
##   Zona Sur            2787 1939
chisq.test(tabla2)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla2
## X-squared = 690.93, df = 4, p-value < 2.2e-16
resultados_ac2 <- CA(tabla2)

Revisión varianza

valores_prop <-resultados_ac$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, addlabels = TRUE, ylim = c(0, 90))+ggtitle("")+
  ylab("Porcentaje de varianza explicado") + xlab("Ejes")

Revisión de variables Estrato-tipo vivienda

tabla3 <- table(vivienda_final$Estrato, vivienda_final$TipoVivienda)
colnames(tabla3) <- c("Apartamento","Casa")
tabla3
##    
##     Apartamento Casa
##   3         639  814
##   4        1404  725
##   5        1766  984
##   6        1291  696
chisq.test(tabla3)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla3
## X-squared = 224.33, df = 3, p-value < 2.2e-16
library(gridExtra)
library(FactoMineR)
library(factoextra)
resultados_ac3 <- CA(tabla3)

Revisión varianza

valores_prop <-resultados_ac$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, addlabels = TRUE, ylim = c(0, 90))+ggtitle("")+
  ylab("Porcentaje de varianza explicado") + xlab("Ejes")

10. Conclusiones