Se cargan librerías para limpieza, visualización, análisis multivariado (PCA, clustering y correspondencia) y diagnóstico de faltantes.

# Cargar librerías necesarias

# Librerías base
library(paqueteMODELOS)
library(dplyr)
library(tidyr)
library(ggplot2)

# Análisis multivariado
library(FactoMineR)
library(factoextra)
library(cluster)

# Datos faltantes
library(mice)

# Mapas
library(sf)

1 Preparación del entorno y datos

En este capítulo se prepara el entorno de trabajo y se realiza una revisión inicial de la base de datos. El objetivo es comprender la estructura de la información, verificar tipos de variables y diagnosticar valores faltantes, dejando el conjunto de datos listo para aplicar posteriormente PCA, clustering y análisis de correspondencia. En esta fase no se eliminan registros, ya que el tratamiento de datos faltantes se definirá por técnica en los capítulos posteriores.

data(vivienda)
dim(vivienda)
## [1] 8322   13
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

1.1 Elección de variables relevantes

Para el análisis se seleccionan variables relevantes en cuatro grupos:
- Identificación: id
- Categóricas: zona, piso, estrato, tipo, barrio
- Cuantitativas: preciom, areaconst, parqueaderos, banios, habitaciones
- Geográficas: longitud, latitud

vivienda_df <- vivienda %>%
  transmute(
    id,
    zona,
    piso,
    estrato,
    tipo,
    barrio,
    preciom,
    areaconst,
    parqueaderos,
    banios,
    habitaciones,
    longitud,
    latitud
  )

str(vivienda_df)
## 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 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ 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 ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

1.2 Tipificación de variables (sin modificar valores)

Se tipifican las variables categóricas como factores para asegurar el tratamiento correcto en tablas de contingencia y análisis de correspondencia. Las variables cuantitativas se mantienen como numéricas para PCA y clustering.

Nota: Aquí solo se cambia el tipo, no se imputan ni eliminan registros.

vivienda_df <- vivienda_df %>%
  mutate(
    zona   = factor(zona),
    piso   = factor(piso),
    estrato = factor(estrato),
    tipo   = factor(tipo),
    barrio = factor(barrio)
  )

str(vivienda_df)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : Factor w/ 5 levels "Zona Centro",..: 4 4 4 5 2 2 2 2 2 2 ...
##  $ piso        : Factor w/ 12 levels "01","02","03",..: NA NA NA 2 1 1 1 1 2 2 ...
##  $ estrato     : Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
##  $ tipo        : Factor w/ 2 levels "Apartamento",..: 2 2 2 2 1 1 1 1 2 2 ...
##  $ barrio      : Factor w/ 436 levels "20 de julio",..: 1 1 1 2 3 3 3 3 3 3 ...
##  $ 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 ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

1.3 Diagnóstico de valores faltantes

Se inspeccionan valores faltantes para entender su magnitud y distribución. El tratamiento definitivo se realizará por técnica (PCA, clustering o correspondencia), evitando eliminar información de manera general.

md.pattern(vivienda_df, rotate.names = TRUE)

##      preciom id zona estrato tipo barrio areaconst banios habitaciones longitud
## 4808       1  1    1       1    1      1         1      1            1        1
## 1909       1  1    1       1    1      1         1      1            1        1
## 876        1  1    1       1    1      1         1      1            1        1
## 726        1  1    1       1    1      1         1      1            1        1
## 1          1  0    0       0    0      0         0      0            0        0
## 2          0  0    0       0    0      0         0      0            0        0
##            2  3    3       3    3      3         3      3            3        3
##      latitud parqueaderos piso     
## 4808       1            1    1    0
## 1909       1            1    0    1
## 876        1            0    1    1
## 726        1            0    0    2
## 1          0            0    0   12
## 2          0            0    0   13
##            3         1605 2638 4275

El diagnóstico de valores faltantes muestra que la mayoría de las observaciones cuentan con información completa. Se identifican valores faltantes principalmente en las variables piso, parqueaderos y, en menor medida, en algunas variables categóricas como zona, estrato y tipo, con apenas 3 registros incompletos en estas últimas. Dado que los patrones de faltantes no son homogéneos y afectan variables utilizadas por técnicas distintas, no se realiza eliminación general de registros en esta etapa. El tratamiento de valores faltantes se abordará de forma específica en cada técnica (PCA, clustering y análisis de correspondencia).

1.4 Resumen rápido (control del dataset)

Se presentan conteos básicos para entender la distribución de variables categóricas y verificar consistencia general del dataset.

# Conteos básicos de variables categóricas (top 10 cuando aplique)
vivienda_df %>% count(zona, sort = TRUE)
## # A tibble: 6 × 2
##   zona             n
##   <fct>        <int>
## 1 Zona Sur      4726
## 2 Zona Norte    1920
## 3 Zona Oeste    1198
## 4 Zona Oriente   351
## 5 Zona Centro    124
## 6 <NA>             3
vivienda_df %>% count(estrato, sort = TRUE)
## # A tibble: 5 × 2
##   estrato     n
##   <fct>   <int>
## 1 5        2750
## 2 4        2129
## 3 6        1987
## 4 3        1453
## 5 <NA>        3
vivienda_df %>% count(tipo, sort = TRUE)
## # A tibble: 3 × 2
##   tipo            n
##   <fct>       <int>
## 1 Apartamento  5100
## 2 Casa         3219
## 3 <NA>            3
# Barrios con más oferta (solo para conocer cardinalidad)
vivienda_df %>% count(barrio, sort = TRUE) %>% slice_head(n = 10)
## # A tibble: 10 × 2
##    barrio             n
##    <fct>          <int>
##  1 valle del lili  1008
##  2 ciudad jardín    516
##  3 pance            409
##  4 la flora         366
##  5 santa teresita   262
##  6 el caney         208
##  7 el ingenio       202
##  8 la hacienda      164
##  9 acopi            158
## 10 los cristales    154
nlevels(vivienda_df$barrio)
## [1] 436

