Introducción al caso de estudio:

Este informe aborda la evaluación de la oferta inmobiliaria urbana con el fin de entregar evidencia práctica para decisiones de compra, venta y valoración de propiedades. Partimos de una base de datos amplia con información de inmuebles (precio, área, amenidades, ubicación y atributos socioeconómicos) y aplicaremos un enfoque multidimensional para: (i) entender las características que explican la variación del mercado, (ii) segmentar la oferta en grupos comparables y accionables, y (iii) explorar relaciones entre variables categóricas (tipo, zona, estrato). El propósito final es comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.

library(dplyr)
library(FactoMineR)
library(factoextra)
library(ggplot2)
library(VIM)
library(forcats)
library(scales)
library(stringr)
library(tidyr)
library(knitr)
library(viridis)
library(cluster)
library(ca)
library(stringi)
library(tibble)

Paso 1. Carga de datos

Los datos utilizados provienen del paquete paqueteMODELOS. En este caso, se emplea el conjunto denominado vivenda. Con ello se constata que el dataset incluye atributos numéricos clave (precio, área construida, parqueaderos, baños, habitaciones) y categóricos (zona, estrato, tipo, barrio) necesarios para los análisis posteriores (EDA, PCA, clústeres, correspondencias y mapeo interactivo).

library(paqueteMODELOS)
data("vivenda")
str(vivienda) 

Paso 2. Análisis exploratorio de datos (EDA)

Antes de aplicar técnicas de análisis multidimensional, es fundamental comprender la estructura y distribución de la información disponible.
En esta sección se analizan las variables principales del conjunto de datos, observando sus rangos, tendencias y posibles valores atípicos.
Este análisis inicial permitirá identificar patrones generales, verificar la calidad de los datos y orientar las decisiones sobre transformaciones y limpieza necesarias antes de los modelos.

Cálculo de variables derivadas para la exploración

Aquí creamos variables adicionales que nos permitan tener una mejor lectura del conjunto de datos durante la exploración, como lo es precio por metro cuadrado.

# Creamos precio por metro cuadrado
vivienda <- vivienda %>%
  mutate(precio_m2 = ifelse(areaconst > 0, (preciom * 1e6) / areaconst, NA))

Se incorpora esta variable para facilitar comparaciones más homogéneas entre inmuebles de diferentes tamaños. El precio por m² permite evaluar el valor relativo de las propiedades sin que el área total distorsione la percepción del costo, siendo un indicador clave para segmentar el mercado.

Resumen estadístico de variables numéricas

El resumen estadístico ofrece una visión rápida de la centralidad y dispersión de las variables, así como de la presencia de valores extremos. Esta información es esencial para detectar rangos atípicos, posibles errores de captura y magnitudes que podrían requerir transformaciones antes de aplicar modelos.

vivienda %>%
  select(preciom, areaconst, precio_m2, parqueaderos, banios, habitaciones) %>%
  summary()
    preciom         areaconst        precio_m2        parqueaderos   
 Min.   :  58.0   Min.   :  30.0   Min.   : 146132   Min.   : 1.000  
 1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.:1916667   1st Qu.: 1.000  
 Median : 330.0   Median : 123.0   Median :2640000   Median : 2.000  
 Mean   : 433.9   Mean   : 174.9   Mean   :2722217   Mean   : 1.835  
 3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.:3379470   3rd Qu.: 2.000  
 Max.   :1999.0   Max.   :1745.0   Max.   :9468085   Max.   :10.000  
 NA's   :2        NA's   :3        NA's   :3         NA's   :1605    
     banios        habitaciones   
 Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 2.000   1st Qu.: 3.000  
 Median : 3.000   Median : 3.000  
 Mean   : 3.111   Mean   : 3.605  
 3rd Qu.: 4.000   3rd Qu.: 4.000  
 Max.   :10.000   Max.   :10.000  
 NA's   :3        NA's   :3       

A continuación, se describe cada una de forma simple para facilitar su lectura:

  • Precio (en millones COP): El más barato cuesta 58 millones y el más caro 1.999 millones. La mitad de las propiedades cuestan menos de 330 millones (mediana) y el promedio es de 433 millones, lo que indica que existen propiedades muy caras que elevan el promedio. Hay 2 datos faltantes.
  • Área construida (m²): Van desde 30 m² hasta 1.745 m². La mitad de las propiedades tienen menos de 123 m² y el promedio es de 175 m². Hay 3 datos faltantes.
  • Precio por m²: Va desde 146 mil COP/m² hasta 9,4 millones COP/m². La mitad de las propiedades cuestan menos de 2,64 millones COP/m², con un promedio de 2,72 millones COP/m². Hay 3 datos faltantes.
  • Parqueaderos: Entre 1 y 10 parqueaderos. Lo más común es tener 1 o 2. El promedio es de 1,83 parqueaderos. Hay 1.605 datos sin información (lo que amerita una revisión de la calidad de este dato).
  • Baños: Entre 0 y 10 baños. Lo más común es tener 3 baños, con un promedio de 3,1. Hay 3 datos faltantes.
  • Habitaciones: Entre 0 y 10 habitaciones. Lo más común es tener 3 o 4 habitaciones, con un promedio de 3,6. Hay 3 datos faltantes.

Distribución de precio y precio por m²

La distribución del precio total de los inmuebles muestra una concentración de valores en rangos bajos a medios, con una cola hacia precios altos que podría asociarse a propiedades premium o localizadas en zonas de alta valorización. Esto sugiere la existencia de un mercado heterogéneo en términos de valor absoluto.

hist(vivienda$preciom,
     breaks = 40,
     col = "skyblue",
     main = "Distribución del precio\n(en millones COP)",
     xlab = "Precio (millones COP)",
     ylab = "Frecuencia")

Al analizar el precio por m², se observa que este indicador reduce parcialmente el sesgo causado por el tamaño del inmueble, permitiendo una comparación más equitativa entre propiedades. Las colas de la distribución podrían reflejar tanto unidades de lujo como propiedades en zonas de menor oferta.

Si bien el precio total es útil para entender el perfil de inversión, su comparación directa entre inmuebles de distinto tamaño puede generar sesgos, por lo que resulta clave complementarlo con métricas relativas como el precio por m².

hist(vivienda$precio_m2 / 1e6,
     breaks = 40,
     col = "orange",
     main = "Distribución del precio por m²",
     xlab = "Precio por m² (millones COP)",
     ylab = "Frecuencia")

Este indicador es uno de los más utilizados por analistas y agentes inmobiliarios para comparar propiedades con tamaños diferentes.

En términos estratégicos, la dirección de la empresa podría emplear esta métrica para priorizar captaciones en zonas de alto valor por m² si busca propiedades exclusivas, o en zonas de bajo valor por m² con potencial de crecimiento si busca oportunidades de arbitraje.

Composición por zona y tipo de inmueble

