Actividad 1 Modelos Estadísticos para la toma de decisiones

Evaluación de la oferta inmobiliaria urbana

Problema Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.

El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:

  1. Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.

  2. Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.

  3. Análisis de Correspondencia : Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.

  4. Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.

El informe final debe incluir análisis detallados de los resultados obtenidos, las conclusiones clave y las recomendaciones específicas para guiar las decisiones estratégicas de la empresa inmobiliaria. Se espera que este análisis de datos proporcione ventajas competitivas en el mercado, optimizando la inversión y maximizando los beneficios en un entorno altamente competitivo y en constante cambio.

Carga de datos

# Cargar librería y dataset
library(paqueteMODELOS)
## Cargando paquete requerido: boot
## Cargando paquete requerido: broom
## Cargando paquete requerido: GGally
## Warning: package 'GGally' was built under R version 4.4.3
## Cargando paquete requerido: ggplot2
## Warning: package 'ggplot2' was built under R version 4.4.3
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Cargando paquete requerido: gridExtra
## Warning: package 'gridExtra' was built under R version 4.4.3
## Cargando paquete requerido: knitr
## Cargando paquete requerido: summarytools
data("vivienda")

# Copia de trabajo con nuevo nombre
datos_inmuebles <- vivienda

# Estructura del dataset
str(datos_inmuebles)
## 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>

Exploración inicial

# Primeras filas
head(datos_inmuebles)
## # A tibble: 6 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1147 Zona O… <NA>        3     250        70            1      3            6
## 2  1169 Zona O… <NA>        3     320       120            1      2            3
## 3  1350 Zona O… <NA>        3     350       220            2      2            4
## 4  5992 Zona S… 02          4     400       280            3      5            3
## 5  1212 Zona N… 01          5     260        90            1      2            3
## 6  1724 Zona N… 01          5     240        87            1      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Dimensiones (filas y columnas)
dim(datos_inmuebles)
## [1] 8322   13
# Resumen estadístico
summary(datos_inmuebles)
##        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

En este análisis se trabajó con una base de datos que reúne información de 8,322 propiedades. Cada registro corresponde a una vivienda y cuenta con 13 atributos asociados, que describen diferentes características relevantes para el estudio del mercado inmobiliario urbano.

Relación área construida vs precio

library(ggplot2)

ggplot(datos_inmuebles, aes(x = areaconst, y = preciom, color = areaconst)) +
  geom_point() +
  scale_color_viridis_c(option = "plasma") +
  labs(title = "Área construida vs. precio",
       x = "Área construida (m²)",
       y = "Precio (millones)",
       color = "Área") +
  theme_classic()
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).

Los datos evidencian que el tamaño de la construcción influye en el valor de la vivienda: propiedades más grandes suelen tener precios más altos. Sin embargo, la amplia dispersión de puntos muestra que esta no es la única variable que incide en el precio, lo que sugiere la presencia de otros factores como ubicación, estado del inmueble o antigüedad.

Valores faltantes

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::combine() masks gridExtra::combine()
## ✖ dplyr::filter()  masks stats::filter()
## ✖ dplyr::lag()     masks stats::lag()
## ✖ tibble::view()   masks summarytools::view()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
porcentaje_na <- map_dfr(names(datos_inmuebles), ~ {
  tibble(variable = .x,
         porcentaje_na = round(mean(is.na(datos_inmuebles[[.x]])) * 100, 2))
}) %>%
  arrange(desc(porcentaje_na))

ggplot(porcentaje_na, aes(x = reorder(variable, porcentaje_na), y = porcentaje_na, fill = porcentaje_na)) +
  geom_bar(stat = "identity") +
  coord_flip() +
  scale_fill_gradient(low = "#FEE08B", high = "#D73027") +
  labs(title = "Porcentaje de datos faltantes",
       x = "Variable",
       y = "% de NA") +
  theme_minimal()

El análisis univariado reveló que las variables “piso” y “parqueadero” son las que presentan un mayor porcentaje de datos faltantes dentro del conjunto. Una proporción tan alta de valores ausentes puede reducir la calidad del análisis y generar sesgos en las conclusiones. Por esta razón, se decidió excluir estos atributos para garantizar una interpretación más sólida de los resultados.