La oferta inmobiliaria se concentra principalmente en la Zona Sur, seguida por la Zona Norte y Zona Oeste, lo que sugiere una fuerte dinámica del mercado residencial en estas áreas.

En términos socioeconómicos, los estratos 4, 5 y 6 concentran la mayor parte de la oferta, siendo el estrato 5 el más representativo.

Predominan los apartamentos sobre las casas, lo cual es consistente con la densificación urbana.

La variable barrio presenta una alta cardinalidad (436 niveles), lo que indica una fuerte heterogeneidad espacial y sugiere la necesidad de agrupar o filtrar esta variable para análisis multivariados posteriores.

Con lo anterior se deja preparado el conjunto de datos vivienda_df con variables tipificadas y diagnóstico de faltantes. En el siguiente capítulo se realiza un análisis exploratorio inicial (EDA) para describir distribuciones y relaciones base, que servirán como justificación para PCA, clustering y correspondencia.

2 Análisis Exploratorio Inicial (EDA)

En este capítulo se realiza un análisis exploratorio inicial con el fin de describir la distribución de las variables clave, identificar patrones preliminares y evaluar relaciones entre características de las viviendas. Este análisis sirve como base para la aplicación de técnicas multivariadas en los capítulos posteriores.

2.1 Distribución del precio de la vivienda

ggplot(vivienda_df, aes(x = preciom)) +
  geom_histogram(bins = 40, fill = "#2C7FB8", color = "white") +
  labs(
    title = "Distribución del precio de la vivienda",
    x = "Precio (millones)",
    y = "Frecuencia"
  )

La distribución del precio de las viviendas presenta una marcada asimetría positiva, con una alta concentración de inmuebles en rangos de precios bajos y medios, y una cola larga hacia valores elevados. Este comportamiento es típico en mercados inmobiliarios urbanos y sugiere la presencia de valores extremos asociados a propiedades de alta gama. Esta asimetría será considerada en los análisis multivariados posteriores.

2.2 Relación precio – área construida

Explorar si el precio aumenta con el tamaño del inmueble y evaluar la forma de la relación.

ggplot(vivienda_df, aes(x = areaconst, y = preciom)) +
  geom_point(alpha = 0.4, color = "#1B9E77") +
  labs(
    title = "Relación entre precio y área construida",
    x = "Área construida (m2)",
    y = "Precio (millones)"
  )

Se observa una relación positiva entre el área construida y el precio de la vivienda, indicando que, en general, los inmuebles de mayor tamaño tienden a presentar precios más altos. Sin embargo, la dispersión creciente para áreas grandes sugiere que el precio no depende exclusivamente del tamaño, sino también de otros factores como la ubicación, el estrato y las características del inmueble.

2.3 Precio por zona

Identificar diferencias espaciales en el mercado.

ggplot(vivienda_df, aes(x = zona, y = preciom)) +
  geom_boxplot(fill = "#FDAE61") +
  labs(
    title = "Distribución del precio por zona",
    x = "Zona",
    y = "Precio (millones)"
  )

El análisis por zona evidencia diferencias espaciales claras en la distribución de precios. Zonas como Oeste y Sur presentan medianas y rangos de precios superiores, mientras que Oriente y Centro concentran inmuebles de menor valor. Estas diferencias reflejan la heterogeneidad del mercado inmobiliario urbano y anticipan posibles segmentaciones geográficas relevantes.

2.4 Precio por estrato socioeconómico

ggplot(vivienda_df, aes(x = estrato, y = preciom)) +
  geom_boxplot(fill = "#ABDDA4") +
  labs(
    title = "Distribución del precio por estrato",
    x = "Estrato",
    y = "Precio (millones)"
  )

Existe una relación creciente entre el estrato socioeconómico y el precio de la vivienda. Los estratos más altos presentan no solo mayores precios medianos, sino también una mayor dispersión, lo que sugiere una oferta más diversa en términos de valor y características. Este patrón valida la relevancia del estrato como variable explicativa del mercado inmobiliario.

2.5 Relación entre variables cuantitativas

Evaluar correlaciones preliminares que justifican PCA.

vars_num <- vivienda_df %>%
  select(preciom, areaconst, parqueaderos, banios, habitaciones)

cor(vars_num, use = "pairwise.complete.obs")
##                preciom areaconst parqueaderos    banios habitaciones
## preciom      1.0000000 0.6873520    0.6886785 0.6691456    0.2640912
## areaconst    0.6873520 1.0000000    0.5848290 0.6484165    0.5169129
## parqueaderos 0.6886785 0.5848290    1.0000000 0.5705065    0.2844808
## banios       0.6691456 0.6484165    0.5705065 1.0000000    0.5899064
## habitaciones 0.2640912 0.5169129    0.2844808 0.5899064    1.0000000

La matriz de correlaciones muestra asociaciones moderadas y altas entre el precio y variables como el área construida, número de parqueaderos y baños. Estas relaciones indican la presencia de multicolinealidad y justifican el uso del Análisis de Componentes Principales (PCA) como técnica para reducir la dimensionalidad y sintetizar la información contenida en las variables cuantitativas.