La oferta está fuertemente concentrada en Zona Sur, que reúne la mayor cantidad de inmuebles del conjunto. En segundo lugar aparece Zona Norte, seguida por Zona Oeste. En contraste, Zona Oriente y Zona Centro están claramente subrepresentadas.

df_zona <- vivienda %>%
  filter(!is.na(zona)) %>%
  count(zona, name = "n") %>%
  mutate(zona = fct_reorder(str_wrap(zona, width = 12), n))  # wrap nombres y ordenar

ggplot(df_zona, aes(x = zona, y = n)) +
  geom_col(fill = "#9BE7A6", color = "grey30") +
  coord_flip() +
  geom_text(aes(label = comma(n)), hjust = -0.05, size = 4) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +
  labs(title = "Oferta por zona (ordenada)",
       x = NULL, y = "Número de inmuebles") +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold"),
    panel.grid = element_blank() # elimina cuadrícula
  )

Predominan los apartamentos (alrededor de ~5.1k registros) frente a las casas (~3.2k), lo que indica un mercado orientado a unidades en propiedad horizontal.

df_tipo <- vivienda %>%
  filter(!is.na(tipo)) %>%
  count(tipo, name = "n") %>%
  mutate(tipo = fct_reorder(tipo, n))  # ordenar por frecuencia

ggplot(df_tipo, aes(x = tipo, y = n)) +
  geom_col(fill = "#F4A6A6", color = "grey30") +
  coord_flip() +
  geom_text(aes(label = comma(n)), hjust = -0.05, size = 4) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.12))) +
  labs(title = "Oferta por tipo de inmueble",
       x = NULL, y = "Número de inmuebles") +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold"),
    panel.grid = element_blank()
  )

Detección de valores atípicos en variables cuantitativas

Se busca identificar la presencia de valores atípicos. Estos valores, que se encuentran muy alejados del rango central de los datos, pueden deberse a errores de captura, condiciones excepcionales del mercado o características particulares de ciertos inmuebles. Identificarlos permite tomar decisiones informadas sobre su tratamiento: corregir, excluir o conservar, dependiendo de su origen y relevancia para el estudio.

vars_num <- c("areaconst", "banios", "habitaciones", "parqueaderos", "preciom")

etiquetas_vars <- c(
  areaconst    = "Área construida (m²)",
  banios       = "Baños",
  habitaciones = "Habitaciones",
  parqueaderos = "Parqueaderos",
  preciom      = "Precio (millones COP)"
)

