Introducción

La toma de decisiones estratégicas en el sector inmobiliario requiere un análisis profundo y multidimensional del mercado de vivienda urbana. Las características estructurales de los inmuebles, su ubicación geográfica y su contexto socioeconómico influyen directamente en la formación de precios y en la dinámica de la oferta y la demanda.

En este estudio se realiza un análisis integral de la oferta inmobiliaria urbana a partir de una base de datos que contiene información detallada de viviendas disponibles en una gran ciudad. Mediante el uso de técnicas estadísticas multivariadas, se busca identificar patrones relevantes, segmentar la oferta inmobiliaria y explorar relaciones entre variables clave que sirvan de apoyo a la toma de decisiones estratégicas.

Descripción de la base de datos

La base de datos utilizada corresponde a un conjunto de información recolectada a partir de anuncios inmobiliarios urbanos. Contiene variables de tipo estructural, socioeconómico y geográfico, tales como el precio, el área construida, el estrato, el número de habitaciones, baños y parqueaderos, así como variables categóricas relacionadas con la ubicación y el tipo de vivienda.

La base de datos utilizada corresponde al conjunto de datos vivienda, el cual contiene información estructural, socioeconómica y geográfica de viviendas urbanas. Dicho conjunto de datos fue cargado previamente en el entorno de trabajo como objeto R y utilizado como base para los análisis estadísticos desarrollados en este estudio.

# Cargar librerías necesarias
library(paqueteMODELOS)
library(tidyverse)
library(FactoMineR)
library(factoextra)
library(DT)


# Carga del conjunto de datos vivienda desde archivo .rda
url <- "https://github.com/centromagis/paqueteMODELOS/raw/master/data/vivienda.rda"
destino <- tempfile(fileext = ".rda")

# Cargar los datos
data("vivienda")

A continuación, se presenta una exploración inicial de la estructura del conjunto de datos.

# Verificar estructura del dataset
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   id = col_double(),
##   ..   zona = col_character(),
##   ..   piso = col_character(),
##   ..   estrato = col_double(),
##   ..   preciom = col_double(),
##   ..   areaconst = col_double(),
##   ..   parqueaderos = col_double(),
##   ..   banios = col_double(),
##   ..   habitaciones = col_double(),
##   ..   tipo = col_character(),
##   ..   barrio = col_character(),
##   ..   longitud = col_double(),
##   ..   latitud = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>
# Resumen general
summary(vivienda)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
##  Median :4160   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4160                                         Mean   :4.634  
##  3rd Qu.:6240                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :6.000  
##  NA's   :3                                            NA's   :3      
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
##  NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.605                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##  NA's   :3                                              NA's   :3       
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498  
##  NA's   :3

Exploración de Datos

Este estudio analiza 8,322 registros de viviendas. A continuación, se presenta una muestra de los datos y su distribución básica.

datatable(head(vivienda, 100), options = list(pageLength = 5)) # Tabla interactiva

Limpieza de datos

Para asegurar que los modelos multivariados sean precisos, realizamos un tratamiento de valores ausentes (NAs) y analizamos la relación entre las variables numéricas.

# 1. Eliminamos filas con NA para asegurar que los algoritmos funcionen
vivienda_limpia <- na.omit(vivienda)

# 2. Seleccionamos solo variables numéricas para el PCA
vivienda_num <- vivienda_limpia %>% 
  select(preciom, areaconst, parqueaderos, banios, habitaciones, estrato)

# Confirmamos cuántos datos quedaron después de limpiar
nrow(vivienda_limpia)
## [1] 4808

Tras un proceso de limpieza que incluyó la eliminación de valores ausentes, se trabajó con una muestra de 4,808 registros. El resumen estadístico inicial mostró una gran variabilidad en los precios, con una media de 433.9 millones de pesos.

Sustento de Variabilidad y Correlación

library(patchwork) # Para juntar gráficos
library(GGally)

# Gráfico A: Distribución de Precios
p1 <- ggplot(vivienda_limpia, aes(x = preciom)) +
  geom_histogram(fill = "steelblue", color = "white", bins = 30) +
  labs(title = "Distribución de Precios", x = "Precio (Millones)", y = "Frecuencia") +
  theme_minimal()

# Gráfico B: Matriz de Correlación
p2 <- ggcorr(vivienda_num, method = c("everything", "pearson"), 
             label = TRUE, label_size = 3, low = "indianred", mid = "white", high = "steelblue") +
  labs(title = "Matriz de Correlación")

