Cargue de Librerias

library(paqueteMODELOS)
## Warning: package 'broom' was built under R version 4.4.3
## Warning: package 'GGally' was built under R version 4.4.3
## Warning: package 'gridExtra' was built under R version 4.4.3
data("vivienda")  # Este es el objeto que usarás
  1. Limpieza y procesamiento de los Datos
# Cargar datos
data("vivienda")

# Conversión de tipos
vivienda$piso=as.numeric(vivienda$piso)
vivienda$estrato=as.factor(vivienda$estrato)

# Eliminación de filas con múltiples valores faltantes
vivienda_clean <-vivienda[rowSums(!is.na(vivienda) & vivienda != "") > 1, ]

# Validar datos duplicados
duplicados <- duplicated(vivienda_clean$id)
vivienda_clean [duplicados, ]
## # A tibble: 0 × 13
## # ℹ 13 variables: id <dbl>, zona <chr>, piso <dbl>, estrato <fct>,
## #   preciom <dbl>, areaconst <dbl>, parqueaderos <dbl>, banios <dbl>,
## #   habitaciones <dbl>, tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Normalización de la variable piso
vivienda_clean <- vivienda_clean %>%
  mutate(piso = as.character(piso)) %>%
  mutate(piso = case_when(
    piso %in% c("PB", "Sótano", "Terraza") ~ "0",
    grepl("^[0-9]+$", piso) ~ piso,
    TRUE ~ NA_character_
  )) %>%
  mutate(piso = as.numeric(piso))

La normalización de la variable piso permite convertirla en una escala numérica coherente, eliminando ambigüedades categóricas y facilitando su uso en análisis multivariado como PCA y clustering, al mejorar la comparabilidad, la calidad del dato y la interpretación estadística.

  1. Imputación de datos faltantes
# Selección de variables numéricas para imputar
vars_para_imputar <- vivienda_clean %>%
  select(preciom, areaconst, parqueaderos, banios, habitaciones, piso)

2.1 Configuración de los métodos de imputación

#Se define un metodo hibrido de imputación usando mice, donde se evaluan las variables con el metodo más adecuado
init <- mice(vars_para_imputar, maxit = 0)
metodos <- init$method

metodos["preciom"] <- "pmm"
metodos["areaconst"] <- "cart"
metodos["parqueaderos"] <- "pmm"
metodos["banios"] <- "cart"
metodos["habitaciones"] <- ""  # Se excluye
metodos["piso"] <- "pmm"

2.2 Ejecución de la imputación

imputacion_hibrida <- mice(vars_para_imputar, m = 5, method = metodos, seed = 123)
## 
##  iter imp variable
##   1   1  parqueaderos  piso
##   1   2  parqueaderos  piso
##   1   3  parqueaderos  piso
##   1   4  parqueaderos  piso
##   1   5  parqueaderos  piso
##   2   1  parqueaderos  piso
##   2   2  parqueaderos  piso
##   2   3  parqueaderos  piso
##   2   4  parqueaderos  piso
##   2   5  parqueaderos  piso
##   3   1  parqueaderos  piso
##   3   2  parqueaderos  piso
##   3   3  parqueaderos  piso
##   3   4  parqueaderos  piso
##   3   5  parqueaderos  piso
##   4   1  parqueaderos  piso
##   4   2  parqueaderos  piso
##   4   3  parqueaderos  piso
##   4   4  parqueaderos  piso
##   4   5  parqueaderos  piso
##   5   1  parqueaderos  piso
##   5   2  parqueaderos  piso
##   5   3  parqueaderos  piso
##   5   4  parqueaderos  piso
##   5   5  parqueaderos  piso
datos_imputados <- complete(imputacion_hibrida)

Se utiliza la función evaluar imputacion mice() para generar un diagnóstico completo, incluyendo:

evaluar_imputacion_mice <- function(vars, datos_originales, datos_imputados) {
  resumen <- lapply(vars, function(var) {
    original <- datos_originales[[var]]
    imputado <- datos_imputados[[var]]
    
    tibble(
      variable = var,
      media_original = mean(original, na.rm = TRUE),
      media_imputado = mean(imputado, na.rm = TRUE),
      sd_original = sd(original, na.rm = TRUE),
      sd_imputado = sd(imputado, na.rm = TRUE),
      n_na_original = sum(is.na(original)),
      n_imputado = sum(is.na(imputado))
    )
  })
  
  bind_rows(resumen)
}
# Variables imputadas
vars_para_imputar <- c("preciom", "areaconst", "parqueaderos", "banios", "piso")

# Datos imputados
datos_imputados <- complete(imputacion_hibrida)