vivienda %>%
  select(all_of(names(etiquetas_vars))) %>%
  pivot_longer(everything(), names_to = "variable", values_to = "valor") %>%
  ggplot(aes(x = variable, y = valor, fill = variable)) +
  geom_boxplot(show.legend = FALSE) +
  facet_wrap(~variable, scales = "free",
             labeller = as_labeller(etiquetas_vars)) +
  labs(
    title = "Detección de valores atípicos en variables cuantitativas",
    x = "", y = "Valor"
  ) +
  theme_minimal() +
  theme(
    strip.text = element_text(face = "bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank()
  )

La gráfica muestra la distribución de las principales variables cuantitativas del conjunto de datos de viviendas. Se observa lo siguiente:

  • Área construida (m²): presenta una alta concentración de valores entre 50 y 200 m², con un número considerable de inmuebles que superan los 500 m², considerados atípicos.
  • Baños: la mayoría de los inmuebles tiene entre 2 y 4 baños, pero se identifican casos con más de 7, lo que los convierte en valores atípicos.
  • Habitaciones: predominan viviendas con 3 a 4 habitaciones, aunque existen registros con más de 7 habitaciones y algunos con 0, ambos extremos siendo atípicos.
  • Parqueaderos: la mayoría cuenta con 1 o 2 parqueaderos, pero hay inmuebles con más de 6, lo que los ubica como atípicos.
  • Precio (millones COP): la mayor parte de las propiedades se encuentra en un rango inferior a 500 millones, sin embargo, se observan valores que superan los 2.000 millones, claramente atípicos.

Estos hallazgos indican que, aunque la mayoría de los datos se concentra en rangos esperados, existe una proporción relevante de registros con características extremas que deben ser evaluadas antes de proceder con análisis estadísticos o modelamientos posteriores.

Paso 3. Limpieza y transformación de datos

Con la información obtenida en el análisis exploratorio, el siguiente paso es preparar la base de datos para el análisis estadístico. En esta etapa, el objetivo es asegurar la calidad, coherencia y comparabilidad de los datos, de manera que los resultados de técnicas como el PCA o el clustering no se vean distorsionados por errores, vacíos o escalas incompatibles.

Definición de Parámetros y Funciones Auxiliares

Antes de realizar modificaciones, establecemos los parámetros que guiarán la limpieza. Se definen los umbrales para la detección de valores atípicos, basados en el análisis exploratorio previo, y una función auxiliar para calcular la moda, que podría ser necesaria en futuras imputaciones. Esta separación asegura que nuestros criterios sean consistentes y fáciles de auditar.

# --- Helpers y parámetros
moda <- function(x) {
  x <- x[!is.na(x)]
  if (length(x) == 0) return(NA)
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

# Umbrales para la detección de outliers (coherentes con el EDA)
UMBRAL_AREA_GRANDE   <- 500
UMBRAL_PRECIO_PREMIUM <- 2000   # millones COP
UMBRAL_BANIOS_EXT    <- 7
UMBRAL_HAB_EXT       <- 7
UMBRAL_PARQ_EXT      <- 6

Tratamiento de valores faltantes

El análisis exploratorio reveló dos tipos de problemas con los datos faltantes:

  1. La variable parqueaderos tiene una gran cantidad de valores ausentes (~20%).
  2. Otras variables clave (banios, habitaciones, areaconst) tienen muy pocos datos faltantes (<0.1%).

Aplicar un único método de imputación no sería óptimo. Por ello, adoptamos las siguientes estrategias:

  • Para parqueaderos, donde el volumen de datos faltantes es alto, utilizamos el método k-Nearest Neighbors (k-NN). Este algoritmo realiza una predicción informada para cada valor ausente, buscando las 5 propiedades más similares (sus “vecinos”) y usando sus datos para estimar el valor más probable. Esto evita la distorsión que generaría asignar un único valor a más de 1,600 propiedades.

  • Para banios, habitaciones y areaconst, donde los faltantes son escasos, un método simple y robusto como la mediana es suficiente y computacionalmente eficiente.

# --- 1. Imputación para Parqueaderos usando k-NN ---
# Seleccionamos variables numéricas clave para encontrar "vecinos" similares.
vars_for_knn <- c("preciom", "areaconst", "banios", "habitaciones", "estrato")

# Aplicamos k-NN para imputar solo 'parqueaderos'.
vivienda_imputada_knn <- kNN(
  data = vivienda,
  variable = "parqueaderos",
  k = 5,
  dist_var = vars_for_knn
)
# Reintegramos la columna 'parqueaderos' ya imputada a nuestro dataframe principal.
vivienda$parqueaderos <- vivienda_imputada_knn$parqueaderos

# --- 2. Imputación simple para el resto de variables ---
med_banios       <- median(vivienda$banios, na.rm = TRUE)
med_habitaciones <- median(vivienda$habitaciones, na.rm = TRUE)
med_areaconst    <- median(vivienda$areaconst, na.rm = TRUE)

vivienda <- vivienda %>%
  mutate(
    banios       = ifelse(is.na(banios),       med_banios,       banios),
    habitaciones = ifelse(is.na(habitaciones), med_habitaciones, habitaciones),
    areaconst    = ifelse(is.na(areaconst),    med_areaconst,    areaconst)
  ) %>%
  mutate(
    # Recalcular precio_m2 por si 'areaconst' fue imputada
    precio_m2 = ifelse(areaconst > 0, (preciom * 1e6) / areaconst, NA_real_)
  )

Tratamiento de valores atípicos

Los valores atípicos son observaciones que se alejan significativamente del resto.
En un análisis de mercado inmobiliario:

  • Pueden representar propiedades excepcionales (lujo, gran tamaño, equipamiento especial).
  • También pueden ser errores de captura o registros poco representativos.
    Aquí no los eliminamos, sino que:
  • Creamos etiquetas para identificarlos y analizarlos por separado si es necesario.
  • Facilitamos comparaciones entre segmentos “estándar” y “extremos” del mercado.
vivienda <- vivienda %>%
  mutate(
    premium        = preciom      > UMBRAL_PRECIO_PREMIUM,
    gran_tamano    = areaconst    > UMBRAL_AREA_GRANDE,
    extremos_banios = banios       > UMBRAL_BANIOS_EXT,
    extremos_hab    = habitaciones > UMBRAL_HAB_EXT,
    extremos_parq   = parqueaderos > UMBRAL_PARQ_EXT
  )

Homogeneización de categorías

Las variables categóricas como “zona” o “tipo de inmueble” deben estar escritas de forma consistente.
Errores comunes como variaciones ortográficas, tildes, uso desigual de mayúsculas o abreviaturas, pueden fragmentar la información y producir resultados engañosos.
En este paso:

  • Unificamos la escritura (mayúsculas, minúsculas, acentos).
  • Agrupamos categorías equivalentes para que cada etiqueta represente un concepto único.
vivienda <- vivienda %>%
  mutate(
    zona  = zona  |> stringr::str_squish() |> stringi::stri_trans_general("Latin-ASCII") |> stringr::str_to_title(),
    barrio= barrio|> stringr::str_squish() |> stringi::stri_trans_general("Latin-ASCII") |> stringr::str_to_title(),
    tipo  = tipo  |> stringr::str_squish() |> stringr::str_to_lower()
  ) %>%
  mutate(
    tipo = dplyr::recode(tipo,
      "apto" = "apartamento", "apartamento" = "apartamento",
      "casa" = "casa", "casas" = "casa",
      .default = stringr::str_to_title(tipo)
    ),
    estrato = factor(estrato, levels = sort(unique(estrato)), ordered = TRUE)
  )

Normalización de variables cuantitativas para análisis multivariado

En técnicas como el PCA o el clustering, es esencial que todas las variables estén en la misma escala para que ninguna domine el análisis por tener valores numéricos más grandes. Aquí estandarizamos las variables numéricas principales y preparamos dos conjuntos de datos (_abs y _rel) para los análisis posteriores, evitando la colinealidad.

vars_abs <- vivienda %>%
  dplyr::select(preciom, areaconst, parqueaderos, banios, habitaciones)

vars_rel <- vivienda %>%
  dplyr::select(precio_m2, areaconst, parqueaderos, banios, habitaciones)

# Se eliminan filas con NA en precio_m2 que pudieron originarse de areaconst=0
vars_rel <- na.omit(vars_rel)

# Los datos se escalan para los análisis de PCA y Clustering
vars_abs_sc <- scale(vars_abs)
vars_rel_sc <- scale(vars_rel)

Paso 4. Análisis de Componentes Principales (PCA)

El Análisis de Componentes Principales (PCA) es una técnica estadística que permite:

  • Reducir la dimensionalidad del conjunto de datos, resumiendo la información en un número menor de variables (componentes) que explican la mayor parte de la variabilidad.
  • Identificar patrones y relaciones entre variables.
  • Visualizar la estructura de los datos para detectar agrupaciones o tendencias.

En este caso, realizaremos dos PCA independientes:

  1. PCA_ABS: analiza el mercado según valor absoluto de los inmuebles (preciom), junto con área y amenidades.
  2. PCA_REL: analiza el mercado según valor relativo (precio_m2), para detectar eficiencia y comparación homogénea entre propiedades.

De esta manera evitamos la colinealidad que surgiría al incluir preciom y precio_m2 en el mismo análisis.

PCA con valor absoluto del inmueble (PCA_ABS)

En esta primera visión, analizamos la estructura del mercado basándonos en el valor total de las propiedades y sus características físicas. Los siguientes gráficos resumen los hallazgos clave.

# PCA con precio absoluto
pca_abs <- FactoMineR::PCA(as.data.frame(vars_abs_sc),
                           scale.unit = FALSE,  # ya está escalado
                           graph = FALSE)

# Scree plot
factoextra::fviz_screeplot(pca_abs, addlabels = TRUE, ylim = c(0, 70)) +
  labs(title = "PCA_ABS - Varianza explicada por componente")

El gráfico de sedimentación (Scree plot) es la primera herramienta que utilizamos para decidir cuántos componentes conservar. La pendiente de la curva revela que:

  • El Componente 1 (Dim 1) es claramente dominante: explica por sí solo un 64,8% de la variabilidad total. Esto indica la presencia de una dimensión principal que captura gran parte de la información, probablemente asociada al tamaño y valor general de la propiedad.
  • El Componente 2 (Dim 2) aporta un 17,6% adicional, y el Componente 3 (Dim 3) un 7,2%.
  • Varianza acumulada: los dos primeros componentes ya explican el 82,4% de la información, y al incluir el tercero alcanzamos un 89,6%, lo que confirma que podemos trabajar con un número reducido de dimensiones sin perder representatividad.
# Contribución de variables a Dim.1 y Dim.2
factoextra::fviz_contrib(pca_abs, choice = "var", axes = 1, top = 10) +
  labs(title = "PCA_ABS - Contribución de variables a Dim.1")

factoextra::fviz_contrib(pca_abs, choice = "var", axes = 2, top = 10) +
  labs(title = "PCA_ABS - Contribución de variables a Dim.2")

El gráfico de contribución nos permite identificar qué variables son más influyentes en cada componente:

  • Dimensión 1 – Tamaño y Valor Absoluto:
    Las variables que más peso tienen son preciom, areaconst, habitaciones, parqueaderos y banios. Todas están fuertemente correlacionadas y apuntan en la misma dirección, lo que indica que este eje representa propiedades más grandes, con más dependencias y mayor valor total.

  • Dimensión 2 – Calidad y Estatus:
    Este eje está organizado principalmente por habitaciones (positivo) frente a parqueaderos y preciom (negativos). Para un mismo tamaño (Dim1), separa inmuebles con más habitaciones (distribuciones más “espaciosas”) de aquellos con más parqueaderos o mayor precio a igualdad de área (configuraciones más “compactas/premium”).

# Círculo de correlaciones
factoextra::fviz_pca_var(pca_abs,
                         col.var = "contrib",
                         gradient.cols = viridis::viridis(3, direction = -1),
                         repel = TRUE) +
  labs(title = "PCA_ABS - Círculo de correlaciones",
       color = "Contribución")

El círculo de correlaciones nos da una visión más intuitiva de estas relaciones:

  • Las variables de tamaño y valor (preciom, areaconst, banios, habitaciones) forman un grupo compacto y orientado a lo largo del eje horizontal (Dim 1), reforzando su papel en la primera dimensión.
  • En el eje vertical (Dim 2) se diferencian principalmente habitaciones frente a parqueaderos y preciom, lo que refleja una configuración interna: propiedades con más habitaciones tienden a tener menos parqueaderos o menor precio a igualdad de tamaño, y viceversa.
  • La disposición ortogonal de estos grupos indica independencia entre el “tamaño/valor” y la “configuración interna”: conocer que una propiedad es grande no garantiza que cuente con más parqueaderos o que su distribución interna sea más amplia.

En conjunto, esta lectura confirma que en el mercado inmobiliario analizado existen dos ejes fundamentales y complementarios: uno que refleja la magnitud física y económica de la propiedad, y otro que sintetiza su configuración interna y dotaciones.

# ---- Mapa de individuos para PCA_ABS ----
factoextra::fviz_pca_ind(
  pca_abs,
  geom = "point",
  pointshape = 19,
  pointsize = 1.4,
  label = "none",
  col.ind = "cos2",                       # color según calidad de representación
  gradient.cols = viridis::viridis(3, direction = -1),
  repel = FALSE
) +
  ggplot2::labs(
    title = "PCA_ABS - Mapa de individuos (Dim1 vs Dim2)",
    subtitle = "Color = cos2 (mejor calidad de representación → color más intenso)"
  ) +
  ggplot2::theme_minimal(base_size = 13)
Mapa de individuos para PCA_ABS coloreado por calidad de representación cos2

Mapa de individuos para PCA_ABS coloreado por calidad de representación cos2

El mapa de individuos muestra cada propiedad proyectada sobre las dos primeras dimensiones del PCA. El color indica la calidad de representación (cos2): cuanto más intenso, mejor está “explicada” la observación por el plano Dim1–Dim2.

  • Estructura general. El cloud principal se orienta a lo largo de Dim 1 (64,8%), confirmando que la mayor variabilidad del mercado se organiza alrededor del eje de tamaño/valor absoluto.
  • Centro vs. periferia. Los puntos cercanos al origen tienen cos2 menor (color menos intenso): su variabilidad no se explica completamente con solo dos dimensiones, por lo que deben interpretarse con cautela. En cambio, los puntos alejados del origen suelen exhibir cos2 mayor: el plano captura bien su posición relativa y, por tanto, sus conclusiones son más confiables.
  • Cuadrantes e intuición económica.
    • Dim1 alto, Dim2 alto: propiedades grandes y caras con rasgos de estatus/calidad (más parqueaderos, pisos altos, estratos más altos o menor antigüedad).
    • Dim1 alto, Dim2 bajo: propiedades grandes y caras pero con menor estatus relativo (más antiguas o con menos parqueaderos).
    • Dim1 bajo: inmuebles pequeños/menos costosos, donde Dim2 separa los de mejor estatus (arriba) de los más antiguos o con menor dotación (abajo).
  • Implicación para la segmentación. La orientación alargada sobre Dim1 y la presencia de individuos bien representados en la periferia sugiere que k-means sobre las coordenadas de PCA capturará grupos principalmente diferenciados por tamaño/valor, con un refinamiento adicional por estatus/calidad a lo largo de Dim2.
# pca_abs$var$coord  -> cargas (correlaciones variable–componente)
# pca_abs$var$contrib -> % contribución de cada variable a cada dimensión

load_abs <- as.data.frame(pca_abs$var$coord[, 1:2])
colnames(load_abs) <- c("Loading_Dim1", "Loading_Dim2")

contrib_abs <- as.data.frame(pca_abs$var$contrib[, 1:2])
colnames(contrib_abs) <- c("%Contrib_Dim1", "%Contrib_Dim2")

tbl_abs <- load_abs %>%
  dplyr::mutate(Variable = row.names(load_abs)) %>%
  dplyr::bind_cols(contrib_abs) %>%
  dplyr::select(Variable, Loading_Dim1, Loading_Dim2, `%Contrib_Dim1`, `%Contrib_Dim2`) %>%
  dplyr::mutate(
    dplyr::across(dplyr::starts_with("Loading_"), ~round(.x, 3)),
    dplyr::across(dplyr::starts_with("%Contrib_"), ~round(.x, 1))
  ) %>%
  dplyr::arrange(dplyr::desc(abs(Loading_Dim1)))

knitr::kable(
  tbl_abs,
  caption = "PCA_ABS — Cargas (loadings) y % de contribución por variable (Dim1–Dim2).",
  align = c("l", "r", "r", "r", "r"),
  row.names = FALSE     # <-- evita duplicar el nombre de la variable
)
PCA_ABS — Cargas (loadings) y % de contribución por variable (Dim1–Dim2).
Variable Loading_Dim1 Loading_Dim2 %Contrib_Dim1 %Contrib_Dim2
banios 0.871 0.163 23.4 3.0
areaconst 0.863 0.040 23.0 0.2
preciom 0.849 -0.373 22.3 15.9
parqueaderos 0.791 -0.405 19.3 18.7
habitaciones 0.623 0.740 12.0 62.3

La tabla de loadings y contribuciones confirma y cuantifica el papel de cada variable en los dos componentes:

  • Dimensión 1 – Tamaño/Valor Absoluto (64,8%).
    Las variables con mayor loading y mayor % de contribución son: banios (loading 0.871; 23,4%), areaconst (0.863; 23,0%), preciom (0.849; 22,3%) y parqueaderos (0.791; 19,3%). También aporta habitaciones (0.623; 12,0%). Todas en signo positivo y alineadas, por lo que Dim1 sintetiza propiedades más grandes, con más dependencias y mayor valor total.

  • Dimensión 2 – Estatus/Calidad (17,6%).
    La variable que organiza casi por completo esta dimensión es habitaciones (loading 0.740; 62,3% de contribución), acompañada de parqueaderos (loading −0.405; 18,7%) y preciom (loading −0.373; 15,9%). El signo opuesto entre habitaciones (positivo) y parqueaderos/preciom (negativos) indica un gradiente de configuración interna: para un mismo tamaño/valor, Dim2 separa inmuebles con más habitaciones (distribuciones más “espaciosas”) frente a aquellos con más parqueaderos o mayor precio a igualdad de tamaño (configuraciones más “premium/compactas” o con amenities de estatus).
    banios y areaconst apenas contribuyen en Dim2 (3,0% y 0,2%), reforzando que su papel es casi exclusivo de Dim1.

¿Qué nos llevamos?
1) El mercado se explica principalmente por un eje de magnitud económica y física (Dim1) y, en segundo lugar, por un eje de estatus/configuración (Dim2).
2) El color por cos2 ayuda a ponderar la confianza en la lectura de cada punto: la periferia es más fiable que el centro.
3) Esta estructura respalda usar k-means sobre las coordenadas de PCA, pues habrá clústeres claramente diferenciados por tamaño/valor y, dentro de ellos, matices de calidad/estatus que refinan la segmentación.

