Evaluación de la oferta inmobiliaria urbana


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.

Retos:

El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:

  1. Análisis de Componentes Principales: 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.

  1. Análisis de Conglomerados: 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.

  1. Análisis de Correspondencia : 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.

Análisis exploratorio de datos:

skim(creditos)
Data summary
Name creditos
Number of rows 8322
Number of columns 13
_______________________
Column type frequency:
character 4
numeric 9
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
zona 3 1.00 8 12 0 5 0
piso 2638 0.68 2 2 0 12 0
tipo 3 1.00 4 11 0 2 0
barrio 3 1.00 4 29 0 436 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id 3 1.00 4160.00 2401.63 1.00 2080.50 4160.00 6239.50 8319.00 ▇▇▇▇▇
estrato 3 1.00 4.63 1.03 3.00 4.00 5.00 5.00 6.00 ▅▆▁▇▆
preciom 2 1.00 433.89 328.65 58.00 220.00 330.00 540.00 1999.00 ▇▂▁▁▁
areaconst 3 1.00 174.93 142.96 30.00 80.00 123.00 229.00 1745.00 ▇▁▁▁▁
parqueaderos 1605 0.81 1.84 1.12 1.00 1.00 2.00 2.00 10.00 ▇▁▁▁▁
banios 3 1.00 3.11 1.43 0.00 2.00 3.00 4.00 10.00 ▇▇▃▁▁
habitaciones 3 1.00 3.61 1.46 0.00 3.00 3.00 4.00 10.00 ▂▇▂▁▁
longitud 3 1.00 -76.53 0.02 -76.59 -76.54 -76.53 -76.52 -76.46 ▁▅▇▂▁
latitud 3 1.00 3.42 0.04 3.33 3.38 3.42 3.45 3.50 ▃▇▅▇▅

Observaciones:

  • El conjunto de datos tiene un tamaño de 13 columnas y 8,322 registros.

  • Este conjunto de datos contiene 9 variables numéricas y 4 variables tipo cadena de texto.

  • Se observa una cantidad representativa de datos faltantes en las variables parqueaderos (1,605) y piso (2,638). En las demás variables no se observa un valor mayor a 3 registros faltantes. Se realiza limpieza de datos eliminando los registros con todas las variables faltantes; a la variable parqueaderos se le asigna el valor cero a los datos faltantes, indicando que los predios no cuentan con parqueadero; a la variable piso no se le realizó tratamiento, conservándose los registros faltantes y utilizándose con criterio limitado en el análisis.

creditos <- creditos %>%                                  # Se borran registros con NA en todas las filas.
  filter(!if_all(everything(), is.na))
creditos$parqueaderos[is.na(creditos$parqueaderos)] <- 0  # Remplazar NA por cero: ningun parquadero.
creditos <- creditos[-8320, ]                             # linea con valores NA.

Análisis de Componentes Principales:

Para iniciar el análisis de componentes principales (PCA), se seleccionaron las variables cuantitativas preciom, areaconst, parqueadero, banios y habitaciones. Posteriormente, se realizó el proceso de estandarización de los datos para asegurar que todas las variables tuvieran la misma escala. A continuación, se generaron las gráficas correspondientes a:

  • La varianza explicada por cada componente principal.

  • La contribución de cada variable en las distintas dimensiones del PCA.

  • Las variables en el plano de los componentes principales.

credi_clear= scale(creditos[, 5:9]) 
res.pca <- prcomp(credi_clear)          #Análisis de Componentes Principales (PCA)
fviz_eig(res.pca, addlabels = TRUE)     # Varianza explicada
fviz_contrib(res.pca, choice = "var", axes = 1)
fviz_contrib(res.pca, choice = "var", axes = 2)
fviz_pca_var(res.pca,
             col.var = "contrib",
             gradient.cols = c("#B3E0A6", "#DECC66", "#9E3D22"),
             repel = TRUE    
            )                           

Conclusiones:

  • Sumando PC1 (62,6%) y PC2 (18,4%), se explica aproximadamente el 81% de la varianza total; por lo tanto, PC1 y PC2 son suficientes para representar la mayor parte de la información.

  • La variable con mayor contribución a la dimensión Dim-1 es banios (~25%), mientras que, para la Dim-2, la más representativa es la variable habitaciones (~60%).

  • La gráfica de variables en el plano de dimensiones permite evidenciar que la variable con mayor contribución es habitaciones, seguida de banios y preciom. Las variables agrupadas revelan una correlación positiva, como es el caso de parqueaderos–preciom y areaconst–banios. Un ángulo de 90 grados entre variables representa una correlación nula, como se observa en la relación entre parqueaderos-habitaciones.

Análisis de Conglomerados:

Para iniciar el análisis de conglomerados, se seleccionaron como variables cuantitativas: preciom, areaconst, parqueadero, baños y habitaciones.Adicionalmente, se incluyeron en el análisis las variables categóricas estrato y tipo, realizando las siguientes transformaciones:

  • Estrato: se agruparon los niveles 3 y 4 en una sola categoría, y los niveles 5 y 6 en otra.

  • Tipo: indica si la propiedad corresponde a un apartamento o a una casa.

