# Función para dar formato uniforme a las tablas
formato_tabla <- function(tabla, caption, digits = 2) {
  
  tabla %>%
    kable(
      caption = caption,
      digits = digits,
      align = "c",
      booktabs = TRUE
    ) %>%
    kable_styling(
      bootstrap_options = c("striped", "hover", "condensed"),
      full_width = FALSE,
      position = "center",
      font_size = 11
    ) %>%
    row_spec(0, bold = TRUE) %>%
    column_spec(1, bold = TRUE)
}

1 Introducción

El mercado inmobiliario residencial presenta diferencias importantes entre las propiedades no solo por su precio, sino también por variables como el área construida, el número de habitaciones y baños, la disponibilidad de parqueaderos, el estrato y la localización. Un análisis aislado de cada variable puede ocultar patrones que aparecen cuando las características se estudian de manera conjunta.

En este trabajo se analiza una base de viviendas de Cali mediante técnicas de análisis multivariado. El propósito es identificar estructuras comunes en las propiedades, segmentar la oferta residencial y estudiar la asociación entre sus principales características categóricas. Los resultados se orientan a una lectura empresarial del mercado, de manera que puedan utilizarse como apoyo para decisiones de comercialización, segmentación e inversión.

El análisis se desarrolla en cuatro etapas: preparación y exploración de la información, análisis de componentes principales, análisis de conglomerados y análisis de correspondencias. Finalmente, los resultados se integran mediante visualizaciones y recomendaciones para la toma de decisiones.

2 Objetivo

2.1 Objetivo general

Caracterizar la oferta residencial de Cali mediante técnicas de análisis multivariado que permitan reducir la dimensionalidad, identificar segmentos homogéneos y estudiar las asociaciones entre las principales características categóricas de las viviendas.

3 Preparación y exploración de la información

3.1 Carga de la base

La base vivienda contiene información de viviendas residenciales de Cali. Primero se revisan sus dimensiones, nombres de variables y estructura general.

data("vivienda")

cat("Número de observaciones:", nrow(vivienda), "\n")
## Número de observaciones: 8322
cat("Número de variables:", ncol(vivienda), "\n")
## Número de variables: 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")=
##   .. 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")=<pointer: (nil)>
head(vivienda)

3.2 Calidad de los datos

Se revisa la presencia de valores faltantes y duplicados. La variable id se utiliza únicamente como identificador y, por tanto, no debe participar en las técnicas multivariadas.

na_resumen <- data.frame(
  variable = names(vivienda),
  faltantes = sapply(vivienda, function(x) sum(is.na(x))),
  porcentaje = round(sapply(vivienda, function(x) mean(is.na(x)) * 100), 2)
)

colnames(na_resumen) <- c(
  "Variable",
  "Valores faltantes",
  "Porcentaje de faltantes"
)

formato_tabla(
  na_resumen,
  "Valores faltantes por variable",
  digits = 2
)
Valores faltantes por variable
Variable Valores faltantes Porcentaje de faltantes
id id 3 0.04
zona zona 3 0.04
piso piso 2638 31.70
estrato estrato 3 0.04
preciom preciom 2 0.02
areaconst areaconst 3 0.04
parqueaderos parqueaderos 1605 19.29
banios banios 3 0.04
habitaciones habitaciones 3 0.04
tipo tipo 3 0.04
barrio barrio 3 0.04
longitud longitud 3 0.04
latitud latitud 3 0.04
cat("Duplicados en id:", sum(duplicated(vivienda$id)), "\n")
## Duplicados en id: 2

Se eliminan los registros sin identificador y los identificadores duplicados, conservando una sola observación por vivienda.

vivienda <- vivienda %>%
  filter(!is.na(id)) %>%
  distinct(id, .keep_all = TRUE)

cat("Duplicados en id:", sum(duplicated(vivienda$id)), "\n")
## Duplicados en id: 0

3.3 Tratamiento de valores faltantes

Se ientifica que variables poseen faltantes:

colSums(is.na(vivienda))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1602            0            0            0            0            0 
##      latitud 
##            0

3.3.1 Piso

El número de piso es una variable discreta. Para conservar la información disponible y evitar una imputación basada únicamente en un promedio, se utiliza mice con imputación múltiple mediante predictive mean matching (PMM), tomando como variables auxiliares características físicas y de localización de las viviendas.

vivienda <- vivienda %>%
  mutate(piso = as.numeric(piso))