En conjunto, el análisis exploratorio inicial permitió identificar patrones claros de distribución, relaciones entre variables y diferencias espaciales y socioeconómicas en el mercado inmobiliario urbano. Estos hallazgos respaldan la aplicación de técnicas multivariadas en los capítulos siguientes, orientadas a reducir la dimensionalidad, identificar segmentos homogéneos y explorar asociaciones estructurales en los datos.

3 Análisis de Componentes Principales (PCA)

En este capítulo se aplica Análisis de Componentes Principales (PCA) con el fin de reducir la dimensionalidad de las variables cuantitativas del inmueble, identificar factores latentes asociados al valor y características físicas, y facilitar análisis posteriores como la segmentación mediante clustering.

Dado que el Análisis de Componentes Principales no admite valores faltantes, se construye un subconjunto de datos específico para PCA, eliminando únicamente los registros con información incompleta en las variables cuantitativas seleccionadas. Este tratamiento se realiza exclusivamente para esta técnica, sin afectar el conjunto de datos general.

3.1 Selección de variables cuantitativas para PCA

Se seleccionan variables cuantitativas asociadas al tamaño y atributos del inmueble. Estas variables presentan correlaciones entre sí, lo que sugiere redundancia de información y justifica el uso de PCA.

# Variables cuantitativas para PCA
vars_pca <- vivienda_df %>% 
  select(preciom, areaconst, parqueaderos, banios, habitaciones)

El tamaño muestral resultante sigue siendo suficientemente grande para garantizar estabilidad en las componentes.

# Subconjunto completo para PCA (sin valores faltantes)
vars_pca_complete <- vars_pca %>% 
  na.omit()

dim(vars_pca_complete)
## [1] 6717    5

El Análisis de Componentes Principales muestra que la primera componente explica el 65.0% de la variabilidad total de las variables cuantitativas, mientras que la segunda componente aporta un 17.2% adicional. En conjunto, las dos primeras componentes concentran aproximadamente el 82.3% de la variabilidad del conjunto de datos, lo que indica que una representación bidimensional es suficiente para capturar la estructura principal de la información.

vars_pca_scaled <- scale(vars_pca_complete)

summary(vars_pca_scaled)
##     preciom          areaconst        parqueaderos         banios       
##  Min.   :-1.2264   Min.   :-1.0488   Min.   :-0.7425   Min.   :-2.3587  
##  1st Qu.:-0.6593   1st Qu.:-0.6602   1st Qu.:-0.7425   1st Qu.:-0.9096  
##  Median :-0.3399   Median :-0.3549   Median : 0.1465   Median :-0.1851  
##  Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000   Mean   : 0.0000  
##  3rd Qu.: 0.3317   3rd Qu.: 0.3599   3rd Qu.: 0.1465   3rd Qu.: 0.5394  
##  Max.   : 4.5669   Max.   :10.8526   Max.   : 7.2582   Max.   : 4.8866  
##   habitaciones    
##  Min.   :-2.6466  
##  1st Qu.:-0.4477  
##  Median :-0.4477  
##  Mean   : 0.0000  
##  3rd Qu.: 0.2852  
##  Max.   : 4.6831
res_pca <- prcomp(vars_pca_scaled, center = TRUE, scale. = TRUE)

summary(res_pca)
## Importance of components:
##                           PC1    PC2     PC3     PC4     PC5
## Standard deviation     1.8035 0.9280 0.60371 0.57319 0.43938
## Proportion of Variance 0.6505 0.1722 0.07289 0.06571 0.03861
## Cumulative Proportion  0.6505 0.8228 0.89568 0.96139 1.00000
res_pca$sdev
## [1] 1.8035261 0.9280189 0.6037130 0.5731917 0.4393816

El gráfico de valores propios confirma que las dos primeras componentes principales concentran la mayor parte de la varianza total, evidenciando un punto de inflexión a partir de la tercera componente. Este comportamiento respalda la selección de dos componentes principales para los análisis posteriores.

# Varianza explicada por componente
fviz_eig(res_pca, addlabels = TRUE, ylim = c(0, 100))

La primera componente principal está fuertemente asociada al tamaño y valor del inmueble, con altas contribuciones del precio, el área construida y el número de parqueaderos. Esta componente puede interpretarse como un factor de escala y valorización del inmueble.

La segunda componente principal presenta mayor contribución de las variables relacionadas con la distribución interna del inmueble, como el número de baños y habitaciones, capturando diferencias en la configuración y funcionalidad de las viviendas.

# Contribución de las variables a las componentes principales
fviz_pca_var(
  res_pca,
  col.var = "contrib",
  gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
  repel = TRUE
)

La proyección de los inmuebles en el plano definido por las dos primeras componentes principales permite visualizar la distribución de las observaciones en el espacio reducido. Se observan concentraciones y dispersión de puntos que sugieren la posible existencia de segmentos de viviendas con características similares, lo cual será explorado mediante análisis de conglomerados.

# Representación de los inmuebles en el plano PCA
fviz_pca_ind(
  res_pca,
  geom = "point",
  alpha.ind = 0.4,
  col.ind = "#2C7FB8"
)

En conclusión, el PCA permitió reducir de cinco variables cuantitativas originales a dos componentes principales interpretables, que concentran más del 80% de la variabilidad del mercado inmobiliario urbano. Estas componentes sintetizan factores asociados al valor, tamaño y configuración de los inmuebles y constituyen una base adecuada para el análisis de conglomerados que se desarrolla en el capítulo siguiente.

4 Análisis de Conglomerados (Clustering)

4.1 Construcción del dataset para clustering

Se utilizan las dos primeras componentes principales como variables de entrada para el análisis de conglomerados, dado que concentran más del 80% de la variabilidad total del conjunto de datos.

