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.
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>
Identificar la dimensión de los datos
dimension <- dim(vivienda)
dimension
## [1] 8322 13
Revisemos el head del dataset.
head(vivienda)
## # A tibble: 6 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona O… <NA> 3 250 70 1 3 6
## 2 1169 Zona O… <NA> 3 320 120 1 2 3
## 3 1350 Zona O… <NA> 3 350 220 2 2 4
## 4 5992 Zona S… 02 4 400 280 3 5 3
## 5 1212 Zona N… 01 5 260 90 1 2 3
## 6 1724 Zona N… 01 5 240 87 1 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Ver las clases de cada columna en el dataset
sapply(vivienda, class)
## id zona piso estrato preciom areaconst
## "numeric" "character" "character" "numeric" "numeric" "numeric"
## parqueaderos banios habitaciones tipo barrio longitud
## "numeric" "numeric" "numeric" "character" "character" "numeric"
## latitud
## "numeric"
# Filtrar las variables categóricas
categorical_vars <- sapply(vivienda, function(x) is.character(x) | is.factor(x))
categorical_vars_names <- names(vivienda)[categorical_vars]
categorical_vars_names
## [1] "zona" "piso" "tipo" "barrio"
Los datos faltantes del conjunto de catos corresponden a:
# Contar valores faltantes por columna y mostrar resultados
missing_values <- colSums(is.na(vivienda))
cat("Valores faltantes por columna:\n")
## Valores faltantes por columna:
print(missing_values)
## id zona piso estrato preciom areaconst
## 3 3 2638 3 2 3
## parqueaderos banios habitaciones tipo barrio longitud
## 1605 3 3 3 3 3
## latitud
## 3
# Calcular el porcentaje de valores faltantes por columna
missing_percentage <- colSums(is.na(vivienda)) / nrow(vivienda) * 100
missing_percentage
## id zona piso estrato preciom areaconst
## 0.03604903 0.03604903 31.69911079 0.03604903 0.02403268 0.03604903
## parqueaderos banios habitaciones tipo barrio longitud
## 19.28622927 0.03604903 0.03604903 0.03604903 0.03604903 0.03604903
## latitud
## 0.03604903
# Determinar si excluir alguna columna con más del 15% de faltantes
exclude_vars <- names(missing_percentage)[missing_percentage > 15]
exclude_vars
## [1] "piso" "parqueaderos"
# Crear un nuevo dataframe sin las variables "piso" y "parqueaderos"
vivienda_sin_excluir <- vivienda[, !(names(vivienda) %in% c("piso", "parqueaderos"))]
# Eliminar las filas con valores faltantes
vivienda_sin_na <- na.omit(vivienda_sin_excluir)
# Ver el tamaño del dataframe
dim(vivienda_sin_na)
## [1] 8319 11
# Estandarizar los nombres de "Apartamento" y "Casa" en la columna "tipo"
vivienda_sin_na <- vivienda_sin_na %>%
mutate(tipo = case_when(
tolower(tipo) %in% c("apartamento", "apto") ~ "Apartamento",
tolower(tipo) %in% c("casa") ~ "Casa",
TRUE ~ tipo
))
# Verificar los cambios
unique(vivienda_sin_na$tipo)
## [1] "Casa" "Apartamento"
# Paso 1: Dividir la columna 'areaconst' en rangos
vivienda_sin_na$area_rango <- cut(vivienda_sin_na$areaconst,
breaks = c(0, 50, 100, 150, 200, 250, 300, Inf),
labels = c("0-50", "51-100", "101-150", "151-200", "201-250", "251-300", "300+"),
right = FALSE)
# Calcular los valores Q1, Q3 y el IQR por cada rango de área construida
outliers_info <- vivienda_sin_na %>%
group_by(area_rango) %>%
summarise(
Q1 = quantile(preciom, 0.25),
Q3 = quantile(preciom, 0.75),
IQR = IQR(preciom),
lim_inferior = Q1 - 1.5 * IQR,
lim_superior = Q3 + 1.5 * IQR
)
# Filtrar los outliers para cada grupo de área_rango
outliers <- vivienda_sin_na %>%
left_join(outliers_info, by = "area_rango") %>%
filter(preciom < lim_inferior | preciom > lim_superior)
# Ver los outliers
outliers
## # A tibble: 99 × 17
## id zona estrato preciom areaconst banios habitaciones tipo barrio
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 7497 Zona Norte 6 520 98 2 2 Aparta… acopi
## 2 5094 Zona Oeste 6 1700 224 4 3 Aparta… arbol…
## 3 7277 Zona Oeste 6 450 86 2 2 Aparta… arbol…
## 4 7315 Zona Oeste 6 1200 199 5 4 Aparta… arbol…
## 5 5371 Zona Norte 5 440 92 3 3 Aparta… Cali
## 6 3557 Zona Norte 6 435 90 3 3 Aparta… chipi…
## 7 3993 Zona Norte 6 290 48 1 1 Aparta… chipi…
## 8 4112 Zona Norte 5 445 47 1 1 Aparta… chipi…
## 9 5151 Zona Sur 6 1700 250 3 3 Casa ciuda…
## 10 5313 Zona Sur 6 480 95 2 3 Aparta… ciuda…
## # ℹ 89 more rows
## # ℹ 8 more variables: longitud <dbl>, latitud <dbl>, area_rango <fct>,
## # Q1 <dbl>, Q3 <dbl>, IQR <dbl>, lim_inferior <dbl>, lim_superior <dbl>
ggplot(vivienda_sin_na, aes(x = area_rango, y = preciom)) +
geom_boxplot(outlier.colour = "red", outlier.shape = 16, outlier.size = 2) +
labs(title = "Boxplot de precios por rango de área construida",
x = "Rango de Área Construida",
y = "Precio de la propiedad (en miles)") +
theme_minimal()
El Análisis de Componentes Principales (PCA) se utiliza para reducir la dimensionalidad de un conjunto de datos manteniendo la mayor cantidad de variabilidad posible. En este ejercicio, aplicamos PCA a un conjunto de datos inmobiliarios para identificar las principales características que influyen en el precio de las propiedades, como el área construida, el número de baños y habitaciones. Al reducir las variables originales a unos pocos componentes, buscamos simplificar el análisis y descubrir patrones clave que puedan mejorar la toma de decisiones en el mercado inmobiliario.
# Filtrar las columnas numéricas relevantes para el PCA
numerical_data <- vivienda_sin_na %>%
dplyr::select(preciom, areaconst, banios, habitaciones)
# Estandarizar las variables numéricas
numerical_data_scaled <- scale(numerical_data)
library(factoextra)
## Warning: package 'factoextra' was built under R version 4.4.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# Realizar PCA
pca_result <- prcomp(numerical_data_scaled, center = TRUE, scale. = TRUE)
# Visualizar la varianza explicada por cada componente
fviz_eig(pca_result, addlabels = TRUE, ylim = c(0, 100)) +
labs(title = "Varianza Explicada por los Componentes Principales")
# Visualizar las variables
fviz_pca_var(pca_result,
col.var = "contrib", # Color por contribución a la PC
gradient.cols = c("#FF7F00", "#034D94"), # Colores del gradiente
repel = TRUE # Evitar que el texto se sobreponga
)
# Extraer las cargas de las variables
loadings <- pca_result$rotation
# Extraer la varianza explicada de cada componente
explained_variance <- pca_result$sdev^2
# Calcular la contribución de cada variable a cada componente principal
contribution <- (loadings^2) / explained_variance
contribution
## PC1 PC2 PC3 PC4
## preciom 0.08955472 0.128454483 0.005931492 0.1454059
## areaconst 0.37828998 0.023023268 0.727296566 0.2052612
## banios 0.84704272 0.004965739 1.254190410 0.7783418
## habitaciones 0.91912438 3.228407334 0.019836297 0.9309523
Del Análisis de Componentes Principales (PCA) realizado se concluye lo siguiente:
A partir de la visualización de los resultados, se observa que los componentes principales logran capturar gran parte de la variabilidad de los datos, siendo el primer componente el más influyente. El primero explica el 67.69% de la varianza observada en los datos, mientras que el segundo componente explica un 18.74% adicional. Los dos primeros componentes juntos explican el 86.43% de la varianza total, lo que indica que son suficientes para representar de manera adecuada la información contenida en las cuatro variables consideradas.
En (PC1), las variables baños y habitaciones tienen una alta contribución, lo que sugiere que estas dos variables son las que más influyen en la varianza explicada por este componente. En (PC2), la variable areaconst muestra una mayor contribución, indicando que el área construida es el factor principal que influye en este componente.
Aunque PC1 y PC2 explican la mayor parte de la varianza, el tercer y cuarto componente tienen una contribución más baja y representan solo un pequeño porcentaje de la varianza total. A pesar de esto, podrían ser útiles para capturar relaciones complejas no explicadas por las primeras dos componentes.
En cuanto a las variables, se observa que las variables baños y habitaciones tienen un peso significativo en los primeros componentes, lo que podría sugerir que el número de baños y habitaciones tienen un mayor impacto en el comportamiento general de las viviendas dentro de este conjunto de datos.
Recomendaciones: En caso de utilizar este análisis para construir modelos predictivos, sería recomendable utilizar las dos primeras componentes principales, ya que capturan la mayor parte de la información relevante de las variables originales. Para modelos más complejos, también podría considerarse el uso de las cuatro variables originales y luego comparar los resultados de ambos enfoques.
El Análisis de Conglomerados es una técnica estadística utilizada para agrupar objetos similares en grupos o “conglomerados”. En este ejercicio, aplicamos el análisis de conglomerados para segmentar las propiedades inmobiliarias en grupos homogéneos basados en características clave como el precio, el área construida, y el número de baños y habitaciones. El objetivo es identificar patrones de comportamiento en el mercado inmobiliario, lo que permitirá a la empresa inmobiliaria comprender mejor las dinámicas del mercado y tomar decisiones más informadas sobre las propiedades en diferentes segmentos.
# Seleccionar las variables numéricas relevantes para el análisis de conglomerados
numerical_data <- vivienda_sin_na %>%
dplyr::select(preciom, areaconst, banios, habitaciones)
# Estandarizar las variables numéricas
numerical_data_scaled <- scale(numerical_data)
# Evaluar el número óptimo de clusters utilizando tanto el método del codo como el método de la silueta
set.seed(123)
wss <- sapply(1:10, function(k) {
kmeans(numerical_data_scaled, centers = k, nstart = 10)$tot.withinss
})
## Warning: did not converge in 10 iterations
## Warning: did not converge in 10 iterations
## Warning: Quick-TRANSfer stage steps exceeded maximum (= 415950)
## Warning: did not converge in 10 iterations
# Graficar el método del codo
plot(1:10, wss, type = "b", main = "Método del Codo", xlab = "Número de Clusters", ylab = "Suma de cuadrados dentro del cluster")
# Método de la silueta
library(factoextra)
fviz_nbclust(numerical_data_scaled, kmeans, method = "silhouette") +
labs(subtitle = "Método de la Silueta")
# Ajuste del modelo K-means con el número óptimo de clusters (k=2)
set.seed(123) # Para reproducibilidad
kmeans_result <- kmeans(numerical_data_scaled, centers = 2, nstart = 25)
# Añadir los clusters al dataframe original (numerical_data)
numerical_data$Cluster <- factor(kmeans_result$cluster)
# Calcular las medianas de cada variable para cada cluster
medianas_clusters <- aggregate(numerical_data[,1:4], # Seleccionamos solo las columnas numéricas
by = list(numerical_data$Cluster),
FUN = median)
# Mostrar las medianas por cluster
medianas_clusters
## Group.1 preciom areaconst banios habitaciones
## 1 1 260 92.5 2 3
## 2 2 685 296.0 5 4
# Mostrar la distribución de los registros en cada cluster
table(numerical_data$Cluster)
##
## 1 2
## 5784 2535
# Visualización con 'fviz_cluster' (utilizando los clusters identificados)
library(factoextra)
fviz_cluster(list(data = numerical_data_scaled,
cluster = numerical_data$Cluster),
palette = c("#2E9FDF", "#E7B800"),
ellipse.type = "convex",
repel = FALSE,
show.clust.cent = TRUE,
ggtheme = theme_minimal())
El análisis de conglomerados (clustering) realizado a través del algoritmo K-means permitió identificar dos grupos distintos dentro del conjunto de datos de viviendas, basados en las variables de precio, área construida, número de baños y número de habitaciones. El proceso de clustering mostró que el número óptimo de clusters es 2, ya que las métricas de validación, como el método del codo y la silueta, indicaron que esta cantidad maximiza la separabilidad entre los grupos.
Los dos clusters obtenidos presentan características claras que los diferencian:
Cluster 1: Este grupo está compuesto por viviendas de menor precio, con un área construida más pequeña y un menor número de baños y habitaciones. Estas viviendas probablemente corresponden a propiedades más pequeñas o con menos comodidades.
Cluster 2: Este grupo agrupa viviendas de mayor precio, con áreas construidas más grandes y un mayor número de baños y habitaciones, lo que podría reflejar propiedades más grandes o de mayor calidad. La segmentación de las viviendas en estos dos clusters es útil para comprender mejor los patrones de comportamiento dentro del mercado inmobiliario, ya que refleja diferencias clave en las características físicas y el precio de las propiedades. Este análisis puede ser utilizado para tomar decisiones informadas en la fijación de precios, la identificación de segmentos de mercado específicos o en la formulación de estrategias de marketing más dirigidas.
En resumen, el análisis de conglomerados proporcionó una visión clara de cómo se agrupan las viviendas según sus características, lo cual puede ser valioso tanto para análisis de mercado como para el desarrollo de modelos predictivos más precisos.
El Análisis de Correspondencia es una técnica estadística utilizada para explorar la relación entre variables categóricas. En este ejercicio, aplicamos el análisis de correspondencia para examinar las relaciones entre variables como el tipo de propiedad, la zona y estrato. Esta técnica permite visualizar asociaciones entre las categorías y entender cómo se distribuyen las propiedades en función de sus características, facilitando la identificación de patrones de comportamiento que puedan influir en las decisiones estratégicas del mercado inmobiliario.
library(dplyr)
library(FactoMineR)
## Warning: package 'FactoMineR' was built under R version 4.4.2
library(factoextra)
# Limpiar los datos eliminando filas con valores faltantes
vivienda_categoricas_clean <- vivienda %>%
filter(!is.na(zona), !is.na(estrato), !is.na(tipo))
# Convertir las variables 'zona', 'estrato', y 'tipo' a factores
vivienda_categoricas_clean$zona <- as.factor(vivienda_categoricas_clean$zona)
vivienda_categoricas_clean$estrato <- as.factor(vivienda_categoricas_clean$estrato)
vivienda_categoricas_clean$tipo <- as.factor(vivienda_categoricas_clean$tipo)
# Verificar las conversiones
sapply(vivienda_categoricas_clean[, c("zona", "estrato", "tipo")], class)
## zona estrato tipo
## "factor" "factor" "factor"
library(dplyr)
# Limpiar los datos: eliminar filas con valores faltantes
vivienda_clean <- vivienda %>%
filter(!is.na(zona), !is.na(estrato))
# Crear la tabla de contingencia entre zona y estrato
tabla_contingencia <- table(vivienda_clean$zona, vivienda_clean$estrato)
# Visualizar la tabla de contingencia
print(tabla_contingencia)
##
## 3 4 5 6
## 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
# Realizar la prueba de Chi-cuadrado
chisq_result <- chisq.test(tabla_contingencia)
# Ver el resultado de la prueba de Chi-cuadrado
chisq_result
##
## Pearson's Chi-squared test
##
## data: tabla_contingencia
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
Dado que el valor p es mucho menor que 0.05, rechazamos la hipótesis nula de independencia. Esto indica que existe una relación significativa entre las variables zona y estrato en el conjunto de datos de las viviendas. En otras palabras, el tipo de estrato y la zona de la vivienda están asociadas y no son independientes.
Puedes proceder a analizar esta relación en profundidad, buscando patrones específicos o realizando más pruebas para explorar cómo se comportan conjuntamente estas dos variables.
# Realizar el Análisis de Correspondencia (CA) sobre la tabla de contingencia
ac_result <- CA(tabla_contingencia)
# Visualizar el biplot del Análisis de Correspondencia
fviz_ca_biplot(ac_result,
col.row = "blue", # Colorear las categorías de las filas (zona) en azul
col.col = "red", # Colorear las categorías de las columnas (estrato) en rojo
title = "Análisis de Correspondencia entre Zona y Estrato")
# Calcular los valores propios y el porcentaje de varianza explicada
valores_prop <- ac_result$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
# Crear el grafico para visualizar la varianza explicada
fviz_screeplot(ac_result,
addlabels = TRUE,
ylim = c(0, 80),
title = "Scree Plot del Análisis de Correspondencia",
ylab = "Porcentaje de varianza explicado",
xlab = "Ejes")
El Análisis de Correspondencia realizado entre las variables categóricas “zona”, “estrato” y “tipo de vivienda” ha revelado patrones significativos en las relaciones entre estas variables. Los resultados obtenidos a través de la descomposición en dimensiones nos proporcionan una representación efectiva de la variabilidad contenida en los datos.
Varianza explicada: Las dos primeras dimensiones juntas explican un 96.6% de la varianza total, lo que indica que estos dos ejes son suficientes para capturar la mayor parte de la información relevante del análisis. La dimensión 1 explica un 70% de la varianza, mientras que la dimensión 2 contribuye con 26.6%. Esto sugiere que las relaciones entre las categorías de las variables “zona”, “estrato” y “tipo de vivienda” pueden ser analizadas de manera efectiva utilizando solo estas dos dimensiones, con una contribución marginal de las siguientes dimensiones.
Representación gráfica: El biplot generado para las dos primeras dimensiones permitió visualizar cómo se distribuyen las categorías de “zona”, “estrato” y “tipo” dentro de este espacio bidimensional. Se observó que las zonas geográficas (Centro, Norte, Oeste, Oriente y Sur) están claramente agrupadas, lo que sugiere que las relaciones espaciales y las características del estrato y tipo de vivienda son significativas al definir las agrupaciones.
Significado de las dimensiones: Las primeras dos dimensiones son altamente representativas, capturando la mayor parte de la variabilidad de los datos. Esto implica que las relaciones entre las variables categóricas analizadas pueden ser interpretadas a partir de estas dimensiones, permitiendo hacer inferencias sobre cómo se distribuyen los tipos de viviendas en diferentes zonas y estratos.
Importancia del análisis: Este análisis proporciona una comprensión clara de cómo se distribuyen las viviendas según su tipo, ubicación y estrato, permitiendo la identificación de patrones que podrían ser útiles para estudios de mercado, toma de decisiones urbanísticas y análisis de accesibilidad a servicios. Además, la alta varianza explicada por las primeras dimensiones demuestra que el análisis de correspondencia es una herramienta eficaz para reducir la dimensionalidad sin perder información clave.
En resumen, los resultados sugieren que las dimensiones seleccionadas capturan adecuadamente la variabilidad de las variables categóricas, permitiendo realizar un análisis profundo y accesible para estudios futuros sobre el mercado inmobiliario