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.
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.
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
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.
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")]
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
# 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")
# 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")
# 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")
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.
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.
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)
# 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 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
# 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")
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")
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.
# 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)
# Realizar el análisis de correspondencia múltiple sin grupos
acm_resultado <- MCA(vivienda_categoricas, graph = FALSE)
# 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")
# 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")