# Extraer scores de las dos primeras componentes
scores_pca <- as.data.frame(res_pca$x[, 1:2])

colnames(scores_pca) <- c("PC1", "PC2")

head(scores_pca)
##          PC1         PC2
## 1 -0.4709937 -1.89415199
## 2 -1.3348909  0.01795498
## 3 -0.3126854 -0.17229691
## 4  1.1414677  0.46924164
## 5 -1.5187600 -0.04608461
## 6 -1.2047147 -0.18845448

Determinación del número óptimo de clusters

Usaremos método del codo + silueta

Método del codo

La siguiente gráfico muestra:

  • Eje X → número de clusters (k)
  • Eje Y → variabilidad interna (qué tan compactos son los grupos)

El punto dónde la mejora deja de ser fuerte es:

  • De k=1 a k=2 hay caída fuerte
  • De k=2 a k=3 sigue bajando pero menos

Después la curva se aplana

El “codo” visual está entre 2 y 3 clusters.

fviz_nbclust(scores_pca, kmeans, method = "wss") +
  labs(title = "Método del Codo")

Método de la silueta

Este es más robusto.

  • Eje X → número de clusters
  • Eje Y → calidad del agrupamiento (entre -1 y 1)

El valor más alto está en k = 2, eso significa que 2 clusters es la mejor separación estructural del mercado.

fviz_nbclust(scores_pca, kmeans, method = "silhouette") +
  labs(title = "Método de la Silueta")

k-means

En esta etapa se aplica el algoritmo k-means utilizando como variables de entrada las dos primeras componentes principales, las cuales concentran más del 80% de la variabilidad total del conjunto de datos. Se selecciona k = 2 con base en los resultados obtenidos mediante el método del codo y el análisis de la silueta, los cuales indicaron que esta partición ofrece la mejor estructura de agrupamiento. Se fija una semilla para garantizar la reproducibilidad del procedimiento.

# Aplicar k-means con k = 2
set.seed(123)  # Para reproducibilidad

kmeans_res <- kmeans(scores_pca, centers = 2, nstart = 25)

# Agregar cluster al dataset de scores
scores_pca$cluster <- as.factor(kmeans_res$cluster)

head(scores_pca)
##          PC1         PC2 cluster
## 1 -0.4709937 -1.89415199       2
## 2 -1.3348909  0.01795498       2
## 3 -0.3126854 -0.17229691       2
## 4  1.1414677  0.46924164       1
## 5 -1.5187600 -0.04608461       2
## 6 -1.2047147 -0.18845448       2

Visualización de los clusters en el espacio PCA

La visualización de los clusters en el espacio definido por las dos primeras componentes principales evidencia una clara separación principalmente a lo largo de la primera componente (PC1), la cual está asociada al tamaño y valorización del inmueble.

El Cluster 1 agrupa viviendas con valores altos de PC1, caracterizadas por mayores precios, mayor área construida y mayor número de parqueaderos. Por su parte, el Cluster 2 concentra inmuebles con valores bajos de PC1, correspondientes a viviendas de menor escala y menor nivel de valorización.

Se observa que la segunda componente principal (PC2), relacionada con la configuración interna del inmueble (baños y habitaciones), no constituye el principal eje de diferenciación entre los grupos. Esto indica que la segmentación del mercado inmobiliario urbano está determinada principalmente por factores de escala y valor económico, más que por diferencias en la distribución interna de los espacios.

En consecuencia, el análisis de conglomerados revela la existencia de dos grandes segmentos estructurales del mercado: uno orientado a viviendas de mayor valorización y otro correspondiente a un segmento más económico.

library(ggplot2)

ggplot(scores_pca, aes(x = PC1, y = PC2, color = cluster)) +
  geom_point(alpha = 0.6, size = 1.5) +
  labs(
    title = "Segmentación del mercado inmobiliario (k = 2)",
    x = "Componente Principal 1",
    y = "Componente Principal 2",
    color = "Cluster"
  ) +
  theme_minimal()

Unir cluster al dataset original usado en PCA

# Recuperar dataset completo usado en PCA
dataset_cluster <- vars_pca_complete
dataset_cluster$cluster <- scores_pca$cluster

# Resumen por cluster
dataset_cluster %>%
  group_by(cluster) %>%
  summarise(
    precio_prom = mean(preciom),
    area_prom = mean(areaconst),
    parqueaderos_prom = mean(parqueaderos),
    banios_prom = mean(banios),
    habitaciones_prom = mean(habitaciones),
    n = n()
  )
## # A tibble: 2 × 7
##   cluster precio_prom area_prom parqueaderos_prom banios_prom habitaciones_prom
##   <fct>         <dbl>     <dbl>             <dbl>       <dbl>             <dbl>
## 1 1              854.      344.              2.95        4.87              4.80
## 2 2              322.      119.              1.41        2.64              3.16
## # ℹ 1 more variable: n <int>

Tamaño relativo de cada cluster

dataset_cluster %>% 
  count(cluster) %>% 
  mutate(prop = n / sum(n))
## # A tibble: 2 × 3
##   cluster     n  prop
##   <fct>   <int> <dbl>
## 1 1        1855 0.276
## 2 2        4862 0.724

Cluster con estrato o zona

vivienda_df_complete <- vivienda_df %>% 
  filter(!is.na(preciom), !is.na(areaconst),
         !is.na(parqueaderos), !is.na(banios), !is.na(habitaciones))

vivienda_df_complete$cluster <- scores_pca$cluster

vivienda_df_complete %>% 
  count(cluster, estrato)
