Introducción

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:

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 preferencias del mercado.

Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas y demandas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.

Análisis de Correspondencia : Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio) y las variables numéricas (precio, área construida, número de parqueaderos, baños, habitaciones) para identificar patrones de comportamiento del mercado inmobiliario.

Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.

El informe final debe incluir análisis detallados de los resultados obtenidos, las conclusiones clave y las recomendaciones específicas para guiar las decisiones estratégicas de la empresa inmobiliaria. Se espera que este análisis de datos proporcione ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.

Carga y revisión de los datos

head(vivienda)
##     id         zona piso estrato preciom areaconst parqueaderos banios
## 1 1147 Zona Oriente   NA       3     250        70            1      3
## 2 1169 Zona Oriente   NA       3     320       120            1      2
## 3 1350 Zona Oriente   NA       3     350       220            2      2
## 4 5992     Zona Sur    2       4     400       280            3      5
## 5 1212   Zona Norte    1       5     260        90            1      2
## 6 1724   Zona Norte    1       5     240        87            1      3
##   habitaciones        tipo      barrio  longitud latitud
## 1            6        Casa 20 de julio -76.51168 3.43382
## 2            3        Casa 20 de julio -76.51237 3.43369
## 3            4        Casa 20 de julio -76.51537 3.43566
## 4            3        Casa  3 de julio -76.54000 3.43500
## 5            3 Apartamento       acopi -76.51350 3.45891
## 6            3 Apartamento       acopi -76.51700 3.36971
str(vivienda)
## 'data.frame':    8322 obs. of  13 variables:
##  $ id          : int  1147 1169 1350 5992 1212 1724 2326 4386 1209 1592 ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : int  NA NA NA 2 1 1 1 1 2 2 ...
##  $ estrato     : int  3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : int  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: int  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : int  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: int  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr  "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  3.43 3.43 3.44 3.44 3.46 ...

La tabla contiene datos de 8,322 viviendas, registrando 12 variables: zona, piso, estrato, preciom, areaconst, parqueaderos, baños, habitaciones, tipo, barrio, longitud y latitud. Tambien se observa que en la base de datos, hay 9 variables numéricas y 3 de tipo texto. Además, se observan datos faltantes.

Manejo de valores faltantes

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

Como podemos observar existen registros en el dataset que no tienen datos, asi como registros que no tienen una columna en especifico, como por ejemplo, hay 726 registros que no tienen valor en parqueadero ni piso. Estos son los visualizados en rosado, por lo que procederemos a realizar una limpieza de datos.

El conjunto de datos de ofertas de viviendas en OLX incluye 3 variables categóricas (zona, piso, tipo, barrio) y 7 variables numéricas. En este análisis, decidiremos omitir la variable “id”. Esta exclusión se debe a que el enfoque del análisis es identificar patrones mediante un método no supervisado, en el cual la variable “id” no aporta información relevante ni tiene significado semántico para el estudio.

# Eliminar registros donde la columna id = "NA"
vivienda <- subset(vivienda, id != "NA")
# Reemplazar los valores NA por el promedio en la columna parqueaderos con eso no introducimos sesgo en el analisis

media_parqueaderos <- mean(vivienda$parqueaderos, na.rm = TRUE)
media_parqueaderos_redondeada <- round(media_parqueaderos)
vivienda$parqueaderos[is.na(vivienda$parqueaderos)] <- media_parqueaderos_redondeada


# Reemplazar los valores NA por 0 en la columna piso, ya que reemplazarlo por 1 aumentaria el sesgo en esta categoria.
vivienda$piso[is.na(vivienda$piso)] <- 0

# unificar valores Apartamento y apto
vivienda$tipo <- ifelse(vivienda$tipo == "apto", "Apartamento", vivienda$tipo)

Con esto tenemos un nuevo dataset, sin datos faltantes:

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

Reto 1

Análisis de Componentes Principales

Este análisis tiene como objetivo describir la variación observada en \(p\) variables aleatorias mediante un conjunto de nuevas variables, llamadas componentes, que están correlacionadas entre sí. Estas componentes se obtienen a partir de combinaciones lineales de las variables originales.

Se utiliza para complementar los análisis descriptivos y para mejorar los modelos predictivos al reducir el número de variables utilizadas en el modelo.

Seleccionar Variables Numéricas

Convertiremos las variables categóricas a factores

vivienda$zona <- as.factor(vivienda$zona)
vivienda$tipo <- as.factor(vivienda$tipo)
vivienda$barrio <- as.factor(vivienda$barrio)

Luego, Seleccionamos variables numéricas para PCA

vivienda_numeric <- vivienda[, c("estrato", "preciom", "areaconst", "parqueaderos", "banios", "habitaciones")]

Escalar los datos

El PCA por lo general se ve afectado por la escala de las variables, así que estandarizaremos los datos

