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.
Durante esta etapa vamos a realizar el analisis exploratorio de los datos donde la limpieza de datos es un proceso crucial en el análisis de datos que implica la identificación, corrección y eliminación de errores, inconsistencias y valores atípicos. Este proceso es fundamental para garantizar la calidad y confiabilidad de los datos antes de realizar análisis estadísticos o modelos predictivos.
# Importar Librerias
library(paqueteMODELOS)
library(dplyr)
library(naniar)
library(ggplot2)
library(leaflet)
library(tidyr)
library(mice)
library(FactoMineR)
library(factoextra)
library(ggplot2)
library(factoextra)
# Cargar los datos
data("vivienda")
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:8322] 1147 1169 1350 5992 1212 ...
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
## $ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
# Definir las características (features) excluyendo 'preciom'
features <- dplyr::select(vivienda, -preciom, -id)
# Definir la variable objetivo (target)
target <- vivienda$preciom
# Conteo de valores NA por variable y graficar datos faltantes
DataNA <- features %>%
summarise_all(~sum(is.na(.))) %>%
pivot_longer(cols = everything(), names_to = "variable", values_to = "n_missings")
print(DataNA)
## # A tibble: 11 × 2
## variable n_missings
## <chr> <int>
## 1 zona 3
## 2 piso 2638
## 3 estrato 3
## 4 areaconst 3
## 5 parqueaderos 1605
## 6 banios 3
## 7 habitaciones 3
## 8 tipo 3
## 9 barrio 3
## 10 longitud 3
## 11 latitud 3
gg_miss_var(features)
De la grafica se peude determinar que Piso y Parqueaderos son los que tienen mayor numero de de datos faltantes, se realizara un analisis de estos datos faltante vs el precio promedio de venta para ver cuanto puede afectar la imputacion de estos datos.
# Imputación de valores faltantes con una nueva categoría "Desconocido"
features <- features %>%
mutate(parqueaderos = ifelse(is.na(parqueaderos), "Desconocido", parqueaderos),
piso = ifelse(is.na(piso), "Desconocido", piso))
# Graficar la distribución de 'preciom' según valores faltantes en 'parqueaderos' y 'piso'
ggplot(features, aes(x = parqueaderos == "Desconocido", y = target)) +
geom_boxplot() +
labs(x = "Parqueaderos desconocidos", y = "Precio", title = "Distribución de precios según parqueaderos desconocidos")
ggplot(features, aes(x = piso == "Desconocido", y = target)) +
geom_boxplot() +
labs(x = "Piso desconocido", y = "Precio", title = "Distribución de precios según piso desconocido")
Una forma efectiva de manejar estos datos faltantes en variables categóricas, como “piso” o “parqueadero”, es crear una nueva categoría que represente estos valores faltantes en lugar de eliminarlos. Esto permite conservar todas las observaciones en el dataset sin perder información.
# Mostrar los valores únicos de 'piso' y 'parqueaderos'
valores_unicos_piso <- unique(features$piso)
print(valores_unicos_piso)
## [1] "Desconocido" "02" "01" "03" "04"
## [6] "05" "06" "07" "08" "09"
## [11] "10" "11" "12"
valores_unicos_parq <- unique(features$parqueaderos)
print(valores_unicos_parq)
## [1] "1" "2" "3" "Desconocido" "4"
## [6] "7" "5" "8" "6" "9"
## [11] "10"
Para las variables numericas y categoricas restantes los datos faltantes son pocos estos se imputaran por la medidana y la moda.
# Imputar valores faltantes en otras variables numéricas con la mediana
features <- features %>%
mutate(across(c(areaconst, banios, habitaciones, longitud, latitud),
~ifelse(is.na(.), median(., na.rm = TRUE), .)))
# Imputar con la moda
mode_imputation <- function(x) {
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
features <- features %>%
mutate(
zona = ifelse(is.na(zona), mode_imputation(zona), zona),
estrato = ifelse(is.na(estrato), mode_imputation(estrato), estrato),
tipo = ifelse(is.na(tipo), mode_imputation(tipo), tipo),
barrio = ifelse(is.na(barrio), mode_imputation(barrio), barrio)
)
Verificar que el dataset no tenga datos faltantes:
# Recuento final de valores NA
DataNA <- features %>%
summarise_all(~sum(is.na(.))) %>%
pivot_longer(cols = everything(), names_to = "variable", values_to = "n_missings")
print(DataNA)
## # A tibble: 11 × 2
## variable n_missings
## <chr> <int>
## 1 zona 0
## 2 piso 0
## 3 estrato 0
## 4 areaconst 0
## 5 parqueaderos 0
## 6 banios 0
## 7 habitaciones 0
## 8 tipo 0
## 9 barrio 0
## 10 longitud 0
## 11 latitud 0
# Gráfico de barras para 'zona'
ggplot(features, aes(x = zona)) +
geom_bar() +
labs(title = "Distribución de Zona", x = "Zona", y = "Frecuencia") +
theme_minimal()
# Gráfico de barras para 'estrato'
ggplot(features, aes(x = as.factor(estrato))) +
geom_bar() +
labs(title = "Distribución de Estrato", x = "Estrato", y = "Frecuencia") +
theme_minimal()
# Gráfico de barras para 'tipo'
ggplot(features, aes(x = tipo)) +
geom_bar() +
labs(title = "Distribución de Tipo de Vivienda", x = "Tipo", y = "Frecuencia") +
theme_minimal()
El PCA se utilizó para reducir la dimensionalidad del conjunto de datos y revelar la estructura subyacente de las variables que influyen en la variabilidad de los precios de las viviendas. Se seleccionaron variables numéricas: estrato, areaconst, banios, habitaciones, longitud, y latitud.
# Seleccionar las variables numéricas, excluyendo 'preciom'
features_numeric <- features %>%
select_if(is.numeric) %>%
na.omit() # Eliminar NA antes de la estandarización
# Verificar el resultado
head(features_numeric)
## # A tibble: 6 × 6
## estrato areaconst banios habitaciones longitud latitud
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 3 70 3 6 -76.5 3.43
## 2 3 120 2 3 -76.5 3.43
## 3 3 220 2 4 -76.5 3.44
## 4 4 280 5 3 -76.5 3.44
## 5 5 90 2 3 -76.5 3.46
## 6 5 87 3 3 -76.5 3.37
Los datos fueron estandarizados antes de aplicar el PCA para asegurar que todas las variables contribuyeran equitativamente al análisis.
# Estandarizar los datos
viviendaZ <- scale(features_numeric)
res.pca <- prcomp(viviendaZ)
fviz_eig(res.pca, addlabels = TRUE)
# Realizar PCA
pca_result <- PCA(viviendaZ, scale.unit = TRUE, ncp = 5, graph = FALSE)
# Visualización de componentes principales
fviz_pca_var(pca_result, col.var = "cos2", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE)
El PCA reveló que los dos primeros componentes principales explican aproximadamente el 65.3% de la varianza total (Dim1: 40.6%, Dim2: 24.7%). El primer componente está altamente correlacionado con variables relacionadas al tamaño de la propiedad como areaconst, habitaciones y baños. El segundo componente refleja variaciones en la ubicación geográfica (longitud, latitud) y en la calidad de vida representada por el estrato.
Las variables de tamaño (habitaciones, baños) y estrato tienen un impacto significativo en la variabilidad del precio de las propiedades.
Este resultado sugiere que la empresa debe centrar sus estrategias de valoración y promoción de propiedades en estos factores para maximizar el valor percibido y la competitividad en el mercado.
El Análisis de Conglomerados se llevó a cabo utilizando el método K-means para identificar segmentos de mercado homogéneos basados en características similares de las propiedades.
# Método del Codo para determinar el número óptimo de clusters
fviz_nbclust(viviendaZ, kmeans, method = "wss")
# Método de la Silueta
fviz_nbclust(viviendaZ, kmeans, method = "silhouette")
# Aplicar K-means con el número óptimo de clusters (por ejemplo, 3)
set.seed(123) # Para reproducibilidad
km_res <- kmeans(viviendaZ, centers = 3, nstart = 25)
# Visualizar los clusters en el espacio PCA
fviz_cluster(km_res, data = viviendaZ, ellipse.type = "norm", geom = "point", stand = FALSE, palette = "jco")
Se determinó el número óptimo de clusters usando el método del codo son 3 y el método de la silueta indica 2. La discrepancia entre k=2 y k=3 indica que hay una posible estructura en los datos que podría ser capturada por 2 clusters con alta calidad (cohesión interna), pero visualmente (y tal vez en términos de interpretación de los segmentos del mercado), 3 clusters también podrían ser una opción válida. En la práctica, elegir entre k=2 o k=3 dependería del balance entre la simplicidad del modelo y la necesidad de una segmentación más detallada.
Para este caso de analisis se determino el uso de K=3.
Cada cluster representa un segmento de mercado distinto, con necesidades y preferencias particulares. La empresa puede diseñar estrategias de marketing personalizadas para cada segmento, como:
El Análisis de Correspondencia se utilizó para investigar las relaciones entre variables categóricas como zona, banios, habitaciones, y tipo de vivienda.
# Cálculo de la tabla de correspondencia
tabla_contingencia <- table(features$zona, features$banios)
print(tabla_contingencia)
##
## 0 1 2 3 4 5 6 7 8 9 10
## Zona Centro 3 16 43 26 19 11 4 0 1 1 0
## Zona Norte 12 186 811 438 270 130 48 11 11 1 2
## Zona Oeste 7 38 237 315 350 185 43 19 2 2 0
## Zona Oriente 5 68 108 78 52 19 13 2 4 1 1
## Zona Sur 18 188 1747 1139 765 545 206 75 30 10 6
# Prueba X2 para determinar si existe o no una asociación significativa entre las variables
chisq.test(tabla_contingencia)
##
## Pearson's Chi-squared test
##
## data: tabla_contingencia
## X-squared = 560.98, df = 40, p-value < 2.2e-16
# Análisis de correspondencia
resultados_ac <- CA(tabla_contingencia)
# Verifica que resultados_ac tiene dimensiones y variables
print(resultados_ac)
## **Results of the Correspondence Analysis (CA)**
## The row variable has 5 categories; the column variable has 11 categories
## The chi square of independence between the two variables is equal to 560.9798 (p-value = 4.369821e-93 ).
## *The results are available in the following objects:
##
## name description
## 1 "$eig" "eigenvalues"
## 2 "$col" "results for the columns"
## 3 "$col$coord" "coord. for the columns"
## 4 "$col$cos2" "cos2 for the columns"
## 5 "$col$contrib" "contributions of the columns"
## 6 "$row" "results for the rows"
## 7 "$row$coord" "coord. for the rows"
## 8 "$row$cos2" "cos2 for the rows"
## 9 "$row$contrib" "contributions of the rows"
## 10 "$call" "summary called parameters"
## 11 "$call$marge.col" "weights of the columns"
## 12 "$call$marge.row" "weights of the rows"
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.0456251374 67.683793 67.68379
## dim 2 0.0182336297 27.049151 94.73294
## dim 3 0.0026529861 3.935641 98.66858
## dim 4 0.0008974969 1.331415 100.00000
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
fviz_ca_biplot(resultados_ac, repel = TRUE, ggtheme = theme_minimal())
# Cálculo de la tabla de correspondencia
tabla_contingencia <- table(features$zona, features$habitaciones)
print(tabla_contingencia)
##
## 0 1 2 3 4 5 6 7 8 9 10
## Zona Centro 3 3 12 28 19 20 11 9 8 7 4
## Zona Norte 29 17 220 1008 342 146 60 42 29 14 13
## Zona Oeste 8 16 173 622 272 64 22 6 5 6 4
## Zona Oriente 6 2 31 89 52 34 42 31 32 26 6
## Zona Sur 20 21 490 2353 1044 415 183 85 64 30 24
# Prueba X2 para determinar si existe o no una asociación significativa entre las variables
chisq.test(tabla_contingencia)
##
## Pearson's Chi-squared test
##
## data: tabla_contingencia
## X-squared = 721.79, df = 40, p-value < 2.2e-16
# Análisis de correspondencia
resultados_ac <- CA(tabla_contingencia)
# Verifica que resultados_ac tiene dimensiones y variables
print(resultados_ac)
## **Results of the Correspondence Analysis (CA)**
## The row variable has 5 categories; the column variable has 11 categories
## The chi square of independence between the two variables is equal to 721.7885 (p-value = 6.225371e-126 ).
## *The results are available in the following objects:
##
## name description
## 1 "$eig" "eigenvalues"
## 2 "$col" "results for the columns"
## 3 "$col$coord" "coord. for the columns"
## 4 "$col$cos2" "cos2 for the columns"
## 5 "$col$contrib" "contributions of the columns"
## 6 "$row" "results for the rows"
## 7 "$row$coord" "coord. for the rows"
## 8 "$row$cos2" "cos2 for the rows"
## 9 "$row$contrib" "contributions of the rows"
## 10 "$call" "summary called parameters"
## 11 "$call$marge.col" "weights of the columns"
## 12 "$call$marge.row" "weights of the rows"
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.074547379 85.950844 85.95084
## dim 2 0.006698030 7.722623 93.67347
## dim 3 0.003381309 3.898545 97.57201
## dim 4 0.002105856 2.427988 100.00000
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80))+ggtitle("")+
ylab("Porcentaje de varianza explicado") + xlab("Ejes")
fviz_ca_biplot(resultados_ac, repel = TRUE, ggtheme = theme_minimal())
# Cálculo de la tabla de correspondencia
tabla_contingencia <- table(features$zona, features$tipo)
print(tabla_contingencia)
##
## Apartamento Casa
## Zona Centro 24 100
## Zona Norte 1198 722
## Zona Oeste 1029 169
## Zona Oriente 62 289
## Zona Sur 2790 1939
# Prueba X2 para determinar si existe o no una asociación significativa entre las variables
chisq.test(tabla_contingencia)
##
## Pearson's Chi-squared test
##
## data: tabla_contingencia
## X-squared = 690.79, df = 4, p-value < 2.2e-16
# Análisis de correspondencia
resultados_ac <- CA(tabla_contingencia)
# Verifica que resultados_ac tiene dimensiones y variables
print(resultados_ac)
## **Results of the Correspondence Analysis (CA)**
## The row variable has 5 categories; the column variable has 2 categories
## The chi square of independence between the two variables is equal to 690.787 (p-value = 3.444123e-148 ).
## *The results are available in the following objects:
##
## name description
## 1 "$eig" "eigenvalues"
## 2 "$col" "results for the columns"
## 3 "$col$coord" "coord. for the columns"
## 4 "$col$cos2" "cos2 for the columns"
## 5 "$col$contrib" "contributions of the columns"
## 6 "$row" "results for the rows"
## 7 "$row$coord" "coord. for the rows"
## 8 "$row$cos2" "cos2 for the rows"
## 9 "$row$contrib" "contributions of the rows"
## 10 "$call" "summary called parameters"
## 11 "$call$marge.col" "weights of the columns"
## 12 "$call$marge.row" "weights of the rows"
valores_prop <-resultados_ac$eig
print(valores_prop)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08300733 100 100
Se construyeron tablas de contingencia para analizar la asociación entre zona y el número de banios, habitaciones, y tipo de vivienda. Las pruebas de Chi-cuadrado confirmaron asociaciones significativas entre las variables (p-value < 2.2e-16). Los mapas biplot del análisis de correspondencia mostraron agrupaciones claras, como la fuerte asociación entre la Zona Norte y propiedades con mayor número de banios y habitaciones, o la predominancia de Casas en la Zona Oriente.
El análisis revela patrones de comportamiento en la oferta de propiedades según la zona, destacando cómo ciertas características son más comunes en determinadas áreas. La empresa puede usar esta información para orientar sus esfuerzos de adquisición y desarrollo de proyectos en zonas donde ciertas características de las propiedades están en alta demanda.
Optimización de la Oferta: Concentrarse en las características de las propiedades que tienen un impacto significativo en el precio y la preferencia del mercado, como el tamaño y la ubicación.
Segmentación de Mercado: Desarrollar estrategias de marketing dirigidas a los diferentes segmentos identificados en el análisis de conglomerados, personalizando las ofertas según las características y necesidades de cada grupo.
Planificación Geográfica: Aprovechar las asociaciones descubiertas en el análisis de correspondencia para focalizar las inversiones en áreas donde la demanda de ciertas características es más alta, optimizando la rentabilidad de los proyectos inmobiliarios.