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.
estrato,
ya que al codificarla numéricamente como variable activa distorsiona las
correlaciones).tipo, zona y
estrato, evaluando además si conviene incluir
barrio.suppressMessages({
library(dplyr)
library(ggplot2)
library(FactoMineR)
library(factoextra)
library(corrplot)
library(cluster)
library(RColorBrewer)
library(Hmisc)
library(scales)
library(gridExtra)
})
set.seed(123)
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
library(paqueteMODELOS)
data("vivienda")
El conjunto de datos consta de 8.322 registros de propiedades residenciales en oferta en la ciudad de Cali, Colombia, con las siguientes variables originales: identificador, zona, piso, estrato socio-económico (3 a 6), precio de venta (millones de pesos), área construida (m²), número de parqueaderos, baños y habitaciones, tipo de vivienda (casa/apartamento), barrio y coordenadas geográficas (longitud/latitud).
# Resumen de tipos de variables
data.frame(
Clase = sapply(vivienda, class),
Faltantes = colSums(is.na(vivienda)),
pct_Faltantes = round(colMeans(is.na(vivienda)) * 100, 2),
Valores_Unicos = sapply(vivienda, function(x) {
if (is.factor(x) || is.character(x)) {
length(unique(x))
} else {
'Na'
}})
)
Los datos faltantes no están distribuidos uniformemente.La
variablepiso tiene aproximadamente el 32% de datos
faltantes, parqueaderos tiene 19.29% de faltantes y se
interpreta en este caso como ausencia de parqueadero.
Preparamos los datos de vivienda para un análisis estadístico,
estandarizando variables categóricas y normalizando texto para evitar
problemas por diferencias en escritura. Eliminamos registros con valores
faltantes que no representan una cantidad significativa:
preciom, areaconst, banios,
habitaciones, tipo, zona y
estrato. Imputamos los valores nulos de la variable
parqueaderos con cero como un indicador de falta de
parqueadero en la vivienda; creamos una métrica de utilidad (precios por
metro cuadrado) y reducimos la complejidad de la variable barrio pasando
de 437 categorías a 15 categorías donde las primeras 14 corresponden a
los barrios más mencionados en los registros y la quinceava
‘Otros’, facilitando así el análisis estadísticos y las
visualizaciones.
vivienda$zona <- factor(vivienda$zona)
vivienda$tipo <- factor(vivienda$tipo)
vivienda$estrato <- factor(vivienda$estrato, levels = 3:6, ordered = TRUE)
## Estandarización y Normalizamos de la información de la variable Barrio
normaliza_txt <- function(x){
x <- iconv(x, from = "", to = "UTF-8", sub = "byte")
x <- tolower(trimws(x))
x <- gsub("[\u00e1]", "a", x); x <- gsub("[\u00e9]", "e", x)
x <- gsub("[\u00ed]", "i", x); x <- gsub("[\u00f3]", "o", x)
x <- gsub("[\u00fa]", "u", x); x <- gsub("[\u00f1]", "n", x)
x <- gsub("\\s+", " ", x)
x
}
vivienda$barrio <- factor(normaliza_txt(vivienda$barrio))
vivienda_c <- vivienda %>%
filter(!is.na(preciom), !is.na(areaconst), !is.na(banios),
!is.na(habitaciones), !is.na(tipo), !is.na(zona), !is.na(estrato))
vivienda_c$parqueaderos[is.na(vivienda_c$parqueaderos)] <- 0
## Variable derivada: precio por metro cuadrado
vivienda_c$precio_m2 <- vivienda_c$preciom * 1e6 / vivienda_c$areaconst
top_barrios <- names(sort(table(vivienda_c$barrio), decreasing = TRUE))[1:14]
vivienda_c$barrio_agr <- as.character(vivienda_c$barrio)
vivienda_c$barrio_agr[!(vivienda_c$barrio_agr %in% top_barrios)] <- "Otros barrios"
vivienda_c$barrio_agr <- factor(vivienda_c$barrio_agr)
Por medio de visualizaciones y estadísticas descriptivas buscamos observar el comportamiento de las variables del conjunto de datos que describen el mercado inmobiliario en la ciudad de Cali.
var_num <- c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones", "precio_m2")
sapply(vivienda_c[, var_num], function(x) round(c(media = mean(x), sd = sd(x), asimetria = mean((x-mean(x))^3)/sd(x)^3),2))
## preciom areaconst parqueaderos banios habitaciones precio_m2
## media 433.90 174.93 1.48 3.11 3.61 2722216.95
## sd 328.67 142.96 1.24 1.43 1.46 1082160.59
## asimetria 1.85 2.69 1.65 0.93 1.63 0.68
En la siguiente gráfica se observa que Precio,
área y precio por m² tienen distribuciones
asimétricas a la derecha, considerando la mediana de precio $330 M vs.el
promedio del precio en $434 M.
p1 <- ggplot(vivienda_c, aes(x = preciom)) +
geom_histogram(bins = 50, fill = "#2166AC") +
scale_x_continuous(labels = comma) +
labs(title = "Precio de venta", x = "Millones $", y = NULL) +
theme_minimal(base_size = 10)
p2 <- ggplot(vivienda_c, aes(x = areaconst)) +
geom_histogram(bins = 50, fill = "#B2182B") +
labs(title = "Área construida", x = "m²", y = NULL) +
theme_minimal(base_size = 10)
p3 <- ggplot(vivienda_c, aes(x = precio_m2)) +
geom_histogram(bins = 50, fill = "#4DAF4A") +
scale_x_continuous(labels = comma) +
labs(title = "Precio por m²", x = "$/m²", y = NULL) +
theme_minimal(base_size = 10)
grid.arrange(p1, p2, p3, nrow = 3)
El mercado está dominado por apartamentos con un 61%, se observa que la Zona Sur concentra más de la mitad de la oferta y los estratos 4 y 5 representan cerca del 59% de las propiedades. El estrato 3 y la Zona Centro/Oriente son los segmentos menos representados.
p4 <- ggplot(vivienda_c, aes(x = tipo, fill = tipo)) +
geom_bar() + scale_fill_manual(values = c("#2166AC","#B2182B")) +
labs(title = "Tipo de vivienda", x = NULL, y = NULL) +
theme_minimal(base_size = 10) + theme(legend.position = "none")
p5 <- ggplot(vivienda_c, aes(x = reorder(zona, zona, function(x) -length(x)), fill = zona)) +
geom_bar() + scale_fill_brewer(palette = "Blues") +
labs(title = "Zona", x = NULL, y = NULL) +
theme_minimal(base_size = 10) + theme(legend.position = "none",
axis.text.x = element_text(angle = 20, hjust = 1))
p6 <- ggplot(vivienda_c, aes(x = estrato, fill = estrato)) +
geom_bar() + scale_fill_brewer(palette = "Blues", direction = -1) +
labs(title = "Estrato", x = NULL, y = NULL) +
theme_minimal(base_size = 10) + theme(legend.position = "none")
grid.arrange(p4, p5, p6, nrow= 3)
En cuanto a la composición de la vivienda, la oferta se concentra en 1-2 parqueaderos, 2-3 baños y 3 habitaciones: el perfil “típico” de la ciudad es una vivienda de tamaño medio orientada a hogares familiares estándar.
p5 <- ggplot(vivienda_c, aes(x = factor(parqueaderos))) +
geom_bar(fill = "#2166AC") +
labs(title = "Parqueaderos", x = "N° parqueaderos", y = "N° propiedades") +
theme_minimal(base_size = 11)
p6 <- ggplot(vivienda_c, aes(x = factor(banios))) +
geom_bar(fill = "#B2182B") +
labs(title = "Baños", x = "N° baños", y = "N° propiedades") +
theme_minimal(base_size = 11)
p7 <- ggplot(vivienda_c, aes(x = factor(habitaciones))) +
geom_bar(fill = "#4DAF4A") +
labs(title = "Habitaciones", x = "N° habitaciones", y = "N° propiedades") +
theme_minimal(base_size = 11)
gridExtra::grid.arrange(p5, p6, p7, nrow = 3)
En la búsqueda de valores atípicos bajo la regla clásica del IQR, se obtuvo que el precio y el precio por m² presentan la mayor proporción de valores atípicos. No se considera la eliminación de estos registros ya que son propiedades reales de alto valor que van concorde al comportamiento observado en ambas variables .
detecta_outliers <- function(x) {
q <- quantile(x, c(.25, .75))
iqr <- diff(q)
lim_inf <- q[1] - 1.5 * iqr
lim_sup <- q[2] + 1.5 * iqr
sum(x < lim_inf | x > lim_sup)
}
data.frame(
variable = var_num,
num_outliers_iqr = sapply(vivienda_c[, var_num], detecta_outliers),
pct_outliers = round(100 * sapply(vivienda_c[, var_num], detecta_outliers) / nrow(vivienda_c), 2)
)
El precio de venta está fuertemente correlacionado con el área construida, la cantidad de baños y el número de parqueaderos. Por otro lado el precio por m² tiene una correlación negativa con el área y con el número de habitaciones.
mcor <- cor(vivienda_c[, var_num])
corrplot(mcor, method = "color", type = "upper", addCoef.col = "black",
tl.col = "black", number.cex = 0.8, tl.srt = 45,
col = colorRampPalette(c("#B2182B", "white", "#2166AC"))(200))
La relación precio-área es positiva para ambos tipos, a igual incremento de área, el precio de un apartamento aumenta más “rápido” que el de una casa.
ggplot(vivienda_c, aes(x = areaconst, y = preciom, color = tipo)) +
geom_point(alpha = 0.4, size = 1.3) +
geom_smooth(method = "lm", se = FALSE) +
scale_color_manual(values = c("#2166AC", "#B2182B")) +
labs(title = "Precio de venta vs. área construida, por tipo de vivienda",
x = "Área construida (m²)", y = "Precio (millones $)", color = "Tipo") +
theme_minimal(base_size = 12)
Se aplico PCA sobre las variables cuantitativas preciom,
areaconst, parqueaderos, banios,
habitaciones, precio_m2. No se incluye
estrato como variable cuantitativa en el PCA dado que es
una variable ordinal cuya codificación numérica distorsiona las
correlaciones. Al igual que longitud y latitud
considerando que no representan información relevante relacionada a las
viviendas.
Realizamos el calculo de las componentes principales y de la varianza explicada:
vars_pca <- c("preciom", "areaconst", "parqueaderos", "banios",
"habitaciones", "precio_m2")
datos_full <- vivienda_c[, c(vars_pca, "zona", "tipo", "estrato")]
datos_full <- as.data.frame(datos_full)
res.pca <- PCA(datos_full, quali.sup = c(7, 8, 9), scale.unit = TRUE, graph = FALSE)
res.pca$eig
## eigenvalue percentage of variance cumulative percentage of variance
## comp 1 3.1294253 52.157088 52.15709
## comp 2 1.5296141 25.493569 77.65066
## comp 3 0.5822580 9.704300 87.35496
## comp 4 0.4304802 7.174671 94.52963
## comp 5 0.2501311 4.168852 98.69848
En este caso el primer componente principal explica el 52.15% de la variabilidad contenida en la base de datos, en conjunto con la segunda componentes explican el 77,6% de la varianza, lo cual indicaría que con solo una variable (CP1) que se obtiene mediante una combinación lineal de las variables se puede resumir gran parte de la variabilidad que contiene la base de datos.
fviz_eig(res.pca, addlabels = TRUE, barfill = "#2166AC", barcolor = "#2166AC",
main = "Varianza explicada por componente") +
theme_minimal(base_size = 12)
Al visualizar las variables en el plano de los componentes principales podemos identificar el sentido y la caracterización de los componentes. La primera dimensión representa principalmente el tamaño y las características generales de la vivienda, está fuertemente asociada con el precio total, el área construida, el número de baños, los parqueaderos y las habitaciones. Debido a que estas variables presentan cargas positivas y relativamente elevadas, mientras, la segunda dimensión está dominada por el precio por metro cuadrado, que presenta una carga de 0,923, mientras que las habitaciones y el área presentan cargas negativas.
fviz_pca_var(res.pca, col.var = "contrib",
gradient.cols = c("#2166AC", "#F4A582", "#B2182B"), repel = TRUE,
title = "Mapa de variables - PCA") +
theme_minimal(base_size = 12)
Las viviendas con mayor área tienden también a tener mayor precio, más baños y más parqueaderos, mientras que en la estructura capturada por la Dimensión 2, las viviendas con mayor precio por metro cuadrado tienden a diferenciarse de aquellas caracterizadas por mayor número de habitaciones y mayor superficie.
round(res.pca$var$coord[, 1:3], 3)
## Dim.1 Dim.2 Dim.3
## preciom 0.852 0.391 -0.043
## areaconst 0.851 -0.269 -0.296
## parqueaderos 0.726 0.351 -0.322
## banios 0.877 -0.050 0.266
## habitaciones 0.617 -0.572 0.446
## precio_m2 0.041 0.923 0.345
round(res.pca$var$cos2[, 1:3], 3)
## Dim.1 Dim.2 Dim.3
## preciom 0.726 0.153 0.002
## areaconst 0.724 0.072 0.088
## parqueaderos 0.527 0.123 0.104
## banios 0.769 0.003 0.071
## habitaciones 0.380 0.327 0.199
## precio_m2 0.002 0.852 0.119
Calculamos el coeficiente Eta-cuadrado \((η²)\) el cual nos explica la contribución
de las variables cualitativas suplementarias establecidas
(zona, tipo, estrato) al PCA.
res.pca$quali.sup$eta2
## Dim.1 Dim.2 Dim.3 Dim.4 Dim.5
## zona 0.03801574 0.2121739 0.0069025596 0.024575275 2.044422e-02
## tipo 0.23561419 0.2232951 0.0017183934 0.001021696 1.864644e-05
## estrato 0.25464191 0.3444657 0.0004660155 0.018786301 2.461392e-02
Las siguientes gráficas nos permiten ver los individuos en las primeras dos componentes según las variables cualitativas suplementarias.
El estrato presenta una asociación con la estructura cuantitativa identificada por el PCA, sin embargo su coeficiente no representa un valor significativo. Los estratos altos tienden a ubicarse hacia el espacio asociado con mayores dimensiones de tamaño, características de la vivienda y valor por metro cuadrado.
set.seed(123)
muestra <- sample(nrow(datos_full), 2500)
fviz_pca_ind(res.pca, geom = "point", habillage = "estrato",
select.ind = list(name = as.character(muestra)),
alpha.ind = 0.6, pointsize = 1.6, palette = "Set1",
title = "Individuos en el plano PC1-PC2 por estrato") +
theme_minimal(base_size = 12)
fviz_pca_ind(res.pca, geom = "point", habillage = "tipo",
select.ind = list(name = as.character(muestra)),
alpha.ind = 0.6, pointsize = 1.6,
palette = c("#2166AC", "#B2182B"),
title = "Individuos en el plano PC1-PC2 por tipo de vivienda") +
theme_minimal(base_size = 12)
Los resultados sugieren que existen diferencias territoriales en el valor relativo de las propiedades. Sin embargo, la dispersión de las observaciones dentro de cada zona indica que estas diferencias no son homogéneas y que existen viviendas con características diversas dentro de una misma zona.
fviz_pca_ind(res.pca, geom = "point", habillage = "zona",
select.ind = list(name = as.character(muestra)),
alpha.ind = 0.6, pointsize = 1.6, palette = "Dark2",
title = "Individuos en el plano PC1-PC2 por zona") +
theme_minimal(base_size = 12)
Se segmenta con k-means sobre las mismas seis variables númericas estandarizadas del PCA (no se incluye estrato como variable, por la misma razón anterior). Estrato, zona y tipo se usan después para caracterizar los grupos resultantes.
Para determinar el número adecuado de grupos, evaluamos diferentes valores de \(k\) mediante el método de la silueta.
vars_clu <- vars_pca
X <- scale(vivienda_c[, vars_clu])
set.seed(123)
idx_h <- sample(nrow(X), 700)
fviz_nbclust(X[idx_h, ], kmeans, method = "silhouette", k.max = 10) +
labs(title = "Método de la silueta") + theme_minimal(base_size = 12)
El codo y la silueta sugieren una solución razonable entre 3 y 4 grupos. Seleccionamos \(k=4\).
Aplicamos el algoritmo k-means sobre las seis variables cuantitativas estandarizadas. El algoritmo asignó cada vivienda al grupo cuyo centroide presentaba la menor distancia respecto a sus características.
Finalmente clasificamos las 8.319 viviendas en cuatro conglomerados de tamaños diferentes:
k <- 4
set.seed(123)
km <- kmeans(X, centers = k, nstart = 50, iter.max = 100)
vivienda_c$cluster <- factor(km$cluster)
table(vivienda_c$cluster)
##
## 1 2 3 4
## 4433 661 1558 1667
El primer conglomerado concentra más de la mitad de las viviendas analizadas (53.3%), mientras el segundo un grupo menor y el tercero y cuarto presentan una participación intermedia.
Para facilitar la visualización de resultados, proyectamos los conglomerados en un plano formado por las primeras dos componentes principales usando una muestra de 2.500 observaciones.
set.seed(1)
muestra2 <- sample(nrow(vivienda_c), 2500)
fviz_cluster(list(data = X[muestra2, ], cluster = km$cluster[muestra2]),
geom = "point", ellipse.type = "convex",
pointsize = 1.4, alpha = 0.6, palette = "Set1",
main = "Conglomerados k-means en el plano PC1-PC2") +
theme_minimal(base_size = 12)
La posición de cada conglomerado en el plano permite obtener una primera aproximación a las diferencias entre los grupos.
Calculamos medidas descriptivas para cada grupo permitiéndonos caracterizar cada conglomerado.
El grupo más grande representado por el clúster 1 podemos caracterizarlo como un segmento de viviendas relativamente pequeñas con precios “moderados” (promedio 2,60 millones por metro cuadrado); el clúster 2 representa viviendas de gran tamaño y alta dotación (en cuanto a distribución de la vivienda); el clúster 3 se puede interpretar como un segmento de viviendas grandes con menor valor relativo por superficie y finalmente el clúster 4 se caracteriza como un segmento de propiedades con el valor relativo por superficie es elevado a comparación de los demás segmentos.
car_num <- vivienda_c %>%
group_by(cluster) %>%
summarise(n = n(),
precio_prom = round(mean(preciom), 0),
precio_med = round(median(preciom), 0),
area_prom = round(mean(areaconst), 0),
precio_m2_prom = round(mean(precio_m2), 0),
parqueaderos_prom = round(mean(parqueaderos), 2),
banios_prom = round(mean(banios), 2),
habitaciones_prom = round(mean(habitaciones), 2))
car_num
Las siguientes tablas y gráficas caracterizan de manera cualitativa
los conglomerados, concorde al tipo, la zona y
el Estrato
El clúster 1 esta dominado por apartamentos en un 80.1%, lo que refuerza su perfil de vivienda de menor tamaño y precio. Adicional se ha visto que representa el segmento de mayor volumen del mercado. Mientras en el clúster 2 y 3 la mayoría corresponden a casas en un 85.2% y 90.8% respectivamente.
tab_tipo <- round(prop.table(table(vivienda_c$cluster, vivienda_c$tipo), 1) * 100, 1)
tab_zona <- round(prop.table(table(vivienda_c$cluster, vivienda_c$zona), 1) * 100, 1)
tab_estrato <- round(prop.table(table(vivienda_c$cluster, vivienda_c$estrato), 1) * 100, 1)
tab_tipo
##
## Apartamento Casa
## 1 80.1 19.9
## 2 14.8 85.2
## 3 9.2 90.8
## 4 78.3 21.7
df_tipo <- as.data.frame(prop.table(table(vivienda_c$cluster, vivienda_c$tipo), 1) * 100)
names(df_tipo) <- c("cluster", "tipo", "pct")
ggplot(df_tipo, aes(x = cluster, y = pct, fill = tipo)) +
geom_col() + scale_fill_manual(values = c("#2166AC", "#B2182B")) +
labs(title = "Composición por tipo dentro de cada conglomerado",
x = "Conglomerado", y = "%", fill = "Tipo") + theme_minimal(base_size = 12)
Por otro lado, la Zona Sur, aparece como la principal en lo cuatro grupos, manteniendo una presencia importante en todo el mercado analizado. Además, se puede observar que el clúster 4 tiene una presencia mayor del 40.4% de viviendas en la zona oeste.
tab_zona
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## 1 1.3 27.6 6.8 4.0 60.3
## 2 0.3 10.7 17.1 0.2 71.7
## 3 3.8 24.8 7.1 11.0 53.3
## 4 0.2 14.3 40.4 0.1 45.0
df_zona <- as.data.frame(prop.table(table(vivienda_c$cluster, vivienda_c$zona), 1) * 100)
names(df_zona) <- c("cluster", "zona", "pct")
ggplot(df_zona, aes(x = cluster, y = pct, fill = zona)) +
geom_col() + scale_fill_brewer(palette = "Dark2") +
labs(title = "Composición por zona dentro de cada conglomerado",
x = "Conglomerado", y = "%", fill = "Zona") + theme_minimal(base_size = 12)
tab_estrato
##
## 3 4 5 6
## 1 22.9 36.7 34.9 5.5
## 2 1.4 5.0 22.8 70.8
## 3 26.9 26.1 37.5 9.5
## 4 0.7 3.6 28.1 67.7
df_estr <- as.data.frame(prop.table(table(vivienda_c$cluster, vivienda_c$estrato), 1) * 100)
names(df_estr) <- c("cluster", "estrato", "pct")
ggplot(df_estr, aes(x = cluster, y = pct, fill = estrato)) +
geom_col() + scale_fill_brewer(palette = "RdBu", direction = -1) +
labs(title = "Composición por estrato dentro de cada conglomerado",
x = "Conglomerado", y = "%", fill = "Estrato") + theme_minimal(base_size = 12)
La caracterización cualitativa nos permite complementar las diferencias númericas entre los conglomerados, se pudo observar los sigueinte: - El clúster 1 está compuesto principalmente por apartamentos y presenta una distribución concentrada en las zonas Sur y Norte. - El clúster 2 está fuertemente caracterizado por casas, estrato 6 y Zona Sur. - El clúster 3 está compuesto mayoritariamente por casas y presenta una distribución más heterogénea entre los estratos 3, 4 y 5. - Finalmente, el clúster 4 está dominado por apartamentos y presenta una concentración particularmente elevada en la Zona Oeste.
Presentamos la distribución geográfica de los conglomerados en la ciudad de Cali.
ggplot(vivienda_c, aes(x = longitud, y = latitud, color = cluster)) +
geom_point(alpha = 0.55, size = 1.1) +
scale_color_brewer(palette = "Set1") +
labs(title = "Distribución geográfica de los conglomerados",
x = "Longitud", y = "Latitud", color = "Conglomerado") +
theme_minimal(base_size = 12) + coord_fixed()
Se examinó la relación entre las variables categóricas del mercado —
zona, tipo de vivienda y estrato
— mediante Análisis de Correspondencias simple para pares de variables y
Análisis de Correspondencias Múltiple para las tres variables en
conjunto. En todos los casos las pruebas de independencia chi-cuadrado
resultaron significativas (p < 0,001), confirmando que estas
variables no son independientes entre sí.
tab_zona_tipo <- table(vivienda_c$zona, vivienda_c$tipo)
tab_zona_tipo
##
## Apartamento Casa
## Zona Centro 24 100
## Zona Norte 1198 722
## Zona Oeste 1029 169
## Zona Oriente 62 289
## Zona Sur 2787 1939
Rechazamos la prueba hipótesis de independencia dados los resultados de la prueba de Chi-cuadrado.
chisq.test(tab_zona_tipo)
##
## Pearson's Chi-squared test
##
## data: tab_zona_tipo
## X-squared = 690.93, df = 4, p-value < 2.2e-16
Existe una única dimensión de correspondencia que explica el 100% de la inercia.
ca1 <- CA(tab_zona_tipo, graph = FALSE)
ca1$eig
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08305442 100 100
df1 <- as.data.frame(prop.table(tab_zona_tipo, 1) * 100)
names(df1) <- c("zona", "tipo", "pct")
ggplot(df1, aes(x = zona, y = pct, fill = tipo)) +
geom_col(width = 0.65) +
geom_text(aes(label = paste0(round(pct), "%")),
position = position_stack(vjust = 0.5), color = "white",
fontface = "bold", size = 4) +
scale_fill_manual(values = c("Apartamento" = "#2166AC", "Casa" = "#B2182B")) +
labs(title = "Composición de la oferta por zona",
subtitle = "Porcentaje de apartamentos vs. casas en cada zona",
x = "", y = "% de la oferta", fill = "") +
theme_minimal(base_size = 12) +
theme(legend.position = "top", panel.grid.major.x = element_blank())
La Zona Oeste y la Zona Sur se asocian con apartamentos, mientras la
Zona Centro y la Zona Oriente se asocian marcadamente con casas.
tab_zona_estrato <- table(vivienda_c$zona, vivienda_c$estrato)
tab_zona_estrato
##
## 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
Rechazamos la prueba hipótesis de independencia dados los resultados de la prueba de Chi-cuadrado.
chisq.test(tab_zona_estrato)
##
## Pearson's Chi-squared test
##
## data: tab_zona_estrato
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
Dos dimensiones (dim 1 y dim 2) en conjunto explican el 97,7% de la inercia.
ca2 <- CA(tab_zona_estrato, graph = FALSE)
ca2$eig
## 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
Gráficamente podemos ver que La Zona Oeste se asocia con el estrato 6, mientras la Zona Centro y Oriente con el estrato 3.
fviz_ca_biplot(ca2, repel = TRUE) +
labs(title = "AC simple: Zona x Estrato") + theme_minimal(base_size = 12)
tab_tipo_estrato <- table(vivienda_c$tipo, vivienda_c$estrato)
tab_tipo_estrato
##
## 3 4 5 6
## Apartamento 639 1404 1766 1291
## Casa 814 725 984 696
Rechazamos la prueba hipótesis de independencia dados los resultados de la prueba de Chi-cuadrado.
chisq.test(tab_tipo_estrato)
##
## Pearson's Chi-squared test
##
## data: tab_tipo_estrato
## X-squared = 224.33, df = 3, p-value < 2.2e-16
Existe una única dimensión de correspondencia que explica el 100% de la inercia.
ca3 <- CA(tab_tipo_estrato, graph = FALSE)
ca3$eig
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.0269661 100 100
Las casas presentan mayor asociación con el estrato 3, mientras que los apartamentos adquieren mayor presencia relativa en los estratos superiores, especialmente 4 y 5.
df3 <- as.data.frame(prop.table(tab_tipo_estrato, 2) * 100)
names(df3) <- c("tipo", "estrato", "pct")
ggplot(df3, aes(x = estrato, y = pct, fill = tipo)) +
geom_col(width = 0.6) +
geom_text(aes(label = paste0(round(pct), "%")),
position = position_stack(vjust = 0.5), color = "white",
fontface = "bold", size = 4) +
scale_fill_manual(values = c("Apartamento" = "#2166AC", "Casa" = "#B2182B")) +
labs(title = "Composición de la oferta por estrato",
subtitle = "Porcentaje de apartamentos vs. casas en cada estrato",
x = "Estrato", y = "% de la oferta", fill = "") +
theme_minimal(base_size = 12) +
theme(legend.position = "top", panel.grid.major.x = element_blank())
## AC Múltiple: Tipo, Zona y Estrato
datos_mca <- vivienda_c[, c("tipo", "zona", "estrato")]
mca <- MCA(datos_mca, graph = FALSE)
mca$eig
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.5620882 21.07831 21.07831
## dim 2 0.4531232 16.99212 38.07043
## dim 3 0.3796450 14.23669 52.30711
## dim 4 0.3334444 12.50416 64.81128
## dim 5 0.3233137 12.12426 76.93554
Las dos primeras dimensiones explican en conjunto el 38,1% de la inercia. En la gráfica podemos ver el porcentaje de varianza explicada por dimensión.
fviz_eig(mca, addlabels = TRUE, barfill = "#2166AC", barcolor = "#2166AC",
main = "ACM - Varianza explicada") + theme_minimal(base_size = 12)
El ACM muestra una diferencia entre perfiles inmobiliarios según zona, estrato y tipo de vivienda. En el cuadrante superior izquierdo se agrupan Zona Oeste y estrato 6; en el cuadrante superior derecho, Zona Oriente, Zona Centro y estrato 3; la Zona Norte y Zona Sur, junto con los estratos 4 y 5, ocupan posiciones intermedias y diversas.
fviz_mca_var(mca, repel = TRUE, col.var = "contrib",
gradient.cols = c("#2166AC", "#F4A582", "#B2182B"),
title = "ACM - Categorías (tipo, zona, estrato)") +
theme_minimal(base_size = 12)
En caso de considerar la variable Barrio, obtendríamos
los siguientes resultados gráficos:
datos_mca_b <- vivienda_c[, c("tipo", "zona", "estrato", "barrio_agr")]
mca_b <- MCA(datos_mca_b, graph = FALSE)
mca_b$eig[1:5, ]
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.5351874 9.730680 9.73068
## dim 2 0.4756645 8.648445 18.37912
## dim 3 0.4196909 7.630743 26.00987
## dim 4 0.3543291 6.442348 32.45222
## dim 5 0.3109197 5.653085 38.10530
Mientras que obtenemos una inercia del 38.1% con las primeras dos
dimensiones sin considerar barrio, la inercia de se reduce al 18.4% al
introducirla. Requeriríamos de 5 dimensiones para llegar al 38.1% de
inercia -considerando Barrio-. Adicionalmente se reduce la
capacidad de síntesis del mapa.
fviz_mca_var(mca_b, repel = TRUE, col.var = "contrib",
gradient.cols = c("#2166AC", "#F4A582", "#B2182B"),
title = "ACM incluyendo barrio agregado (15 categorías)") +
theme_minimal(base_size = 12)
El análisis realizado evidencia que el mercado inmobiliario presenta segmentos diferenciados según las características del inmueble (precio, zona, estrato y demás). Se identificaron cuatro grupos de vivienda mediante el análisis de conglomerados: “vivienda masiva a precio asequible” (Clúster 1 - 53%), “casas de buen valor/m²” (Clúster 2 - 19%), “apartamentos premium” (Clúster 3 - 20%) y “casas de lujo” (Clúster 4 - 8%). Finalmente los análisis de correspondencias muestran una fuerte relación entre zona, estrato y tipo de vivienda. La Zona Oeste se caracteriza por perfiles de mayor nivel, mientras que Centro y Oriente presentan perfiles asociados a estratos más bajos.