# Distribución de piso antes de la imputación
ggplot(vivienda, aes(x = piso)) +
  geom_bar(fill = "#4E79A7", width = 0.7) +
  geom_text(
    stat = "count",
    aes(label = after_stat(count)),
    vjust = -0.4,
    size = 3.5
  ) +
  labs(
    title = "Distribución del número de piso",
    subtitle = "Antes del proceso de imputación",
    x = "Piso",
    y = "Número de viviendas"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(size = 11),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  )

# Variables auxiliares para imputar piso
imp_piso <- vivienda %>%
  select(estrato, preciom, areaconst, parqueaderos, banios,
         habitaciones, zona, tipo, piso)

# PMM: la variable piso toma valores de viviendas observadas similares
imp <- mice(imp_piso, m = 5, maxit = 30, method = "pmm", seed = 123, printFlag = FALSE)
imp_completo <- complete(imp, 1)

vivienda$piso <- imp_completo$piso

ggplot(vivienda, aes(x = piso)) +
  geom_bar(fill = "#4E79A7", width = 0.7) +
  geom_text(
    stat = "count",
    aes(label = after_stat(count)),
    vjust = -0.4,
    size = 3.5
  ) +
  labs(
    title = "Distribución del número de piso",
    subtitle = "Después del proceso de imputación",
    x = "Piso",
    y = "Número de viviendas"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 16),
    plot.subtitle = element_text(size = 11),
    axis.title = element_text(face = "bold"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  )

No se evidencias cambios en la distribucion de las frecuencias posterior al proceso de imputaicon en la vriable piso.

3.3.2 Parqueaderos

Durante la revisión de la variable parqueaderos se identificaron valores faltantes en una variable que registra el número de parqueaderos disponibles en cada vivienda. En los registros observados se presentan valores enteros correspondientes a la cantidad de parqueaderos, mientras que los valores faltantes no permiten establecer directamente si se trata de una ausencia de parqueadero o de información no registrada.

Para efectos del análisis y con el propósito de conservar las observaciones disponibles, se adopta como supuesto operativo que los valores faltantes representan la ausencia de parqueaderos y, por tanto, se recodifican como cero. Esta decisión debe interpretarse como un criterio de tratamiento de datos y no como una confirmación de que todas las viviendas con información faltante carezcan efectivamente de parqueadero.

vivienda <- vivienda %>%
  mutate(
    parqueaderos = as.numeric(parqueaderos),
    parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos)
  )

sapply(vivienda[c("piso", "parqueaderos")], function(x) sum(is.na(x)))
##         piso parqueaderos 
##            0            0

3.4 Revisión exploratoria

variables_numericas <- c(
  "piso",
  "estrato",
  "areaconst",
  "parqueaderos",
  "preciom",
  "banios",
  "habitaciones"
)

# Estadísticos descriptivos
describe_resultado <- psych::describe(
  vivienda[, variables_numericas]
)

# Seleccionar estadísticos relevantes
tabla_descriptiva <- describe_resultado[, c(
  "n",
  "mean",
  "sd",
  "min",
  "max"
)]

# Nombres de las columnas
colnames(tabla_descriptiva) <- c(
  "Observaciones",
  "Media",
  "Desviación estándar",
  "Mínimo",
  "Máximo"
)

# Nombres descriptivos de las variables
rownames(tabla_descriptiva) <- c(
  "Piso",
  "Estrato",
  "Área construida (m²)",
  "Parqueaderos",
  "Precio (millones)",
  "Baños",
  "Habitaciones"
)

# Tabla final
formato_tabla(
  tabla_descriptiva,
  "Resumen descriptivo de las variables cuantitativas",
  digits = 2
)
Resumen descriptivo de las variables cuantitativas
Observaciones Media Desviación estándar Mínimo Máximo
Piso 8319 3.71 2.59 1 12
Estrato 8319 4.63 1.03 3 6
Área construida (m²) 8319 174.93 142.96 30 1745
Parqueaderos 8319 1.48 1.24 0 10
Precio (millones) 8319 433.90 328.67 58 1999
Baños 8319 3.11 1.43 0 10
Habitaciones 8319 3.61 1.46 0 10

La revisión inicial permite comprobar la escala de las variables y detectar diferencias importantes entre magnitudes. Por esta razón, para las técnicas que dependen de distancias y varianzas se utilizarán variables estandarizadas.

3.5 Matriz de correlaciones

Antes de aplicar PCA se estudian las asociaciones lineales entre las variables cuantitativas seleccionadas.