vivienda_numeric_scaled <- scale(vivienda_numeric)

Por último, realizamos el PCA

# Ejecución PCA
pca_result <- prcomp(vivienda_numeric_scaled, center = TRUE, scale. = TRUE)

# Ver el resumen del PCA
summary(pca_result)
## Importance of components:
##                          PC1    PC2    PC3     PC4     PC5     PC6
## Standard deviation     1.835 1.0958 0.7890 0.62298 0.48690 0.43075
## Proportion of Variance 0.561 0.2001 0.1038 0.06468 0.03951 0.03092
## Cumulative Proportion  0.561 0.7611 0.8649 0.92956 0.96908 1.00000

Visualización de los datos

Varianza explicada

# Calcular la varianza explicada
pca_var <- pca_result$sdev^2
pca_var_prop <- pca_var / sum(pca_var)
pca_var_cumsum <- cumsum(pca_var_prop)

# Crear un data frame para ggplot
pca_var_df <- data.frame(
  Component = 1:length(pca_var_prop),
  Variance_Proportion = pca_var_prop,
  Cumulative_Variance = pca_var_cumsum
)

# Graficar la varianza explicada
ggplot(pca_var_df, aes(x = Component)) +
  geom_line(aes(y = Cumulative_Variance), color = "blue") +
  geom_point(aes(y = Cumulative_Variance), color = "blue") +
  labs(title = "Varianza Explicada por Componentes Principales",
       x = "Componente Principal",
       y = "Proporción Cumulativa de Varianza")

Cargas de componentes principales

# Crear un data frame con las cargas de los componentes principales
pca_loadings <- as.data.frame(pca_result$rotation)

# Ajustar los nombres de las columnas para melt
pca_loadings$Variable <- rownames(pca_loadings)
melted_loadings <- melt(pca_loadings, id.vars = "Variable")

# Graficar las cargas de los componentes principales
ggplot(melted_loadings, aes(x = variable, y = value, fill = variable)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ Variable) +
  labs(title = "Cargas de Componentes Principales",
       x = "Componente Principal",
       y = "Carga")

Proyección en el Espacio de Componentes

# Obtener las coordenadas de los componentes principales
pca_scores <- as.data.frame(pca_result$x)

# Añadir la variable de precios para la visualización
pca_scores$preciom <- vivienda$preciom

# Graficar las proyecciones de los datos
ggplot(pca_scores, aes(x = PC1, y = PC2, color = preciom)) +
  geom_point() +
  labs(title = "Proyección de Datos en los Primeros Dos Componentes Principales",
       x = "Componente Principal 1",
       y = "Componente Principal 2")

Conclusiones

En el Análisis de Componentes Principales (ACP), el examen detallado de los resultados revela una comprensión clave sobre la contribución de cada componente a la variabilidad total. Específicamente, la primera componente principal, que explica el 52.57% de la variabilidad, y la segunda componente principal, con una contribución del 22.2%, en conjunto explican un notable 74.59% de la variabilidad observada. Estos hallazgos destacan la capacidad del ACP para identificar patrones y descomponer las relaciones subyacentes que explican las diferencias en los datos.

Es importante notar que las variables derivadas de las componentes principales ofrecen un enfoque estratégico para futuros análisis y modelización. Por ejemplo, en la selección de modelos de regresión, las dos primeras componentes pueden servir como predictores clave. En contraste, incluir las cinco variables originales en un modelo de regresión múltiple proporciona una visión más completa.

Reto 2

Análisis de Conglomerados

El análisis de conglomerados, o clustering, es una técnica estadística para agrupar objetos o individuos con características similares. Este método permite identificar grupos homogéneos en los que los elementos son más parecidos entre sí que con los de otros grupos. Consiste en seleccionar variables para medir las características y luego usar un algoritmo de agrupamiento para formar los conglomerados.

Preparación del conjunto de datos

Como primer paso seleccionaremos las variables numéricas para el análisis

vivienda2 <- vivienda

Luego, realizamos los siguientes pasos con el dataset nuevamente:

# Usar dummy variables para las variables categóricas
vivienda_dummies <- model.matrix(~ zona + tipo + barrio - 1, data = vivienda2)
vivienda_numeric <- cbind(vivienda[, c("estrato", "preciom", "areaconst", "parqueaderos", "banios", "habitaciones")], vivienda_dummies)

# Estandarizar los datos
vivienda_scaled <- scale(vivienda_numeric)

Determinar el Número Óptimo de Clusters

# Tomar una muestra representativa del conjunto de datos para determinar el número óptimo de clusters
set.seed(123)  # Para reproducibilidad
sample_size <- 1000  # Ajusta según sea necesario
vivienda_sample <- vivienda_scaled[sample(1:nrow(vivienda_scaled), sample_size), ]

