knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE,
error = FALSE,
fig.align = "center",
fig.width = 8,
fig.height = 5
)
Este informe busca comprender de manera multidimensional la oferta de viviendas urbanas para apoyar decisiones estratégicas (valoración, segmentación de mercado y oportunidades). Para ello, aplique tres técnicas clave:
ACP para resumir variables numéricas en pocos componentes interpretables.
Clúster para agrupar inmuebles en segmentos homogéneos.
Correspondencia para explorar asociaciones entre variables categóricas (p. ej., tipo, zona y/o barrio).
El valor del análisis radica en convertir una base extensa en patrones accionables: qué caracteriza a los inmuebles más costosos, qué combinaciones de atributos se repiten y qué segmentos del mercado conviene priorizar.
#install.packages("devtools")
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
#install.packages("janitor")
#install.packages("naniar")
library(paqueteMODELOS)
library(dplyr)
library(tidyr)
library(stringr)
library(ggplot2)
library(janitor)
library(naniar)
library(FactoMineR)
library(factoextra)
library(cluster)
data("vivenda")
if (exists("vivenda")) vivienda <- vivenda
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…
Esta etapa valida la estructura y tipos de variables, la cual es importante porque el análisis ACP y el Clúster requieren variables numéricas consistentes, mientras que el análisis de Correspondencia trabaja con tablas de contingencia (categóricas).
estructura_datos <- data.frame(
Variable = names(vivienda),
Tipo = sapply(vivienda, class)
)
knitr::kable(estructura_datos, caption = "Estructura del conjunto de datos")
| Variable | Tipo | |
|---|---|---|
| id | id | numeric |
| zona | zona | character |
| piso | piso | character |
| estrato | estrato | numeric |
| preciom | preciom | numeric |
| areaconst | areaconst | numeric |
| parqueaderos | parqueaderos | numeric |
| banios | banios | numeric |
| habitaciones | habitaciones | numeric |
| tipo | tipo | character |
| barrio | barrio | character |
| longitud | longitud | numeric |
| latitud | latitud | numeric |
na_resumen <- sapply(vivienda, function(x) sum(is.na(x)))
sort(na_resumen, decreasing = TRUE)
## piso parqueaderos id zona estrato areaconst
## 2638 1605 3 3 3 3
## banios habitaciones tipo barrio longitud latitud
## 3 3 3 3 3 3
## preciom
## 2
# Visual rápido (si hay NAs)
gg_miss_var(vivienda)
El diagnóstico de valores faltantes permitió identificar que el conjunto de datos presenta una proporción relativamente baja de datos ausentes en la mayoría de las variables numéricas, lo cual indica una buena calidad general de la información. No obstante, la presencia de algunos valores faltantes podría afectar la estimación de distancias y correlaciones utilizadas en técnicas multivariadas como el Análisis de Componentes Principales y el análisis de conglomerados.
Por esta razón, optaré por aplicar una estrategia de imputación simple, utilizando la mediana para variables numéricas y la moda para variables categóricas, lo que permite conservar el tamaño de la muestra sin introducir sesgos importantes, además de ser un enfoque adecuado cuando el porcentaje de valores faltantes es reducido. En consecuencia, el conjunto de datos queda preparado para las etapas posteriores de análisis multivariado sin pérdida significativa de información.
sum(duplicated(vivienda))
## [1] 1
La evaluación de registros duplicados mostró que no existe una cantidad significativa de observaciones repetidas en el conjunto de datos, lo cual sugiere que cada registro representa una propiedad distinta dentro del mercado inmobiliario analizado. Este resultado es relevante porque la presencia de duplicados podría generar distorsiones en los análisis estadísticos, particularmente en la identificación de patrones y segmentaciones mediante técnicas de clustering.
Así mismo, la revisión general de consistencia permitió confirmar que las variables presentan formatos adecuados para su análisis, aunque se identificaron posibles variaciones en variables categóricas relacionadas con ubicación. Estas inconsistencias fueron abordadas mediante procesos de limpieza y estandarización de texto, lo que contribuye a mejorar la calidad de la información y la confiabilidad de los resultados obtenidos en las fases posteriores del estudio.
vivienda2 <- vivienda %>%
clean_names() %>%
mutate(across(where(is.character), ~ str_squish(.x)))
Normalizar nombres y espacios reduce errores en recodificación y tabulaciones, especialmente en barrio/zona/tipo. Esto ayudará para el análisis de correspondencia.
moda <- function(x){
x <- x[!is.na(x)]
if(length(x) == 0) return(NA)
ux <- unique(x)
ux[which.max(tabulate(match(x, ux)))]
}
vivienda2 <- vivienda2 %>%
mutate(across(where(is.numeric), ~ ifelse(is.na(.x), median(.x, na.rm = TRUE), .x))) %>%
mutate(across(where(is.character), ~ ifelse(is.na(.x) | .x=="", moda(.x), .x)))
Esta imputación es pragmática: mantiene el tamaño muestral para los modelos y evita perder información. Al ser un informe para toma de decisiones, interesa conservar cobertura del mercado.
Para ACP y Clúster usaré variables numéricas que describen precio/tamaño/características.
Para Correspondencia usaré variables categóricas y una versión controlada de barrio (solo top barrios) para evitar miles de categorías.
# Variables numéricas típicas
vars_num <- vivienda2 %>%
select(where(is.numeric))
names(vars_num)
## [1] "id" "estrato" "preciom" "areaconst" "parqueaderos"
## [6] "banios" "habitaciones" "longitud" "latitud"
Separar por tipo de variable evita “mezclar” métodos. Los métodos ACP y Clúster operan por geometría/distancias, mientras que Correspondencia por asociaciones entre categorías.
if ("preciom" %in% names(vivienda2) && "areaconst" %in% names(vivienda2)) {
ggplot(vivienda2, aes(x = areaconst, y = preciom)) +
geom_point(alpha = 0.25) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Relación entre área construida y precio", x = "Área construida", y = "Precio")
}
Esta gráfica muestra una relación positiva entre el área construida y el precio de las viviendas, evidenciada por la tendencia creciente de la línea de regresión. Esto indica que, en general, las propiedades con mayor área tienden a presentar precios más altos, lo cual es consistente con la lógica del mercado inmobiliario, donde el tamaño del inmueble constituye uno de los principales determinantes del valor.
No obstante, se observa una dispersión considerable de los puntos alrededor de la línea de tendencia, especialmente en rangos medios y altos de área construida. Esta variabilidad sugiere que el precio no depende únicamente del tamaño, sino también de otros factores relevantes como la ubicación, el estrato socioeconómico, el tipo de vivienda y las características adicionales del inmueble. La presencia de algunos valores atípicos también indica que existen propiedades con precios significativamente diferentes a lo esperado según su área, lo cual podría estar asociado a condiciones particulares del mercado o características no incluidas en el modelo.
# Estandarizamos porque las variables están en distintas escalas (m2, precio, conteos)
X <- scale(vars_num)
pca_res <- prcomp(X, center = TRUE, scale. = TRUE)
summary(pca_res)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 1.9544 1.3735 0.9858 0.9493 0.7613 0.61947 0.48608
## Proportion of Variance 0.4244 0.2096 0.1080 0.1001 0.0644 0.04264 0.02625
## Cumulative Proportion 0.4244 0.6340 0.7420 0.8421 0.9065 0.94918 0.97544
## PC8 PC9
## Standard deviation 0.4285 0.19358
## Proportion of Variance 0.0204 0.00416
## Cumulative Proportion 0.9958 1.00000
El análisis de componentes principales muestra que el primer componente (PC1) explica aproximadamente el 42.4% de la variabilidad total, mientras que el segundo componente (PC2) aporta cerca del 21% adicional. En conjunto, los dos primeros componentes explican alrededor del 63.4% de la variación, lo que indica que una parte importante de la información del conjunto de datos puede resumirse en solo dos dimensiones.
Esto sugiere que existe una estructura subyacente clara en las variables analizadas, donde múltiples características de los inmuebles se combinan para explicar el comportamiento del mercado. A medida que se incorporan más componentes, el incremento en la varianza explicada es menor, lo que confirma que los primeros componentes contienen la información más relevante.
Desde una perspectiva práctica, la reducción de dimensionalidad permite simplificar el análisis del mercado inmobiliario sin perder información significativa, facilitando la identificación de patrones y la posterior aplicación de técnicas como el análisis de conglomerados.
fviz_eig(pca_res, addlabels = TRUE)
El scree plot muestra una disminución pronunciada en la varianza explicada después de los primeros componentes, especialmente entre el primer y el segundo componente, lo que evidencia la existencia de un punto de inflexión o “codo” en la gráfica. Este comportamiento confirma que los primeros componentes concentran la mayor parte de la información relevante del conjunto de datos.
Considerando que los dos primeros componentes explican más del 60% de la variabilidad total y que los componentes posteriores aportan incrementos menores, resulta razonable utilizar entre dos y cuatro componentes para representar la estructura del mercado. Esta elección permite lograr un equilibrio entre simplicidad e información, facilitando la visualización y la interpretación de los patrones existentes en las características de las viviendas.
En términos estratégicos, la identificación de estos componentes principales permite comprender mejor los factores que diferencian las propiedades dentro del mercado, contribuyendo a mejorar procesos de segmentación, valoración y toma de decisiones empresariales.
fviz_pca_var(pca_res, col.var = "contrib")
La gráfica de cargas muestra que variables como precio, área construida, baños, parqueaderos y habitaciones se encuentran fuertemente correlacionadas y contribuyen principalmente a la Dimensión 1, lo que sugiere que este componente representa un eje de tamaño y valor del inmueble.
La variable estrato también presenta una relación positiva con estas características, mientras que las variables de ubicación geográfica introducen variabilidad adicional en el modelo. En conjunto, los resultados evidencian que el mercado inmobiliario está influenciado por la combinación de factores físicos, socioeconómicos y espaciales, lo que justifica el uso de técnicas multivariadas para su análisis.
#fviz_pca_ind(pca_res, alpha.ind = 0.2)
fviz_pca_ind(pca_res,
col.ind = "steelblue",
alpha.ind = 0.3,
label = "none")
El mapa de observaciones en el espacio de los dos primeros componentes principales permite visualizar la distribución de las propiedades según sus características multivariadas. Dado que las dos primeras dimensiones explican aproximadamente el 63% de la variabilidad total, la representación proporciona una visión adecuada de la estructura general del mercado inmobiliario.
Se observa una alta concentración de observaciones en la zona central del gráfico, lo que indica que una gran proporción de las viviendas presenta características similares en términos de tamaño, precio y atributos físicos. Sin embargo, también se identifican propiedades ubicadas en regiones más alejadas del centro, lo que sugiere la existencia de inmuebles con características diferenciadas, posiblemente asociadas a valores extremos de área, precio o nivel socioeconómico. Estos puntos pueden representar segmentos específicos del mercado, como viviendas de mayor valor o propiedades con características particulares.
La forma alargada de la nube de puntos a lo largo de la Dimensión 1 refuerza la interpretación de este componente como un eje relacionado con el tamaño y valor de los inmuebles, donde las propiedades con valores más altos en esta dimensión corresponden a viviendas de mayor área y precio. La dispersión observada en la Dimensión 2 sugiere la influencia de factores adicionales, posiblemente asociados a ubicación o características socioeconómicas, que introducen variabilidad en el mercado.
Desde una perspectiva analítica, la presencia de posibles subgrupos o zonas de concentración en el gráfico constituye evidencia visual de segmentación potencial, lo que justifica la aplicación posterior del análisis de conglomerados para identificar de manera formal los distintos perfiles de viviendas.
set.seed(123)
# Probamos K de 2 a 8 (rango razonable para lectura de negocio)
sil <- sapply(2:8, function(k){
km <- kmeans(X, centers = k, nstart = 20)
ss <- silhouette(km$cluster, dist(X))
mean(ss[, 3])
})
data.frame(k = 2:8, silhouette_prom = sil)
plot(2:8, sil, type = "b", xlab = "Número de clusters (k)", ylab = "Silhouette promedio")
El análisis del coeficiente de silhouette permite evaluar la calidad de la segmentación considerando la cohesión interna de los grupos y la separación entre ellos. Los resultados muestran que el valor más alto del silhouette promedio se obtiene cuando k = 2, lo que indica que una división en dos grupos presenta la mayor separación relativa entre segmentos. Sin embargo, también se observan valores relativamente estables para otros números de clusters, especialmente en k = 4 y k = 8, lo que sugiere la existencia de una estructura de segmentación más compleja dentro del mercado.
Desde una perspectiva práctica, aunque un número menor de clusters puede maximizar la calidad estadística, también es importante considerar la interpretabilidad y utilidad para la toma de decisiones. En el contexto inmobiliario, trabajar con entre 3 y 5 segmentos suele ser más adecuado para diferenciar perfiles de propiedades sin generar una segmentación excesivamente fragmentada.
En conjunto, los resultados indican que el mercado presenta heterogeneidad suficiente para ser segmentado en varios grupos, lo que justifica la aplicación del análisis de conglomerados para identificar perfiles diferenciados de viviendas y apoyar estrategias comerciales específicas.
k_opt <- (2:8)[which.max(sil)]
k_opt
## [1] 2
set.seed(123)
km_final <- kmeans(X, centers = k_opt, nstart = 30)
vivienda2$cluster <- factor(km_final$cluster)
# Perfil promedio por cluster (en variables numéricas)
perfil <- vivienda2 %>%
group_by(cluster) %>%
summarise(across(where(is.numeric), mean, .names = "mean_{.col}"))
perfil
El perfil por clúster describe “tipos de inmueble”:
Un clúster con alto precio + alta área + más baños/parqueaderos puede representar segmento premium.
Un clúster con área menor + precio menor puede ser segmento de entrada/inversión.
Si aparece un clúster con precio alto pero área moderada, podría sugerir fuerte efecto de ubicación/estrato.
El modelo de k-means identifica dos segmentos diferenciados de propiedades. El primer cluster agrupa viviendas de mayor precio, área y nivel socioeconómico, lo que corresponde a un segmento premium del mercado, mientras que el segundo cluster representa propiedades de menor tamaño y valor, asociadas a un segmento más accesible. Esta segmentación evidencia la heterogeneidad del mercado inmobiliario y permite orientar estrategias comerciales y de inversión según el perfil de cada grupo.
# Graficamos clusters sobre PC1/PC2
pca_df <- as.data.frame(pca_res$x[,1:2])
pca_df$cluster <- vivienda2$cluster
ggplot(pca_df, aes(x = PC1, y = PC2, color = cluster)) +
geom_point(alpha = 0.25) +
labs(title = "Clusters sobre el plano PC1–PC2")
La visualización de los clusters en el plano definido por los dos primeros componentes principales permite evaluar la coherencia de la segmentación obtenida mediante el algoritmo k-means. Se observa una separación relativamente clara entre los dos grupos, principalmente a lo largo del eje PC1, lo que confirma que este componente, asociado al tamaño y valor de los inmuebles, constituye el principal factor de diferenciación entre los segmentos identificados.
El cluster ubicado hacia valores más altos de PC1 corresponde a propiedades con mayores características físicas y económicas, mientras que el cluster con valores más bajos se asocia a viviendas de menor tamaño y precio. Aunque existe cierta superposición en la zona central, lo cual es normal en datos reales, la diferenciación general entre grupos es consistente y respalda la validez de la segmentación realizada.
Desde una perspectiva aplicada, esta representación gráfica permite confirmar que los segmentos identificados tienen fundamento estadístico y pueden utilizarse como base para estrategias comerciales diferenciadas, facilitando la identificación de perfiles de clientes y oportunidades de inversión según las características predominantes de cada grupo.
library(FactoMineR)
library(factoextra)
library(dplyr)
if (all(c("tipo","zona") %in% names(vivienda2))) {
tab_tipo_zona <- table(vivienda2$tipo, vivienda2$zona)
cat("Tabla tipo vs zona:\n")
print(tab_tipo_zona)
cat("\nProporciones por zona (columnas):\n")
print(round(prop.table(tab_tipo_zona, margin = 2), 3))
# CA (1D) - sin factoextra
ca_tz <- FactoMineR::CA(tab_tipo_zona, graph = FALSE)
# EXTRAER COORDENADAS DE FORMA ROBUSTA (vector o matriz)
row_coord <- ca_tz$row$coord
col_coord <- ca_tz$col$coord
# si vienen como matriz/dataframe, tomar la primera columna; si vienen vector, dejarlos
if (is.matrix(row_coord) || is.data.frame(row_coord)) row_coord <- row_coord[,1, drop = TRUE]
if (is.matrix(col_coord) || is.data.frame(col_coord)) col_coord <- col_coord[,1, drop = TRUE]
row_coord <- as.numeric(row_coord); names(row_coord) <- rownames(ca_tz$row$coord)
col_coord <- as.numeric(col_coord); names(col_coord) <- rownames(ca_tz$col$coord)
par(mfrow=c(1,2))
dotchart(row_coord, main="CA 1D - Tipo (Dim 1)", xlab="Coordenada Dim 1")
dotchart(col_coord, main="CA 1D - Zona (Dim 1)", xlab="Coordenada Dim 1")
par(mfrow=c(1,1))
}
## Tabla tipo vs zona:
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## Apartamento 24 1198 1029 62 2790
## Casa 100 722 169 289 1939
##
## Proporciones por zona (columnas):
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## Apartamento 0.194 0.624 0.859 0.177 0.590
## Casa 0.806 0.376 0.141 0.823 0.410
La relación entre el tipo de vivienda y la zona muestra diferencias
claras en la distribución de la oferta inmobiliaria. Se observa que en
la zona oeste predomina ampliamente la oferta de apartamentos, mientras
que en la zona oriente y zona centro predominan las casas. Por su parte,
la zona norte y la zona sur presentan una distribución más equilibrada
entre ambos tipos de vivienda, aunque con una ligera mayor presencia de
apartamentos.
Debido a que la variable tipo solo tiene dos categorías, el análisis de correspondencia genera una única dimensión, lo que permite interpretar el eje principal como el contraste entre casas y apartamentos según su asociación con cada zona. Estos resultados evidencian que la ubicación geográfica influye en la configuración del mercado inmobiliario.
if (all(c("barrio","zona") %in% names(vivienda2))) {
top_barrios <- vivienda2 %>%
count(barrio, sort = TRUE) %>%
slice_head(n = 15) %>%
pull(barrio)
vivienda2$barrio_top <- ifelse(vivienda2$barrio %in% top_barrios, vivienda2$barrio, "OTROS")
tab_bz <- table(vivienda2$barrio_top, vivienda2$zona)
tab_bz <- tab_bz[rowSums(tab_bz) > 0, colSums(tab_bz) > 0]
ca_bz <- FactoMineR::CA(tab_bz, graph = FALSE)
fviz_ca_biplot(ca_bz, repel = TRUE)
# Contribuciones (avanzado)
fviz_contrib(ca_bz, choice = "row", axes = 1, top = 10)
fviz_contrib(ca_bz, choice = "col", axes = 1, top = 10)
}
tab_tipo_zona <- table(vivienda2$tipo, vivienda2$zona)
round(prop.table(tab_tipo_zona, margin = 2), 3)
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## Apartamento 0.194 0.624 0.859 0.177 0.590
## Casa 0.806 0.376 0.141 0.823 0.410
El análisis de correspondencia muestra que la zona oeste y la zona sur son las que más contribuyen a la diferenciación territorial de la oferta inmobiliaria, lo que indica que estas áreas tienen mayor influencia en la estructura del mercado. En contraste, las demás zonas presentan una menor contribución relativa. Estos resultados permiten identificar sectores prioritarios.
El análisis multivariado realizado me permitió comprender de manera integral la estructura del mercado inmobiliario urbano a partir de la información disponible sobre las propiedades residenciales y reflejó los siguiente resultados:
El análisis de componentes principales evidenció que las características físicas de los inmuebles, especialmente el área construida, el número de habitaciones, baños y parqueaderos, junto con variables socioeconómicas como el estrato, constituyen factores determinantes en la variación de precios. La reducción de dimensionalidad facilitó la identificación de patrones.
El análisis de conglomerados permitió identificar segmentos diferenciados de viviendas, destacando la existencia de un grupo de propiedades con mayores características físicas y económicas, asociado a un segmento premium, y otro grupo con valores más moderados, correspondiente a un segmento más accesible del mercado. La visualización de estos clusters en el espacio de componentes principales confirmó la coherencia de la segmentación obtenida y evidenció la heterogeneidad existente en la oferta inmobiliaria.
Finalmente, el análisis de correspondencia permitió identificar patrones territoriales en la distribución de la oferta, evidenciando que determinadas zonas y barrios presentan asociaciones específicas con tipos de vivienda y características del mercado.
Con base en los resultados obtenidos, se recomendaría a la empresa inmobiliaria considerar estrategias diferenciadas según los segmentos identificados, priorizando acciones comerciales específicas para propiedades de mayor valor y características superiores, así como estrategias orientadas a mercados de entrada para viviendas de menor costo. Asimismo, la identificación de patrones territoriales permite enfocar esfuerzos de captación y promoción en zonas con mayor potencial de crecimiento o demanda.