# Unir gráficos para una presentación limpia
p1 + p2

A. Distribución de Precios (Histograma)

  • Sesgo de la Oferta: El gráfico muestra una asimetría positiva (hacia la derecha). La mayor concentración de viviendas se encuentra en el rango de 200 a 500 millones de pesos.
  • Valores Atípicos (Outliers): Se observa una “cola” que se extiende hasta los 2,000 millones. Estos registros representan el segmento de lujo de la ciudad, los cuales, aunque son pocos en cantidad, tienen un impacto significativo en el promedio general de precios.
  • Estado de la Base: La distribución es continua y no presenta “huecos” extraños, lo que confirma que la limpieza de datos (na.omit) no eliminó rangos de precio enteros, manteniendo la representatividad del mercado.

B. Matriz de Correlación - Relación Precio-Área (\(0.7\)): Existe una correlación fuerte y positiva entre el precio y el área construida. Esto confirma que el tamaño es el principal motor del valor en este mercado. - Multicolinealidad: Se observa que variables como areaconst, banios y habitaciones tienen correlaciones entre sí superiores a \(0.6\).Importancia técnica: Esta interdependencia justifica plenamente el uso de un PCA (Análisis de Componentes Principales), ya que el PCA podrá resumir estas variables relacionadas en un solo factor de “amplitud del inmueble”.El - Factor Estrato (\(0.6\)): El estrato tiene una correlación moderada-alta con el precio, lo que sugiere que la ubicación socioeconómica es el segundo factor más importante después de las características físicas de la vivienda.

Ejecución y Visualización del PCA

# Ejecutar el PCA con datos escalados
res_pca <- PCA(vivienda_num, scale.unit = TRUE, graph = FALSE)

# Visualizar el círculo de correlación
# Usamos print() para asegurar que aparezca en el informe
grafico_var <- fviz_pca_var(res_pca, col.var = "contrib", 
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             repel = TRUE)

print(grafico_var)

Análisis de Componentes Principales (PCA) A partir del círculo de correlación obtenido, se pueden extraer las siguientes conclusiones estratégicas sobre la oferta inmobiliaria:

Relación entre variables (Correlación): Existe una correlación positiva muy fuerte entre el precio (preciom), el área construida (areaconst), el número de baños y los parqueaderos. Esto indica que el mercado valora estas características de forma conjunta; a medida que aumenta el espacio físico, el valor comercial se incrementa proporcionalmente.

Dimensión 1 (59.3% de la varianza): Este eje representa la “Capacidad y Magnitud” de la vivienda. Dado que casi todas las variables numéricas apuntan hacia la derecha, este componente nos permite diferenciar entre viviendas pequeñas/básicas (izquierda) y viviendas amplias/completas (derecha).

Dimensión 2 (19.7% de la varianza): Este eje revela una dinámica interesante de segmentación. Se observa que el estrato y el número de habitaciones tienen direcciones opuestas en este eje. Esto sugiere que existen dos tipos de oferta diferenciada:

Viviendas de estratos altos que priorizan el lujo y la ubicación sobre la cantidad de habitaciones (posiblemente apartamentos premium).

Viviendas con un alto número de habitaciones, pero que no necesariamente pertenecen a los estratos más altos (posiblemente casas familiares más antiguas o en zonas de menor densidad de valor).

Aporte del modelo: En conjunto, los dos primeros componentes explican el 79% de la variabilidad total de los datos, lo que significa que este análisis es altamente confiable para entender el comportamiento del mercado.

Análisis de Conglomerados (Clustering)

# 1. Escalar datos
vivienda_esc <- scale(vivienda_num)

# 2. Agrupar en 3 segmentos
set.seed(123)
res_km <- kmeans(vivienda_esc, centers = 3, nstart = 25)

# 3. Graficar SIN etiquetas de texto (geom = "point")
fviz_cluster(res_km, data = vivienda_num,
             geom = "point",      
             palette = "Set2",
             ellipse.type = "convex",
             fill = "white",      # Hace las áreas de los clusters más sutiles
             ggtheme = theme_minimal()) +
  labs(title = "Segmentación de la Oferta Inmobiliaria")