## # A tibble: 8 × 3
##   cluster estrato     n
##   <fct>   <fct>   <int>
## 1 1       3         124
## 2 1       4         190
## 3 1       5         529
## 4 1       6        1012
## 5 2       3         560
## 6 2       4        1451
## 7 2       5        1993
## 8 2       6         858

Caracterización de los conglomerados

Al incorporar la asignación de clusters al conjunto de datos utilizado en el PCA y calcular estadísticas descriptivas por grupo, se evidencian diferencias estructurales claras entre los segmentos identificados.

El Cluster 1, que representa el 27.6% del total de viviendas analizadas, presenta un precio promedio de 854 millones y un área construida promedio de 344 m², junto con mayores valores en parqueaderos, baños y habitaciones. Estas características indican que este grupo corresponde a viviendas de mayor escala y alto nivel de valorización, asociadas a segmentos de mercado superiores y oferta de tipo premium.

El análisis por estrato socioeconómico confirma esta interpretación: el Cluster 1 se concentra principalmente en los estratos 5 y 6, especialmente en el estrato 6, lo que refuerza su carácter de segmento de alta gama dentro del mercado inmobiliario urbano.

Por su parte, el Cluster 2, que concentra el 72.4% del mercado, muestra un precio promedio de 322 millones y un área promedio de 119 m², junto con menores valores en las demás variables físicas. Este grupo representa viviendas de menor tamaño y menor valorización relativa.

En términos socioeconómicos, el Cluster 2 presenta mayor presencia en los estratos 4 y 5, y también una proporción relevante en estrato 3, lo que sugiere que este conglomerado corresponde al segmento medio del mercado urbano y constituye la base principal de la oferta inmobiliaria.

La diferencia de más de 500 millones de pesos en precio promedio entre ambos clusters confirma que la segmentación identificada no es marginal sino estructural. La coherencia entre la interpretación de la primera componente principal —asociada al tamaño y valorización del inmueble— y los resultados del análisis de conglomerados valida la existencia de dos grandes perfiles del mercado inmobiliario urbano: un segmento de alta valorización concentrado en estratos superiores y un segmento mayoritario orientado a viviendas de escala media y menor precio.

5 Análisis de Correspondencia (CA)

En este capítulo se aplica Análisis de Correspondencia (CA) con el fin de examinar la relación entre variables categóricas del mercado inmobiliario, específicamente el tipo de vivienda, la zona y el barrio. Esta técnica permite identificar asociaciones estructurales entre categorías y visualizar patrones de comportamiento en la oferta del mercado urbano.

Seleccionar variables categóricas

Se seleccionan las variables categóricas relevantes para el análisis estructural del mercado. Todas las variables se convierten a factor para garantizar su adecuado tratamiento en el análisis de correspondencia.

# Selección de variables categóricas
vars_ca <- vivienda_df %>%
  select(tipo, zona, barrio, estrato)

# Convertir a factor (por seguridad)
vars_ca <- vars_ca %>%
  mutate(across(everything(), as.factor))

str(vars_ca)
## spc_tbl_ [8,322 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ tipo   : Factor w/ 2 levels "Apartamento",..: 2 2 2 2 1 1 1 1 2 2 ...
##  $ zona   : Factor w/ 5 levels "Zona Centro",..: 4 4 4 5 2 2 2 2 2 2 ...
##  $ barrio : Factor w/ 436 levels "20 de julio",..: 1 1 1 2 3 3 3 3 3 3 ...
##  $ estrato: Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

5.1 Tipo x Zona

Tabla de contingencia

Se construye una tabla de contingencia entre tipo de vivienda y zona con el fin de analizar la distribución conjunta de estas categorías.

tabla_tipo_zona <- table(vars_ca$tipo, vars_ca$zona)
tabla_tipo_zona
##              
##               Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
##   Apartamento          24       1198       1029           62     2787
##   Casa                100        722        169          289     1939

Aplicación de análisis de correspondencia

# Eliminar filas o columnas con frecuencia cero
tabla_tipo_zona <- tabla_tipo_zona[rowSums(tabla_tipo_zona) > 0,
                                   colSums(tabla_tipo_zona) > 0]

# Volver a correr CA
res_ca <- CA(tabla_tipo_zona, graph = FALSE)

res_ca$eig
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.08305442                    100                               100

Extraer coordenadas

res_ca$row$coord
## Apartamento        Casa 
##  -0.2289585   0.3627488
res_ca$col$coord
##                     [,1]
## Zona Centro   0.86131854
## Zona Norte   -0.02238755
## Zona Oeste   -0.50482822
## Zona Oriente  0.89603812
## Zona Sur      0.04791693

Representación gráfica del análisis de correspondencia

library(ggplot2)
library(ggrepel)

# Etiquetas reales desde la tabla de contingencia
row_lab <- rownames(tabla_tipo_zona)   # "Apartamento", "Casa"
col_lab <- colnames(tabla_tipo_zona)   # "Zona Centro", ... etc.

# Extraer coordenadas de CA (pueden venir como vector o como matriz)
r <- res_ca$row$coord
c <- res_ca$col$coord

# Forzar a vector numérico 1D
r1 <- if (is.null(dim(r))) as.numeric(r) else as.numeric(r[,1])
c1 <- if (is.null(dim(c))) as.numeric(c) else as.numeric(c[,1])

# Asegurar mismo largo que las etiquetas
stopifnot(length(r1) == length(row_lab))
stopifnot(length(c1) == length(col_lab))

rows <- data.frame(categoria = row_lab, Dim1 = r1)
cols <- data.frame(categoria = col_lab, Dim1 = c1)