base__viv <- vivienda %>%
  select(
    piso,
    estrato,
    areaconst,
    parqueaderos,
    preciom,
    banios,
    habitaciones
  ) %>%
  mutate(across(everything(), as.numeric))

cor_matrix <- cor(
  base__viv,
  use = "pairwise.complete.obs"
)
corrplot(cor_matrix,
         method = "color",
         type = "upper",
         tl.col = "black",
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.65,
         title = "Correlaciones entre características de las viviendas",
         mar = c(0, 0, 2, 0))

La matriz permite identificar qué características tienden a moverse conjuntamente. Estas relaciones sirven como punto de partida para comprender por qué una reducción de dimensionalidad puede ser útil en este conjunto de datos.

4 Análisis de Componentes Principales (PCA)

El PCA permite representar un conjunto de variables cuantitativas mediante nuevas dimensiones que concentran la mayor parte de la variabilidad observada. Para evitar que las variables con mayor escala dominen el resultado, se trabaja con variables estandarizadas.

pca_data <- vivienda %>%
  select(piso, estrato, areaconst, parqueaderos,
         preciom, banios, habitaciones) %>%
  drop_na()

pca_scale <- scale(pca_data)
res_pca <- prcomp(pca_scale, center = TRUE, scale. = TRUE)

fviz_eig(res_pca, addlabels = TRUE, ylim = c(0, 80)) +
  labs(title = "Varianza explicada por los componentes principales",
       x = "Componente",
       y = "Porcentaje de varianza explicada")

var_exp <- summary(res_pca)$importance[2, ] * 100
var_acum <- summary(res_pca)$importance[3, ] * 100

cat(
  "Los dos primeros componentes explican conjuntamente ",
  round(var_acum[2], 2),
  "% de la variabilidad total. Este resultado permite evaluar qué tan adecuadamente ",
  "puede representarse la información original mediante un número reducido de dimensiones. ",
  "La selección de los componentes se complementa con el análisis de sus cargas y contribuciones, ",
  "con el fin de identificar las características que definen cada dimensión."
)

Los dos primeros componentes explican conjuntamente 69.82 % de la variabilidad total. Este resultado permite evaluar qué tan adecuadamente puede representarse la información original mediante un número reducido de dimensiones. La selección de los componentes se complementa con el análisis de sus cargas y contribuciones, con el fin de identificar las características que definen cada dimensión.

4.1 Contribución de las variables

fviz_pca_var(
  res_pca,
  col.var = "contrib",
  gradient.cols = c("#D95F02", "#1B9E77", "#7570B3"),
  repel = TRUE
) +
  labs(title = "Contribución de las variables a los componentes")

cargas <- as.data.frame(res_pca$rotation[, 1:3])
cargas$variable <- rownames(cargas)

tabla_cargas <- cargas[, c("variable", "PC1", "PC2", "PC3")]

colnames(tabla_cargas) <- c(
  "Variable",
  "Componente 1",
  "Componente 2",
  "Componente 3"
)

formato_tabla(
  tabla_cargas,
  "Cargas de las variables en los tres primeros componentes",
  digits = 3
)
Cargas de las variables en los tres primeros componentes
Variable Componente 1 Componente 2 Componente 3
piso piso 0.070 0.547 -0.816
estrato estrato -0.323 0.518 0.222
areaconst areaconst -0.444 -0.207 -0.043
parqueaderos parqueaderos -0.407 0.221 0.193
preciom preciom -0.475 0.183 0.076
banios banios -0.467 -0.121 -0.205
habitaciones habitaciones -0.290 -0.542 -0.445

Los resultados indican que los dos primeros componentes explican conjuntamente el 69,82% de la variabilidad total, lo que evidencia una adecuada capacidad de síntesis de la información original. En términos prácticos, la estructura del mercado inmobiliario puede representarse mediante dos dimensiones principales sin perder una cantidad significativa de información.

El Componente Principal 1 (49%) está asociado principalmente con las variables precio, área construida, número de baños y parqueaderos. Las cargas observadas sugieren que este eje representa una dimensión de valor inmobiliario y nivel de equipamiento residencial. Las viviendas ubicadas en los extremos de este componente corresponden a inmuebles de mayor tamaño, mejor dotación y precios superiores, mientras que en el extremo opuesto se encuentran viviendas más compactas y de menor valor económico.