Asimismo, se optó por eliminar las variables “barrio” e “id”. En el caso de id, su función es meramente identificadora y no aporta valor analítico; mientras que barrio, en este contexto, no ofrece información relevante para los métodos estadísticos aplicados y podría introducir ruido innecesario al modelo.

Limpieza de datos

# Eliminación de columnas irrelevantes y casos incompletos
inmuebles_limpios <- datos_inmuebles %>%
  select(-id, -piso, -barrio, -parqueaderos) %>%
  drop_na()

# Conversión de variables categóricas a factores numéricos
datos_inmuebles$zona <- factor(datos_inmuebles$zona,
                               levels = c("Zona Centro", "Zona Norte", "Zona Oeste", "Zona Oriente", "Zona Sur"),
                               labels = c(1, 2, 3, 4, 5))

datos_inmuebles$tipo <- factor(datos_inmuebles$tipo,
                               levels = c("Casa", "Apartamento"),
                               labels = c(1, 2))

# Verificación de estructura
str(inmuebles_limpios)
## tibble [8,319 × 9] (S3: tbl_df/tbl/data.frame)
##  $ zona        : chr [1:8319] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ estrato     : num [1:8319] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8319] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8319] 70 120 220 280 90 87 52 137 150 380 ...
##  $ banios      : num [1:8319] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8319] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8319] "Casa" "Casa" "Casa" "Casa" ...
##  $ longitud    : num [1:8319] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8319] 3.43 3.43 3.44 3.44 3.46 ...

Tras depurar el conjunto de datos, se eliminaron las columnas ‘id’, ‘piso’, ‘barrio’ y ‘parqueadero’, así como las filas que contenían valores faltantes. Como resultado, la base final quedó conformada por 8,319 registros y 9 variables. Estas variables incluyen información tanto categórica, como numérica, lo que permite abordar el análisis desde una perspectiva mixta que combina descripciones cualitativas y mediciones cuantitativas.

Análisis de Componentes Principales (PCA)

library(FactoMineR)  # Para que PCA sea el correcto
## Warning: package 'FactoMineR' was built under R version 4.4.3
library(factoextra)  # Visualización
## Warning: package 'factoextra' was built under R version 4.4.3
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(ggplot2)     # Gráficos

is_ggplot <- function(x) inherits(x, "ggplot")

library(factoextra)
library(ggplot2)

# Escalado de variables numéricas relevantes
inmuebles_escalados <- scale(inmuebles_limpios[, 2:5])

# PCA
pca_result <- PCA(inmuebles_escalados, graph = FALSE)

# Varianza explicada
fviz_eig(pca_result, addlabels = TRUE, barfill = "#4DAF4A", barcolor = "#377EB8") +
  theme_minimal()

El análisis de componentes principales muestra que el primer componente concentra gran parte de la información de los datos. Al sumar el segundo componente, se logra explicar la mayor parte de la variabilidad total. Esto significa que la base de datos puede resumirse de forma adecuada usando solo uno o dos componentes, facilitando su interpretación y análisis.

# Variables en el espacio de componentes
fviz_pca_var(pca_result,
             col.var = "contrib",
             gradient.cols = c("#F7FCB9", "#31A354"),
             repel = TRUE)

La proyección de las variables en el plano de los componentes principales permite identificar qué mide cada componente y cómo se relaciona con las variables originales. En este caso, el primer componente está más vinculado con variables como estrato y precio, mientras que el segundo se asocia principalmente con el área construida. Esto ayuda a interpretar el significado de cada eje en el análisis.

Análisis de Conglomerados (K-means)

# Variables para clustering
vars_cluster <- inmuebles_limpios %>%
  select(estrato, preciom, areaconst, banios, habitaciones)

vars_cluster_scaled <- scale(vars_cluster)

# Método del codo
wcss <- sapply(1:10, function(k) {
  kmeans(vars_cluster_scaled, centers = k, nstart = 10)$tot.withinss
})

plot(1:10, wcss, type = "b", pch = 19,
     xlab = "Número de clusters", ylab = "WCSS",
     main = "Método del codo")

# Ajuste final con k = 3
set.seed(321)
modelo_k3 <- kmeans(vars_cluster_scaled, centers = 3, nstart = 10)