PCA con valor relativo (PCA_REL)

En este segundo análisis, utilizamos el precio por metro cuadrado (precio_m2) junto con variables físicas y de dotación para evaluar el mercado desde una perspectiva relativa. Esto nos permite comparar propiedades de distinto tamaño en condiciones más homogéneas, detectando patrones de eficiencia y de relación calidad–precio.

# PCA con precio relativo
pca_rel <- FactoMineR::PCA(as.data.frame(vars_rel_sc),
                           scale.unit = FALSE,
                           graph = FALSE)

# Scree plot
factoextra::fviz_screeplot(pca_rel, addlabels = TRUE, ylim = c(0, 70)) +
  labs(title = "PCA_REL - Varianza explicada por componente")

El gráfico de sedimentación muestra que:

  • Dimensión 1 (52,6%) concentra más de la mitad de la variabilidad total, lo que indica un eje principal claro en el análisis relativo.
  • Dimensión 2 (25,0%) añade una proporción relevante de información.
  • Varianza acumulada: los dos primeros componentes explican el 77,6% de la variabilidad del mercado, lo que permite una reducción de dimensionalidad eficaz sin pérdida importante de información.
# Contribución de variables a Dim.1 y Dim.2
factoextra::fviz_contrib(pca_rel, choice = "var", axes = 1, top = 10) +
  labs(title = "PCA_REL - Contribución de variables a Dim.1")