# Plot filas (tipo de vivienda)
ggplot(rows, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 3) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  ggrepel::geom_text_repel() +
  theme_minimal() +
  labs(title = "CA (1D): Tipo de vivienda", x = "Dimensión 1", y = "")

# Plot columnas (zonas)
ggplot(cols, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 3) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  ggrepel::geom_text_repel() +
  theme_minimal() +
  labs(title = "CA (1D): Zonas", x = "Dimensión 1", y = "")

El análisis de correspondencia muestra que la Dimensión 1 explica el 100% de la inercia del modelo, lo que es esperado dado que la tabla de contingencia es de dimensión 2×5. La prueba de independencia Chi-cuadrado resulta altamente significativa (p < 0.001), indicando que existe una relación estructural entre el tipo de vivienda y la zona de ubicación.

La Dimensión 1 representa un eje que diferencia claramente entre apartamentos y casas. Las coordenadas negativas se asocian con la categoría Apartamento, mientras que las positivas corresponden a Casa.

En cuanto a la localización geográfica, se observa que:

  • Zona Oeste presenta una fuerte asociación con apartamentos.
  • Zona Centro y Zona Oriente muestran una clara asociación con casas.
  • Zona Norte y Zona Sur presentan posiciones cercanas al origen, lo que indica una distribución más equilibrada entre ambos tipos de vivienda.

Estos resultados evidencian que la estructura de la oferta inmobiliaria no es homogénea en la ciudad, sino que existen patrones espaciales diferenciados según el tipo de inmueble.

5.2 Tipo x Estrato

Tabla de contingencia

# Tabla Tipo x Estrato
tabla_tipo_estrato <- table(vars_ca$tipo, vars_ca$estrato)
tabla_tipo_estrato
##              
##                  3    4    5    6
##   Apartamento  639 1404 1766 1291
##   Casa         814  725  984  696

Aplicación de análisis de correspondencia

tabla_tipo_estrato <- table(vars_ca$tipo, vars_ca$estrato)

# Limpiar posibles ceros
tabla_tipo_estrato <- tabla_tipo_estrato[rowSums(tabla_tipo_estrato) > 0,
                                         colSums(tabla_tipo_estrato) > 0]

res_ca_te <- CA(tabla_tipo_estrato, graph = FALSE)

res_ca_te$eig
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1  0.0269661                    100                               100

Extraer coordenadas

res_ca_te$row$coord
## Apartamento        Casa 
##  -0.1304621   0.2066968
res_ca_te$col$coord
##          [,1]
## 3  0.35576291
## 4 -0.09528793
## 5 -0.05980352
## 6 -0.07528728

Representación gráfica del análisis de correspondencia

library(ggplot2)
library(ggrepel)

row_lab_te <- rownames(tabla_tipo_estrato)
col_lab_te <- colnames(tabla_tipo_estrato)

r_te <- res_ca_te$row$coord
c_te <- res_ca_te$col$coord

r1_te <- if (is.null(dim(r_te))) as.numeric(r_te) else as.numeric(r_te[,1])
c1_te <- if (is.null(dim(c_te))) as.numeric(c_te) else as.numeric(c_te[,1])

rows_te <- data.frame(categoria = row_lab_te, Dim1 = r1_te)
cols_te <- data.frame(categoria = col_lab_te, Dim1 = c1_te)

# Gráfico tipo
ggplot(rows_te, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 3) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  geom_text_repel() +
  theme_minimal() +
  labs(title = "CA (1D): Tipo de vivienda", x = "Dimensión 1", y = "")

ggplot(cols_te, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 3, color = "darkred") +
  geom_vline(xintercept = 0, linetype = "dashed") +
  geom_text_repel() +
  theme_minimal() +
  labs(title = "CA (1D): Estratos", x = "Dimensión 1", y = "")

El análisis de correspondencia entre tipo de vivienda y estrato socioeconómico muestra una estructura clara en la Dimensión 1. Las casas se ubican en el lado positivo del eje, asociadas principalmente al estrato 3, el cual presenta la mayor coordenada positiva (0.356).

Por su parte, los apartamentos se sitúan en el lado negativo de la dimensión, junto con los estratos 4, 5 y 6, cuyos valores son relativamente próximos entre sí. Esto indica que los apartamentos tienen mayor presencia relativa en estratos medios y altos, mientras que las casas se concentran más en el segmento medio-bajo del mercado.

La cercanía entre los estratos 4, 5 y 6 sugiere que, desde la perspectiva del tipo de vivienda, estos segmentos presentan patrones de oferta similares, consolidando una diferenciación estructural entre vivienda de tipo casa en estrato 3 y vivienda en apartamento en estratos superiores.

5.3 Tipo x Barrio

Reducir dimensionalidad (Top 15 barrios)

Tenemos identificados 436 barrios, un CA con 436 columnas será difícil de interpretar, el gráfico será ilegible, la inercia se dispersará muchísimo, el informe perderá claridad. Entonces seleccionamos los barrios con mayor oferta:

# Identificar los 15 barrios con mayor número de inmuebles
top_barrios <- vivienda_df %>%
  count(barrio, sort = TRUE) %>%
  slice_head(n = 15) %>%
  pull(barrio)

top_barrios
##  [1] valle del lili   ciudad jardín    pance            la flora        
##  [5] santa teresita   el caney         el ingenio       la hacienda     
##  [9] acopi            los cristales    normandía        el limonar      
## [13] prados del norte el refugio       aguacatal       
## 436 Levels: 20 de julio 3 de julio acopi agua blanca aguablanca ... zona sur