# Evaluación
evaluacion <- evaluar_imputacion_mice(vars_para_imputar, vivienda_clean, datos_imputados)
print(evaluacion)
## # A tibble: 5 × 7
##   variable   media_original media_imputado sd_original sd_imputado n_na_original
##   <chr>               <dbl>          <dbl>       <dbl>       <dbl>         <int>
## 1 preciom            434.           434.        329.        329.               0
## 2 areaconst          175.           175.        143.        143.               0
## 3 parqueade…           1.84           1.76        1.12        1.08          1602
## 4 banios               3.11           3.11        1.43        1.43             0
## 5 piso                 3.77           3.74        2.61        2.61          2635
## # ℹ 1 more variable: n_imputado <int>

2.3 Comparativo antes y despues de la imputación

# Se comparan las distribuciones de las variables imputadas antes y después del proceso, utilizando histogramas y densidades. Esto permite evaluar si la imputación ha conservado la forma original de los datos.

# Variables imputadas
vars <- c("preciom", "areaconst", "parqueaderos", "banios", "piso")

# Datos imputados
datos_imputados <- complete(imputacion_hibrida)

# Reorganizar en formato largo para graficar
datos_comparacion <- bind_rows(
  vivienda_clean %>% select(all_of(vars)) %>% mutate(tipo = "Original"),
  datos_imputados %>% select(all_of(vars)) %>% mutate(tipo = "Imputado")
) %>%
  pivot_longer(cols = all_of(vars), names_to = "variable", values_to = "valor")

2.4 Visualización de las distribuciones

# Filtrar valores no finitos antes de graficar
datos_comparacion_filtrados <- datos_comparacion %>%
  filter(!is.na(valor), is.finite(valor))

ggplot(datos_comparacion_filtrados, aes(x = valor, fill = tipo)) +
  geom_histogram(position = "identity", alpha = 0.5, bins = 30) +
  facet_wrap(~variable, scales = "free") +
  theme_minimal() +
  labs(title = "Distribución antes y después de la imputación",
       x = "Valor", y = "Frecuencia", fill = "Tipo")

La imputación funcionó bien para algunas variables (como banios y preciom), pero no fue tan precisa para otras (parqueaderos y piso). Esto sugiere que el método actual no es el mas apropiado para estas variables.

2.5 Ajuste de imputación por regresión (parqueaderos y piso)

#Despues de evaluar la calidad de la imputación híbrida, se identificaron discrepancias en las variables `parqueaderos` y `piso`. Para mejorar su coherencia, se aplica una imputación dirigida por regresión multivariada.
vars_modelo <- c("parqueaderos", "piso", "preciom", "areaconst", "banios")
datos_modelo <- datos_imputados %>% select(all_of(vars_modelo))

2.5.1. Imputación por Regresion para Parqueaderos

modelo_parq <- lm(parqueaderos ~ preciom + areaconst + banios + piso, 
                  data = datos_modelo, na.action = na.exclude)

datos_modelo$parqueaderos_imputado <- ifelse(
  is.na(datos_modelo$parqueaderos),
  predict(modelo_parq, newdata = datos_modelo),
  datos_modelo$parqueaderos
)

2.5.2. Imputación por Regresion para Pisos

modelo_piso <- lm(piso ~ preciom + areaconst + banios + parqueaderos_imputado, 
                  data = datos_modelo, na.action = na.exclude)

datos_modelo$piso_imputado <- ifelse(
  is.na(datos_modelo$piso),
  predict(modelo_piso, newdata = datos_modelo),
  datos_modelo$piso
)

2.5.3. Ajuste en el Dataset Final

datos_imputados$parqueaderos <- datos_modelo$parqueaderos_imputado
datos_imputados$piso <- datos_modelo$piso_imputado

Este ajuste mejora la coherencia contextual de las variables imputadas, especialmente aquellas con patrones complejos o relaciones no lineales que no fueron capturadas adecuadamente por el método inicial.

2.6. Comparativo gráfico de nuevas imputaciones

# Guardamos las versiones anteriores antes del ajuste
datos_imputados$parqueaderos_antes <- datos_imputados$parqueaderos
datos_imputados$piso_antes <- datos_imputados$piso

# Reemplazamos con las versiones ajustadas
datos_imputados$parqueaderos_despues <- datos_modelo$parqueaderos_imputado
datos_imputados$piso_despues <- datos_modelo$piso_imputado