factoextra::fviz_contrib(pca_rel, choice = "var", axes = 2, top = 10) +
  labs(title = "PCA_REL - Contribución de variables a Dim.2")

  • Dimensión 1 – Tamaño y dotación relativa:
    Las variables que más aportan son areaconst (29,0%), banios (28,4%), habitaciones (21,3%) y parqueaderos (19,6%). Este eje refleja el tamaño y la dotación física del inmueble, sin incluir el valor absoluto. La carga negativa y baja contribución de precio_m2 (1,6%) confirma que este eje no está directamente relacionado con el precio por unidad de área, sino con características físicas y de uso.

  • Dimensión 2 – Precio por m² y estatus relativo:
    La variable dominante es precio_m2 con una contribución muy elevada (66,6%) y carga positiva (0,912), seguida a distancia por parqueaderos (18,8%) y habitaciones (10,6%). Esto indica que el segundo eje separa claramente inmuebles por su valor relativo: hacia un extremo, propiedades con alto precio por metro cuadrado y más parqueaderos; hacia el otro, aquellas con menor precio relativo y mayor número de habitaciones.

# Círculo de correlaciones
factoextra::fviz_pca_var(pca_rel,
                         col.var = "contrib",
                         gradient.cols = viridis::viridis(3, direction = -1),
                         repel = TRUE) +
  labs(title = "PCA_REL - Círculo de correlaciones",
       color = "Contribución")

El círculo de correlaciones muestra:

  • Variables de tamaño/dotación (areaconst, banios, habitaciones) alineadas sobre Dim 1, lo que confirma que esta dimensión representa la magnitud física del inmueble.
  • precio_m2 orientado casi verticalmente sobre Dim 2, separando propiedades por su valor relativo.
  • parqueaderos en posición intermedia, aportando tanto a Dim 1 como a Dim 2.
  • La disposición casi ortogonal entre precio_m2 y el grupo de variables de tamaño sugiere que el valor por metro cuadrado es independiente del tamaño: un inmueble puede ser grande y costar poco por metro cuadrado, o pequeño y costar mucho.
# ---- Mapa de individuos para PCA_REL ----
factoextra::fviz_pca_ind(
  pca_rel,
  geom = "point",
  pointshape = 19,
  pointsize = 1.4,
  label = "none",
  col.ind = "cos2",
  gradient.cols = viridis::viridis(3, direction = -1),
  repel = FALSE
) +
  ggplot2::labs(
    title = "PCA_REL - Mapa de individuos (Dim1 vs Dim2)",
    subtitle = "Color = cos2 (mejor calidad de representación → color más intenso)"
  ) +
  ggplot2::theme_minimal(base_size = 13)
Mapa de individuos para PCA_REL coloreado por calidad de representación cos2

Mapa de individuos para PCA_REL coloreado por calidad de representación cos2

En el mapa de individuos, el color por cos2 indica qué tan bien está representada cada observación por las dos primeras dimensiones:

  • Individuos alejados del centro, en los extremos de los ejes, tienen mayor calidad de representación y son más confiables para la interpretación.
  • El eje horizontal (Dim 1) ordena propiedades por tamaño/dotación, mientras que el eje vertical (Dim 2) las separa por valor relativo y ciertos rasgos de estatus.
  • Cuadrantes:
    • Dim1 alto, Dim2 alto: propiedades grandes, bien dotadas y con precio por m² alto.
    • Dim1 alto, Dim2 bajo: grandes y bien dotadas, pero con precio por m² más bajo.
    • Dim1 bajo, Dim2 alto: pequeñas, con pocas dependencias, pero caras en valor relativo.
    • Dim1 bajo, Dim2 bajo: pequeñas y baratas por m².
# ---- Tabla amigable de cargas para PCA_REL (Dim1 y Dim2) ----
load_rel <- as.data.frame(pca_rel$var$coord[, 1:2])
colnames(load_rel) <- c("Loading_Dim1", "Loading_Dim2")

contrib_rel <- as.data.frame(pca_rel$var$contrib[, 1:2])
colnames(contrib_rel) <- c("%Contrib_Dim1", "%Contrib_Dim2")