El Componente Principal 2 (20,9%) está determinado principalmente por el estrato y el número de piso, en contraposición al número de habitaciones. Esta dimensión refleja diferencias asociadas al contexto socioeconómico y al tipo de desarrollo urbano, distinguiendo viviendas localizadas en estratos superiores y edificaciones verticales frente a inmuebles con configuraciones más orientadas al tamaño familiar.

Estos resultados indican que la heterogeneidad del mercado inmobiliario de Cali se explica fundamentalmente por dos factores: el valor físico-económico de la vivienda y su contexto socioespacial. Este hallazgo resulta especialmente relevante para procesos de valoración, ya que muestra que el precio no depende únicamente del tamaño del inmueble, sino también de variables asociadas al nivel socioeconómico y a las características constructivas de la propiedad.

La elevada contribución conjunta de las variables área construida, precio, baños y parqueaderos sugiere que el mercado residencial se encuentra fuertemente segmentado por atributos de calidad y tamaño de las viviendas. En consecuencia, las estrategias de valoración no deberían sustentarse únicamente en variables socioeconómicas como el estrato, sino incorporar simultáneamente atributos físicos que capturan una proporción importante de la heterogeneidad observada en la oferta.

5 Análisis de conglomerados

El objetivo del clustering es segmentar las viviendas en grupos internamente homogéneos y externamente diferenciados. Para evitar que el identificador o variables categóricas codificadas artificialmente influyan sobre las distancias, el agrupamiento se realiza únicamente con variables cuantitativas relevantes.

# Seleccionamos las variables base para el modelo y conservamos p_m2 e id para el perfil
cluster_data_full <- vivienda %>%
  select(id, estrato, areaconst, parqueaderos, preciom,
         banios, habitaciones, piso) %>%
  drop_na()

# Seleccionamos SOLO las variables cuantitativas base para escalar y meter al K-means
cluster_scale <- scale(cluster_data_full)

5.1 Selección del número de conglomerados

fviz_nbclust(cluster_scale, kmeans, method = "wss") +
  geom_vline(xintercept = 3, linetype = 2) +
  labs(title = "Método del codo para seleccionar el número de conglomerados",
       x = "Número de conglomerados",
       y = "Suma de cuadrados intra-grupo")

El método del codo evidenció una reducción importante de la variabilidad intra-grupo hasta tres conglomerados, a partir de los cuales la mejora marginal disminuye considerablemente. En consecuencia, se seleccionó una solución de tres segmentos, permitiendo identificar grupos con perfiles claramente diferenciados y utilidad estratégica para la segmentación del mercado inmobiliario.

set.seed(123)
modelo_kmeans <- kmeans(cluster_scale, centers = 3, nstart = 50)

# Asignamos el cluster a la tabla que SÍ contiene p_m2
clustered <- cluster_data_full %>%
  mutate(cluster = factor(modelo_kmeans$cluster))

fviz_cluster(modelo_kmeans,
             data = cluster_scale,
             geom = "point",
             ellipse.type = "norm",
             ggtheme = theme_minimal()) +
  labs(title = "Segmentación de viviendas mediante K-means")

5.2 Perfil de los conglomerados

perfil_cluster <- clustered %>%
  group_by(cluster) %>%
  summarise(
    viviendas = n(),
    estrato_promedio = mean(estrato, na.rm = TRUE),
    area_promedio = mean(areaconst, na.rm = TRUE),
    precio_promedio = mean(preciom, na.rm = TRUE),
    habitaciones_promedio = mean(habitaciones, na.rm = TRUE),
    banios_promedio = mean(banios, na.rm = TRUE),
    parqueaderos_promedio = mean(parqueaderos, na.rm = TRUE),
    piso_promedio = mean(piso, na.rm = TRUE),
    .groups = "drop"
  )

tabla_perfil <- perfil_cluster

colnames(tabla_perfil) <- c(
  "Conglomerado",
  "Viviendas",
  "Estrato promedio",
  "Área promedio (m²)",
  "Precio promedio (millones)",
  "Habitaciones promedio",
  "Baños promedio",
  "Parqueaderos promedio",
  "Piso promedio"
)

formato_tabla(
  tabla_perfil,
  "Perfil promedio de los conglomerados",
  digits = 2
)
Perfil promedio de los conglomerados
Conglomerado Viviendas Estrato promedio Área promedio (m²) Precio promedio (millones) Habitaciones promedio Baños promedio Parqueaderos promedio Piso promedio
1 1603 5.31 380.87 904.02 5.04 5.07 2.90 2.84
2 3600 3.82 109.16 224.94 3.30 2.24 0.74 3.36
3 3116 5.23 144.99 433.49 3.22 3.11 1.60 4.56