# Función para graficar comparaciones
comparar_histograma <- function(df, variable, antes, despues) {
  df_long <- df %>%
    select(all_of(c(antes, despues))) %>%
    pivot_longer(cols = everything(), names_to = "version", values_to = "valor")
  
  ggplot(df_long, aes(x = valor, fill = version)) +
    geom_histogram(alpha = 0.6, position = "identity", bins = 30) +
    scale_fill_manual(values = c("steelblue", "darkorange")) +
    labs(title = paste("Comparación de", variable, "antes y después de ajuste"),
         x = variable, y = "Frecuencia") +
    theme_minimal()
}

# Graficar parqueaderos
comparar_histograma(datos_imputados, "parqueaderos", "parqueaderos_antes", "parqueaderos_despues")

# Graficar piso
comparar_histograma(datos_imputados, "piso", "piso_antes", "piso_despues")

Las gráficas de parqueaderos y piso muestran que el ajuste por regresión logró reducir la concentración artificial de valores imputados por el método inicial (mice). Sin embargo, ambos modelos generaron valores continuos para variables que conceptualmente deberían ser discretas (enteras).

Esto puede introducir inconsistencias en análisis posteriores, especialmente si estas variables se usan como factores, en agrupamientos o en modelos que asumen conteo. Para preservar la coherencia semántica y facilitar la interpretación, por ende se redondearan los valores imputados.

# Redondear parqueaderos y piso a enteros
datos_imputados$parqueaderos <- round(datos_imputados$parqueaderos)
datos_imputados$piso <- round(datos_imputados$piso)
  1. Preparación de Datos PCA
# Selección de variables numéricas relevantes
vars_pca <- c("preciom", "areaconst", "banios", "parqueaderos", "piso")
datos_pca <- datos_imputados %>% select(all_of(vars_pca)) %>% na.omit()

# Escalado
datos_pca_scaled <- scale(datos_pca)

3.1. Analisis de componentes principales PPCA

El objetivo es 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.

pca_result <- prcomp(datos_pca_scaled, center = TRUE, scale. = TRUE)

# Resumen de varianza explicada
summary(pca_result)
## Importance of components:
##                          PC1    PC2     PC3     PC4     PC5
## Standard deviation     1.720 1.0042 0.66389 0.58446 0.49927
## Proportion of Variance 0.592 0.2017 0.08815 0.06832 0.04986
## Cumulative Proportion  0.592 0.7937 0.88183 0.95014 1.00000
# Scree plot
fviz_eig(pca_result, addlabels = TRUE, ylim = c(0, 60))

3.2. Visualización

fviz_pca_ind(pca_result,
             geom.ind = "point",
             palette = "jco",
             addEllipses = FALSE)

Resultados:

  1. Analisis de conglomerados Clustering- sobre el PCA obtenido

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.

# Extraer los componentes principales
pca_coords <- pca_result$x[, 1:3]  # puedes ajustar a 2 si lo prefieres

# Clustering con k-means (provisionalmente con 3 clusters)
set.seed(123)
km_res <- kmeans(pca_coords, centers = 3, nstart = 25)

4.1. Evaluar el número optimo de Clusters

fviz_nbclust(pca_coords, kmeans, method = "wss") +
  labs(title = "Método del Codo para elegir número óptimo de clusters")

Este método busca el punto donde la reducción de la varianza intra-cluster comienza a desacelerarse (“el codo”).

El gráfico muestra una caída pronunciada en la varianza intra-cluster hasta aproximadamente k = 3 o k = 4, donde la curva comienza a aplanarse. Este punto de inflexión —el “codo”— indica que:

k = 3 o k = 4 son opciones óptimas para el número de clusters.

Agregar más clusters después de ese punto no reduce significativamente la varianza, lo que sugiere sobreajuste.

fviz_nbclust(pca_coords, kmeans, method = "silhouette") +
  labs(title = "Método de la Silueta para elegir número óptimo de clusters")

Este método evalúa qué tan bien se separan los clusters. Un valor cercano a 1 indica buena separación.

El gráfico muestra que la mayor anchura promedio de silueta ocurre en k = 2, lo que indica que los datos se agrupan mejor en dos clusters bien definidos y separados.

A partir de k = 3 en adelante, la anchura disminuye, lo que sugiere que los clusters se vuelven menos compactos o más solapados.

4.1.1 Interpretación Combinada Metodo del Codo (k=3 o 4) y Metodo de la silueta (k=2)

k = 2 ofrece la mejor separación entre grupos, ideal para claridad estructural.

k = 3 o 4 podrían capturar más matices internos, pero con menor separación.

Por ende seleccionamos el codo con k=2

4.2 Visualización

fviz_cluster(km_res, data = pca_coords,
             ellipse.type = "convex",
             palette = "jco",
             ggtheme = theme_minimal(),
             main = "Segmentación de propiedades por clustering en espacio PCA")