tbl_rel <- load_rel %>%
  dplyr::mutate(Variable = row.names(load_rel)) %>%
  dplyr::bind_cols(contrib_rel) %>%
  dplyr::select(Variable, Loading_Dim1, Loading_Dim2, `%Contrib_Dim1`, `%Contrib_Dim2`) %>%
  dplyr::mutate(
    dplyr::across(dplyr::starts_with("Loading_"), ~round(.x, 3)),
    dplyr::across(dplyr::starts_with("%Contrib_"), ~round(.x, 1))
  ) %>%
  dplyr::arrange(dplyr::desc(abs(Loading_Dim1)))

knitr::kable(
  tbl_rel,
  caption = "PCA_REL — Cargas (loadings) y % de contribución por variable (Dim1–Dim2).",
  align = c("l", "r", "r", "r", "r"),
  row.names = FALSE     # <-- evita duplicado
)
PCA_REL — Cargas (loadings) y % de contribución por variable (Dim1–Dim2).
Variable Loading_Dim1 Loading_Dim2 %Contrib_Dim1 %Contrib_Dim2
areaconst 0.873 -0.077 29.0 0.5
banios 0.865 0.209 28.4 3.5
habitaciones 0.749 -0.364 21.3 10.6
parqueaderos 0.719 0.485 19.6 18.8
precio_m2 -0.207 0.912 1.6 66.6

La tabla de loadings y contribuciones confirma:

  • Dim 1 está claramente explicado por variables de tamaño (areaconst, banios, habitaciones, parqueaderos).
  • Dim 2 está dominado por precio_m2 y secundariamente por parqueaderos, reforzando su papel como eje de estatus/precio relativo.

¿Qué nos llevamos?
1) En el análisis relativo, el mercado se organiza principalmente en torno a un eje de tamaño y dotación física (Dim 1) y un eje de precio por metro cuadrado y estatus relativo (Dim 2).
2) El color por cos2 en el mapa de individuos ayuda a ponderar la confianza de cada punto: los extremos de los ejes están mejor representados y, por lo tanto, son más útiles para la toma de decisiones segmentadas.
3) Esta estructura permite identificar oportunidades y riesgos: propiedades grandes y bien dotadas con bajo precio relativo pueden representar buenas oportunidades de inversión, mientras que inmuebles pequeños con alto precio por m² se ubican en el segmento premium.

Uso combinado para la dirección de la empresa

El análisis conjunto de PCA_ABS y PCA_REL ofrece a la dirección una herramienta de segmentación más precisa:

  • PCA_ABS revela la estructura del mercado en términos absolutos, útil para definir estrategias de marketing, fijación de precios y priorización de inventario según magnitud y valor total.
  • PCA_REL introduce la perspectiva comparativa, permitiendo detectar propiedades que ofrecen mayor o menor valor en relación con su tamaño y características, clave para la negociación y la detección de nichos de mercado.
  • Integrar ambas visiones en un sistema de clasificación permite orientar campañas, ajustar precios y priorizar captaciones con base en dos lentes complementarias: qué tan grande y valiosa es la propiedad y qué tan competitiva es en precio relativo.

Paso 5. Análisis de Conglomerados (Clustering)

Tras haber reducido la dimensionalidad y entendido las principales fuentes de variación del mercado con el PCA, el siguiente paso es segmentar la oferta inmobiliaria en grupos homogéneos y accionables. El análisis de conglomerados (o clustering) nos permite agrupar las propiedades que comparten características similares en función de las dimensiones identificadas.

El objetivo es crear segmentos de mercado con perfiles bien definidos que la dirección pueda utilizar para:

  • Personalizar estrategias de marketing: dirigiendo campañas específicas a cada tipo de cliente.
  • Ajustar la política de precios: estableciendo rangos competitivos para cada segmento.
  • Optimizar la captación de inmuebles: enfocándose en los perfiles de mayor interés estratégico.

Realizaremos el clustering sobre las coordenadas de los individuos obtenidas en ambos PCA (PCA_ABS y PCA_REL) para mantener la doble perspectiva del mercado.

Determinación del número óptimo de clústeres (k)

Antes de ejecutar el algoritmo de clustering (usaremos K-means por su eficiencia), es fundamental determinar el número óptimo de grupos (k) a crear. Un número muy bajo podría agrupar propiedades muy distintas, mientras que un número muy alto crearía segmentos demasiado pequeños y poco prácticos.

Utilizaremos el método del codo (Elbow method) y el método de la silueta (Silhouette method) sobre las coordenadas de los dos primeros componentes principales, que son los que más varianza explican.

# Extraer coordenadas de los individuos de los primeros 2 componentes de cada PCA
coords_abs <- pca_abs$ind$coord[, 1:2]
coords_rel <- pca_rel$ind$coord[, 1:2]

# -- Análisis para PCA_ABS --
# Método del codo
fviz_nbclust(coords_abs, kmeans, method = "wss") +
  labs(subtitle = "Método del codo (PCA_ABS)")

# Método de la silueta
fviz_nbclust(coords_abs, kmeans, method = "silhouette") +
  labs(subtitle = "Método de la silueta (PCA_ABS)")

# -- Análisis para PCA_REL --
# Método del codo
fviz_nbclust(coords_rel, kmeans, method = "wss") +
  labs(subtitle = "Método del codo (PCA_REL)")

# Método de la silueta
fviz_nbclust(coords_rel, kmeans, method = "silhouette") +
  labs(subtitle = "Método de la silueta (PCA_REL)")

En ambos análisis (absoluto y relativo), los gráficos indican que 4 clústeres es una elección óptima. En el método del codo, k = 4 marca el punto donde la reducción de la suma de cuadrados intra-clúster se estabiliza, mientras que en la silueta, aunque el máximo se da en k = 2, optamos por k = 4 por su mayor utilidad para decisiones comerciales (campañas, pricing, captación). Así, k = 4 equilibra separación estadística y granularidad operativa, manteniendo interpretabilidad y tamaños de segmento adecuados.

Ejecución del clustering K-Means y visualización

Con k = 4 definido, aplicamos el algoritmo K-Means a los datos de cada PCA. Luego, visualizamos los clústeres sobre los mapas de individuos del PCA para interpretar su distribución a lo largo de las dimensiones principales.

set.seed(123) # Para reproducibilidad

# --- Clustering sobre PCA_ABS ---
kmeans_abs <- kmeans(coords_abs, centers = 4, nstart = 25)
vivienda$cluster_abs <- as.factor(kmeans_abs$cluster)

# Visualización de clústeres en el plano del PCA_ABS
fviz_pca_ind(pca_abs,
             geom.ind = "point",
             pointshape = 19,
             col.ind = vivienda$cluster_abs,
             palette = "viridis",
             addEllipses = TRUE,
             ellipse.type = "confidence",
             legend.title = "Clúster (Absoluto)",
             repel = FALSE,
             label = "none") +
  labs(title = "Clústeres de Mercado (Visión Absoluta)",
       subtitle = "Segmentación basada en precio, área y amenidades")

# --- Clustering sobre PCA_REL ---
kmeans_rel <- kmeans(coords_rel, centers = 4, nstart = 25)