Interpretación de los conglomerados

El análisis de conglomerados permitió identificar tres segmentos claramente diferenciados dentro del mercado residencial de Cali. La segmentación se encuentra principalmente determinada por diferencias en área construida, precio, número de baños y disponibilidad de parqueaderos, variables que previamente habían mostrado una elevada contribución dentro del PCA.

Conglomerado 1: Segmento Premium

Este grupo concentra 1.603 viviendas y presenta los mayores niveles de área construida (380,87 m²), precio promedio (904 millones), baños (5,07) y parqueaderos (2,90). Su estrato promedio es 5,31. Estas características reflejan una oferta orientada a hogares de altos ingresos y corresponden al segmento de lujo del mercado residencial.

Conglomerado 2: Segmento Masivo de Precio Accesible

Agrupa 3.600 viviendas y presenta los valores más bajos de precio (224 millones), área (109 m²), baños (2,24) y parqueaderos (0,74). Además registra el menor estrato promedio (3,82). Este conglomerado representa el mercado de mayor volumen y constituye el principal segmento para estrategias orientadas a vivienda de amplio alcance comercial.

Conglomerado 3: Segmento Intermedio Consolidado

Incluye 3.116 viviendas y presenta niveles intermedios de área (145 m²), precio (433 millones) y equipamiento residencial. A pesar de exhibir un estrato promedio similar al del conglomerado 1 (5,23), sus características físicas y económicas son considerablemente menores, lo que evidencia que viviendas ubicadas en contextos socioeconómicos similares pueden presentar perfiles de mercado distintos.

Implicaciones estratégicas

La segmentación obtenida demuestra que el estrato por sí solo no explica completamente el valor inmobiliario. La coexistencia de los conglomerados 1 y 3 en estratos altos evidencia que el tamaño, el equipamiento y las características constructivas generan diferencias significativas dentro de un mismo contexto socioeconómico. Esto abre oportunidades para diseñar estrategias diferenciadas de valoración, comercialización y captación de inmuebles.

5.3 Visualización de perfiles

perfil_largo <- perfil_cluster %>%
  select(cluster, area_promedio, precio_promedio,
         habitaciones_promedio, banios_promedio,
         parqueaderos_promedio) %>%
  pivot_longer(-cluster, names_to = "indicador", values_to = "valor")

ggplot(perfil_largo, aes(x = cluster, y = valor, fill = cluster)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ indicador, scales = "free_y") +
  labs(title = "Indicadores promedio por conglomerado",
       x = "Conglomerado",
       y = "Valor promedio") +
  theme_minimal()

6 Análisis de Correspondencias

El análisis de correspondencias permite estudiar asociaciones entre variables categóricas a partir de tablas de contingencia. En este caso se consideran especialmente tipo, zona y barrio, ya que permiten examinar cómo se distribuye la oferta residencial en el territorio.

6.1 Análisis de la Relación Estrato y Zona

En primer lugar, se analiza la distribución espacial de las viviendas según su nivel socioeconómico (estrato).

# Graficar la distribución de viviendas por zona y estrato
vivienda %>%
  count(zona, estrato) %>%
  ggplot(aes(x = fct_reorder(zona, n), y = n, fill = as.factor(estrato))) +
  geom_col(position = "stack", width = 0.7) +
  scale_fill_brewer(palette = "Set2", name = "Estrato") +
  coord_flip() +
  labs(
    title = "Distribución de viviendas por zona y estrato",
    x = "Zona",
    y = "Unidades de vivienda"
  ) +
  theme_minimal(base_size = 12) 

tabla_estrato_zona <- table(vivienda$zona, vivienda$estrato)
colnames(tabla_estrato_zona) <- paste("Estrato", 3:6)

tabla_estrato_zona
##               
##                Estrato 3 Estrato 4 Estrato 5 Estrato 6
##   Zona Centro        105        14         4         1
##   Zona Norte         572       407       769       172
##   Zona Oeste          54        84       290       770
##   Zona Oriente       340         8         2         1
##   Zona Sur           382      1616      1685      1043
# Prueba Chi-cuadrado de independencia
chi_estrato_zona <- chisq.test(tabla_estrato_zona)
chi_estrato_zona
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_estrato_zona
## X-squared = 3830.4, df = 12, p-value < 2.2e-16

