1. Introducción y Contexto del Problema

Este informe analiza la oferta inmobiliaria urbana a partir de una base de datos obtenida mediante web scraping en la plataforma OLX. El objetivo es aplicar técnicas de análisis multivariado, reducción de dimensionalidad, segmentación y análisis de correspondencias, que permitan traducir el comportamiento de los datos en información útil para la toma de decisiones comerciales, como: qué variables explican el precio de las propiedades, cómo se agrupan naturalmente las viviendas ofertadas y qué relación existe entre su tipología, ubicación y estrato socioeconómico.

Diccionario de Datos General

Variable Tipo de Dato Definición del Campo Uso Analítico en los Modelos
preciom Numérico Continuo Precio comercial del inmueble (expresado en millones). Variable clave de valor e inversión para PCA y Clustering.
areaconst Numérico Continuo Área total construida de la vivienda en metros cuadrados (\(m^2\)). Dimensión física fundamental para PCA y Clustering.
parqueaderos Numérico Discreto Cantidad de espacios de estacionamiento privados del inmueble. Define la capacidad y comodidades para PCA y Clustering.
banios Numérico Discreto Número de baños completos y medios disponibles. Define el tamaño y capacidad operativa (PCA y Clustering).
habitaciones Numérico Discreto Cantidad de cuartos, alcobas o dormitorios. Define la capacidad de habitabilidad (PCA y Clustering).
zona Categórico Sector o zona cardinal de la ciudad donde se ubica (ej. Norte, Sur). Segmentación macro del mercado urbano. Objetivo del ACM.
tipo Categórico Clasificación arquitectónica de la propiedad (ej. Casa, Apartamento). Segmentación de la tipología constructiva. Objetivo del ACM.
barrio Categórico Nombre específico del barrio o vecindario de ubicación. Segmentación micro y geográfica detallada. Objetivo del ACM.
estrato Categórico (Ordinal) Nivel socioeconómico oficial asignado al predio (generalmente de 1 a 6). Segmentación de poder adquisitivo y entorno. Objetivo del ACM.
piso Categórico Nivel o planta del edificio en el que se encuentra el inmueble. Caracterización de accesibilidad y altura. Objetivo del ACM.
latitud Coordenadas Posición geográfica exacta en el eje Norte-Sur. Fundamental para la georreferenciación y el mapa interactivo.
longitud Coordenadas Posición geográfica exacta en el eje Este-Oeste. Fundamental para la georreferenciación y el mapa interactivo.

2. Marco Teórico y Fundamentos Estadísticos

2.1 Preprocesamiento: Escalamiento de Variables