Con el fin de evitar que la diferencia de escalas entre variables numéricas afectara la formación de conglomerados, se aplicó un proceso de estandarización a las variables cuantitativas, garantizando que todas presentaran media cero y desviación estándar igual a uno.

A continuación, se generaron las gráficas correspondientes a:

  • Diagrama de índice Silhouette.

  • Dendrogramas de los grupos.

  • Diagrama de dispersión de variables preciom y areaconst vs Cluster.

cre_casa_3_4<- creditos[creditos$tipo == "Casa",]
cre_casa_3_4<- cre_casa_3_4[cre_casa_3_4$estrato == 3|cre_casa_3_4$estrato == 4,]
cre_casa_3_4= scale(cre_casa_3_4[, 5:9])
cre_casa_3_4 <- as.data.frame(cre_casa_3_4)

cre_casa_5_6<- creditos[creditos$tipo == "Casa",]
cre_casa_5_6<- cre_casa_5_6[cre_casa_5_6$estrato == 5|cre_casa_5_6$estrato == 6,]
cre_casa_5_6= scale(cre_casa_5_6[, 5:9])
cre_casa_5_6 <- as.data.frame(cre_casa_5_6)

cre_apt_3_4<- creditos[creditos$tipo == "Apartamento",]
cre_apt_3_4<- cre_apt_3_4[cre_apt_3_4$estrato == 3|cre_apt_3_4$estrato == 4,]
cre_apt_3_4= scale(cre_apt_3_4[, 5:9])
cre_apt_3_4 <- as.data.frame(cre_apt_3_4)

cre_apt_5_6<- creditos[creditos$tipo == "Apartamento",]
cre_apt_5_6<- cre_apt_5_6[cre_apt_5_6$estrato == 5|cre_apt_5_6$estrato == 6,]
cre_apt_5_6= scale(cre_apt_5_6[, 5:9])
cre_apt_5_6 <- as.data.frame(cre_apt_5_6)
p1 <- fviz_nbclust(cre_casa_3_4, hcut, method = "silhouette")+
  ggtitle("Casas estratos 3 y 4")
p2 <- fviz_nbclust(cre_casa_5_6, hcut, method = "silhouette")+
  ggtitle("Casas estratos 5 y 6")
p3 <- fviz_nbclust(cre_apt_3_4, hcut, method = "silhouette")+
  ggtitle("Apartamentos estratos 3 y 4")
p4 <- fviz_nbclust(cre_apt_5_6, hcut, method = "silhouette")+
  ggtitle("Apartamentos estratos 5 y 6")

((p1 | p2) / (p3 | p4)) +
  plot_annotation(title = "Número óptimo de clusters (método Silhouette)",
                  theme = theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5)))

# Casas 3-4
dist_prop <- dist(cre_casa_3_4, method = "euclidean")
hc_prop1 <- hclust(dist_prop, method = "ward.D2")
p1 <- fviz_dend(hc_prop1, show_labels = FALSE, main = "Casas estratos 3 y 4")

# Casas 5-6
dist_prop <- dist(cre_casa_5_6, method = "euclidean")
hc_prop2 <- hclust(dist_prop, method = "ward.D2")
p2 <- fviz_dend(hc_prop2, show_labels = FALSE, main = "Casas estratos 5 y 6")

# Aptos 3-4
dist_prop <- dist(cre_apt_3_4, method = "euclidean")
hc_prop3 <- hclust(dist_prop, method = "ward.D2")
p3 <- fviz_dend(hc_prop3, show_labels = FALSE, main = "Aptos estratos 3 y 4")

# Aptos 5-6
dist_prop <- dist(cre_apt_5_6, method = "euclidean")
hc_prop4 <- hclust(dist_prop, method = "ward.D2")
p4 <- fviz_dend(hc_prop4, show_labels = FALSE, main = "Aptos estratos 5 y 6")

# Combinar en panel
((p1 | p2) / (p3 | p4)) +
  plot_annotation(
    title = "Dendrogramas",
    theme = theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5))
  )
grupo <- cutree(hc_prop1, k = 2)
datos_cluster <- cre_casa_3_4 %>% mutate(cluster = as.factor(grupo))


p1 <- ggplot(datos_cluster, aes(x = preciom, y = areaconst, color = cluster)) +
  geom_point(alpha = 0.5) +
  theme_minimal() +
  ggtitle("Casas estratos 3 y 4")

grupo <- cutree(hc_prop2, k = 2)
datos_cluster <- cre_casa_5_6 %>% mutate(cluster = as.factor(grupo))


p2 <- ggplot(datos_cluster, aes(x = preciom, y = areaconst, color = cluster)) +
  geom_point(alpha = 0.5) +
  theme_minimal()+
  ggtitle("Casas estratos 5 y 6")