Análisis de Conglomerados (Segmentación del Mercado) La segmentación mediante el algoritmo K-means ha permitido identificar tres nichos de mercado claramente diferenciados, los cuales se distribuyen a lo largo de las dimensiones de capacidad y estatus identificadas previamente en el PCA:

  • Cluster 3 (Color Azul - Segmento de Entrada): Este grupo se ubica a la izquierda del gráfico (valores negativos en Dim1). Representa la oferta de viviendas más pequeñas y económicas. Son propiedades con menor área construida, pocos parqueaderos y situadas en estratos medios (3 y 4). Es el segmento con mayor volumen de oferta, ideal para compradores de primera vivienda o inversores de renta baja.

  • Cluster 1 (Color Verde - Segmento Familiar/Medio): Ubicado en la zona central. Representa viviendas con un equilibrio entre precio y espacio. Suelen tener un número moderado de habitaciones (3 a 4) y baños, situándose mayoritariamente en estrato 4 y 5. Es el “corazón” del mercado inmobiliario urbano, con características estandarizadas.

  • Cluster 2 (Color Naranja - Segmento Premium/Lujo): Se ubica a la derecha (altos valores en Dim1). Agrupa las propiedades de mayor valor comercial, grandes extensiones de área construida y altos niveles de equipamiento (más de 3 baños y varios parqueaderos). Este segmento está fuertemente asociado a los estratos 5 y 6. Las empresas inmobiliarias deben enfocar aquí sus estrategias de marketing personalizado y servicios de exclusividad.

Análisis de Correspondencia (Zonas y Tipos)

library(ggrepel)

# 1. Crear la tabla de contingencia
tabla_cat <- table(vivienda_limpia$zona, vivienda_limpia$tipo)

# 2. Ejecutar el Análisis de Correspondencia
res_ca <- FactoMineR::CA(tabla_cat, graph = FALSE)

# 3. Extraer coordenadas
filas <- as.data.frame(res_ca$row$coord)
columnas <- as.data.frame(res_ca$col$coord)

# 4. Ajuste de dimensiones para visualización consistente
if(ncol(filas) < 2) { filas$D2 <- 0 }
if(ncol(columnas) < 2) { columnas$D2 <- 0 }

# Renombramos para asegurar consistencia
colnames(filas)[1:2] <- c("D1", "D2")
colnames(columnas)[1:2] <- c("D1", "D2")

# 5. Graficar 
ggplot() +
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray80") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "gray80") +
  
  # Zonas (Azul) 
  geom_point(data = filas, aes(x = D1, y = D2), color = "steelblue", size = 3) +
  geom_text_repel(data = filas, aes(x = D1, y = D2, label = rownames(filas)), 
                  color = "steelblue", fontface = "bold", size = 3.8,
                  nudge_y = 0.01,   
                  direction = "y",  # Prioriza el movimiento vertical
                  max.overlaps = Inf) +
  
  # Tipos (Rojo) 
  geom_point(data = columnas, aes(x = D1, y = D2), color = "indianred", size = 3) +
  geom_text_repel(data = columnas, aes(x = D1, y = D2, label = rownames(columnas)), 
                  color = "indianred", fontface = "italic", size = 3.8,
                  nudge_y = -0.01,  
                  direction = "y",  # Prioriza el movimiento vertical
                  max.overlaps = Inf) +
  
  theme_minimal() +
  labs(title = "Relación entre Zona y Tipo de Vivienda",
       x = "Dimensión 1 (Asociación)", y = "") +
    ylim(-0.05, 0.05)

El análisis de correspondencia muestra una segregación geográfica evidente:

Predominio de Casas: Las zonas Centro y Oriente están fuertemente asociadas al tipo Casa. Son sectores tradicionalmente residenciales de baja altura.

Densificación Urbana: Las zonas Sur, Norte y Oeste se agrupan cerca de Apartamento. Esto indica que el crecimiento inmobiliario moderno y vertical se concentra en estos tres polos de la ciudad.

Conclusiones Estratégicas

Enfoque Comercial: Las estrategias de marketing deben diferenciarse; el segmento Premium (Cluster 2) requiere exclusividad, mientras que el segmento de entrada (Cluster 3) debe enfocarse en accesibilidad.

Desarrollo Territorial: Los nuevos proyectos de apartamentos tienen mayor viabilidad en las zonas Norte y Sur, donde el mercado ya muestra una tendencia hacia la densificación.

Valorización: El número de parqueaderos y el área construida siguen siendo los mayores impulsores del precio final en todos los estratos.