Problema 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.
El conjunto de datos analiza la dinámica inmobiliaria en la ciudad de Cali y está compuesto por 8,322 observaciones y 13 variables. Estas incluyen características físicas de las propiedades, como el número de habitaciones, baños, área construida y parqueaderos, además de información geoespacial, como la zona, el barrio, la latitud y la longitud. Asimismo, se incluyen variables económicas como el estrato socioeconómico y el precio de venta, junto con el tipo de vivienda (casa o apartamento). Esta estructuración permite un análisis integral del mercado inmobiliario en la ciudad.
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")=
.. cols(
.. id = col_double(),
.. zona = col_character(),
.. piso = col_character(),
.. estrato = col_double(),
.. preciom = col_double(),
.. areaconst = col_double(),
.. parqueaderos = col_double(),
.. banios = col_double(),
.. habitaciones = col_double(),
.. tipo = col_character(),
.. barrio = col_character(),
.. longitud = col_double(),
.. latitud = col_double()
.. )
- attr(*, "problems")=<externalptr>
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>
tabla_variables <- data.frame(
Variable = c("id", "zona", "piso", "estrato", "precio", "area construida",
"parqueaderos", "banios", "habitaciones", "tipo", "barrio",
"longitud", "latitud"),
Tipo = c("Cuantitativa(Discreta)", "Cualitativa(Nominal)",
"Cuantitativa(Discreta)", "Cualitativa(Ordinal)",
"Cuantitativa(Discreta)", "Cuantitativa(Continua)",
"Cuantitativa(Discreta)", "Cuantitativa(Discreta)",
"Cuantitativa(Discreta)", "Cualitativa(Nominal)",
"Cualitativa(Nominal)", "Cuantitativa(Continua)",
"Cuantitativa(Continua)"))
tabla_variables
Variable Tipo
1 id Cuantitativa(Discreta)
2 zona Cualitativa(Nominal)
3 piso Cuantitativa(Discreta)
4 estrato Cualitativa(Ordinal)
5 precio Cuantitativa(Discreta)
6 area construida Cuantitativa(Continua)
7 parqueaderos Cuantitativa(Discreta)
8 banios Cuantitativa(Discreta)
9 habitaciones Cuantitativa(Discreta)
10 tipo Cualitativa(Nominal)
11 barrio Cualitativa(Nominal)
12 longitud Cuantitativa(Continua)
13 latitud Cuantitativa(Continua)
El análisis de valores faltantes revela que las principales variables afectadas son piso y parqueaderos, con 2,638 y 1,605 datos ausentes, respectivamente, además de algunas ausencias menores en otras columnas. Para su tratamiento, se puede optar por la imputación con la mediana en variables numéricas y la moda en categóricas, o bien eliminar registros incompletos si representan una fracción pequeña del total, asegurando así la integridad del análisis sin distorsionar los resultados.
colSums(is.na(vivienda))
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
Se realizó una limpieza y transformación de datos para garantizar la
calidad del insumo. Primero, se eliminaron registros completamente
vacíos y aquellos sin identificador único. Luego, se normalizaron las
variables categóricas estandarizando nomenclaturas en minúsculas.
Posteriormente, se imputaron valores faltantes en piso
utilizando la mediana por tipo de vivienda, mientras que los valores
nulos en parqueaderos se reemplazaron por cero. Finalmente,
se filtraron registros inconsistentes eliminando aquellos sin
habitaciones o baños y asegurando la presencia de al menos una variable
relevante con valores mayores a cero, obteniendo así un dataset
completamente limpio y sin valores faltantes, listo para el
análisis.
# 1. Limpieza de valores NA y registros no válidos
vivienda <- vivienda[rowSums(is.na(vivienda)) != ncol(vivienda),] # Eliminar filas completamente vacías
vivienda <- vivienda[!is.na(vivienda$id),] # Eliminar registros sin identificador único
# 2. Normalización de datos categóricos
vivienda$zona <- tolower(vivienda$zona) # Convertir zonas a minúsculas
vivienda$tipo <- tolower(vivienda$tipo) # Convertir tipo de vivienda a minúsculas
vivienda$tipo[vivienda$tipo == "apto"] <- "apartamento" # Unificar nomenclatura
vivienda$barrio <- tolower(vivienda$barrio) # Convertir barrios a minúsculas
# 3. Transformaciones y limpieza de variables numéricas
vivienda$piso <- as.numeric(vivienda$piso) # Convertir a numérico
vivienda$parqueaderos[is.na(vivienda$parqueaderos)] <- 0 # Reemplazar NA en parqueaderos por 0
# 4. Filtrado de datos inconsistentes o irrelevantes
vivienda <- vivienda[vivienda$habitaciones != 0,] # Eliminar registros sin habitaciones
vivienda <- vivienda[vivienda$banios != 0,] # Eliminar registros sin baños
vivienda <- vivienda[rowSums(vivienda[,c("parqueaderos", "banios", "habitaciones")] == 0) < 3,] # Mantener registros con al menos una de estas variables > 0
vivienda <- vivienda[rowSums(vivienda[,c("banios", "habitaciones")] == 0) < 2,] # Mantener registros con al menos una de estas variables > 0
# Calcular la mediana del piso por tipo de vivienda
vivienda <- vivienda %>%
group_by(tipo) %>%
mutate(piso = ifelse(is.na(piso), median(piso, na.rm = TRUE), piso)) %>%
ungroup()
colSums(is.na(vivienda))
id zona piso estrato preciom areaconst
0 0 0 0 0 0
parqueaderos banios habitaciones tipo barrio longitud
0 0 0 0 0 0
latitud
0
Se realizó un tratamiento a la variable barrio con el
objetivo de corregir inconsistencias en la nomenclatura. Primero, se
normalizaron los valores convirtiéndolos a minúsculas, eliminando tildes
y espacios adicionales. Luego, se aplicó una lista de correcciones para
unificar nombres que presentaban variaciones en su escritura. Como
resultado, el número de barrios distintos se redujo de
437 a 386, mejorando la coherencia y
calidad del dataset para un análisis más preciso.
# Cargar el paquete stringr
library(stringr)
normalize_barrio <- function(barrio) {
barrio <- tolower(barrio) # Convertir a minúsculas
barrio <- str_replace_all(barrio, "[áéíóú]", function(x) chartr("áéíóú", "aeiou", x)) # Reemplazar tildes
barrio <- str_trim(barrio) # Eliminar espacios en blanco adicionales
return(barrio)
}
# Aplicar la función a la columna 'barrio'
vivienda <- vivienda %>%
mutate(barrio = normalize_barrio(barrio))
# Definir la lista de correcciones
correcciones <- list(
"meléndez" = "melendez",
"ciudad meléndez" = "ciudad melendez",
"juanamb√∫" = "juanambu",
"el trébol" = "el trebol",
"las américas" = "las americas",
"rep√∫blica de israel" = "republica de israel",
"base aérea" = "base aerea",
"alférez real" = "alferez real"
)
# Corregir los nombres en la columna 'barrio'
vivienda$barrio <- sapply(vivienda$barrio, function(barrio) {
if (barrio %in% names(correcciones)) {
return(correcciones[[barrio]])
} else {
return(barrio)
}
})
El análisis de las variables numéricas muestra una distribución diversa en las características de las propiedades. La variable piso presenta valores entre 1 y 12, con una mediana de 3, lo que indica que la mayoría de las viviendas se encuentran en niveles bajos o medios. En cuanto al estrato, este oscila entre 3 y 6, con una mediana de 5, reflejando una concentración en los segmentos socioeconómicos medios y altos.
El precio de las propiedades varía significativamente, con valores entre 58 y 1999 y una mediana de 330, lo que evidencia una dispersión considerable en los valores del mercado inmobiliario. El área construida tiene un rango amplio, desde 30 m² hasta 1745 m², con una mediana de 122 m², lo que sugiere una oferta inmobiliaria con viviendas de diferentes dimensiones. En términos de comodidades, el número de parqueaderos, baños y habitaciones muestra distribuciones coherentes, con medianas de 1, 3 y 3, respectivamente, y valores máximos de 10, lo que sugiere variaciones en la cantidad de espacios disponibles en las propiedades.
vivienda_final <- vivienda %>% select(-id)
summary(vivienda_final)
zona piso estrato preciom
Length:8243 Min. : 1.000 Min. :3.000 Min. : 58.0
Class :character 1st Qu.: 2.000 1st Qu.:4.000 1st Qu.: 220.0
Mode :character Median : 3.000 Median :5.000 Median : 330.0
Mean : 3.545 Mean :4.637 Mean : 433.3
3rd Qu.: 4.000 3rd Qu.:5.000 3rd Qu.: 540.0
Max. :12.000 Max. :6.000 Max. :1999.0
areaconst parqueaderos banios habitaciones
Min. : 30 Min. : 0.000 Min. : 1.000 Min. : 1.000
1st Qu.: 80 1st Qu.: 1.000 1st Qu.: 2.000 1st Qu.: 3.000
Median : 122 Median : 1.000 Median : 3.000 Median : 3.000
Mean : 174 Mean : 1.488 Mean : 3.128 Mean : 3.634
3rd Qu.: 227 3rd Qu.: 2.000 3rd Qu.: 4.000 3rd Qu.: 4.000
Max. :1745 Max. :10.000 Max. :10.000 Max. :10.000
tipo barrio longitud latitud
Length:8243 Length:8243 Min. :-76.59 Min. :3.333
Class :character Class :character 1st Qu.:-76.54 1st Qu.:3.381
Mode :character Mode :character Median :-76.53 Median :3.416
Mean :-76.53 Mean :3.418
3rd Qu.:-76.52 3rd Qu.:3.452
Max. :-76.46 Max. :3.498
El análisis de los boxplots refleja una amplia diversidad en el mercado inmobiliario. La mediana del precio es de 330, con la mayoría de las propiedades por debajo de 500, aunque algunas superan los 1000. En cuanto al área construida, la mediana es 122 m², con una oferta mayoritaria de viviendas compactas y algunas de gran tamaño. La distribución de baños y habitaciones se concentra entre 2 y 4, con una mediana de 3, mientras que los parqueaderos suelen oscilar entre 1 y 2.
El análisis del piso muestra que la mayoría de las propiedades están entre los niveles 1 y 4, con una mediana de 3. También se observan viviendas con hasta 10 habitaciones y 10 baños, indicando la presencia de inmuebles más amplios o multifamiliares. En general, los datos revelan un mercado heterogéneo, con propiedades que van desde unidades accesibles hasta viviendas exclusivas.
par(mfrow=c(2,3)) # Configurar una cuadrícula de 2 filas y 3 columnas para visualizar múltiples gráficos
# Boxplots de variables numéricas
boxplot(vivienda_final$preciom, main = "Distribución de Precios", col = "lightblue")
boxplot(vivienda_final$areaconst, main = "Distribución de Área Construida", col = "lightgreen")
boxplot(vivienda_final$parqueaderos, main = "Distribución de Parqueaderos", col = "lightcoral")
boxplot(vivienda_final$banios, main = "Distribución de Baños", col = "lightgoldenrod")
boxplot(vivienda_final$habitaciones, main = "Distribución de Habitaciones", col = "lightpink")
boxplot(vivienda_final$piso, main = "Distribución de Piso", col = "lightgray")
Para este análisis, se seleccionaron las variables numéricas más representativas del dataset: piso, precio, área construida, parqueaderos, baños y habitaciones. Aunque la variable estrato es numérica, no fue incluida en el análisis, ya que representa una escala de orden y no una variable continua en sentido estricto. Su naturaleza categórica ordinal no aporta información útil en métodos como el Análisis de Componentes Principales (PCA) o el Clustering, donde las distancias y relaciones matemáticas entre variables juegan un papel clave.
Dado que las variables seleccionadas tienen escalas diferentes, se realizó un escalamiento de los datos, normalizando cada variable con una media de 0 y una desviación estándar de 1. Esto garantiza que todas las variables contribuyan equitativamente en el análisis, evitando sesgos causados por diferencias de magnitud.
# Selección de variables numéricas relevantes (sin estrato)
dataset_numerico <- vivienda_final %>% select(piso, preciom, areaconst, parqueaderos, banios, habitaciones)
# Escalamiento de los datos (media = 0, desviación estándar = 1)
dataset_escalado <- scale(dataset_numerico)
summary(dataset_escalado) # Datos escalados
piso preciom areaconst parqueaderos
Min. :-1.1294 Min. :-1.1409 Min. :-1.0121 Min. :-1.2014
1st Qu.:-0.6857 1st Qu.:-0.6484 1st Qu.:-0.6607 1st Qu.:-0.3938
Median :-0.2420 Median :-0.3140 Median :-0.3656 Median :-0.3938
Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
3rd Qu.: 0.2017 3rd Qu.: 0.3245 3rd Qu.: 0.3722 3rd Qu.: 0.4137
Max. : 3.7512 Max. : 4.7601 Max. :11.0383 Max. : 6.8744
banios habitaciones
Min. :-1.50598 Min. :-1.8450
1st Qu.:-0.79820 1st Qu.:-0.4438
Median :-0.09042 Median :-0.4438
Mean : 0.00000 Mean : 0.0000
3rd Qu.: 0.61737 3rd Qu.: 0.2568
Max. : 4.86407 Max. : 4.4603
par(mfrow=c(2,3))
hist(dataset_escalado[, "piso"], main="Distribución de Piso", col="lightblue", xlab="Piso", breaks=30)
hist(dataset_escalado[, "preciom"], main="Distribución de Precio", col="lightgreen", xlab="Precio", breaks=30)
hist(dataset_escalado[, "areaconst"], main="Distribución de Área Construida", col="lightcoral", xlab="Área Construida", breaks=30)
hist(dataset_escalado[, "parqueaderos"], main="Distribución de Parqueaderos", col="lightgoldenrod", xlab="Parqueaderos", breaks=30)
hist(dataset_escalado[, "banios"], main="Distribución de Baños", col="lightpink", xlab="Baños", breaks=30)
hist(dataset_escalado[, "habitaciones"], main="Distribución de Habitaciones", col="lightgray", xlab="Habitaciones", breaks=30)
El Análisis de Componentes Principales (PCA) permite reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar las características más influyentes en la variación de precios y la oferta del mercado.
# Dataset para PCA
dataset_pca_vars <- as.data.frame(dataset_escalado)
head(dataset_pca_vars)
piso preciom areaconst parqueaderos banios habitaciones
1 -0.6856881 -0.5571698 -0.7309968 -0.3938460 -0.09041565 1.6579542
2 -0.6856881 -0.3443558 -0.3796746 -0.3938460 -0.79819935 -0.4438323
3 -0.6856881 -0.2531498 0.3229699 0.4137343 -0.79819935 0.2567632
4 -0.6856881 -0.1011398 0.7445566 1.2213146 1.32515174 -0.4438323
5 -1.1293750 -0.5267678 -0.5904679 -0.3938460 -0.79819935 -0.4438323
6 -1.1293750 -0.5875718 -0.6115473 -0.3938460 -0.09041565 -0.4438323
El diagrama de Pareto permite visualizar la cantidad de varianza explicada por cada componente. En este caso, los tres primeros componentes (PC1, PC2 y PC3) explican conjuntamente un 85.05% de la variabilidad total del dataset:
Dado que estos tres componentes explican la mayor parte de la variabilidad en los datos, se pueden utilizar para simplificar el análisis sin perder información significativa.
pca_result <- prcomp(dataset_pca_vars, center = FALSE, scale = FALSE)
# Verificar el resumen del PCA
summary(pca_result)
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6
Standard deviation 1.7937 1.0742 0.8555 0.63755 0.55081 0.43231
Proportion of Variance 0.5362 0.1923 0.1220 0.06775 0.05057 0.03115
Cumulative Proportion 0.5362 0.7286 0.8505 0.91829 0.96885 1.00000
# Transformar los datos originales al nuevo espacio PCA
pca_data <- as.data.frame(pca_result$x)
# Obtener las cargas de cada variable en los componentes principales
loadings_pca <- pca_result$rotation
# Mostrar las cargas
print(loadings_pca)
PC1 PC2 PC3 PC4 PC5
piso 0.1428170 -0.731598161 0.65171357 0.02420535 -0.1328810
preciom -0.4651244 -0.314579140 -0.18529860 0.41463618 0.2140121
areaconst -0.4857836 0.072555109 0.02354688 0.47667740 -0.6052699
parqueaderos -0.3947308 -0.392385188 -0.38275041 -0.69101615 -0.2508503
banios -0.4913407 -0.004926551 0.21677389 -0.04675384 0.6895593
habitaciones -0.3606228 0.454477111 0.58897419 -0.34724682 -0.1782469
PC6
piso -0.03733089
preciom 0.65775328
areaconst -0.40573712
parqueaderos -0.05739679
banios -0.48362298
habitaciones 0.40516520
# Cargar librerías necesarias
library(ggplot2)
library(reshape2)
Adjuntando el paquete: 'reshape2'
The following object is masked from 'package:tidyr':
smiths
# Obtener las cargas (loadings)
loadings_pca <- as.data.frame(pca_result$rotation)
loadings_pca$Variable <- rownames(loadings_pca) # Agregar nombres de variables
# Convertir a formato largo para graficar el mapa de calor
loadings_long <- melt(loadings_pca, id.vars = "Variable", variable.name = "Componente", value.name = "Carga")
# Obtener la varianza explicada
var_exp <- pca_result$sdev^2 / sum(pca_result$sdev^2)
var_exp_cum <- cumsum(var_exp)
# Crear dataframe con varianza explicada y acumulada
pareto_df <- data.frame(
PC = factor(paste0("PC", 1:length(var_exp)), levels = paste0("PC", 1:length(var_exp))),
VarianzaExplicada = var_exp,
VarianzaAcumulada = var_exp_cum
)
# Graficar el diagrama de Pareto con etiquetas
ggplot(pareto_df, aes(x = PC, y = VarianzaExplicada)) +
geom_bar(stat = "identity", fill = "steelblue", alpha = 0.7) +
geom_text(aes(label = round(VarianzaExplicada, 3)), vjust = -0.5, color = "black", size = 4) + # Etiquetas en barras
geom_line(aes(y = VarianzaAcumulada), group = 1, color = "red", linewidth = 1) +
geom_point(aes(y = VarianzaAcumulada), color = "red", size = 2) +
geom_text(aes(y = VarianzaAcumulada, label = round(VarianzaAcumulada, 3)), vjust = -0.5, color = "red", size = 4) + # Etiquetas en la línea acumulada
labs(title = "Diagrama de Pareto de la Varianza Explicada",
x = "Componentes Principales",
y = "Proporción de Varianza Explicada") +
theme_minimal()
El mapa de calor de las cargas permite identificar qué variables tienen mayor influencia en cada componente:
# Mapa de calor de las cargas
ggplot(loadings_long, aes(x = Componente, y = Variable, fill = Carga)) +
geom_tile() +
geom_text(aes(label = round(Carga, 2)), size = 4) +
scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
labs(title = "Mapa de Calor de las Cargas en los Componentes",
x = "Componentes",
y = "Variables") +
theme_minimal()
El gráfico de componentes principales proporciona una interpretación visual de la relación entre variables en el espacio PCA. En este caso:
# Graficar la contribución de las variables en el espacio PCA con ajustes
fviz_pca_var(pca_result,
col.var = "contrib",
gradient.cols = c("blue", "white", "red"), # Cambio de colores
repel = TRUE, # Evita sobreposición de etiquetas
title = "Visualización de las variables en el plano de los componentes principales") +
xlim(-1, 1) + ylim(-1, 1) # Ajuste de zoom para mejor visualización
La reducción dimensional mediante PCA permitió identificar que el mercado inmobiliario se estructura en torno a tres ejes principales: tamaño y precio de la propiedad (PC1), ubicación vertical y distribución de espacios (PC2), y relación entre número de habitaciones y altura del inmueble (PC3). Estos hallazgos facilitan una mejor segmentación del mercado y pueden utilizarse para desarrollar modelos predictivos o estrategias de análisis de precios en función de las características estructurales de las viviendas.
# Dataset para conglometros
dataset_conglomerados <- as.data.frame(dataset_escalado)
head(dataset_conglomerados)
piso preciom areaconst parqueaderos banios habitaciones
1 -0.6856881 -0.5571698 -0.7309968 -0.3938460 -0.09041565 1.6579542
2 -0.6856881 -0.3443558 -0.3796746 -0.3938460 -0.79819935 -0.4438323
3 -0.6856881 -0.2531498 0.3229699 0.4137343 -0.79819935 0.2567632
4 -0.6856881 -0.1011398 0.7445566 1.2213146 1.32515174 -0.4438323
5 -1.1293750 -0.5267678 -0.5904679 -0.3938460 -0.79819935 -0.4438323
6 -1.1293750 -0.5875718 -0.6115473 -0.3938460 -0.09041565 -0.4438323
El método del codo indica que a partir de tres clusters (K=3) la disminución en la varianza intra-cluster se estabiliza, sugiriendo que esta cantidad de grupos ofrece una segmentación eficiente sin añadir demasiada complejidad. Por otro lado, el índice de silueta presenta su valor máximo en dos clusters (K=2), lo que indica que con esta cantidad se logra la mejor separación y cohesión entre los grupos. La elección final dependerá del equilibrio entre granularidad y calidad de segmentación: si se busca una estructura más definida, K=2 es la mejor opción, mientras que K=3 permite una diferenciación más detallada en el mercado inmobiliario.
# Método del codo para determinar el número óptimo de clusters
fviz_nbclust(dataset_conglomerados, kmeans, method = "wss") +
ggtitle("Método del Codo: Selección del Número de Clusters") +
xlab("Número de Clusters (K)") +
ylab("Suma de Cuadrados Intra-cluster") +
geom_vline(xintercept = 3, linetype = "dashed", color = "red") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
# Método del coeficiente de silueta para validar la calidad de los clusters
fviz_nbclust(dataset_conglomerados, kmeans, method = "silhouette") +
ggtitle("Índice de Silueta: Calidad de la Segmentación") +
xlab("Número de Clusters (K)") +
ylab("Ancho Promedio de la Silueta") +
geom_vline(xintercept = 3, linetype = "dashed", color = "red") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
El análisis de segmentación con 2 y 3 clusters evidencia diferencias
significativas en la clasificación de las propiedades según su precio y
área construida. Con 2 clusters, se observa una segmentación general
donde las viviendas de menor área y menor precio se agrupan en azul,
mientras que las de mayor tamaño y precio se concentran en rojo,
sugiriendo una división clara entre propiedades más asequibles y de alto
costo. Al aumentar a 3 clusters, se introduce un nuevo grupo en verde,
que parece captar un segmento intermedio en cuanto a área y precio,
separando mejor las propiedades más grandes y costosas. Sin embargo, la
distribución del grupo rojo sigue predominando, lo que indica que la
segmentación en dos clusters ya capturaba gran parte de la estructura
del mercado. La elección entre dos o tres clusters dependerá del nivel
de granularidad requerido para el análisis del mercado inmobiliario.
# Definir el número de clusters a comparar
set.seed(123) # Fijar semilla para reproducibilidad
# Aplicar K-means con 2 y 3 clusters
kmeans_2 <- kmeans(dataset_conglomerados, centers = 2, nstart = 25)
kmeans_3 <- kmeans(dataset_conglomerados, centers = 3, nstart = 25)
# Agregar etiquetas de cluster al dataset original
dataset_conglomerados$cluster_2 <- as.factor(kmeans_2$cluster)
dataset_conglomerados$cluster_3 <- as.factor(kmeans_3$cluster)
# Crear gráficos de dispersión comparativos con paleta de colores personalizada
plot_2_clusters <- ggplot(dataset_conglomerados, aes(x = areaconst, y = preciom, color = cluster_2)) +
geom_point(alpha = 0.6) +
labs(title = "Segmentación con 2 Clusters", x = "Área Construida (m²)", y = "Precio de la Vivienda (MM)") +
scale_color_manual(values = c("red", "blue")) + # Paleta de colores para 2 clusters
theme_minimal()
plot_3_clusters <- ggplot(dataset_conglomerados, aes(x = areaconst, y = preciom, color = cluster_3)) +
geom_point(alpha = 0.6) +
labs(title = "Segmentación con 3 Clusters", x = "Área Construida (m²)", y = "Precio de la Vivienda (MM)") +
scale_color_manual(values = c("red", "blue", "green")) + # Paleta de colores para 3 clusters
theme_minimal()
# Mostrar gráficos en paralelo
grid.arrange(plot_2_clusters, plot_3_clusters, ncol = 2)
El análisis de conglomerados aplicado a la relación entre el número de
habitaciones y el precio de la vivienda revela patrones interesantes en
la segmentación del mercado inmobiliario. Con dos clusters, la
diferenciación principal parece radicar en los precios, donde un grupo
(rojo) concentra la mayoría de las viviendas con precios elevados y
mayor número de habitaciones, mientras que el otro (azul) agrupa
aquellas con precios más bajos. Sin embargo, al incrementar a tres
clusters, se evidencia una segmentación más refinada, donde el tercer
grupo (verde) representa una categoría intermedia en términos de precios
y número de habitaciones, este análisis sugiere que la variable
habitaciones tiene un impacto importante en la diferenciación del
mercado.
# Gráfico con 2 clusters
p1 <- ggplot(dataset_conglomerados, aes(x = habitaciones, y = preciom, color = cluster_2)) +
geom_point(alpha = 0.6) +
labs(title = "Segmentación con 2 Clusters",
x = "Número de Habitaciones",
y = "Precio de la Vivienda (MM)",
color = "Cluster") +
theme_minimal() +
scale_color_manual(values = c("red", "blue"))
# Gráfico con 3 clusters
p2 <- ggplot(dataset_conglomerados, aes(x = habitaciones, y = preciom, color = cluster_3)) +
geom_point(alpha = 0.6) +
labs(title = "Segmentación con 3 Clusters",
x = "Número de Habitaciones",
y = "Precio de la Vivienda (MM)",
color = "Cluster") +
theme_minimal() +
scale_color_manual(values = c("red", "blue", "green"))
# Mostrar ambos gráficos en paralelo
grid.arrange(p1, p2, ncol = 2)
Dado que la base de datos contiene 382 barrios, la tabla de contingencia es extensa. Para efectos de presentación, se muestran únicamente los primeros 20 barrios, lo que nos permite identificar tendencias en la distribución de casas y apartamentos sin saturar la visualización. La tabla permite observar cómo en ciertos barrios predomina un tipo de vivienda sobre el otro, reflejando diferencias en el desarrollo inmobiliario.
tabla_barrio_tipo <- table(vivienda_final$barrio, vivienda_final$tipo)
head(tabla_barrio_tipo, 20)
apartamento casa
20 de julio 0 3
3 de julio 0 1
acopi 88 60
agua blanca 0 1
aguablanca 1 1
aguacatal 98 11
alameda 4 11
alameda del rio 2 1
alamos 11 3
alborada 0 1
alcazares 2 0
alferez real 5 2
alfonso lopez 2 19
alfonso lopez i 0 1
alto jordan 1 0
altos de guadalupe 1 3
altos de menga 3 0
altos de santa 1 0
antonio nariño 0 2
aranjuez 0 15
El resultado muestra un p-valor < 0.05, lo que confirma que existe una relación estadísticamente significativa entre el barrio y el tipo de vivienda. Esto significa que la presencia de casas o apartamentos en un barrio no es aleatoria,sino que sigue un patrón estructural determinado por la planificación urbana y las dinámicas del mercado inmobiliario.
# Prueba de Chi-cuadrado con simulación de Montecarlo
print(chisq.test(tabla_barrio_tipo, simulate.p.value = TRUE, B = 2000))
Pearson's Chi-squared test with simulated p-value (based on 2000
replicates)
data: tabla_barrio_tipo
X-squared = 2416.3, df = NA, p-value = 0.0004998
El analisis nos permite visualizar cómo se agrupan los barrios según el tipo de vivienda predominante. Sin embargo, el resultado muestra que toda la variabilidad de los datos se explica en una única dimensión, con un eigenvalor de 0.2931 y una varianza explicada del 100%. Esto implica que no hay una estructura multivariada compleja, sino que la relación entre barrios y tipos de vivienda es altamente estructurada y predecible.
# Aplicar Análisis de Correspondencia
ca_barrio_tipo <- CA(tabla_barrio_tipo, graph = TRUE)
ca_barrio_tipo$eig
eigenvalue percentage of variance cumulative percentage of variance
dim 1 0.2931299 100 100
La distribución de casas y apartamentos por barrio no es aleatoria, sino que sigue patrones definidos por el mercado y la zonificación urbana. El Chi-cuadrado confirma que la relación es significativa, mientras que el CA muestra que la diferenciación es unidimensional, es decir, la segmentación de los barrios en términos de tipo de vivienda sigue un único eje de variabilidad
La tabla de contingencia muestra que la distribución de apartamentos y casas no es homogénea entre las distintas zonas, evidenciando que ciertas zonas tienen una mayor presencia de apartamentos, mientras que otras predominan en casas. Este comportamiento sugiere una segmentación clara en el mercado inmobiliario, donde la oferta de vivienda está influenciada por la ubicación geográfica.
tabla_tipo_zona <- table(vivienda_final$tipo, vivienda_final$zona)
print(tabla_tipo_zona)
zona centro zona norte zona oeste zona oriente zona sur
apartamento 24 1188 1024 61 2777
casa 97 700 164 284 1924
La prueba de Chi-cuadrado evalúa si existe una relación significativa entre tipo de vivienda y zona, obteniendo un p-valor menor a 0.05, lo que indica que la distribución observada no es aleatoria. Esto significa que la ubicación geográfica influye de manera significativa en el tipo de vivienda disponible, confirmando que hay diferencias estructurales en la distribución de apartamentos y casas según la zona.
# Prueba de Chi-cuadrado con simulación de Montecarlo
chi2_montecarlo <- chisq.test(tabla_tipo_zona, simulate.p.value = TRUE, B = 2000)
print(chi2_montecarlo)
Pearson's Chi-squared test with simulated p-value (based on 2000
replicates)
data: tabla_tipo_zona
X-squared = 688.15, df = NA, p-value = 0.0004998
El Análisis de Correspondencia muestra que la varianza se explica en una única dimensión, lo que refleja una relación estructurada entre tipo de vivienda y zona. Esto indica que la diferenciación entre zonas en términos de oferta inmobiliaria es clara, permitiendo identificar agrupaciones específicas en la distribución de apartamentos y casas.
# Aplicar Análisis de Correspondencia
ca_tipo_zona <- CA(tabla_tipo_zona, graph = TRUE)
print(ca_tipo_zona$eig)
eigenvalue percentage of variance cumulative percentage of variance
dim 1 0.08348344 100 100
Los resultados indican que el tipo de vivienda está directamente relacionado con la zona donde se ubica, evidenciando que ciertas zonas presentan una mayor densidad de apartamentos, mientras que otras tienen más casas. Esta segmentación podría estar influenciada por factores como la planificación urbana, la demanda del mercado y las características sociodemográficas de cada sector.
El análisis multivariado realizado sobre la oferta inmobiliaria en la ciudad de Cali permitió extraer información clave sobre las principales dimensiones que caracterizan el mercado de bienes raíces. Se destacó la importancia de variables como el número de habitaciones y baños, así como el área construida de los bienes inmuebles, diferenciando entre casas y apartamentos. Además, el mercado inmobiliario de Cali es diverso y segmentado según el tipo de vivienda y la ubicación. Mientras que las casas ofrecen mayor espacio y comodidades a un precio más alto, los apartamentos resultan más accesibles y predominan en las zonas de mayor densidad poblacional.