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.
En la unidad 1 se va a 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, Análisis de conglomerados, Análisis de correspondencia y visualización de resultados.
Se carga la base de datos con la columna barrios con sus categorías corregidas
library(readxl)
library(dplyr) # <- Esta línea es necesaria
vivienda <- read_excel("vivienda.xlsx")
glimpse(vivienda)
Rows: 8,322
Columns: 13
$ id <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
$ zona <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
$ piso <chr> NA, NA, NA, "02", "01", "01", "01", "01", "02", "02", "02…
$ estrato <dbl> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
$ preciom <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
$ areaconst <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
$ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
$ banios <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
$ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
$ tipo <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
$ barrio <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
$ longitud <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
$ latitud <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…
En este conjunto de datos se identifican como variables categóricas los atributos zona, piso, tipo y barrio, ya que representan categorías o etiquetas. Por otro lado, los atributos id, estrato, precio, área construida, número de parqueaderos, habitaciones, longitud y latitud son de tipo numérico. Sin embargo, es importante destacar que algunas variables que R reconoce como numéricas pueden tener una naturaleza nominal u ordinal, por lo que se debe revisar su significado antes de incluirlas en los análisis estadísticos. Se puede evidenciar que es una base de datos de 8.322 filas y 13 columnas, con valores faltantes.
a. Como piso está en formato de texto (
# Convertir 'piso' a numérico (R interpretará caracteres no convertibles como NA)
vivienda$piso <- as.numeric(vivienda$piso)
b. Convertir la variable estrato a categórica (factor),
aunque estrato es numérico (
# Convertir 'estrato' a factor ordenado (categórico)
vivienda$estrato <- factor(vivienda$estrato,
levels = sort(unique(na.omit(vivienda$estrato))),
ordered = TRUE)
🔎 Verificamos los cambios:
str(vivienda$piso)
num [1:8322] NA NA NA 2 1 1 1 1 2 2 ...
str(vivienda$estrato)
Ord.factor w/ 4 levels "3"<"4"<"5"<"6": 1 1 1 2 3 3 2 3 3 3 ...
Ahora, guardamos la base de datos modificada
# Guardar la base modificada con otro nombre
vivienda_1 <- vivienda
head(vivienda_1, 20)
# A tibble: 20 × 13
id zona piso estrato preciom areaconst parqueaderos banios habitaciones
<dbl> <chr> <dbl> <ord> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1147 Zona … NA 3 250 70 1 3 6
2 1169 Zona … NA 3 320 120 1 2 3
3 1350 Zona … NA 3 350 220 2 2 4
4 5992 Zona … 2 4 400 280 3 5 3
5 1212 Zona … 1 5 260 90 1 2 3
6 1724 Zona … 1 5 240 87 1 3 3
7 2326 Zona … 1 4 220 52 2 2 3
8 4386 Zona … 1 5 310 137 2 3 4
9 1209 Zona … 2 5 320 150 2 4 6
10 1592 Zona … 2 5 780 380 2 3 3
11 4057 Zona … 2 6 750 445 NA 7 6
12 4460 Zona … 2 4 625 355 3 5 5
13 6081 Zona … 2 5 750 237 2 6 6
14 7497 Zona … 2 6 520 98 2 2 2
15 7824 Zona … 2 4 600 160 1 4 5
16 7987 Zona … 2 5 420 200 4 4 5
17 3495 Zona … 3 5 490 118 2 4 4
18 5424 Zona … 3 4 320 108 2 3 3
19 6271 Zona … 3 5 385 103 2 2 3
20 6857 Zona … 3 3 100 49 NA 1 2
# ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Categorías únicas de la variable barrio
head(unique(vivienda_1$barrio), 5)
[1] "20 de julio" "3 de julio" "acopi" "agua blanca" "aguacatal"
c. Identificación de los valores faltantes
vivienda_2 <- vivienda_1
colSums(is.na(vivienda_2))
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
Según los resultados, se puede evidenciar que hay valores faltantes en cada atributo, se muestra que las variables con más datos faltantes son piso 2638 y parqueaderos 1605, lo que podría afectar el análisis si no se trata. Otras variables tienen solo 2 y 3 valores faltantes.
Para el tratamiento de los datos faltantes se recomienda eliminar primero los datos faltantes de la columna id, ya que son pocos y no afectan significativamente el análisis, además de que esta variable funciona como identificador único, por lo que no debe contener valores nulos.
d. Tratamiento de los N/A
library(dplyr)
# Eliminar solo las filas donde la columna 'id' tiene NA
vivienda_3 <- vivienda_2 %>% filter(!is.na(id))
nrow(vivienda_3)
[1] 8319
Como ya eliminamos las tres filas de datos faltantes de la columna id, procedemos aplicar el método de imputación para las otras variables que contienen datos faltantes. Por tal motivo se calculan las estadísticas descriptivas para determinar que método de imputación se aplica para cada variable.
# 1. Eliminar la columna 'id'
vivienda_1_sin_id <- vivienda_3 %>% select(-id)
# 2. Identificar variables cuantitativas (numéricas)
vars_cuantitativas <- vivienda_1_sin_id %>% select(where(is.numeric))
# 3. Identificar variables cualitativas (factor u ordinal o carácter)
vars_cualitativas <- vivienda_1_sin_id %>% select(where(~ is.factor(.) || is.character(.)))
# 4. Estadísticas descriptivas para cuantitativas
summary(vars_cuantitativas)
piso preciom areaconst parqueaderos
Min. : 1.000 Min. : 58.0 Min. : 30.0 Min. : 1.000
1st Qu.: 2.000 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000
Median : 3.000 Median : 330.0 Median : 123.0 Median : 2.000
Mean : 3.771 Mean : 433.9 Mean : 174.9 Mean : 1.835
3rd Qu.: 5.000 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000
Max. :12.000 Max. :1999.0 Max. :1745.0 Max. :10.000
NA's :2635 NA's :1602
banios habitaciones longitud latitud
Min. : 0.000 Min. : 0.000 Min. :-76.59 Min. :3.333
1st Qu.: 2.000 1st Qu.: 3.000 1st Qu.:-76.54 1st Qu.:3.381
Median : 3.000 Median : 3.000 Median :-76.53 Median :3.416
Mean : 3.111 Mean : 3.605 Mean :-76.53 Mean :3.418
3rd Qu.: 4.000 3rd Qu.: 4.000 3rd Qu.:-76.52 3rd Qu.:3.452
Max. :10.000 Max. :10.000 Max. :-76.46 Max. :3.498
# 5. Estadísticas descriptivas para cualitativas
summary(vars_cualitativas)
zona estrato tipo barrio
Length:8319 3:1453 Length:8319 Length:8319
Class :character 4:2129 Class :character Class :character
Mode :character 5:2750 Mode :character Mode :character
6:1987
Una vez obtenidas las estadísticas descriptivas, se procede a aplicar un método de imputación, reemplazando los valores faltantes en las variables categóricas con la moda, y en las variables numéricas con la mediana, ya que esta última es menos sensible a la influencia de valores atípicos.
# 1. Separar la columna id
id_col <- vivienda_3 %>% select(id)
# 2. Separar variables cuantitativas y cualitativas (excluyendo id)
vars_cuantitativas <- vivienda_3 %>% select(where(is.numeric), -id)
vars_cualitativas <- vivienda_3 %>% select(where(~ is.factor(.) || is.character(.)))
# 3. Función para imputar con mediana
imputar_mediana <- function(x) {
x[is.na(x)] <- median(x, na.rm = TRUE)
return(x)
}
# 4. Función para imputar con moda
moda <- function(x) {
ux <- na.omit(unique(x))
ux[which.max(tabulate(match(x, ux)))]
}
imputar_moda <- function(x) {
x[is.na(x)] <- moda(x)
return(x)
}
# 5. Imputar
vars_cuantitativas_imputadas <- vars_cuantitativas %>% mutate(across(everything(), imputar_mediana))
vars_cualitativas_imputadas <- vars_cualitativas %>% mutate(across(everything(), imputar_moda))
# 6. Volver a unir todo (id + categóricas + numéricas)
vivienda_4 <- bind_cols(id_col, vars_cualitativas_imputadas, vars_cuantitativas_imputadas)
# Ver estructura final
glimpse(vivienda_4)
Rows: 8,319
Columns: 13
$ id <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
$ zona <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
$ estrato <ord> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
$ tipo <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
$ barrio <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
$ piso <dbl> 3, 3, 3, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, …
$ preciom <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
$ areaconst <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
$ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, 2, 3, 2, 2, 1, 4, 2, 2, 2, …
$ banios <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
$ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
$ longitud <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
$ latitud <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…
colSums(is.na(vivienda_4))
id zona estrato tipo barrio piso
0 0 0 0 0 0
preciom areaconst parqueaderos banios habitaciones longitud
0 0 0 0 0 0
latitud
0
Verificamos que efectivamente se le dió tratamiento a los
valores faltantes.
e. Visualización de valores atípicos
library(dplyr)
library(ggplot2)
library(tidyr)
# 1. Seleccionar solo las variables numéricas, excluyendo 'id'
vivienda_cuantitativas <- vivienda_4 %>%
select(where(is.numeric)) %>%
select(-id)
# 2. Convertir a formato largo para ggplot2
vivienda_long <- vivienda_cuantitativas %>%
pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")
# 3. Crear el boxplot con colores diferentes por variable
ggplot(vivienda_long, aes(x = Variable, y = Valor, fill = Variable)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Boxplot de Variables Cuantitativas",
x = "Variable",
y = "Valor") +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none",
plot.title = element_text(hjust = 0.5) # Centrar título
)
La imagen muestra un boxplot comparativo de varias variables cuantitativas de la base de datos de viviendas. Se observa que las variables “areaconst” y “preciom” presentan una mayor dispersión y presencia de valores atípicos, lo que se evidencia en la longitud de sus bigotes y la cantidad de puntos fuera de los límites del boxplot. Estas variables, además, tienen medianas elevadas en relación con el resto, indicando una mayor variabilidad en los datos de área construida y precio. Por otro lado, las variables como “baños”, “habitaciones”, “parqueaderos”, “piso”, “latitud” y “longitud” muestran una variabilidad mucho menor, con cajas más compactas y pocos valores extremos, lo que sugiere que sus datos están más concentrados en torno a la mediana. En conjunto, el gráfico permite identificar las variables con mayor heterogeneidad y la presencia de posibles outliers que podrían requerir un tratamiento especial en el análisis posterior.
f. Tratamiento de los valores atípicos
Para dar tratamiento a los valores atípicos, se utiliza el método de capping o Winsorización, que consiste en limitar los valores extremos de las variables cuantitativas a determinados percentiles (en este caso, los percentiles 5 y 95). De esta manera, se atenúa el efecto de los valores atípicos sin eliminarlos completamente, reemplazándolos por los valores límites definidos, lo cual permite conservar la estructura general de los datos y minimizar el sesgo que podrían generar en los análisis estadísticos.
# Cargar el paquete si vas a usar Winsorize
# install.packages("DescTools")
library(DescTools)
# Crear una copia del data frame para no modificar el original
vivienda_5 <- vivienda_4
# Aplicar capping del 5% y 95% a cada variable numérica
numeric_vars <- sapply(vivienda_5, is.numeric)
vivienda_5[numeric_vars] <- lapply(vivienda_5[numeric_vars], function(x) {
p5 <- quantile(x, 0.10, na.rm = TRUE)
p95 <- quantile(x, 0.90, na.rm = TRUE)
x[x < p5] <- p5
x[x > p95] <- p95
return(x)
})
Después de aplicar el método de capping, se procede a graficar
los boxplots con el fin de verificar que los valores atípicos han sido
controlados de manera adecuada.
library(tidyr)
library(ggplot2)
# Eliminar 'id' si es numérica y está en el conjunto
vars_sin_id <- names(vivienda_5)[sapply(vivienda_5, is.numeric) & names(vivienda_5) != "id"]
# Formato largo solo con variables numéricas sin 'id'
vivienda_long <- pivot_longer(
vivienda_5,
cols = all_of(vars_sin_id),
names_to = "variable",
values_to = "valor"
)
# Crear boxplot con colores y título centrado
ggplot(vivienda_long, aes(x = variable, y = valor, fill = variable)) +
geom_boxplot() +
theme_minimal() +
labs(
title = enc2utf8("Boxplots de variables numéricas con capping"),
x = "Variable",
y = "Valor"
) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none",
plot.title = element_text(hjust = 0.5, face = "bold", size = 14) # centrado y en negrita
)
Se visualiza en el gráfico que efectivamente, ya no aparecen
valores atípicos en las varibles del área construida y el precio.
g. Ver todas las filas que comparten un id repetido
Ahora vamos a comprobar si hay filas duplicadas, teniendo en cuenta el identificador único:
vivienda_5[vivienda_5$id %in% vivienda_5$id[duplicated(vivienda_5$id)], ]
# A tibble: 1,664 × 13
id zona estrato tipo barrio piso preciom areaconst parqueaderos banios
<dbl> <chr> <ord> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 7487. Zona … 6 Apar… acopi 2 520 98 2 2
2 7487. Zona … 4 Casa acopi 2 600 160 1 4
3 7487. Zona … 5 Casa acopi 2 420 200 3 4
4 833. Zona … 6 Apar… acopi 5 820 350 1 4
5 833. Zona … 3 Casa acopi 3 230 160 2 2
6 833. Zona … 5 Apar… acopi 3 430 105 2 3
7 833. Zona … 3 Casa acopi 3 190 350 2 2
8 833. Zona … 3 Casa acopi 3 180 120 2 3
9 833. Zona … 3 Casa acopi 3 500 210 2 5
10 833. Zona … 3 Apar… acopi 3 199 176 2 2
# ℹ 1,654 more rows
# ℹ 3 more variables: habitaciones <dbl>, longitud <dbl>, latitud <dbl>
Hay valores duplicados, esto puede ser que el id este asignado
por piso, ya que las filas poseen diferentes caraterísticas. Por lo
tanto, no se realiza ninguna modificación a estos valores.
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 oferta del mercado.
a. Estandarización:
Con el fin de evitar que las variables que tienen una escala con valores más grandes afecten las estimaciones realizadas (sesgos) se realiza la estandarización de las variables antes de proceder a realizar el proceso de estimación de los componentes principales. Se utilizó el paquete FactoMineR::PCA dónde la estandarización ya va incluida por defecto.
library(FactoMineR)
library(factoextra)
# PCA solo con variables numéricas (sin 'id')
vars_sin_id <- names(vivienda_5)[sapply(vivienda_5, is.numeric) & names(vivienda_5) != "id"]
pca_res <- PCA(vivienda_5[vars_sin_id], graph = FALSE)
b. Elección del número de componentes principales
# --- 1. Scree plot ---
fviz_eig(pca_res,
addlabels = TRUE,
barfill = "#56B4E9",
barcolor = "#1F78B4",
linecolor = "#E69F00") +
labs(title = "Varianza explicada por componente",
x = "Componentes principales",
y = "Porcentaje de varianza explicada") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Centrar título
El gráfico muestra la varianza explicada por cada componente principal en un análisis de componentes principales (PCA), evidenciando que el primer componente explica el 44% de la variabilidad total de los datos, seguido por el segundo con un 15.5%, y el tercero con un 12.7%; a partir del cuarto componente (9.7%) la contribución disminuye progresivamente, llegando a valores menores al 5% desde el sexto en adelante, lo que sugiere que la mayor parte de la información se concentra en los tres primeros componentes (72.2% acumulado) y que los restantes aportan variabilidad marginal, por lo que podrían descartarse para reducir la dimensionalidad sin pérdida significativa de información.
# --- 2. Biplot profesional ---
fviz_pca_var(pca_res,
col.var = "contrib",
gradient.cols = c("#E69F00", "#56B4E9", "#009E73"),
repel = TRUE,
arrowsize = 0.5,
labelsize = 4,
title = "Biplot - Variables") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5)) # Centrar título
Al visualizar las variables en el plano de los componentes principales permite identificar el sentido y la caracterización de los componentes (característica capturada por los vectores propios de Σ). El biplot de variables muestra la proyección de las variables originales en el plano formado por los dos primeros componentes principales, que explican conjuntamente el 59.5% de la varianza 44% en Dim1 y 15.5% en Dim2. En este plano, variables como precio, parqueaderos, baños, areaconst y habitaciones se alinean fuertemente con Dim1, indicando que este componente está asociado principalmente a características físicas y de valor de las propiedades; en cambio, longitud y latitud se relacionan con Dim2, reflejando una dimensión geográfica. La variable piso tiene una contribución moderada y se proyecta en sentido opuesto a las características de tamaño y precio, sugiriendo una relación inversa. La intensidad y longitud de las flechas representan la importancia de cada variable en la construcción de los componentes, destacándose precio, areaconst y longitud como las de mayor contribución.
El análisis de componentes principales permitió reducir la dimensionalidad del conjunto de datos de 8 variables originales a 3 componentes principales que retienen el 72.2% de la variabilidad total, sin pérdida significativa de información. El primer componente está asociado a variables de tamaño y valor de las propiedades precio, área construida, número de baños, parqueaderos y habitaciones, mientras que el segundo se relaciona con factores geográficos (longitud y latitud) y el tercero con características de localización y piso. Esta reducción facilita la identificación de patrones clave que influyen en la variación de precios y oferta en el mercado, simplificando el análisis y mejorando la interpretación de la información.
Este bloque de código selecciona únicamente las variables numéricas de la base vivienda_4 y las estandariza, es decir, las transforma para que todas tengan media cero y desviación estándar uno. Esto asegura que cada variable tenga el mismo peso en el análisis de conglomerados, evitando que aquellas con valores más grandes influyan de forma desproporcionada en la formación de los grupos.
# Escalar todas las columnas numéricas (ya limpias)
# Seleccionar solo variables numéricas
vivienda_num <- dplyr::select_if(vivienda_5, is.numeric)
# Escalar
vivienda_scaled <- scale(vivienda_num)
library(factoextra)
# Método del codo con título centrado
fviz_nbclust(vivienda_scaled, kmeans, method = "wss") +
ggtitle("Método del Codo") +
theme(plot.title = element_text(hjust = 0.5))
La gráfica del método del codo muestra una fuerte disminución de la varianza interna hasta k = 2, punto a partir del cual la reducción es más suave, indicando que el número óptimo de conglomerados para este análisis es 2.
fviz_nbclust(vivienda_scaled, kmeans, method = "silhouette") +
ggtitle("Optimal Number of Clusters - Silhouette Method") +
theme(plot.title = element_text(hjust = 0.5))
La gráfica del método de la silueta muestra que el valor más alto del ancho promedio de silueta se alcanza en k = 2, lo que indica que dos conglomerados producen la mejor separación y cohesión interna entre grupos en estos datos.
set.seed(123)
km_res <- kmeans(vivienda_scaled, centers = 2, nstart = 25)
# Ver distribución de observaciones
table(km_res$cluster)
1 2
4977 3342
El resultado indica que el algoritmo k-means agrupó los datos en dos clusters, con 4.977 observaciones en el primero y 3.342 en el segundo, mostrando que el primer grupo es más numeroso.
vivienda_5$cluster <- km_res$cluster
fviz_cluster(km_res, data = vivienda_scaled,
geom = "point",
ellipse.type = "convex",
palette = "jco")
El gráfico de dispersión muestra la separación de las observaciones en dos clusters bien definidos: el cluster 1 (en azul) se concentra principalmente en valores negativos de la primera dimensión, mientras que el cluster 2 (en amarillo) ocupa valores positivos. La frontera entre ambos grupos está claramente delimitada, lo que indica una buena diferenciación según las dos primeras dimensiones principales, que explican en conjunto el 61,8 % de la variabilidad de los datos.
aggregate(vivienda_5[ , sapply(vivienda_5, is.numeric)],
by = list(Cluster = km_res$cluster),
mean)
Cluster id piso preciom areaconst parqueaderos banios
1 1 3387.364 3.531846 254.2449 99.11312 1.444043 2.373518
2 2 5310.632 3.027229 619.6864 251.44524 2.209156 4.152902
habitaciones longitud latitud cluster
1 3.004018 -76.52449 3.419481 1
2 4.059844 -76.53635 3.415958 2
Este resultado muestra los valores promedio de las variables para cada uno de los dos clusters formados. El Cluster 1 agrupa propiedades con menor precio promedio aproximadamente 254 millones, menor área construida aproximadamente 99 m², menos baños y parqueaderos, así como menor número de habitaciones, mientras que el Cluster 2 agrupa propiedades con precios más altos de apróximadamente 620 millones, áreas construidas más amplias apróximadamente 251 m² y más comodidades (baños, parqueaderos y habitaciones). En resumen, los clusters diferencian claramente viviendas más pequeñas y económicas frente a viviendas más grandes y costosas.
library(ggplot2)
# Ejemplo: distribución de zonas por cluster
ggplot(vivienda_5, aes(x = zona, fill = factor(cluster))) +
geom_bar(position = "dodge") +
labs(fill = "Cluster", x = "Zona", y = "Cantidad de propiedades")
La gráfica muestra la distribución de propiedades por zona y cluster. El Cluster 1 (rojo) predomina claramente en la Zona Sur y Zona Norte, mientras que el Cluster 2 (azul) tiene mayor presencia relativa en la Zona Oeste y también aparece en la Zona Sur, aunque en menor cantidad que el Cluster 1. En la Zona Centro y Zona Oriente, ambos clusters tienen baja representación. Esto indica que las zonas geográficas tienen una fuerte influencia en la pertenencia al cluster, con el Cluster 1 concentrado en zonas con mayor número de propiedades en general y el Cluster 2 más equilibrado en su distribución, destacando en la Zona Oeste.
La aplicación del análisis de conglomerados permitió agrupar las propiedades residenciales en segmentos homogéneos según sus características, evidenciando patrones diferenciados en la oferta inmobiliaria según la ubicación y el estrato socioeconómico, lo que facilita comprender las dinámicas del mercado en distintas zonas de la ciudad.
Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliari
Se convierte en variable tipo factor la variable tipo y zona:
vivienda_5$tipo <- as.factor(vivienda_5$tipo)
vivienda_5$zona <- as.factor(vivienda_5$zona)
Se revisa si la base tiene datos faltantes (rectángulos
de color rojo)
library(mice)
md.pattern(vivienda_5, rotate.names = TRUE)
Se puede evidenciar que no hay datos faltantes ya que fueron
tratados en el paso 1.
Se construye entonces una tabla cruzada con las variables involucradas en el análisis:
Tipo: Casa y Apartamento.
Zona: Centro, Norte, Oeste, Oriente, Sur
library(FactoMineR)
tabla <- table(vivienda_5$tipo, vivienda_5$zona)
colnames(tabla) <- c("Zona Centro", "Zona Norte", "Zona Oeste", "Zona Oriente", "Zona Sur" )
tabla
Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
Apartamento 24 1198 1029 62 2787
Casa 100 722 169 289 1939
La tabla muestra que en las zonas Norte, Oeste y Sur predominan los apartamentos, con cantidades significativamente mayores que las casas, lo que sugiere una mayor densidad residencial o urbanización vertical en estas áreas. En contraste, las zonas Centro y Oriente presentan más casas que apartamentos, indicando un patrón de vivienda más tradicional o de menor densidad. Estos datos reflejan diferencias claras en la distribución del tipo de vivienda según la zona, lo que puede ser útil para planificación urbana y análisis demográficos.
chisq.test(tabla)
Pearson's Chi-squared test
data: tabla
X-squared = 690.93, df = 4, p-value < 2.2e-16
La prueba chi-cuadrado de Pearson muestra un valor estadístico
muy alto (690.93) con 4 grados de libertad y un p-valor prácticamente
cero, lo que indica que existe una asociación significativa entre la
zona geográfica y el tipo de vivienda. Esto significa que la
distribución de casas y apartamentos varía según la zona, rechazando la
hipótesis de que ambas variables sean independientes.
Finalmente se procede a realizar el análisis de
correspondencia que consiste en estimar las coordenadas para cada uno de
los niveles de ambas variables
library(FactoMineR)
library(factoextra)
library(gridExtra)
Adjuntando el paquete: 'gridExtra'
The following object is masked from 'package:dplyr':
combine
resultados_ac <- CA(tabla)
Para medir el grado de representatividad del proceso calculas los valores de la varianza acumulada, utilizando para ellos los valores propios de la matriz de discrepancias
valores_prop <-resultados_ac$eig ; valores_prop
eigenvalue percentage of variance cumulative percentage of variance
dim 1 0.08305442 100 100
El valor propio eigenvalue de la dimensión 1 es aproximadamente 0.083, y esta única dimensión explica el 100% de la varianza total en los datos, con un porcentaje acumulado también del 100%. Esto indica que todo el patrón de asociación entre las variables categóricas de la tabla de contingencia se resume completamente en esta única dimensión. En otras palabras, no existen dimensiones adicionales que aporten información significativa, por lo que el análisis de correspondencias identifica un solo eje principal que captura toda la variabilidad estructural entre las categorías.
library(pheatmap)
pheatmap(tabla,
cluster_rows = FALSE, # No agrupa filas
cluster_cols = FALSE, # No agrupa columnas
display_numbers = TRUE, # Muestra los números en las celdas
main = "Mapa de calor de la tabla de contingencia")
La gráfica muestra un mapa de calor de la tabla de contingencia entre tipo de vivienda y zona geográfica. Se observa que la mayor concentración de viviendas, tanto apartamentos como casas, está en la Zona Sur; especialmente los apartamentos, cuya cifra (2787) destaca sobre el resto y se representa con el color rojo más intenso. La Zona Centro, por el contrario, tiene una cantidad significativamente menor de viviendas de ambos tipos, evidenciada por los tonos azules. En las demás zonas, la distribución es intermedia, con una mayor presencia de apartamentos frente a casas, excepto en la Zona Oriente, donde hay más casas que apartamentos. Esta visualización permite identificar rápidamente las zonas con mayor desarrollo habitacional y las preferencias de tipo de vivienda en cada área, lo que puede ser útil para análisis urbanos o decisiones de mercado inmobiliario.