Filtramos la base sólo con estos barrios:

vars_ca_barrios <- vars_ca %>%
  filter(barrio %in% top_barrios)
dim(vars_ca_barrios)
## [1] 4091    4

Tabla tipo x barrio:

Se construye una tabla de contingencia entre tipo de vivienda y los 15 barrios con mayor oferta. Debido a su tamaño, la tabla completa no se presenta en el informe; en su lugar se reporta un resumen (dimensión y totales) y se utiliza como insumo para el análisis de correspondencia.

tabla_tipo_barrio <- table(vars_ca_barrios$tipo, vars_ca_barrios$barrio)
tabla_tipo_barrio[, 1:6]
##              
##               20 de julio 3 de julio acopi agua blanca aguablanca aguacatal
##   Apartamento           0          0    88           0          0        98
##   Casa                  0          0    70           0          0        11

Aplicación de análisis de correspondencia

tabla_tipo_barrio <- tabla_tipo_barrio[rowSums(tabla_tipo_barrio) > 0,
                                       colSums(tabla_tipo_barrio) > 0]

res_ca_tb <- CA(tabla_tipo_barrio, graph = FALSE)

res_ca_tb$eig
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1  0.1469317                    100                               100

Coordenadas

res_ca_tb$row$coord
## Apartamento        Casa 
##  -0.2532348   0.5802193
res_ca_tb$col$coord
##                         [,1]
## acopi             0.30266628
## aguacatal        -0.44121405
## ciudad jardín     0.58243079
## el caney          0.21745063
## el ingenio        0.13589284
## el limonar        0.56342186
## el refugio        0.11849109
## la flora         -0.07250471
## la hacienda       0.08181045
## los cristales    -0.42061848
## normandía        -0.60416517
## pance             0.41854580
## prados del norte -0.12568876
## santa teresita   -0.56105379
## valle del lili   -0.29825402

Representación gráfica del análisis de correspondencia

library(ggplot2)
library(ggrepel)

# Etiquetas directamente desde la tabla usada en CA (Top 15)
row_lab <- rownames(tabla_tipo_barrio)  # Apartamento, Casa
col_lab <- colnames(tabla_tipo_barrio)  # los 15 barrios

# Coordenadas de CA (1D)
r <- res_ca_tb$row$coord
c <- res_ca_tb$col$coord

# Forzar a vector numérico 1D (por si viene como vector o como matriz)
r1 <- if (is.null(dim(r))) as.numeric(r) else as.numeric(r[,1])
c1 <- if (is.null(dim(c))) as.numeric(c) else as.numeric(c[,1])

# Dataframes
rows <- data.frame(categoria = row_lab, Dim1 = r1, tipo = "Tipo de vivienda")
cols <- data.frame(categoria = col_lab, Dim1 = c1, tipo = "Barrio (Top 15)")

# Plot combinado en 1D
ggplot() +
  geom_point(data = rows, aes(x = Dim1, y = 0), size = 4, color = "steelblue") +
  geom_text_repel(data = rows, aes(x = Dim1, y = 0, label = categoria),
                  color = "steelblue", size = 4) +
  geom_point(data = cols, aes(x = Dim1, y = 0), size = 3, color = "darkred") +
  geom_text_repel(data = cols, aes(x = Dim1, y = 0, label = categoria),
                  color = "darkred", size = 3) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  theme_minimal() +
  labs(title = "CA (1D): Tipo de vivienda vs Barrios (Top 15)",
       subtitle = "La cercanía en la Dimensión 1 indica asociación entre categorías",
       x = "Dimensión 1", y = "")

Barrios

ggplot(cols, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 3) +
  geom_text_repel(size = 3) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  theme_minimal() +
  labs(title = "CA (1D): Barrios (Top 15)", x = "Dimensión 1", y = "")

Tipo

ggplot(rows, aes(x = Dim1, y = 0, label = categoria)) +
  geom_point(size = 4) +
  geom_text_repel(size = 4) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  theme_minimal() +
  labs(title = "CA (1D): Tipo de vivienda", x = "Dimensión 1", y = "")

Dado que existen 436 barrios en la base de datos, un análisis con todas las categorías produciría un gráfico ilegible y una dispersión excesiva de la inercia. Por tanto, se seleccionaron los 15 barrios con mayor número de inmuebles, concentrando 4.091 observaciones.

El análisis de correspondencia aplicado a esta tabla presenta una única dimensión significativa (inercia = 0.1469), lo que indica que la estructura de asociación puede interpretarse en un eje principal.

La Dimensión 1 separa claramente:

  • Apartamento (coordenada negativa: -0.253)
  • Casa (coordenada positiva: 0.580)

Los barrios se distribuyen a lo largo de este eje de la siguiente manera:

Barrios más asociados a Apartamentos (coordenadas negativas)

  • Normandía (-0.604)
  • Santa Teresita (-0.561)
  • Aguacatal (-0.441)
  • Los Cristales (-0.421)
  • Valle del Lili (-0.298)
  • Prados del Norte (-0.126)

Estos barrios presentan una mayor representación relativa de apartamentos dentro de la oferta disponible.

Barrios más asociados a Casas (coordenadas positivas)

  • Ciudad Jardín (0.582)
  • El Limonar (0.563)
  • Pance (0.419)
  • Acopi (0.303)
  • El Caney (0.217)
  • El Ingenio (0.136)
  • El Refugio (0.118)
  • La Hacienda (0.082)

En estos barrios la proporción relativa de casas es mayor frente a apartamentos.

6 Visualización Estratégica y Recomendaciones