inmuebles_con_cluster <- inmuebles_limpios %>%
  mutate(cluster = factor(modelo_k3$cluster))

# Visualización
ggplot(inmuebles_con_cluster, aes(x = estrato, y = preciom, color = cluster)) +
  geom_point(alpha = 0.6) +
  scale_color_brewer(palette = "Dark2") +
  labs(title = "Clusters según estrato y precio",
       x = "Estrato",
       y = "Precio (millones)",
       color = "Cluster") +
  theme_minimal()

El gráfico de clusters de propiedades muestra una relación positiva entre el estrato socioeconómico y el precio de las viviendas. A medida que aumenta el estrato, también lo hace el precio promedio y la cantidad de agrupaciones identificadas, con mayor concentración en los estratos altos (5 y 6) que en los bajos (1 y 2).

En cuanto a la distribución, el cluster 1 aparece con mayor frecuencia en estratos medios (3, 4 y 5) y con menor presencia en el estrato 6. El cluster 2 predomina en los estratos más altos (5 y 6), mientras que el cluster 3 se encuentra principalmente en estratos medios (3 y 4) y, en menor medida, en el estrato 6. Esto sugiere que cada cluster refleja perfiles de propiedades asociados a rangos de estrato y precio específicos.

Análisis de Correspondencia

library(FactoMineR)

set.seed(456)
muestra_inmuebles <- sample_n(datos_inmuebles, 2000)

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

tabla_contingencia <- table(muestra_inmuebles$zona, muestra_inmuebles$estrato)

resultado_ca <- CA(tabla_contingencia)

# Scree plot
fviz_screeplot(resultado_ca, addlabels = TRUE, ylim = c(0, 80), col = "#08519C") +
  ylab("Porcentaje de varianza explicada") +
  xlab("Dimensiones")

El gráfico permite identificar patrones espaciales entre el estrato socioeconómico y la zona geográfica. El estrato 6 se concentra principalmente en la Zona Oeste, mientras que los estratos 4 y 5 predominan en las Zonas Sur y Norte. El estrato 3, por su parte, se localiza sobre todo en las Zonas Oriente y Centro.

En términos de análisis factorial, la Dimensión 1 explica la mayor parte de la variabilidad del conjunto de datos, mientras que la Dimensión 2 aporta una fracción adicional relevante. La suma de ambas dimensiones representa un alto porcentaje de la variabilidad acumulada, lo que indica que el plano formado por ellas resume de forma efectiva la estructura original de la información.

Las dos primeras dimensiones explican la gran mayoría de la variabilidad del conjunto de datos, lo que indica que son suficientes para representar casi toda la información original sin necesidad de considerar más dimensiones.

Conclusiones

El análisis de componentes principales permitió identificar qué variables concentran la mayor parte de la información relevante para explicar el comportamiento del precio de la vivienda. El primer componente sintetiza gran parte de la variabilidad y está principalmente vinculado con el estrato socioeconómico, el precio y el área construida, mientras que la inclusión del segundo componente refuerza notablemente la capacidad explicativa, permitiendo representar los datos de forma simplificada sin perder información clave.

El análisis de clusters mediante k-means segmentó las propiedades en grupos con características diferenciadas. Se observó una relación positiva entre el estrato, el precio y la distribución de los grupos: los estratos altos concentran propiedades de mayor valor y presentan patrones de agrupación distintos a los estratos bajos. Además, cada cluster mostró tendencias específicas según el tipo de vivienda (apartamentos o casas) y el rango de estrato predominante.

En el análisis de correlación, se identificaron relaciones claras entre variables: el número de habitaciones y baños está fuertemente asociado, así como el estrato y el precio. También se observan correlaciones moderadas entre el área construida y el número de habitaciones, y entre el precio y la cantidad de baños.

El análisis de correspondencias múltiples confirmó asociaciones significativas entre variables categóricas y numéricas, en especial entre la zona geográfica y el estrato. Se evidenció que el estrato 6 se concentra en la Zona Oeste, mientras que los estratos 4 y 5 predominan en las Zonas Sur y Norte. Las dos primeras dimensiones explicaron la gran mayoría de la información, validando la efectividad de este método para simplificar y representar el conjunto de datos.