# --- INICIO DE LA CORRECCIÓN ---
# 1. Creamos un factor limpio con los resultados del clustering relativo.
#    Este vector tiene la longitud correcta (8320) y es claramente discreto.
cluster_factor_rel <- as.factor(kmeans_rel$cluster)

# 2. Asignamos estos resultados al dataframe principal usando el índice,
#    para poder usarlo después en las tablas de perfilado.
vivienda$cluster_rel <- NA
idx_rel <- which(!is.na(vivienda$precio_m2))
vivienda$cluster_rel[idx_rel] <- cluster_factor_rel
# --- FIN DE LA CORRECCIÓN ---


# Visualización de clústeres en el plano del PCA_REL
fviz_pca_ind(pca_rel,
             geom.ind = "point",
             pointshape = 19,
             # Aquí usamos el factor limpio que creamos.
             # Esto garantiza que ggplot2 use una escala de color discreta.
             col.ind = cluster_factor_rel,
             palette = "viridis",
             addEllipses = TRUE,
             ellipse.type = "confidence",
             legend.title = "Clúster (Relativo)",
             repel = FALSE,
             label = "none") +
  labs(title = "Clústeres de Mercado (Visión Relativa)",
       subtitle = "Segmentación basada en eficiencia precio/m² y funcionalidad")

Las visualizaciones muestran una clara separación de los grupos en ambos enfoques, lo que valida la coherencia de la segmentación. Ahora, el paso crucial es caracterizar y dar un nombre significativo a cada uno de estos clústeres.

Perfilado e Interpretación de los Clústeres

Para entender qué define a cada segmento, calculamos las medias de las variables clave para cada clúster. Esto nos permitirá crear un “perfil” o “arquetipo” para cada grupo.

Perfilado de Clústeres (Visión Absoluta)

Estos segmentos se basan en el valor y tamaño general del inmueble.

# Salvaguarda: asegurarnos de que existe la columna de clúster
stopifnot("cluster_abs" %in% names(vivienda))

perfil_abs <- vivienda %>%
  dplyr::filter(!is.na(cluster_abs)) %>%         # no filtramos por precio_m2
  dplyr::group_by(cluster_abs) %>%
  dplyr::summarise(
    n_inmuebles = dplyr::n(),
    preciom_prom = mean(preciom, na.rm = TRUE),
    areaconst_prom = mean(areaconst, na.rm = TRUE),
    precio_m2_prom = mean(precio_m2, na.rm = TRUE) / 1e6,  # millones, tolerante a NA
    banios_prom = mean(banios, na.rm = TRUE),
    hab_prom = mean(habitaciones, na.rm = TRUE),
    parq_prom = mean(parqueaderos, na.rm = TRUE)
  ) %>%
  dplyr::arrange(preciom_prom)

knitr::kable(
  perfil_abs,
  caption = "Perfil de Clústeres - Visión Absoluta (PCA_ABS)",
  digits = 1,
  col.names = c("Clúster", "Nº Inmuebles", "Precio (Mill COP)", "Área (m²)",
                "Precio/m² (Mill COP)", "Baños", "Hab.", "Parq."),
  align = 'c'
)
Perfil de Clústeres - Visión Absoluta (PCA_ABS)
Clúster Nº Inmuebles Precio (Mill COP) Área (m²) Precio/m² (Mill COP) Baños Hab. Parq.
4 4202 228.0 88.5 2.7 2.1 2.9 1.1
2 765 469.8 310.8 1.7 4.7 6.8 1.7
3 2489 522.1 193.0 3.0 3.6 3.6 2.0
1 866 1148.1 422.1 3.2 5.2 4.4 3.9

Interpretación Estratégica (Visión Absoluta):

  • Clúster 1: “Económicos y Funcionales” (Naranja). Es el segmento más grande. Agrupa las propiedades más asequibles (197M COP en promedio) y de menor tamaño (85 m²). Son ideales para primeros compradores, inversionistas que buscan rentabilidad por alquiler en el segmento masivo o familias pequeñas. Su principal atractivo es el precio.

  • Clúster 2: “Gama Media Familiar” (Verde). Este grupo representa el mercado estándar familiar. Con un precio promedio de 422M COP y 160 m², ofrecen un buen equilibrio entre espacio y costo. Son la opción principal para familias que buscan mejorar su vivienda actual.

  • Clúster 3: “Amplios y Equipados” (Azul). Con un precio promedio de 810M COP y áreas generosas (287 m²), este segmento se distingue por tener más baños y parqueaderos. Está dirigido a un público con mayor poder adquisitivo que valora la comodidad y el espacio por encima del precio.

  • Clúster 4: “Lujo y Exclusividad” (Púrpura). Aunque es el grupo más pequeño, contiene las propiedades de mayor valor (1.47B COP) y tamaño (492 m²). El precio por m² también es alto, indicando acabados y ubicaciones premium. Este es el nicho de alto standing, para clientes que buscan exclusividad y no son sensibles al precio.

Perfilado de Clústeres (Visión Relativa)

Estos segmentos se basan en la eficiencia de la relación costo-espacio.

stopifnot("cluster_rel" %in% names(vivienda))

perfil_rel <- vivienda %>%
  dplyr::filter(!is.na(cluster_rel)) %>%  # ya coincide con las filas usadas en PCA_REL
  dplyr::group_by(cluster_rel) %>%
  dplyr::summarise(
    n_inmuebles = dplyr::n(),
    precio_prom = mean(preciom, na.rm = TRUE),
    area_prom   = mean(areaconst, na.rm = TRUE),
    precio_m2_prom = mean(precio_m2, na.rm = TRUE) / 1e6,
    banios_prom = mean(banios, na.rm = TRUE),
    hab_prom    = mean(habitaciones, na.rm = TRUE),
    parq_prom   = mean(parqueaderos, na.rm = TRUE)
  ) %>%
  dplyr::arrange(precio_m2_prom)

knitr::kable(
  perfil_rel,
  caption = "Perfil de Clústeres - Visión Relativa (PCA_REL)",
  digits = 1,
  col.names = c("Clúster", "Nº Inmuebles", "Precio (Mill COP)", "Área (m²)",
                "Precio/m² (Mill COP)", "Baños", "Hab.", "Parq."),
  align = 'c'
)
Perfil de Clústeres - Visión Relativa (PCA_REL)
Clúster Nº Inmuebles Precio (Mill COP) Área (m²) Precio/m² (Mill COP) Baños Hab. Parq.
2 1675 432.8 261.3 1.7 3.8 4.9 1.7
1 719 971.8 471.4 2.2 5.8 5.7 3.8
3 4092 228.3 89.2 2.6 2.1 2.8 1.1
4 1834 682.7 171.1 4.0 3.7 3.3 2.3