La prueba Chi-cuadrado evidenció una asociación altamente significativa entre la zona geográfica y el estrato socioeconómico (χ² = 3830,4; p < 0,001), confirmando que la distribución del nivel socioeconómico no es homogénea dentro de la ciudad.

Las dos primeras dimensiones explican el 97,65% de la inercia total, lo que indica que el mapa factorial representa adecuadamente la estructura de asociación existente entre las categorías analizadas.

El análisis factorial muestra una especialización territorial evidente. La Zona Oeste se encuentra estrechamente asociada con el Estrato 6, consolidándose como el principal mercado de vivienda de alto valor. Por su parte, la Zona Oriente y el Centro muestran proximidad con el Estrato 3, evidenciando una concentración de oferta orientada a segmentos de ingresos medios y bajos. Finalmente, la Zona Sur concentra gran parte de la oferta de los estratos 4 y 5, constituyéndose como el principal corredor residencial de nivel medio y medio alto de la ciudad.

Desde la perspectiva empresarial, estos resultados confirman que la localización geográfica constituye un determinante fundamental de la segmentación inmobiliaria y debe incorporarse explícitamente en los modelos de valoración y en las estrategias de adquisición de inmuebles.

ca_estrato_zona <- CA(tabla_estrato_zona, graph = FALSE)

# Mostrar eigenvalores e inercia explicada
ca_estrato_zona$eig
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.32215213              69.965515                          69.96551
## dim 2 0.12745096              27.680002                          97.64552
## dim 3 0.01084108               2.354483                         100.00000
# Evaluación de dimensiones e impresión de Biplot
num_dim <- ncol(ca_estrato_zona$row$coord)

if (num_dim >= 2) {
  fviz_ca_biplot(
    ca_estrato_zona,
    axes = c(1, 2),
    repel = TRUE,
    col.row = "#D95F02",
    col.col = "#1B9E77"
  ) +
    labs(
      title = "Correspondencia entre zona y estrato",
      x = paste0("Dimensión 1 (", round(ca_estrato_zona$eig[1, 2], 1), "%)"),
      y = paste0("Dimensión 2 (", round(ca_estrato_zona$eig[2, 2], 1), "%)")
    ) +
    theme_minimal(base_size = 12)
} else {
  fviz_ca_row(
    ca_estrato_zona,
    axes = c(1, 1),
    col.row = "#D95F02",
    repel = TRUE
  ) +
    labs(
      title = "Correspondencia (Unidimensional) entre zona y estrato",
      x = paste0("Dimensión 1 (", round(ca_estrato_zona$eig[1, 2], 1), "%)")
    ) +
    theme_minimal(base_size = 12)
}

# Gráfico de inercia
fviz_screeplot(ca_estrato_zona, addlabels = TRUE, barfill = "#1B9E77", barcolor = "#1B9E77") +
  labs(
    title = "Inercia explicada - Zona vs. Estrato",
    x = "Dimensiones",
    y = "Porcentaje de inercia"
  ) +
  theme_minimal(base_size = 12)

Estos hallazgos sugieren la existencia de procesos de segregación residencial y diferenciación territorial de la oferta inmobiliaria, donde determinadas zonas concentran segmentos específicos del mercado.

6.2 Análisis de la Relación Zona y Tipo de Vivienda

A continuación, se evalúa la preferencia de tipo de vivienda (Casa / Apartamento) en los distintos estratos socioeconómicos.

# Distribución de tipos de vivienda según el estrato
ggplot(vivienda, aes(x = as.factor(estrato), fill = tipo)) +
  geom_bar(position = "dodge", width = 0.7) +
  scale_fill_manual(values = c("Apartamento" = "#1B9E77", "Casa" = "#D95F02")) +
  labs(
    title = "Distribución de tipo de vivienda por estrato",
    x = "Estrato",
    y = "Número de viviendas",
    fill = "Tipo de Vivienda"
  ) +
  theme_minimal(base_size = 12)

#Tabla de contingencia entre tipo de vivienda y estrato
tabla_tipo_estrato <- table(vivienda$tipo, vivienda$estrato)
colnames(tabla_tipo_estrato) <- paste("Estrato", 3:6)

tabla_tipo_estrato
##              
##               Estrato 3 Estrato 4 Estrato 5 Estrato 6
##   Apartamento       639      1404      1766      1291
##   Casa              814       725       984       696
# Prueba Chi-cuadrado
chi_tipo_estrato <- chisq.test(tabla_tipo_estrato)
chi_tipo_estrato
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_tipo_estrato
## X-squared = 224.33, df = 3, p-value < 2.2e-16