Los métodos basados en distancias, como el PCA o el k-means, son sensibles a la escala de las variables, si una variable se mide en unidades mucho mayores que las demás (por ejemplo, el precio en millones frente al número de baños), termina dominando el cálculo de la varianza o la distancia euclidiana, aunque no sea necesariamente más relevante desde el punto de vista sustantivo [6][7]. Por esa razón, antes de correr los modelos multivariados es necesario llevar las variables cuantitativas a una escala comparable. Existen varias formas de hacerlo:

  • Estandarización (Z-score): centra la variable en media 0 y varianza 1. \[X'=\frac{X-\mu}{\sigma}\]

  • Normalización (Min-Max): reescala los valores al intervalo [0, 1]. \[X'=\frac{X-\min}{\max-\min}\]

  • Escalado Robusto: usa la mediana y el rango intercuartílico (IQR) en lugar de la media y la desviación estándar, lo que lo hace menos sensible a valores atípicos [8]. \[X'=\frac{X-\text{mediana}}{\text{IQR}}\]

En este trabajo se optó por la estandarización clásica (Z-score), dado que tanto el PCA como el k-means asumen, o al menos funcionan mejor, bajo esta transformación.

2.2 Análisis de Componentes Principales (PCA)

El PCA resume un conjunto de variables correlacionadas en un número menor de componentes no correlacionados, construidos como combinaciones lineales de las variables originales [2]:

\[PC_i = a_{i1}X_1 + a_{i2}X_2 + \dots + a_{ip}X_p\]

Dos elementos son clave para interpretar los resultados. Las cargas (loadings) indican el peso (\(a_{ij}\)) de cada variable original dentro de cada componente, y su signo y magnitud permiten leer qué variables definen cada eje. La calidad de representación (\(\cos^2\)), por su parte, mide qué tan bien queda proyectado un individuo o una variable en el plano factorial, valores cercanos a 1 indican una representación fiel, mientras que valores bajos sugieren que se necesitarían más dimensiones para capturar bien esa observación [1][2].

2.3 Análisis de Conglomerados (Clustering)

El clustering agrupa observaciones de modo que los elementos dentro de un mismo grupo sean más parecidos entre sí que con los de otros grupos [4]. Existen dos grandes familias de métodos:

  • Métodos No Jerárquicos (K-means): parten de un número fijo de grupos \(K\), definido de antemano, y asignan cada observación al centroide más cercano hasta que la partición converge [3].
  • Métodos Jerárquicos (Ward): construyen sucesivas fusiones, uniendo en cada paso el par de grupos cuya unión genera el menor incremento posible en la varianza intra-clúster [4].

En este informe se trabajó con K-means, ya que el tamaño de la base (más de 8.000 registros) hace poco práctico construir y visualizar un dendrograma jerárquico completo.

2.4 Análisis de Correspondencia Múltiple (ACM)

Mientras el PCA trabaja con variables cuantitativas, el ACM es su equivalente para variables categóricas, a partir de las tablas de contingencia entre ellas, construye un espacio factorial de baja dimensión en el que las categorías que tienden a presentarse juntas quedan próximas entre sí [1][2]. En este trabajo se usó para explorar la relación entre el tipo de vivienda, la zona y el estrato socioeconómico.


3. Exploración y Preparación de Datos

Antes de ejecutar los modelos multivariados, se realizó un preprocesamiento e imputación de los datos de la base de datos de vivienda. Este procedimiento abarcó la imputación de los valores faltantes o NA, el filtrado de variables no explicativas y, finalmente, la partición de las matrices de trabajo según su naturaleza cuantitativa o cualitativa.

3.1 Diagnóstico y Tratamiento de Datos Faltantes (Imputación Avanzada)

Diagnóstico Inicial de Valores Nulos (NA):

tabla_nulos <- data.frame(
  Variable = names(colSums(is.na(vivienda))),
  Valores_Nulos = colSums(is.na(vivienda)),
  Porcentaje_Faltante = paste0(round((colSums(is.na(vivienda)) / nrow(vivienda)) * 100, 2), "%")
) %>% filter(Valores_Nulos > 0) %>% arrange(desc(Valores_Nulos))

knitr::kable(tabla_nulos, caption = "Cuantificación de Datos Faltantes (NA) por Variable", row.names = FALSE)
Cuantificación de Datos Faltantes (NA) por Variable
Variable Valores_Nulos Porcentaje_Faltante
piso 2638 31.7%
parqueaderos 1605 19.29%
id 3 0.04%
zona 3 0.04%
estrato 3 0.04%
areaconst 3 0.04%
banios 3 0.04%
habitaciones 3 0.04%
tipo 3 0.04%
barrio 3 0.04%
longitud 3 0.04%
latitud 3 0.04%
preciom 2 0.02%

La tabla anterior muestra que varias variables tienen datos faltantes, en particular piso. Como tanto el PCA como el K-means requieren matrices completas, había dos alternativas: eliminar las filas incompletas o imputarlas. Se descartó la primera, porque al tratarse de un porcentaje considerable de registros, eliminarlos habría reducido la muestra y probablemente introducido sesgo (las propiedades sin dato de piso bien podrían corresponder a un tipo particular de inmueble, como las casas, que no tienen ese campo).

En su lugar se aplicó Imputación Múltiple por Ecuaciones Encadenadas (MICE) con el método Predictive Mean Matching (PMM), que imputa cada valor faltante tomando un dato real observado en un vecino similar según el resto de variables, en lugar de generar un valor sintético [9]. El procedimiento siguió estos pasos:

  1. Adecuación: conversión de variables categóricas a factor y de piso a numérico.
  2. Exclusión: eliminación de id, que no aporta información y podría introducir ruido al modelo de imputación.
  3. Imputación: PMM busca vecinos cercanos según covariables para asignar valores empíricos reales.
  4. Consolidación: obtención de la base imputada vivienda_clean.
vivienda_pre <- vivienda %>%
  mutate(zona = as.factor(zona), tipo = as.factor(tipo), barrio = as.factor(barrio), piso = as.numeric(piso)) %>%
  select(-id)

set.seed(123)
imputacion_mice <- mice(vivienda_pre, m = 1, maxit = 1, method = "pmm", printFlag = FALSE)
vivienda_clean <- complete(imputacion_mice)

Validación de la Imputación:

resumen_imputacion <- data.frame(
  Métrica = c("Valores nulos restantes", "Filas eliminadas", "Valores reemplazados por PMM"),
  Cantidad = c(sum(is.na(vivienda_clean)), nrow(vivienda) - nrow(vivienda_clean), sum(is.na(vivienda)))
)
knitr::kable(resumen_imputacion, caption = "Resultados Finales del Proceso de Imputación (MICE)", row.names = FALSE)
Resultados Finales del Proceso de Imputación (MICE)
Métrica Cantidad
Valores nulos restantes 0
Filas eliminadas 0
Valores reemplazados por PMM 4275
knitr::kable(head(vivienda_clean, 5), caption = "Top 5 registros de la base de datos limpia (sin nulos)", row.names = FALSE)
Top 5 registros de la base de datos limpia (sin nulos)
zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
Zona Oriente 4 3 250 70 1 3 6 Casa 20 de julio -76.51168 3.43382
Zona Oriente 1 3 320 120 1 2 3 Casa 20 de julio -76.51237 3.43369
Zona Oriente 3 3 350 220 2 2 4 Casa 20 de julio -76.51537 3.43566
Zona Sur 2 4 400 280 3 5 3 Casa 3 de julio -76.54000 3.43500
Zona Norte 1 5 260 90 1 2 3 Apartamento acopi -76.51350 3.45891

Tras la imputación no quedan valores nulos en el dataset de vivienda y, no se eliminó ninguna fila; el tamaño original de la muestra se conserva igual.

3.2 Filtrado y Separación de Datasets

Puesto que el PCA opera sobre variables cuantitativas y el ACM sobre variables categóricas, la base vivienda_clean se dividió en dos subconjuntos: uno cuantitativo (vivienda_cuanti), con las variables numéricas asociadas al precio y a las características físicas de la vivienda, y uno cualitativo (vivienda_cuali), con las variables de tipo, zona, barrio y estrato. El estrato, aunque numérico en la base original, se recodificó como factor porque en la práctica funciona como una categoría de nivel socioeconómico y no como una cantidad continua.

vivienda_cuanti <- vivienda_clean %>% select(preciom, areaconst, parqueaderos, banios, habitaciones)
vivienda_cuali <- vivienda_clean %>% select(tipo, zona, barrio, estrato) %>% mutate(estrato = as.factor(estrato))

knitr::kable(head(vivienda_cuanti, 5), caption = "Top 5 registros del Dataset Cuantitativo", row.names = FALSE)
Top 5 registros del Dataset Cuantitativo
preciom areaconst parqueaderos banios habitaciones
250 70 1 3 6
320 120 1 2 3
350 220 2 2 4
400 280 3 5 3
260 90 1 2 3
knitr::kable(head(vivienda_cuali, 5), caption = "Top 5 registros del Dataset Cualitativo", row.names = FALSE)
Top 5 registros del Dataset Cualitativo
tipo zona barrio estrato
Casa Zona Oriente 20 de julio 3
Casa Zona Oriente 20 de julio 3
Casa Zona Oriente 20 de julio 3
Casa Zona Sur 3 de julio 4
Apartamento Zona Norte acopi 5

4. Análisis de Componentes Principales (PCA)

El PCA se aplicó únicamente sobre vivienda_cuanti, ya que solo admite variables numéricas.

4.1 Validación de Normalidad y Matriz de Correlaciones

set.seed(123)
muestra_shapiro <- vivienda_cuanti %>% sample_n(min(5000, n()))
p_valores_shapiro <- apply(muestra_shapiro, 2, function(x) shapiro.test(x)$p.value)

tabla_shapiro <- data.frame(
  Variable = names(p_valores_shapiro),
  p_valor = formatC(p_valores_shapiro, format = "e", digits = 3)
)

knitr::kable(tabla_shapiro, caption = "Prueba de Normalidad (Shapiro-Wilk)", row.names = FALSE)
Prueba de Normalidad (Shapiro-Wilk)
Variable p_valor
preciom 6.078e-60
areaconst 2.189e-65
parqueaderos 5.247e-70
banios 1.371e-48
habitaciones 1.054e-60
cor_matrix_spearman <- cor(vivienda_cuanti, method = "spearman")

knitr::kable(round(cor_matrix_spearman, 2), caption = "Matriz de Correlación de Spearman", row.names = FALSE)
Matriz de Correlación de Spearman
preciom areaconst parqueaderos banios habitaciones
1.00 0.82 0.73 0.77 0.43
0.82 1.00 0.64 0.77 0.65
0.73 0.64 1.00 0.61 0.34
0.77 0.77 0.61 1.00 0.62
0.43 0.65 0.34 0.62 1.00

La prueba de Shapiro-Wilk rechaza la normalidad en las cinco variables (p < 0.001 en todos los casos), lo cual no es atípico tratándose de variables de precio y área, que suelen estar sesgadas hacia la derecha por la presencia de propiedades de alto valor. Por esta razon, es mas conveniente trabajar con la correlación de Spearman, que no asume normalidad y capta relaciones monótonas, no necesariamente lineales. La matriz resultante muestra correlaciones positivas y fuertes entre el área construida, el precio y el número de baños, lo que sugiere que estas tres variables tienden a moverse juntas dentro del mercado.

4.2 Ejecución del PCA y Evaluación de Salidas

res.pca <- PCA(vivienda_cuanti, scale.unit = TRUE, graph = FALSE)

# 1. Extraer la tabla matemática de las varianzas
tabla_varianza <- as.data.frame(res.pca$eig)
names(tabla_varianza) <- c("Eigenvalor", "% de Varianza", "% Varianza Acumulada")

# 2. Guardar el valor exacto de la segunda dimensión en una variable
var_acumulada_dim2 <- round(tabla_varianza[2, 3], 1)

# 3. Mostrar la tabla elegante en el documento
knitr::kable(round(tabla_varianza, 2), caption = "Tabla de Varianza Explicada (Eigenvalores)", row.names = TRUE)
Tabla de Varianza Explicada (Eigenvalores)
Eigenvalor % de Varianza % Varianza Acumulada
comp 1 3.23 64.54 64.54
comp 2 0.87 17.43 81.96
comp 3 0.37 7.36 89.32
comp 4 0.34 6.83 96.15
comp 5 0.19 3.85 100.00
# 4. Generar los gráficos espaciales
fviz_eig(res.pca, addlabels = TRUE, ylim = c(0, 100), 
         title = "Varianza Explicada por Componente")

fviz_pca_var(res.pca, col.var = "cos2",
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), 
             repel = TRUE, title = "Círculo de Correlaciones - Calidad de Representación")

Las dos primeras dimensiones concentran el 82% de la varianza total, por lo que el plano factorial que forman resulta representativo del comportamiento conjunto de las variables. En el círculo de correlaciones, areaconst, preciom y banios presentan valores altos de \(\cos^2\), es decir, están bien representadas en este plano y contribuyen de forma importante a definir el primer componente. En términos prácticos, esto respalda una lectura razonable del mercado; el valor de una propiedad parece depender sobre todo de su tamaño y de su capacidad, número de baños y habitaciones, más que de atributos secundarios como el número de parqueaderos.


5. Análisis de Conglomerados

Para el clustering se usó vivienda_cuanti estandarizada (vivienda_scaled), de modo que la distancia euclidiana entre observaciones no quede dominada por la variable de mayor escala, en este caso el precio.

Para determinar el número óptimo de grupos (\(K\)), se evaluó la métrica WSS (Within-Cluster Sum of Squares), la cual cuantifica la cohesión interna de los clústeres calculando la suma de las distancias al cuadrado entre cada observación y el centroide de su respectivo grupo. El método del codo (popularizado por Thorndike, 1953) consiste en graficar el WSS para distintos valores de \(K\) e identificar el punto de inflexión (“codo”) donde la reducción marginal de la varianza intra-clúster deja de ser significativa [11].

Para no depender de un solo criterio empírico, este método se complementó con el cálculo del coeficiente de silueta promedio, y ambos gráficos de validación se muestran lado a lado empleando la librería gridExtra [10].

vivienda_scaled <- scale(vivienda_cuanti)

# Validación de K con dos criterios: WSS y silueta
p1 <- fviz_nbclust(vivienda_scaled, kmeans, method = "wss") + 
      labs(title = "Método del Codo (WSS)")

# Cálculo de silueta usando solo una muestra de 1000 datos para no colapsar la RAM
set.seed(123)
muestra_silueta <- vivienda_scaled[sample(nrow(vivienda_scaled), 1000), ]
p2 <- fviz_nbclust(muestra_silueta, kmeans, method = "silhouette") + 
      labs(title = "Coeficiente de Silueta")

gridExtra::grid.arrange(p1, p2, ncol = 2)

# Ejecución de K-Means con K = 3 sobre TODA la base de datos
set.seed(123)
km.res <- kmeans(vivienda_scaled, centers = 3, nstart = 25)
fviz_cluster(km.res, data = vivienda_scaled, ellipse.type = "convex", palette = "jco", ggtheme = theme_minimal(), title = "Segmentación de Inmuebles en Clústeres (K-Means)")

vivienda_clean$Cluster <- as.factor(km.res$cluster)

Tanto el método del codo como el coeficiente de silueta coinciden en señalar \(K = 3\) como una elección razonable, es el punto en el que agregar un grupo adicional deja de aportar una mejora. Con \(K = 3\), el K-means separa la oferta en tres perfiles interpretables:

  • Clúster 1 (Oferta Económica / Compacta): viviendas de menor área construida y precio, asociadas probablemente a apartamentos pequeños o casas en zonas de estrato bajo.
  • Clúster 2 (Segmento Residencial Estándar): el grueso de la oferta habitacional urbana de clase media, con área y precio en un rango intermedio; es el segmento “típico” del mercado.
  • Clúster 3 (Segmento de Alto Valor y Extensión): propiedades con mayor área construida, más baños y más parqueaderos, asociadas a un precio significativamente superior.

Esta segmentación es útil en la práctica porque permite tratar de forma diferenciada tres tipos de propiedad e inventario, en lugar de asumir que el mercado se comporta de manera homogénea.


6. Análisis de Correspondencia

El ACM se corrió sobre vivienda_cuali, con el fin de explorar cómo se asocian entre sí el tipo de vivienda, la zona y el estrato.

res.mca <- MCA(vivienda_cuali, graph = FALSE)
fviz_mca_var(res.mca, select.var = list(contrib = 20), repel = TRUE, ggtheme = theme_minimal(), title = "Asociación Categórica: Tipo, Zona y Estrato (ACM)")

El mapa del ACM muestra una asociación clara entre estrato alto, ciertas zonas de la ciudad y tipologías de vivienda específicas por ejemplo, casas de mayor formato, mientras que los estratos bajos se agrupan con las zonas y tipologías donde se concentra la mayor parte de la oferta. En otras palabras, la ubicación y el estrato no son independientes de la tipología de vivienda; el mercado inmobiliario de la ciudad está segmentado geográfica y socioeconómicamente, y esa segmentación se refleja directamente en qué tipo de propiedad se ofrece en cada zona.


7. Visualización Espacial

Para complementar los resultados anteriores con una lectura geográfica, se construyó un mapa interactivo con el paquete leaflet [5]. En lugar de tomar una muestra aleatoria simple sobre toda la base que por azar, podría dejar subrepresentadas zonas con pocas propiedades, se muestreó dentro de cada combinación de zona y tipo de vivienda, y luego se limitó el total a 1.000 puntos para no saturar el mapa.

set.seed(123)

sample_mapa <- vivienda_clean %>% 
  filter(!is.na(latitud) & !is.na(longitud)) %>%
  group_by(zona, tipo) %>%
  slice_sample(prop = 0.15) %>% 
  ungroup() %>%
  sample_n(min(1000, n())) # límite para no sobrecargar el renderizado del mapa

leaflet(data = sample_mapa) %>%
  addTiles() %>%
  addCircleMarkers(~longitud, ~latitud,
                   radius = 3,
                   color = ~ifelse(tipo == "Casa", "#FC4E07", "#00AFBB"),
                   stroke = FALSE, fillOpacity = 0.6,
                   popup = ~paste("<b>Tipo:</b>", tipo, "<br>",
                                  "<b>Precio:</b> $", preciom, "M<br>",
                                  "<b>Área:</b>", areaconst, "m2<br>",
                                  "<b>Estrato:</b>", estrato)) %>%
  addLegend("bottomright", colors = c("#FC4E07", "#00AFBB"), labels = c("Casa", "Apartamento"), title = "Tipo de Vivienda")

El mapa confirma lo que suguiere el ACM, los apartamentos se concentran en corredores céntricos y zonas de mayor densidad, mientras que las casas predominan en sectores periféricos o en zonas residenciales consolidadas de la ciudad.


8. Conclusiones y Recomendaciones Estratégicas

El análisis conjunto de PCA, clustering y ACM ofrece una lectura consistente del mercado inmobiliario estudiado.

Desde el punto de vista de la valoración, el área construida y el número de baños son las variables que más varianza explican, lo que sugiere que estos dos atributos deberían pesar más que otros en cualquier modelo de tasación que la empresa quiera construir a futuro.

En cuanto a la segmentación, el K-means identificó tres perfiles de oferta razonablemente diferenciados económico, estándar y de alto valor, respaldados tanto por el método del codo como por el coeficiente de silueta. Estos perfiles pueden usarse para orientar estrategias comerciales distintas; rotación rápida de inventario en los segmentos económico y estándar, y márgenes más altos por unidad en el segmento de alto valor.

Finalmente, el ACM y el mapa muestran que esta segmentación no ocurre al azar en el espacio urbano está fuertemente ligada a la zona y al estrato socioeconómico, lo que confirma que cualquier estrategia de adquisición o venta de inmuebles debería definirse a nivel de zona y no de manera uniforme para toda la ciudad.

A partir de estos hallazgos, se recomienda a la dirección priorizar la captación de inventario de forma diferenciada por clúster, por ejemplo, concentrarse en el Clúster 2 para maximizar rotación y en el Clúster 3 para maximizar margen y usar el mapa de zonas como insumo adicional para decidir en qué sectores de la ciudad concentrar los esfuerzos comerciales de cada segmento.


9. Referencias

[1] S. Lê, J. Josse, and F. Husson, “FactoMineR: An R Package for Multivariate Analysis,” Journal of Statistical Software, vol. 25, no. 1, pp. 1-18, 2008.

[2] A. Kassambara, Practical Guide to Principal Component Methods in R (PCA, M(CA), FAMD, MFA, HCPC, factoextra). STHDA, 2017.

[3] J. MacQueen, “Some methods for classification and analysis of multivariate observations,” in Proceedings of the Fifth Berkeley Symposium on Mathematical Statistics and Probability, University of California Press, 1967, pp. 281-297.

[4] A. Kassambara, Practical Guide to Cluster Analysis in R: Unsupervised Machine Learning. STHDA, 2017.

[5] J. Cheng, B. Karambelkar, and Y. Xie, Leaflet: Create Interactive Web Maps with the JavaScript ‘Leaflet’ Library, R package version 2.2.1, 2023. [Online]. Available: https://rstudio.github.io/leaflet/

[6] J. Han, M. Kamber, and J. Pei, Data Mining: Concepts and Techniques, 3rd ed. Morgan Kaufmann, 2011.

[7] T. Hastie, R. Tibshirani, and J. Friedman, The Elements of Statistical Learning: Data Mining, Inference, and Prediction, 2nd ed. Springer, 2009.

[8] P. J. Rousseeuw and A. M. Leroy, Robust Regression and Outlier Detection, John Wiley & Sons, 1987.

[9] S. van Buuren and K. Groothuis-Oudshoorn, “mice: Multivariate Imputation by Chained Equations in R,” Journal of Statistical Software, vol. 45, no. 3, pp. 1-67, 2011.

[10] B. Auguie, gridExtra: Miscellaneous Functions for “Grid” Graphics, R package version 2.3, 2017. [Online]. Available: https://cran.r-project.org/package=gridExtra

[11] R. L. Thorndike, “Who belongs in the family?,” Psychometrika, vol. 18, no. 4, pp. 267-276, 1953.