El gráfico muestra tres grupos bien diferenciados en el plano PCA, cada uno rodeado por una elipse convexa que indica su dispersión.

Las dimensiones Dim1 y Dim2 explican juntas el 66.6% de la variabilidad, lo que permite una interpretación bastante fiel del agrupamiento.

Los clusters parecen tener formas y tamaños distintos, lo que sugiere diferencias en la homogeneidad interna:

Cluster 1 (azul): más compacto, posiblemente más homogéneo.

Cluster 2 (amarillo): más disperso, podría contener subgrupos.

Cluster 3 (gris): intermedio en forma y tamaño.

4.3 Caracterizar los segmentos

# Agregar cluster al dataset
datos_segmentados <- datos_imputados %>% 
  drop_na() %>% 
  mutate(cluster = factor(km_res$cluster))

# Resumen por cluster
datos_segmentados %>% 
  group_by(cluster) %>% 
  summarise(across(c(preciom, areaconst, banios, parqueaderos), list(media = mean, sd = sd)))
## # A tibble: 3 × 9
##   cluster preciom_media preciom_sd areaconst_media areaconst_sd banios_media
##   <fct>           <dbl>      <dbl>           <dbl>        <dbl>        <dbl>
## 1 1                295.       145.            127.         74.1         2.60
## 2 2                352.       191.            106.         47.6         2.63
## 3 3                898.       373.            365.        177.          4.96
## # ℹ 3 more variables: banios_sd <dbl>, parqueaderos_media <dbl>,
## #   parqueaderos_sd <dbl>

Esto genera una idea mas clara de cómo se diferencian los grupos en términos de precio, tamaño, número de baños, etc.

Cluster Precio medio (M) Área construida (m²) Baños Parqueaderos Perfil general
1 295.2 127.5 2.6 1.37 Propiedades de gama media, tamaño moderado, funcionales.
2 352.4 106.4 2.6 1.42 Ligeramente más costosas pero más compactas. Posible ubicación céntrica.
3 897.7 365.1 4.96 3.14 Segmento premium: propiedades amplias, con más comodidades.

El Cluster 3 destaca claramente como un grupo de propiedades de alta gama, con casi el triple de precio y área construida que los otros dos.

Los Clusters 1 y 2 son similares en número de baños y parqueaderos, pero difieren en precio y tamaño, lo que sugiere diferencias en ubicación o acabados.

Las desviaciones estándar indican que el Cluster 3 también es más heterogéneo internamente, lo que podría reflejar variedad dentro del segmento de lujo.

  1. Analisis de correspondencia

Permite examinar la relación entre variables categóricas como tipo de vivienda, zona, y barrio, con el fin de identificar patrones de comportamiento en la oferta del mercado inmobiliario.

df_cat_complete <- vivienda %>%
  select(zona, estrato, tipo) %>%
  filter(!is.na(zona) & !is.na(estrato) & !is.na(tipo))
# Seleccionar variables categóricas relevantes
datos_mca <- df_cat_complete %>%
  select(zona, estrato, tipo) %>%
  mutate(across(everything(), as.factor))

str(datos_mca)
## spc_tbl_ [8,319 × 3] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ zona   : Factor w/ 5 levels "Zona Centro",..: 4 4 4 5 2 2 2 2 2 2 ...
##  $ estrato: Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
##  $ tipo   : Factor w/ 2 levels "Apartamento",..: 2 2 2 2 1 1 1 1 2 2 ...
##  - 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>

5.1. Ejecución del MCA

# Ejecutar MCA
res.mca <- MCA(datos_mca, graph = FALSE)

# Inercia explicada por cada dimensión
eig_mca <- res.mca$eig
knitr::kable(eig_mca, caption = "Inercia explicada por cada dimensión")
Inercia explicada por cada dimensión
eigenvalue percentage of variance cumulative percentage of variance
dim 1 0.5620882 21.078306 21.07831
dim 2 0.4531232 16.992119 38.07043
dim 3 0.3796450 14.236687 52.30711
dim 4 0.3334444 12.504164 64.81128
dim 5 0.3233137 12.124263 76.93554
dim 6 0.2717762 10.191608 87.12715
dim 7 0.2013563 7.550862 94.67801
dim 8 0.1419197 5.321990 100.00000

5.2. Visualización

# Biplot de individuos y categorías
fviz_mca_biplot(res.mca, repel = TRUE, ggtheme = theme_minimal(),
                title = "Biplot MCA: Individuos y Categorías")