La distribución observada muestra que los apartamentos predominan en los estratos 4, 5 y 6, mientras que las casas presentan una participación relativamente mayor en el estrato 3. La prueba Chi-cuadrado confirma que estas diferencias no son producto del azar y reflejan patrones estructurales del mercado residencial.

Este resultado sugiere que la verticalización de la vivienda se encuentra asociada a segmentos socioeconómicos medios y altos, mientras que las viviendas unifamiliares mantienen una presencia importante en los mercados de menor estrato. Para la inmobiliaria, estas diferencias constituyen información relevante para orientar la estructuración del portafolio y definir estrategias de promoción diferenciadas según el público objetivo.

ca_tipo_estrato <- CA(tabla_tipo_estrato, graph = FALSE)

# Mostrar eigenvalores
ca_tipo_estrato$eig
##       eigenvalue percentage of variance cumulative percentage of variance
## dim 1  0.0269661                    100                               100
# Gráfico de inercia explicada
fviz_screeplot(ca_tipo_estrato, addlabels = TRUE, barfill = "#D95F02", barcolor = "#D95F02") +
  labs(
    title = "Inercia explicada - Tipo vs. Estrato",
    x = "Dimensiones",
    y = "Porcentaje de inercia"
  ) +
  theme_minimal(base_size = 12)

6.3 Análisis de Correspondencias Múltiples (MCA)

Para evaluar la interacción conjunta entre las tres variables categóricas (Zona, Tipo de Vivienda y Estrato), se aplica un Análisis de Correspondencias Múltiples.

# Selección y formateo de datos
vivienda_mca_data <- vivienda %>%
  select(zona, estrato, tipo) %>%
  mutate(across(everything(), as.factor))

# Ajuste del modelo MCA
mca_resultado <- MCA(vivienda_mca_data, graph = FALSE)

# 1. Varianza explicada (Scree plot)
fviz_screeplot(mca_resultado, addlabels = TRUE, barfill = "#7570B3", barcolor = "#7570B3") +
  labs(
    title = "Varianza explicada por dimensión (MCA)",
    x = "Dimensiones",
    y = "Porcentaje de varianza"
  ) +
  theme_minimal(base_size = 12)

# 2. Biplot del MCA (Solo mostrando variables para evitar saturación de ggrepel)
fviz_mca_var(
  mca_resultado,
  repel = TRUE,
  col.var = "#D95F02"
) +
  labs(
    title = "Biplot de Categorías de Variables (MCA)",
    x = paste0("Dimensión 1 (", round(mca_resultado$eig[1, 2], 1), "%)"),
    y = paste0("Dimensión 2 (", round(mca_resultado$eig[2, 2], 1), "%)")
  ) +
  theme_minimal(base_size = 12)

# 3. Asociacion de variables con las dimensiones
fviz_mca_var(
  mca_resultado,
  choice = "mca.cor",
  repel = TRUE,
  col.var = "#1B9E77"
) +
  labs(
    title = "Asociacion de las variables con las dimensiones principales",
    x = "Dimensión 1",
    y = "Dimensión 2"
  ) +
  theme_minimal(base_size = 12)

El MCA permitió analizar simultáneamente las relaciones entre zona, estrato y tipo de vivienda. Las dos primeras dimensiones explican aproximadamente el 38% de la variabilidad total, porcentaje aceptable considerando la naturaleza categórica de los datos.

El mapa factorial evidencia patrones consistentes con los resultados obtenidos previamente en el análisis de correspondencias simple, destacándose la proximidad entre la Zona Oeste y el Estrato 6, así como la asociación entre la Zona Oriente y el Estrato 3.

La proximidad entre determinadas categorías indica que las características socioeconómicas y territoriales no son independientes, sino que forman estructuras de mercado relativamente estables. En consecuencia, las decisiones de inversión y valoración inmobiliaria deben considerar simultáneamente la localización, el estrato y el tipo de vivienda, en lugar de analizarlos de manera aislada.

7 Visualización espacial

La base contiene coordenadas de localización, por lo que se incorpora una representación geográfica como complemento de los análisis anteriores. La visualización permite observar si determinados segmentos de vivienda se concentran territorialmente.

Para evitar depender del orden de las filas, se construye la asignación a partir de un identificador común:

ids_cluster <- vivienda %>%
  select(id, estrato, areaconst, parqueaderos, preciom, banios,
         habitaciones, piso) %>%
  drop_na() %>%
  mutate(cluster = factor(modelo_kmeans$cluster)) %>%
  select(id, cluster)