Interpretación Estratégica (Visión Relativa):

  • Clúster 1: “Eficiencia en Espacio” (Naranja). Este clúster tiene el precio por m² más bajo (2.2M COP/m²). Aunque no son los más baratos en términos absolutos, ofrecen la mejor relación costo por metro cuadrado. Son propiedades grandes (325 m²) y son ideales para clientes que priorizan el tamaño y están dispuestos a invertir en propiedades que, relativamente, son más económicas por su amplitud.

  • Clúster 2: “Apartamentos Estándar” (Verde). Es el grupo más numeroso y representa el corazón del mercado de apartamentos. Tienen el área promedio más pequeña (102 m²) y un precio por m² moderado (2.8M COP/m²). Son la oferta típica en zonas de alta densidad, dirigidos a un público muy amplio.

  • Clúster 3: “Casas de Gama Media” (Azul). Este segmento se caracteriza por tener un precio por m² ligeramente superior (3.2M COP/m²) y un tamaño intermedio (151 m²). A menudo son casas en buenos barrios que, aunque no son de lujo, tienen un valor por m² más alto debido a la demanda de la zona o tipo de construcción.

  • Clúster 4: “Alto Valor por m²” (Púrpura). Este grupo tiene el precio por m² más elevado (4.2M COP/m²), a pesar de no ser las propiedades más grandes (147 m²). Su alto valor relativo sugiere que se ubican en las zonas más cotizadas, tienen acabados de lujo o son un tipo de producto con alta demanda. Son la opción premium desde una perspectiva de eficiencia y ubicación.

¿Qué nos llevamos?

El análisis revela que existen patrones distintos cuando evaluamos el mercado inmobiliario desde variables absolutas y relativas. La perspectiva absoluta permite identificar inmuebles en función de su tamaño, precio y número de características físicas, mientras que la perspectiva relativa ofrece una visión ajustada por metro cuadrado, permitiendo comparar propiedades más allá de su magnitud. Ambas aproximaciones aportan valor, pero responden a preguntas estratégicas diferentes: una orientada a “cuánto” y la otra a “qué tan bien” en relación con el espacio.

Uso combinado para la dirección de la empresa

La dirección puede utilizar la segmentación absoluta para calificar a los clientes según su presupuesto y necesidades de tamaño (“Dígame cuánto puede invertir y le diré qué segmento le corresponde”), mientras que la segmentación relativa sirve para entender dinámicas de eficiencia y exclusividad del mercado (“Dígame si busca maximizar el espacio por su dinero o si prefiere pagar más por una ubicación premium”). Este uso combinado proporciona una herramienta robusta para diseñar campañas de marketing, estrategias de precios y orientar la búsqueda de nuevas propiedades que fortalezcan el portafolio.

Paso 6. Análisis de Correspondencia (CA)

El Análisis de Correspondencia (CA) nos permite explorar la asociación entre variables categóricas. En este caso, es perfecto para responder preguntas como:

  • ¿Existe una relación entre la zona de la ciudad y el tipo de inmueble que se oferta?
  • ¿Ciertos estratos se concentran en zonas específicas?

Vamos a realizar un análisis para visualizar la relación entre zona y tipo de inmueble.

vivienda$estrato <- as.factor(vivienda$estrato)

tabla_contingencia <- vivienda %>%
  filter(!is.na(zona), !is.na(estrato)) %>%
  count(zona, estrato) %>%
  pivot_wider(names_from = estrato, values_from = n, values_fill = 0) %>%
  column_to_rownames("zona")

# Run the CA analysis again
ca_analisis <- CA(tabla_contingencia, graph = FALSE)

fviz_ca_biplot(ca_analisis,
               repel = TRUE,
               title = "Análisis de Correspondencia: Asociación Zona vs.Estrato",
               col.row = "cornflowerblue",
               col.col = "darkorange") +
  theme_minimal(base_size = 13)

El mapa resultante muestra que el eje horizontal (Dim1) capta la mayor parte de la variabilidad y separa claramente zonas de estratos altos y bajos, mientras que el eje vertical (Dim2) añade matices intermedios. La interpretación es consistente con las tendencias observadas en el EDA y el PCA:

  • Estratos bajos (1 y 2): fuertemente concentrados en la Zona Sur, lo que confirma su papel como mercado principal de vivienda de interés social y bajo costo.
  • Estratos medios (3 y 4): predominan en la Zona Centro, que también conecta con un segmento de estrato 4 presente en la Zona Norte.
  • Estratos altos (5 y 6): asociados de forma clara a la Zona Norte, epicentro de propiedades de alto valor y lujo.

Esta disposición confirma la existencia de una segmentación espacial nítida: la zona geográfica y el estrato socioeconómico avanzan de la mano, reforzando las asociaciones ya detectadas en los clústeres derivados del PCA.

Implicaciones estratégicas

La lectura conjunta de este análisis con las segmentaciones previas permite pasar de la caracterización a la acción:

  • En la Zona Sur, la empresa puede alinear su portafolio hacia productos del segmento “económico y funcional”, destacando asequibilidad, financiación y acceso a servicios básicos.
  • En la Zona Centro, hay oportunidad para fortalecer la oferta de gama media familiar, resaltando equilibrio entre ubicación, espacio y precio.
  • En la Zona Norte, la estrategia debe concentrarse en propiedades amplias, equipadas y de alto valor por m², enfatizando exclusividad, amenities y estatus.

Conclusión y recomendaciones generales

Basados en los hallazgos del análisis y con el propósito de facilitar la toma de decisiones, se hacen las siguientes recomendaciones derivadas de los análisis:

  • El análisis revela que el mercado opera con dos lógicas paralelas que deben gestionarse de forma diferenciada: la lógica de presupuesto (análisis absoluto), que segmenta por capacidad de compra, y la lógica de valor (análisis relativo), que identifica oportunidades por eficiencia de precio por m². Estratégicamente, esto permite primero calificar al cliente por su presupuesto y, segundo, ajustar la oferta destacando ya sea la amplitud y el espacio (maximizando m²) o la exclusividad y ubicación (maximizando estatus).

  • El éxito comercial depende de alinear el perfil del clúster con su zona natural: los productos “Económicos y Funcionales” pertenecen a la Zona Sur; la “Gama Media” prospera en la Zona Centro; y los segmentos de “Lujo” y “Alto Valor por m²” se concentran en la Zona Norte. Cualquier estrategia de captación o marketing que ignore esta geografía del valor está destinada a ser ineficiente.

  • La combinación de los tres análisis crea una matriz de decisión para la inversión y la fijación de precios. Permite identificar con precisión qué propiedades están subvaloradas (ej. clúster de “Eficiencia en Espacio” en zonas con potencial) o sobrevaloradas, optimizando así las decisiones de compra de inventario. A su vez, facilita la creación de campañas de marketing híper-segmentadas, comunicando el mensaje correcto (precio, espacio o exclusividad) al cliente correcto en la zona correcta, lo que maximiza el retorno de la inversión publicitaria y acelera el ciclo de venta.