Este informe analiza una base de 8.322 inmuebles residenciales ofertados en una ciudad colombiana, con el fin de identificar patrones, relaciones y segmentos que apoyen la toma de decisiones de una empresa inmobiliaria en materia de compra,venta y valoración.
Se aplicaron tres técnicas descriptivas de análisis multivariado:
Los resultados se complementan con visualizaciones estadísticas y un mapa interactivo de la oferta.
Una empresa inmobiliaria dispone de una base de datos de propiedades residenciales urbanas obtenida mediante web scraping del portal OLX. La empresa necesita entender cómo se estructura su oferta: qué variables determinan el precio, qué tipos de inmueble compiten entre sí y cómo se distribuye el mercado por zona y estrato socioeconómico.
El problema es multivariado: el precio no depende de una sola característica, sino de la combinación simultánea de área, número de habitaciones, baños, parqueaderos, estrato y ubicación. Analizar cada variable por separado dejaría por fuera precisamente lo más importante: cómo se relacionan entre sí.
Aplicar técnicas descriptivas de análisis multivariado sobre la base
viviendapara resumir la información disponible y generar
recomendaciones de negocio.
## [1] 8322 13
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:8322] 1147 1169 1350 5992 1212 ...
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
## $ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Diccionario de variables
| Variable | Tipo | Descripcion |
|---|---|---|
| id | Identificador | Código único del inmueble |
| zona | Cualitativa nominal | Zona de la ciudad (Norte, Sur, Oriente, Oeste, Centro) |
| piso | Cualitativa ordinal (texto) | Piso en el que se ubica el inmueble (registrado como texto: “01”, “02”, …) |
| estrato | Cualitativa ordinal | Estrato socioeconómico (en esta base: 3 a 6) |
| preciom | Cuantitativa continua | Precio de oferta en millones de pesos |
| areaconst | Cuantitativa continua | Área construida en metros cuadrados |
| parqueaderos | Cuantitativa discreta | Número de parqueaderos |
| banios | Cuantitativa discreta | Número de baños |
| habitaciones | Cuantitativa discreta | Número de habitaciones |
| tipo | Cualitativa nominal | Tipo de inmueble (Casa / Apartamento) |
| barrio | Cualitativa nominal | Barrio donde se ubica |
| longitud | Cuantitativa continua | Coordenada de longitud |
| latitud | Cuantitativa continua | Coordenada de latitud |
| Variable | Faltantes | Porcentaje |
|---|---|---|
| id | 3 | 0.04 |
| zona | 3 | 0.04 |
| piso | 2638 | 31.70 |
| estrato | 3 | 0.04 |
| preciom | 2 | 0.02 |
| areaconst | 3 | 0.04 |
| parqueaderos | 1605 | 19.29 |
| banios | 3 | 0.04 |
| habitaciones | 3 | 0.04 |
| tipo | 3 | 0.04 |
| barrio | 3 | 0.04 |
| longitud | 3 | 0.04 |
| latitud | 3 | 0.04 |
Decisiones tomadas:
piso: se excluye del análisis. Tiene
un porcentaje alto de datos faltantes y además solo tiene sentido para
apartamentos, no para casas. Incluirla distorsionaría la comparación
entre tipos de inmueble.parqueaderos: un dato faltante en esta
variable se interpreta como “el inmueble no tiene parqueadero”,
por lo que se imputa con 0.banios y
habitaciones: presentan muy pocos
faltantes (Tabla 2) y estos se concentran en registros que de todas
formas se eliminan por carecer de precio o área. Se deja de todos modos
una imputación por mediana (medida robusta) como
salvaguarda por si la base se actualizara.preciom y
areaconst: son las variables centrales del
estudio; si faltan, el registro se elimina.## [1] 8319
Las bases de portales inmobiliarios suelen tener errores de digitación (apartamentos de 10 m², casas de 50.000 millones). Estos valores extremos dominan el ACP y el clúster, así que se recortan los percentiles 1 % y 99 % de las dos variables continuas principales.
## 1% 99%
## 92 1650
## 1% 99%
## 50.0 716.4
## Registros finales para el análisis: 8029
## Registros descartados: 293
| Variable | Media | Mediana | DesvEst | Minimo | Maximo |
|---|---|---|---|---|---|
| preciom | 419.18 | 330.00 | 291.92 | 92.00 | 1650.0 |
| areaconst | 166.96 | 122.00 | 117.57 | 50.00 | 700.0 |
| habitaciones | 3.60 | 3.00 | 1.44 | 0.00 | 10.0 |
| banios | 3.10 | 3.00 | 1.38 | 0.00 | 10.0 |
| parqueaderos | 1.45 | 1.00 | 1.17 | 0.00 | 10.0 |
| estrato_num | 4.64 | 5.00 | 1.02 | 3.00 | 6.0 |
| precio_m2 | 2.73 | 2.66 | 1.06 | 0.28 | 8.3 |
La distribución es asimétrica a la derecha: la mayoría de los inmuebles se concentra en precios bajos y medios, y una minoría alcanza precios muy altos. Este es el comportamiento típico de un mercado inmobiliario.
El precio crece de forma consistente con el estrato, lo que confirma que el estrato es un buen indicador del segmento de mercado. Además, la dispersión también aumenta: en estratos altos hay mucha más variabilidad de precios.
Lectura de la matriz: los valores van de −1 a 1. Cerca de 1 significa que las dos variables crecen juntas; cerca de 0, que no tienen relación lineal.
Se observa una correlación fuerte y positiva entre
preciom y areaconst, y correlaciones
moderadas del precio con baños, parqueaderos y estrato. El hecho de que
varias variables estén correlacionadas entre sí es exactamente la razón
por la que tiene sentido aplicar un Análisis de Componentes
Principales: hay información redundante que se puede
resumir.
El ACP toma varias variables numéricas correlacionadas y las reemplaza por unas pocas variables nuevas (llamadas componentes) que concentran la mayor parte de la información. Cada componente es una combinación de las variables originales.
Como las variables están en unidades distintas (millones de pesos
vs. m² vs. número de baños), es indispensable
estandarizarlas (scale.unit = TRUE), es
decir, ponerlas todas en la misma escala antes de compararlas.
| eigenvalue | variance.percent | cumulative.variance.percent | |
|---|---|---|---|
| Dim.1 | 3.376 | 56.274 | 56.274 |
| Dim.2 | 1.318 | 21.974 | 78.247 |
| Dim.3 | 0.474 | 7.893 | 86.140 |
| Dim.4 | 0.409 | 6.822 | 92.962 |
| Dim.5 | 0.245 | 4.077 | 97.039 |
| Dim.6 | 0.178 | 2.961 | 100.000 |
Criterios de decisión:
Los dos primeros componentes concentran la mayor parte de la información, por lo que el análisis se centra en ellos y permite representar seis variables en un único plano de dos dimensiones.
Cómo se lee este gráfico:
| Dim.1 | Dim.2 | Dim.3 | |
|---|---|---|---|
| preciom | 0.881 | -0.229 | -0.133 |
| areaconst | 0.830 | 0.291 | -0.071 |
| parqueaderos | 0.736 | -0.311 | 0.598 |
| banios | 0.863 | 0.221 | -0.135 |
| habitaciones | 0.520 | 0.766 | 0.030 |
| estrato_num | 0.595 | -0.670 | -0.273 |
Componente 1 — “Tamaño y valor del inmueble”.
Recibe contribuciones altas y del mismo signo de preciom,
areaconst, banios y parqueaderos.
Un inmueble con puntaje alto en este componente es grande, caro y bien
dotado. Este eje resume por sí solo la mayor parte de la variación del
mercado, lo que responde directamente a la pregunta del caso: el
precio se explica sobre todo por el tamaño y la dotación del
inmueble.
Componente 2 — “Densidad habitacional
vs. estatus”. Aquí habitaciones y
estrato_num se comportan de manera contrastada: separa
inmuebles con muchas habitaciones en estratos medios (casas familiares)
de inmuebles con pocas habitaciones pero de estrato alto (apartamentos
de lujo). Es un eje de estilo de vivienda, no de
tamaño.
Se aprecia un gradiente claro de estrato a lo largo del Componente 1: los inmuebles de estratos altos se ubican a la derecha. Esto valida la interpretación del eje.
El análisis de conglomerados agrupa los inmuebles de forma que los de un mismo grupo sean parecidos entre sí y distintos de los de otros grupos. A diferencia del ACP, aquí no reducimos variables sino que segmentamos observaciones.
Se usa el método k-medias (k-means), que
requiere decidir de antemano el número de grupos k.
## preciom areaconst parqueaderos banios habitaciones estrato_num
## [1,] -0.580 -0.825 -0.388 -0.072 1.672 -1.609
## [2,] -0.340 -0.399 -0.388 -0.795 -0.415 -1.609
## [3,] -0.237 0.451 0.466 -0.795 0.280 -1.609
Para elegir k se usan dos criterios. Se calculan sobre
una muestra aleatoria porque son computacionalmente costosos sobre las
más de 8.000 observaciones.
Con base en ambos criterios y en la interpretabilidad comercial de los grupos, se trabaja con k = 4 segmentos.
##
## Segmento 1 Segmento 2 Segmento 3 Segmento 4
## 3371 886 2748 1024
## Varianza explicada por los segmentos: 58.7 %
| segmento | Inmuebles | Precio medio (M) | Área media (m²) | Habitaciones | Baños | Parqueaderos | Estrato medio | Precio/m² (M) |
|---|---|---|---|---|---|---|---|---|
| Segmento 1 | 3371 | 213.4 | 89.5 | 2.9 | 2.0 | 0.7 | 3.9 | 2.542 |
| Segmento 2 | 886 | 439.3 | 294.4 | 6.5 | 4.4 | 1.2 | 3.9 | 1.620 |
| Segmento 3 | 2748 | 452.3 | 154.1 | 3.3 | 3.3 | 1.7 | 5.3 | 3.155 |
| Segmento 4 | 1024 | 990.4 | 346.2 | 4.2 | 4.9 | 3.3 | 5.7 | 3.174 |
La Tabla 6, leída en orden de precio medio, muestra una estructura de mercado clara, con un matiz importante: no es una escalera perfecta de precios. Los dos segmentos intermedios tienen precios medios muy similares y se diferencian por el estilo de vivienda, no por el valor.
Que los Segmentos 2 y 3 compartan rango de precio pero difieran en habitaciones y estrato es exactamente el contraste que captura el Componente 2 del ACP, mientras que la separación principal entre segmentos la aporta el Componente 1 (tamaño y dotación). La segmentación es, por tanto, coherente con el ACP.
El Análisis de Correspondencias (AC) es el equivalente del ACP pero para variables categóricas. Parte de una tabla de contingencia (un cruce de dos variables) y la representa en un plano donde:
Dos categorías que aparecen cerca en el gráfico tienden a presentarse juntas en los datos.
##
## Económica Media Media-Alta Premium
## 2041 2012 2024 1952
| Zona Centro | Zona Norte | Zona Oeste | Zona Oriente | Zona Sur | |
|---|---|---|---|---|---|
| 3 | 105 | 516 | 53 | 315 | 359 |
| 4 | 13 | 404 | 82 | 8 | 1591 |
| 5 | 4 | 751 | 283 | 2 | 1671 |
| 6 | 1 | 166 | 744 | 1 | 960 |
##
## Pearson's Chi-squared test
##
## data: tabla_ez
## X-squared = 3735.3, df = 12, p-value < 0.00000000000000022
Interpretación de la prueba ji-cuadrado: la hipótesis nula es que estrato y zona son independientes. Un p-valor menor a 0,05 lleva a rechazarla, es decir, sí existe asociación entre la zona de la ciudad y el estrato de los inmuebles ofertados. Esto justifica continuar con el análisis de correspondencias.
| eigenvalue | variance.percent | cumulative.variance.percent | |
|---|---|---|---|
| Dim.1 | 0.320 | 68.748 | 68.748 |
| Dim.2 | 0.135 | 29.048 | 97.796 |
| Dim.3 | 0.010 | 2.204 | 100.000 |
Las primeras dos dimensiones recogen prácticamente toda la inercia (la “información”) de la tabla, por lo que el mapa es una representación fiel del cruce. La lectura del mapa muestra una segregación socioeconómica del territorio: las zonas se ubican cerca de los estratos que las caracterizan, de manera que cada zona tiene un perfil socioeconómico dominante en lugar de una mezcla uniforme.
Para la empresa esto significa que la estrategia comercial debe ser territorial: el mismo mensaje y el mismo portafolio no funcionan en todas las zonas.
| Económica | Media | Media-Alta | Premium | |
|---|---|---|---|---|
| Zona Centro | 40 | 41 | 34 | 8 |
| Zona Norte | 625 | 427 | 513 | 272 |
| Zona Oeste | 82 | 99 | 357 | 624 |
| Zona Oriente | 165 | 109 | 48 | 4 |
| Zona Sur | 1129 | 1336 | 1072 | 1044 |
##
## Pearson's Chi-squared test
##
## data: tabla_zp
## X-squared = 1077.9, df = 12, p-value < 0.00000000000000022
##
## Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
## Apartamento 24 1141 1002 47 2736
## Casa 99 696 160 279 1845
##
## Pearson's Chi-squared test
##
## data: tabla_tz
## X-squared = 704.3, df = 4, p-value < 0.00000000000000022
El ancho de cada columna representa el peso de la zona en la oferta total, y la altura de cada bloque la proporción de casas y apartamentos dentro de ella. Se confirma que la composición de la oferta no es igual en todas las zonas.
Cuando se quieren analizar más de dos variables categóricas a la vez, se usa el ACM. Aquí se combinan tipo, zona, estrato, rango de precio y segmento.
Este mapa integra todo el análisis en una sola imagen: permite ver simultáneamente qué estratos, zonas, tipos de inmueble y rangos de precio se asocian entre sí, y en qué segmento del clúster caen.
Debe tenerse en cuenta que rango_precio y
segmento son variables derivadas (del
precio y de las variables numéricas, respectivamente), por lo que parte
de las asociaciones que muestra el mapa está garantizada por
construcción. El ACM se usa aquí como resumen visual integrador, no como
evidencia independiente.
## [1] 8029
El mapa confirma visualmente lo que arrojó el análisis de correspondencias: los segmentos no están repartidos al azar por la ciudad, sino concentrados en corredores geográficos específicos.
El tamaño y la dotación explican la mayor parte del valor. El primer componente principal, construido a partir de área, precio, baños y parqueaderos, concentra por sí solo la mayor proporción de la variabilidad del mercado. En términos prácticos, un modelo de valoración que capture bien el área construida y la dotación ya explica buena parte del precio.
La oferta se organiza en cuatro segmentos bien caracterizados: una base económica que concentra la mayor parte del volumen, dos segmentos intermedios de precio similar pero perfil opuesto (casa familiar amplia de estrato medio vs. apartamento dotado de estrato alto) y un segmento premium de alto valor unitario. La segmentación es consistente con el plano del ACP, lo que refuerza su validez: no son grupos arbitrarios sino la estructura real del mercado.
Existe asociación estadísticamente significativa entre zona y estrato, y entre zona y rango de precio (pruebas ji-cuadrado con p < 0,05). El mercado está territorialmente segregado y la ubicación es un determinante del segmento.
La composición por tipo de inmueble varía entre zonas, lo que sugiere que casas y apartamentos no compiten de la misma manera en toda la ciudad.
El precio por metro cuadrado difiere de forma relevante entre zonas, lo que permite identificar zonas con potencial de valorización frente a zonas ya maduras.
piso (cerca de un tercio de faltantes) y
parqueaderos (cerca de una quinta parte), donde la
proporción de datos faltantes limita el análisis.parqueaderos faltantes como 0 es un
supuesto razonable pero no verificable: un anuncio puede omitir el dato
aunque el inmueble sí tenga parqueadero.vivienda, paquete
paqueteMODELOS, https://github.com/centro-magis/paqueteMODELOS.| Segmento | preciom | areaconst | parqueaderos | banios | habitaciones | estrato_num | |
|---|---|---|---|---|---|---|---|
| 2 | Segmento 1 | -0.705 | -0.659 | -0.604 | -0.770 | -0.489 | -0.678 |
| 4 | Segmento 2 | 0.069 | 1.084 | -0.252 | 0.970 | 1.986 | -0.739 |
| 3 | Segmento 3 | 0.113 | -0.109 | 0.225 | 0.142 | -0.192 | 0.680 |
| 1 | Segmento 4 | 1.957 | 1.525 | 1.603 | 1.317 | 0.407 | 1.047 |
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.4.0 leaflet_2.2.3 corrplot_0.95
## [4] cluster_2.1.8.3 factoextra_2.2.0 FactoMineR_2.16
## [7] scales_1.4.0 tidyr_1.3.2 dplyr_1.2.1
## [10] paqueteMODELOS_0.1.0 summarytools_1.1.5 knitr_1.51
## [13] gridExtra_2.3 GGally_2.4.0 ggplot2_4.0.3
## [16] broom_1.0.12 boot_1.3-32
##
## loaded via a namespace (and not attached):
## [1] tidyselect_1.2.1 viridisLite_0.4.3 farver_2.1.2
## [4] S7_0.2.1 fastmap_1.2.0 digest_0.6.39
## [7] estimability_2.0.0 timechange_0.4.0 lifecycle_1.0.5
## [10] multcompView_0.1-12 magrittr_2.0.4 compiler_4.5.2
## [13] rlang_1.1.7 sass_0.4.10 tools_4.5.2
## [16] yaml_2.3.12 ggsignif_0.6.4 labeling_0.4.3
## [19] htmlwidgets_1.6.4 scatterplot3d_0.3-45 plyr_1.8.9
## [22] xml2_1.5.2 showtextdb_3.0 RColorBrewer_1.1-3
## [25] abind_1.4-8 withr_3.0.2 purrr_1.2.1
## [28] grid_4.5.2 ggpubr_1.0.0 sysfonts_0.8.9
## [31] xtable_1.8-8 emmeans_2.0.4 MASS_7.3-65
## [34] flashClust_1.1-4 cli_3.6.5 mvtnorm_1.4-2
## [37] rmarkdown_2.30 generics_0.1.4 otel_0.2.0
## [40] rstudioapi_0.18.0 reshape2_1.4.5 cachem_1.1.0
## [43] pander_0.6.6 stringr_1.6.0 matrixStats_1.5.0
## [46] base64enc_0.1-6 vctrs_0.7.1 Matrix_1.7-4
## [49] carData_3.0-6 jsonlite_2.0.0 car_3.1-5
## [52] rapportools_1.2 rstatix_1.1.0 ggrepel_0.9.8
## [55] Formula_1.2-6 irlba_2.3.7 systemfonts_1.3.1
## [58] magick_2.9.0 crosstalk_1.2.2 jquerylib_0.1.4
## [61] glue_1.8.0 leaflet.providers_3.0.0 ggstats_0.13.0
## [64] ggtext_0.1.2 DT_0.34.0 lubridate_1.9.5
## [67] stringi_1.8.7 gtable_0.3.6 tibble_3.3.1
## [70] pillar_1.11.1 htmltools_0.5.9 showtext_0.9-8
## [73] R6_2.6.1 textshaping_1.0.4 tcltk_4.5.2
## [76] evaluate_1.0.5 lattice_0.22-7 backports_1.5.0
## [79] leaps_3.2 gridtext_0.1.6 bslib_0.10.0
## [82] Rcpp_1.1.2 svglite_2.2.2 checkmate_2.3.4
## [85] xfun_0.56 pkgconfig_2.0.3