mapa_data <- vivienda %>%
  mutate(
    longitud = as.numeric(longitud),
    latitud = as.numeric(latitud)
  ) %>%
  left_join(ids_cluster, by = "id") %>%
  filter(!is.na(longitud), !is.na(latitud), !is.na(cluster))

mapa_sf <- st_as_sf(mapa_data,
                    coords = c("longitud", "latitud"),
                    crs = 4326)

ggplot(mapa_sf) +
  geom_sf(aes(color = cluster), alpha = 0.55, size = 1.3) +
  labs(title = "Distribución espacial de los conglomerados residenciales",
       color = "Conglomerado") +
  theme_minimal()

La distribución espacial de los conglomerados evidencia que los segmentos identificados no se encuentran distribuidos aleatoriamente en el territorio. Se observan concentraciones geográficas diferenciadas que sugieren procesos de especialización residencial dentro de la ciudad. Este resultado refuerza la importancia de incorporar simultáneamente atributos físicos y variables de localización en los procesos de valoración inmobiliaria y selección de oportunidades de inversión.

8 Integración de resultados

Los cuatro enfoques utilizados responden preguntas diferentes y, por ello, deben interpretarse de forma complementaria:

  • PCA: resume las dimensiones cuantitativas más importantes de la oferta y permite identificar qué características físicas y económicas explican la mayor parte de la variabilidad.
  • Conglomerados: transforma esas características en segmentos de propiedades con perfiles diferenciados, útiles para estrategias de comercialización y segmentación.
  • Correspondencias: permite entender cómo se distribuyen conjuntamente las categorías de tipo, zona y barrio y detectar asociaciones que no son evidentes mediante frecuencias simples.
  • Mapas: añaden una dimensión territorial que ayuda a identificar concentraciones espaciales de segmentos y niveles de precio.

9 Conclusiones

La principal contribución de este estudio consiste en demostrar que el mercado inmobiliario residencial de Cali presenta una estructura altamente segmentada, tanto desde el punto de vista físico como socioespacial. La combinación de técnicas multivariadas permitió identificar patrones que no son evidentes mediante análisis descriptivos tradicionales.

El PCA mostró que la mayor parte de la variabilidad del mercado se encuentra explicada por dos dimensiones fundamentales: el valor físico-económico de la vivienda y el contexto socioeconómico asociado a su localización. Estas dimensiones sintetizan los principales factores que diferencian la oferta residencial.

El análisis de conglomerados evidenció la existencia de tres segmentos claramente diferenciados: un mercado premium de alta valorización, un mercado masivo orientado a vivienda de precio accesible y un mercado intermedio asociado a hogares de ingresos medios-altos. Esta clasificación proporciona una base objetiva para la segmentación comercial y la priorización de inversiones.

Los análisis de correspondencias confirmaron que la distribución territorial de la vivienda está estrechamente relacionada con el estrato socioeconómico y el tipo de inmueble. La localización emerge como un factor estructural del mercado y constituye un elemento determinante para la valoración y comercialización de propiedades.

En conjunto, los resultados evidencian que las decisiones inmobiliarias más eficientes requieren integrar simultáneamente variables físicas, económicas y espaciales, permitiendo una comprensión más robusta de la dinámica del mercado residencial urbano

10 Recomendaciones para la empresa inmobiliaria

  1. Segmentar la oferta: utilizar los conglomerados para construir portafolios comerciales diferenciados y evitar estrategias homogéneas para propiedades con perfiles muy distintos.

  2. Priorizar variables de valor: incorporar el precio por metro cuadrado junto con área, estrato y características físicas para comparar oportunidades de forma más consistente que utilizando solamente el precio total.

  3. Incorporar el componente territorial: cruzar los segmentos obtenidos con zona y barrio para identificar dónde se concentra cada perfil de vivienda y apoyar decisiones de adquisición o comercialización.

  4. Orientar la estrategia comercial: utilizar las asociaciones encontradas mediante correspondencias para adaptar la oferta a las características predominantes de cada territorio.

  5. Fortalecer el uso de datos geográficos: integrar los mapas a tableros de gestión para monitorear concentración de inventario, precios por metro cuadrado y evolución de segmentos en el territorio.

  6. Mejorar la calidad de la información: validar con el área de negocio el significado de los valores faltantes de parqueaderos y mantener trazabilidad de cualquier imputación antes de utilizar estos resultados para decisiones de inversión.