El análisis multidimensional del mercado inmobiliario de Cali evidencia que la oferta residencial no es homogénea, sino estructuralmente segmentada.

El PCA mostró que el 82% de la variabilidad del mercado se explica por dos factores principales, dominados por el tamaño y la valorización del inmueble.

El análisis de conglomerados identificó dos segmentos claros:

Segmento premium (27.6%): viviendas de gran tamaño, alto precio (≈ 854 millones) y concentración en estratos 5 y 6.

Segmento medio (72.4%): viviendas de menor tamaño y precio (≈ 322 millones), con mayor presencia en estratos 4 y 5.

El análisis territorial confirmó patrones definidos entre tipo de vivienda, zona y barrio, evidenciando especialización espacial del mercado.

En conclusión, el mercado inmobiliario urbano de Cali presenta una estructura segmentada por escala económica y localización, lo que permite diseñar estrategias diferenciadas de portafolio, precios e inversión con base en evidencia estadística.

6.1 Integración de Resultados

El análisis integral del mercado inmobiliario urbano permitió abordar la información desde tres perspectivas complementarias: estructura cuantitativa (PCA), segmentación (clustering) y relaciones categóricas (análisis de correspondencia).

El PCA evidenció que aproximadamente el 82.3% de la variabilidad de las variables cuantitativas puede resumirse en dos dimensiones. La PC1 concentra el 65.0% y representa un eje de escala y valorización, asociado principalmente con precio, área construida y parqueaderos. La PC2 aporta un 17.2% adicional y captura diferencias asociadas a la configuración interna del inmueble (especialmente baños y habitaciones).

El clustering aplicado sobre los scores de las dos primeras componentes permitió identificar grupos con diferencias estructurales, observándose que la separación ocurre principalmente sobre la PC1, lo que confirma que el mercado se segmenta principalmente por tamaño y valorización.

Finalmente, el análisis de correspondencia mostró asociaciones consistentes entre el tipo de vivienda y variables territoriales y socioeconómicas (zona, estrato y barrios), confirmando que la oferta se distribuye de manera no homogénea en el territorio.

6.2 Perfil Estratégico de los Segmentos Identificados

Con base en k-means (k = 2), se identificaron dos segmentos principales del mercado:

Cluster 1 (27.6% | segmento de alta valorización): presenta un precio promedio de 854 millones y un área promedio de 344 m², además de mayores valores en parqueaderos (2.95), baños (4.87) y habitaciones (4.80). Este segmento se concentra especialmente en los estratos 6 y 5, lo cual es coherente con su perfil premium.

Cluster 2 (72.4% | segmento mayoritario de valorización media): presenta precio promedio de 322 millones y área promedio de 119 m², con menores dotaciones en parqueaderos (1.41), baños (2.64) y habitaciones (3.16). Este cluster representa la base del mercado y concentra mayor presencia en estratos 4 y 5, con participación relevante en estrato 3.

La diferencia de más de 500 millones en precio promedio entre clusters confirma que la segmentación es estructural y no marginal, y respalda el uso de PCA + clustering como aproximación integral para perfilar la oferta.

6.3 Patrones Territoriales del Mercado

El análisis de correspondencia evidenció patrones territoriales consistentes:

Tipo vs Zona: la Zona Oeste se asocia relativamente más a apartamentos, mientras que Zona Centro y Zona Oriente muestran mayor asociación con casas. Las zonas Norte y Sur se ubican más cerca del origen, indicando una distribución más balanceada entre tipos.

Tipo vs Estrato: las casas se asocian principalmente con estrato 3, mientras que los apartamentos muestran mayor presencia relativa en estratos 4, 5 y 6.

Tipo vs Barrios (Top 15): se identifican barrios con mayor asociación relativa a apartamentos (p. ej., Normandía, Santa Teresita, Aguacatal) y otros con mayor asociación relativa a casas (p. ej., Ciudad Jardín, El Limonar, Pance), lo cual sugiere especialización territorial del producto inmobiliario.

Estos resultados muestran que la segmentación del mercado no solo es económica, sino también espacial y socioeconómica.

6.4 Implicaciones Estratégicas para la Empresa

Portafolio y oferta: mantener un portafolio balanceado donde el segmento mayoritario (Cluster 2) soporte el volumen comercial y el segmento premium (Cluster 1) se gestione con estrategias de margen, diferenciación y posicionamiento.

Captación focalizada: orientar captación/oferta por especialización territorial: en zonas y barrios asociados a apartamentos, priorizar producto vertical; en zonas/barrios asociados a casas, priorizar producto de baja densidad y vivienda familiar.

Pricing basado en drivers: usar las variables que estructuran la PC1 (precio/área/parqueaderos) como “drivers” para modelos internos de valoración, complementados por PC2 (baños/habitaciones) como factor de ajuste por configuración.

6.5 Conclusión General

El análisis multidimensional realizado evidencia que el mercado inmobiliario urbano presenta una estructura claramente segmentada, tanto en términos físicos como territoriales.

La aplicación combinada de Análisis de Componentes Principales, Análisis de Conglomerados y Análisis de Correspondencia permitió:

  • Reducir la complejidad del conjunto de datos.
  • Identificar segmentos homogéneos de propiedades.
  • Detectar patrones territoriales y socioeconómicos en la oferta.

Este enfoque proporciona una ventaja competitiva al permitir decisiones fundamentadas en evidencia estadística, optimizando la inversión y fortaleciendo la estrategia comercial en un entorno urbano altamente competitivo.

En síntesis, el análisis permite pasar de una lectura descriptiva del inventario a una lectura estratégica por segmentos y territorios, útil para inversión, captación y pricing.