# Visualización de las categorías
fviz_mca_var(res.mca, repel = TRUE, ggtheme = theme_minimal(),
             title = "Variables categóricas en el espacio MCA")

# Visualización de los individuos
fviz_mca_ind(res.mca, repel = TRUE, ggtheme = theme_minimal(),
             title = "Individuos en el espacio MCA")

El MCA revela asociaciones latentes entre las categorías de zona, estrato y tipo de vivienda:

Apartamentos tienden a agruparse con zonas como Norte y estratos 4 a 6, lo que sugiere una oferta más urbana y vertical.

Casas se asocian con zonas como Sur y estratos 1 a 3, reflejando una distribución más periférica y horizontal.

Algunas zonas como Centro y Oriente muestran mezcla de perfiles, lo que podría indicar transiciones urbanas o diversidad de oferta.

La primera dimensión del MCA captura la oposición entre zonas de alto y bajo estrato, mientras que la segunda puede reflejar diferencias en tipo de vivienda o patrones de densidad.

  1. Integracipon de clustering (PCA) con analisis de correspondencia multiple (MCA)

Consolida la segmentación obtenida mediante clustering sobre PCA con los perfiles categóricos revelados por el MCA. Esta integración permite interpretar los grupos desde una perspectiva multivariada, combinando variables numéricas y categóricas para una lectura más rica del comportamiento del mercado inmobiliario.

Interpretación cruzada

El Clúster 1, caracterizado por propiedades de tamaño medio y precios moderados, se ubica principalmente en zonas de estrato medio y régimen de propiedad. En el espacio MCA, estos individuos se agrupan cerca de las categorías “Apartamento” y “Propiedad”.

El Clúster 2 agrupa inmuebles más económicos, con menor área y número de habitaciones. En el MCA, se asocian con zonas periféricas, estratos bajos y régimen de arriendo, mostrando un perfil más accesible.

El Clúster 3 representa propiedades de gran tamaño y alto valor, ubicadas en zonas centrales y de estrato alto. En el MCA, se alinean con categorías como “Casa”, “Estrato 5-6” y “Propiedad”, reflejando un segmento premium.

Esta correspondencia entre los clústeres numéricos y los perfiles categóricos refuerza la validez de la segmentación y permite una interpretación más completa de los grupos.

6.1. Visualización de clústeres (PCA) en el espacio MCA

res.pca <- PCA(as.data.frame(datos_pca_scaled), graph = FALSE)
coords_pca <- res.pca$ind$coord
res.kmeans <- kmeans(coords_pca[, 1:2], centers = 3)
cluster_pca <- res.kmeans$cluster
# Extraer coordenadas MCA
coords_mca_df <- as.data.frame(res.mca$ind$coord)

# Renombrar columnas para compatibilidad con ggplot
names(coords_mca_df) <- gsub(" ", ".", names(coords_mca_df))

# Añadir etiquetas de clúster
coords_mca_df$cluster_pca <- factor(cluster_pca)

# Visualización
library(ggplot2)

ggplot(coords_mca_df, aes(x = Dim.1, y = Dim.2, color = cluster_pca)) +
  geom_point(alpha = 0.7, size = 2) +
  labs(title = "Punto 6: Clústeres PCA proyectados en espacio MCA",
       x = "Dimensión 1 (MCA)", y = "Dimensión 2 (MCA)",
       color = "Clúster PCA") +
  theme_minimal() +
  scale_color_brewer(palette = "Set1")

En el gráfico se visualiza la distribución de los clústeres obtenidos mediante PCA, proyectados sobre el espacio generado por el Análisis de Correspondencias Múltiples (MCA). Esta representación permite evaluar la coherencia entre las agrupaciones numéricas y la estructura latente de las variables categóricas. Se observa que los clústeres presentan cierta separación en las primeras dos dimensiones del MCA, lo que sugiere que las categorías contribuyen diferencialmente a la segmentación observada.

  1. Conclusiones Finales y Recomendaciones

7.1. Sintesis

ste estudio integró técnicas de reducción dimensional (PCA para variables numéricas y MCA para categóricas) con métodos de agrupamiento, logrando una segmentación multivariada de los clientes o propiedades. La combinación de enfoques permitió una caracterización más profunda y accionable de los grupos identificados.

7.2. Conclusiones Finales

7.3. Recomendaciones

Este análisis proporciona a la empresa inmobiliaria una herramienta poderosa para entender su mercado desde múltiples dimensiones, permitiendo decisiones más informadas, segmentadas y rentables. En un entorno competitivo y cambiante, la capacidad de adaptar estrategias basadas en datos multivariados es clave para maximizar beneficios y optimizar recursos.