# Método del codo
fviz_nbclust(vivienda_sample, kmeans, method = "wss", nstart = 10) +
  labs(title = "Número Óptimo de Clusters - Método del Codo")

# Método del índice de silueta
fviz_nbclust(vivienda_sample, kmeans, method = "silhouette", nstart = 10) +
  labs(title = "Número Óptimo de Clusters - Índice de Silueta")

Aplicar el Algoritmo K-means

# Aplicar k-means con el número óptimo de clusters
set.seed(123)  # Para reproducibilidad
num_clusters <- 3  # Ajusta según el número óptimo encontrado
kmeans_result <- kmeans(vivienda_scaled, centers = num_clusters, nstart = 25)

# Añadir la asignación de clusters al data frame original
vivienda$cluster <- as.factor(kmeans_result$cluster)

# Ver las primeras filas con los clusters asignados
head(vivienda)
##     id         zona piso estrato preciom areaconst parqueaderos banios
## 1 1147 Zona Oriente    0       3     250        70            1      3
## 2 1169 Zona Oriente    0       3     320       120            1      2
## 3 1350 Zona Oriente    0       3     350       220            2      2
## 4 5992     Zona Sur    2       4     400       280            3      5
## 5 1212   Zona Norte    1       5     260        90            1      2
## 6 1724   Zona Norte    1       5     240        87            1      3
##   habitaciones        tipo      barrio  longitud latitud cluster
## 1            6        Casa 20 de julio -76.51168 3.43382       3
## 2            3        Casa 20 de julio -76.51237 3.43369       3
## 3            4        Casa 20 de julio -76.51537 3.43566       3
## 4            3        Casa  3 de julio -76.54000 3.43500       2
## 5            3 Apartamento       acopi -76.51350 3.45891       3
## 6            3 Apartamento       acopi -76.51700 3.36971       3

Visualización de resultados

# Obtener las coordenadas de los componentes principales
pca_scores <- as.data.frame(prcomp(vivienda_scaled)$x)

# Añadir la variable de clusters para la visualización
pca_scores$cluster <- vivienda$cluster

# Graficar los clusters en el espacio de los dos primeros componentes principales
ggplot(pca_scores, aes(x = PC1, y = PC2, color = cluster)) +
  geom_point() +
  labs(title = "Visualización de Clusters en el Espacio de Componentes Principales",
       x = "Componente Principal 1",
       y = "Componente Principal 2")

Análisis de los clusters

cluster_means <- vivienda %>%
  group_by(cluster) %>%
  summarize(across(c(estrato, preciom, areaconst, parqueaderos, banios, habitaciones), mean, na.rm = TRUE))
## Warning: There was 1 warning in `summarize()`.
## ℹ In argument: `across(...)`.
## ℹ In group 1: `cluster = 1`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
# Convertir a formato largo para ggplot2
cluster_means_long <- cluster_means %>%
  pivot_longer(cols = -cluster, names_to = "Característica", values_to = "Valor")

# Graficar las características promedio por cluster
ggplot(cluster_means_long, aes(x = cluster, y = Valor, fill = cluster)) +
  geom_bar(stat = "identity", position = "dodge") +
  facet_wrap(~ Característica, scales = "free_y") +
  labs(title = "Características Promedio por Cluster",
       x = "Cluster",
       y = "Valor Promedio") +
  theme_minimal()

una visualizacion de distribución pos cluster:

# Graficar la distribución de precios por cluster
ggplot(vivienda, aes(x = cluster, y = preciom, color = cluster)) +
  geom_boxplot() +
  labs(title = "Distribución de Precios por Cluster",
       x = "Cluster",
       y = "Precio")

Reto 3

Análisis de Correspondencia

El Análisis de Correspondencia es una técnica estadística que se emplea para visualizar y detectar asociaciones entre variables categóricas, analizando las relaciones entre sus categorías y revelando posibles patrones o estructuras en los datos.

Preparación del conjunto de datos

# Seleccionar las variables categóricas de interés
vivienda_categoricas <- vivienda %>%
  select(tipo, zona, barrio)

# Crear una tabla de contingencia
tabla_contingencia <- table(vivienda_categoricas)

Ejecución del análisis de correspondencia

# Realizar el análisis de correspondencia múltiple sin grupos
acm_resultado <- MCA(vivienda_categoricas, graph = FALSE)

Visualización de los resultados

Gráfico de Factores

# Graficar los factores del análisis de correspondencia múltiple
fviz_mca_biplot(acm_resultado, axes = c(1, 2), 
                title = "Gráfico de Factores del Análisis de Correspondencia Múltiple")

Gráfico de factores

# Graficar las variables categóricas
fviz_mca_var(acm_resultado, choice = "var", axes = c(1, 2), 
              title = "Gráfico de Variables del Análisis de Correspondencia Múltiple")