grupo <- cutree(hc_prop3, k = 10)
datos_cluster <- cre_apt_3_4 %>% mutate(cluster = as.factor(grupo))


p3 <- ggplot(datos_cluster, aes(x = preciom, y = areaconst, color = cluster)) +
  geom_point(alpha = 0.5) +
  theme_minimal()+
  ggtitle("Apartamentos estratos 3 y 4")

grupo <- cutree(hc_prop4, k = 2)
datos_cluster <- cre_apt_5_6 %>% mutate(cluster = as.factor(grupo))


p4 <- ggplot(datos_cluster, aes(x = preciom, y = areaconst, color = cluster)) +
  geom_point(alpha = 0.5) +
  theme_minimal()+
  ggtitle("Apartamentos estratos 5 y 6")

# Combinar en panel
((p1 | p2) / (p3 | p4)) +
  plot_annotation(
    title = "Diagramas de dispersión: preciom Vs areaconst",
    theme = theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5))
  )

Conclusiones:

  • El método del coeficiente de Silhouette sugiere que, para casas estratos 3 y 4, casas estratos 5 y 6 y apartamentos estratos 5 y 6, el número óptimo de clústeres es k = 2, con valores de Silhouette alrededor de 0,34–0,36, lo que indica una separación aceptable entre grupos. En apartamentos estratos 3 y 4, el máximo teórico del coeficiente se da en k = 10, reflejando mayor heterogeneidad interna.

  • Los dendrogramas muestran que en tres de los cuatro conjuntos de datos existen dos grandes grupos principales, consistentes con el resultado de Silhouette. En apartamentos estratos 3 y 4 se observa una estructura más fragmentada, con múltiples subdivisiones a alturas relativamente bajas, lo que confirma la mayor diversidad interna.

  • En todos los casos, los clústeres se diferencian principalmente por preciom y areaconst: un grupo concentra inmuebles más grandes y caros, y el otro agrupa propiedades más pequeñas y económicas. En apartamentos estratos 3 y 4, la presencia de múltiples clústeres sugiere subsegmentos de mercado más definidos, posiblemente influenciados por factores adicionales como ubicación o características internas.

Análisis de Correspondencia:

Para iniciar el análisis de correspondencia, se seleccionaron las variables cualitativas zona y estrato. Posteriormente, se tomó una muestra de 4.150 registros y se verificó si había datos faltantes.

A continuación, se generaron las gráficas correspondientes a:

  • Diagrama de verificación de datos faltantes.

  • Tabla cruzada de las variables zona y estrato.

  • Mapa factorial del análisis de correspondencia.

  • Varianza explicada por cada componente principal.



Datos faltantes en la variables estrato y zona

creditos <- as.data.frame(creditos)

set.seed(123)
credi_muetra <- sample_n(creditos, 4150) 
credi_muetra$estrato <- as.factor(credi_muetra$estrato)
credi_muetra <-credi_muetra [c("estrato","zona")]

colSums(is.na(credi_muetra ))
estrato    zona 
      0       0 



Tabla cruzada de las variables zona y estrato.

tabla <- table(credi_muetra$zona, credi_muetra$estrato)
colnames(tabla) <- c("Estrato_3", "Estrato_4", "Estrato_5", "Estrato_6" )
tabla
              
               Estrato_3 Estrato_4 Estrato_5 Estrato_6
  Zona Centro         57         7         2         1
  Zona Norte         289       206       346        89
  Zona Oeste          32        41       141       382
  Zona Oriente       168         4         2         1
  Zona Sur           208       795       848       531



Prueba Chi-Cuadrado de Pearson.

chisq.test(tabla)

    Pearson's Chi-squared test

data:  tabla
X-squared = 1823.1, df = 12, p-value < 2.2e-16
resul_ca <- CA(tabla)
fviz_screeplot(resul_ca, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")

Conclusiones:

  • Los dos primeros ejes explican el 98.5% de la varianza, esto indica: El mapa factorial de 2D es altamente confiable para interpretar las relacionesy no se necesitan más dimensiones para explicar los datos

  • Existe una asociación estadísticamente significativa (p < 0.001) entre la zona geográfica y el estrato socioeconómico, confirmada por la prueba de Chi-cuadrado.

  • El análisis estadístico y gráfico confirma una clara relación entre la ubicación geográfica y el estrato socioeconómico. Los resultados muestran que el Estrato 6, correspondiente al nivel más alto, se concentra predominantemente en la Zona Oeste, como lo evidencia tanto la tabla de contingencia (382 casos) como su cercana asociación en el mapa factorial. Por otro lado, los Estratos 4 y 5, están principalmente ubicados en las Zonas Sur y Norte, destacándose la Zona Sur con los mayores registros (795 casos en Estrato 4 y 848 en Estrato 5). Finalmente, el Estrato 3, de nivel socioeconómico más bajo, predomina en las Zonas Oriente y Centro, donde la Zona Oriente registra 168 casos en este estrato y casi ninguna presencia en los estratos superiores.