CASO

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 consiste 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:

-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.

-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.

-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 el mercado inmobiliario.

-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.

Instalación de paquetes y librerías

library(paqueteMODELOS)
library(dplyr)
library(ggplot2)
library(FactoMineR)
library(factoextra)
library(cluster)
data("vivienda")
vivienda <- as.data.frame(vivienda)
str(vivienda)
## 'data.frame':    8322 obs. of  13 variables:
##  $ id          : num  1147 1169 1350 5992 1212 ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  NA NA NA "02" ...
##  $ estrato     : num  3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr  "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  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>
View(vivienda)

Procedemos con una limpieza de datos para garantizar la calidad del análisis. Imputamos valores faltantes; la variable piso, estrato, parqueaderos, habitaciones y tipo por la moda de los datos, debido a que lo más probable es que ese sea el valor que corresponde.La variables precio y area la imputamos por la media debudi a que van a ser establecidas espus como variables numéricas y por último, para los NA de las variables barrio longitud y latitud simplemente eliminamos el registro completa que tenga NA debido a que eran muy pocos y son valores que no son sensatos de imputar por media o moda.

vivienda1 <- vivienda

vivienda1 <- vivienda1[!is.na(vivienda1$id), ]

vivienda1$piso[is.na(vivienda1$piso)] <- names(which.max(table(vivienda1$piso)))
vivienda1$estrato[is.na(vivienda1$estrato)] <- names(which.max(table(vivienda1$estrato)))
vivienda1$parqueaderos[is.na(vivienda1$parqueaderos)] <- as.numeric(names(which.max(table(vivienda1$parqueaderos))))
vivienda1$banios[is.na(vivienda1$banios)] <- as.numeric(names(which.max(table(vivienda1$banios))))
vivienda1$habitaciones[is.na(vivienda1$habitaciones)] <- as.numeric(names(which.max(table(vivienda1$habitaciones))))
vivienda1$tipo[is.na(vivienda1$tipo)] <- names(which.max(table(vivienda1$tipo)))

vivienda1$preciom[is.na(vivienda1$preciom)] <- mean(vivienda1$preciom, na.rm = TRUE)
vivienda1$areaconst[is.na(vivienda1$areaconst)] <- mean(vivienda1$areaconst, na.rm = TRUE)


vivienda1 <- vivienda1[!is.na(vivienda1$barrio), ]
vivienda1 <- vivienda1[!is.na(vivienda1$latitud), ]
vivienda1 <- vivienda1[!is.na(vivienda1$longitud), ]

A continuación se puede visualizar los cambios realizados

summary(vivienda1)
##        id           zona               piso             estrato         
##  Min.   :   1   Length:8319        Length:8319        Length:8319       
##  1st Qu.:2080   Class :character   Class :character   Class :character  
##  Median :4160   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :4160                                                           
##  3rd Qu.:6240                                                           
##  Max.   :8319                                                           
##     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 : 1.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.674   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  
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8319        Length:8319        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  
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498
str(vivienda1)    
## 'data.frame':    8319 obs. of  13 variables:
##  $ id          : num  1147 1169 1350 5992 1212 ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  "02" "02" "02" "02" ...
##  $ estrato     : chr  "3" "3" "3" "4" ...
##  $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr  "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  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>

Ahora bien, es importante eliminar cualquier posibilidad de erores tipográficos, y a continuación garantizamos la calidad de los matos por ese medio

#Eliminar errores tipográficos en columna barrio

vivienda1$barrio <- vivienda1$barrio %>%
  tolower() %>%                      
  trimws() %>%                       
  gsub("[^a-záéíóúñ ]", "", .) %>%   
  gsub("aguablanca|agua blanca", "aguablanca", .) %>%  
  gsub("alf@rez real", "alférez real", .) %>%         
  gsub("alfonso lopez|alfonso lópez", "alfonso lópez", .) 

A continuación garantizamos que las variables id, piso, precio, areaconst, parqueaderos, banios, habitaciones, longitud y latitud sean numericas mientras que las demás serán cateóricas.

# Establecer tipo de variable

vivienda1 <- vivienda1 %>%
  mutate (
    id = as.numeric(id),
    zona = as.factor(zona),
    piso = as.numeric(piso),
    estrato = as.factor(estrato),
    preciom = as.numeric(preciom),
    areaconst = as.numeric(areaconst),
    parqueaderos = as.numeric(parqueaderos),
    banios = as.numeric(banios),
    habitaciones = as.numeric(habitaciones),
    tipo = as.factor(tipo),
    barrio = as.factor(barrio),
    longitud = as.numeric(longitud),
    latitud = as.numeric(latitud)
    
  )

Vamos a eliminar valores raros o atípicos. En el caso de la variable pisos vamos a eliminar las casas que tengán 5 o más, debido a que en la reañidad esto no tendría mucho sentido, con respecto a la variable baños eliminaremos los que tengan más de 8, y habitaciones más de 10.

#piso
unique(vivienda1$piso)
##  [1]  2  1  3  4  5  6  7  8  9 10 11 12
table(vivienda1$piso, vivienda1$tipo)
##     
##      Apartamento Casa
##   1          430  430
##   2         1893 2192
##   3          573  524
##   4          545   62
##   5          564    3
##   6          243    2
##   7          200    4
##   8          211    0
##   9          146    0
##   10         128    2
##   11          84    0
##   12          83    0
table(vivienda1$piso)
## 
##    1    2    3    4    5    6    7    8    9   10   11   12 
##  860 4085 1097  607  567  245  204  211  146  130   84   83
vivienda1 <- vivienda1[!(vivienda1$tipo == "Casa" & vivienda1$piso >= 5), ]

table(vivienda1$piso, vivienda1$tipo)
##     
##      Apartamento Casa
##   1          430  430
##   2         1893 2192
##   3          573  524
##   4          545   62
##   5          564    0
##   6          243    0
##   7          200    0
##   8          211    0
##   9          146    0
##   10         128    0
##   11          84    0
##   12          83    0
str(vivienda1) 
## 'data.frame':    8308 obs. of  13 variables:
##  $ id          : num  1147 1169 1350 5992 1212 ...
##  $ zona        : Factor w/ 5 levels "Zona Centro",..: 4 4 4 5 2 2 2 2 2 2 ...
##  $ piso        : num  2 2 2 2 1 1 1 1 2 2 ...
##  $ estrato     : Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
##  $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : Factor w/ 2 levels "Apartamento",..: 2 2 2 2 1 1 1 1 2 2 ...
##  $ barrio      : Factor w/ 403 levels " de julio","acopi",..: 1 1 1 1 2 2 2 2 2 2 ...
##  $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num  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>
# banios
unique(vivienda1$banios)
##  [1]  3  2  5  4  7  6  1  0  8 10  9
table(vivienda1$banios)
## 
##    0    1    2    3    4    5    6    7    8    9   10 
##   45  496 2944 1992 1455  887  313  105   48   15    8
vivienda1 <- vivienda1[vivienda1$banios < 8, ]
table(vivienda1$banios)
## 
##    0    1    2    3    4    5    6    7 
##   45  496 2944 1992 1455  887  313  105
#habitaciones
unique(vivienda1$habitaciones)
##  [1]  6  3  4  5  2  0  1  8  7  9 10
table(vivienda1$habitaciones)
## 
##    0    1    2    3    4    5    6    7    8    9   10 
##   65   59  924 4095 1726  671  307  164  125   65   36
vivienda1 <- vivienda1[vivienda1$habitaciones < 10, ]
table(vivienda1$habitaciones)
## 
##    0    1    2    3    4    5    6    7    8    9 
##   65   59  924 4095 1726  671  307  164  125   65
colSums(is.na(vivienda1))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0

Análisis de componenetes principales

Para efecto de este análisis haremos uso de las variables cuantitativas, razón por la cual, construiremos una tabla con ellas

variables_numericas <- vivienda1 %>% 
  select(preciom, areaconst, parqueaderos, piso, banios, habitaciones) 

Posteriormente ralizamos la normalización de lo datos

variables_numericas_norm <- scale(variables_numericas)

En la siguiente tabla podemos visualizar la matriz de covarianza donde podemos observar que PC1 está fuertemente influenciado por la variable preciom, areaconst, parqueaderos y banios. Esto indica que PC1 representa un eje de tamaño y valor de la vivienda. PC2 tiene alta carga en piso y negativa en habitaciones, lo que sugiere que este componente representa la altura y número de habitaciones de la vivienda. PC3 destaca en parqueaderos y habitaciones, indicando una posible relación contraria entre estos factores. piso, tiene baja influencia en PC1, lo que indica que no es un factor importante en la diferenciación de las viviendas.

respca <- prcomp(variables_numericas_norm, scale = TRUE)
head(respca$rotation)[, 1:5]
##                      PC1          PC2         PC3         PC4        PC5
## preciom       0.47454983  0.285935033  0.23606151  0.31807911  0.2042062
## areaconst     0.48363215 -0.065610706  0.01317038  0.54264228 -0.5642409
## parqueaderos  0.42391288  0.293745098  0.35196252 -0.72123564 -0.2760425
## piso         -0.09962773  0.790016155 -0.58754451  0.03387721 -0.1361340
## banios        0.48708567  0.005239601 -0.21577635 -0.01455173  0.7112237
## habitaciones  0.33767163 -0.451106673 -0.65456543 -0.28779295 -0.1983959
acp <- PCA(variables_numericas_norm, graph = FALSE)

screeplot

Visualizamos el screeplot donde observamos que Se debe reducir la dimensionalidad del dataset manteniendo solo PC1 y PC2, ya que explican la mayoría de la variabilidad en los datos.

fviz_eig(respca, addlabels = TRUE, ylim = c(0, 100))

## contribución de las variables

Visualizamos en la contribución de las variables a los componentes principales. Aquí pareciera ser que los compradores están más influenciado por el precio y tamaño de las viviendas y no tanto por su ubicación en pisos altos.

fviz_pca_var(acp, col.var = "contrib", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE)

## gráfico de individuos

Observamos el gráfico de individuos en el espacio que la mayoría de los puntos están concentrados en el centro, lo que indica que la mayoría de las viviendas tienen características similares. El color indica el cos2 (calidad de representación), donde los puntos en rojo están mejor representados en el PCA.

fviz_pca_ind(acp, geom.ind = "point", col.ind = "cos2", 
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"), repel = TRUE)

## Biplot

En el siguiente Biplot El PCA no segmenta de forma clara los tipos de vivienda, por lo que podríamos explorar otras formas de análisis

fviz_pca_biplot(acp,
                repel = TRUE,
                habillage = vivienda1$tipo,   
                col.var = "#2E9FDF",                  
                col.ind = c("#696969", "#E74C3C")) 

print(fviz_pca_biplot)
## function (X, axes = c(1, 2), geom = c("point", "text"), geom.ind = geom, 
##     geom.var = c("arrow", "text"), col.ind = "black", fill.ind = "white", 
##     col.var = "steelblue", fill.var = "white", gradient.cols = NULL, 
##     label = "all", invisible = "none", repel = FALSE, habillage = "none", 
##     palette = NULL, addEllipses = FALSE, title = "PCA - Biplot", 
##     ...) 
## {
##     is.individuals.colored.by.variable <- .is_grouping_var(fill.ind) | 
##         .is_grouping_var(col.ind)
##     is.variables.colored.by.variable <- .is_continuous_var(col.var) | 
##         .is_grouping_var(col.var)
##     is.gradient.color <- .is_continuous_var(col.ind) | .is_continuous_var(col.var)
##     is.gradient.fill <- .is_continuous_var(fill.ind) | .is_continuous_var(fill.var)
##     is.discrete.color <- .is_grouping_var(col.ind) | .is_grouping_var(habillage) | 
##         .is_grouping_var(col.var)
##     is.discrete.fill <- .is_grouping_var(fill.ind) | .is_grouping_var(fill.var) | 
##         .is_grouping_var(habillage) | (.is_grouping_var(col.ind) & 
##         addEllipses)
##     var <- facto_summarize(X, element = "var", result = c("coord", 
##         "contrib", "cos2"), axes = axes)
##     colnames(var)[2:3] <- c("x", "y")
##     pca.ind <- get_pca_ind(X)
##     ind <- data.frame(pca.ind$coord[, axes, drop = FALSE], stringsAsFactors = TRUE)
##     colnames(ind) <- c("x", "y")
##     r <- min((max(ind[, "x"]) - min(ind[, "x"])/(max(var[, "x"]) - 
##         min(var[, "x"]))), (max(ind[, "y"]) - min(ind[, "y"])/(max(var[, 
##         "y"]) - min(var[, "y"]))))
##     ellipse.border.remove <- FALSE
##     if (is.individuals.colored.by.variable & is.variables.colored.by.variable) 
##         ellipse.border.remove <- TRUE
##     p <- fviz_pca_ind(X, axes = axes, geom = geom.ind, repel = repel, 
##         col.ind = col.ind, fill.ind = fill.ind, label = label, 
##         invisible = invisible, habillage = habillage, addEllipses = addEllipses, 
##         ellipse.border.remove = ellipse.border.remove, ...)
##     p <- fviz_pca_var(X, axes = axes, geom = geom.var, repel = repel, 
##         col.var = col.var, fill.var = fill.var, label = label, 
##         invisible = invisible, scale. = r * 0.7, ggp = p, ...)
##     if (!is.null(gradient.cols)) {
##         if (is.gradient.color) 
##             p <- p + ggpubr::gradient_color(gradient.cols)
##         if (is.gradient.fill) 
##             p <- p + ggpubr::gradient_fill(gradient.cols)
##     }
##     if (!is.null(palette)) {
##         if (is.discrete.color) 
##             p <- p + ggpubr::color_palette(palette)
##         if (is.discrete.fill) 
##             p <- p + ggpubr::fill_palette(palette)
##     }
##     p + labs(title = title)
## }
## <bytecode: 0x000001d779b2fd10>
## <environment: namespace:factoextra>

Los conglomerados para este caso no presentaron patrones claros que pudieramos identificar, procederemos entones con el análisis de correspondencia.

Análisis de correspondencia

Seleccionamos las variables categóricas de interés

mca_data <- vivienda1 %>%
  select(tipo, zona, estrato) %>%
  mutate(across(everything(), as.factor))

Realizar el MCA

mca_result <- MCA(mca_data, graph = FALSE)

Creamos el Scree Plot donde se observa cómo se distribuye la varianza explicada entre los ejes. En este caso, El Dim1 captura el 21.1% de la varianza total. El Dim2 captura el 17%, y juntos estos dos ejes explican el 38.1% de la varianza. Los valores de los ejes restantes tienen menos peso, lo que sugiere que Dim1 y Dim2 son los más relevantes para interpretar los datos

fviz_screeplot(
  mca_result,
  addlabels = TRUE,
  ylim = c(0, 100),
  ggtheme = theme_minimal(),
  barfill = "steelblue",
  barcolor = "black",
  title = "Scree Plot: Varianza Explicada por Ejes"
)

Creamos un Biplot

fviz_mca_var(
  mca_result, 
  repel = TRUE
) + 
  ggtitle("Análisis de Correspondencias Múltiples: Tipo, Zona y Estrato")

A continuación se oberva que Dim1 Captura el 69.6% de la variación en los datos, probablemente asociada a las diferencias entre las zonas y sus combinaciones de estrato. Dim2 Captura una el 28% de la variación, más relacionada con características específicas de ciertos estratos o zonas. Zonas alejadas del centro como la Zona Oeste están más asociadas a estratos altos Zonas como Zona oriente y Zona centro están relacionadas con estratos bajos.

tabla_ampliada <- table(vivienda1$zona, vivienda1$estrato)
res_ac_ampliado <- CA(tabla_ampliada, graph = FALSE)
fviz_ca_biplot(res_ac_ampliado, axes = c(1, 2), repel = TRUE) +
  ggtitle("Análisis de Correspondencia Ampliado: Zona vs Estrato") +
  theme_minimal()

A continuación haremos un experimento, creando una categoría de precio alto precio medio y precio alto, aumentando o disminuyendo un 30% como criterio. Efectivamente vemos como la zona Oriente está muy cerca de la categoría “Alto” y zona Norte de la categoría “bajo”.

# Crear categorías de precios
vivienda1$precio_cat <- cut(vivienda1$preciom, 
                            breaks = quantile(vivienda1$preciom, probs = seq(0, 1, 0.33), na.rm = TRUE), 
                            labels = c("Bajo", "Medio", "Alto"), include.lowest = TRUE)

tabla <- table(vivienda1$zona, vivienda1$precio_cat)
res_ac <- CA(tabla, graph = FALSE)
fviz_ca_biplot(res_ac, repel = TRUE) +
  ggtitle("Análisis de Correspondencia: Zona vs Categorías de Precio") +
  theme_minimal()

En la siguiente gráfica podemos ver que se considera un precio “Alto” para el estrato 6 y “bajo” para estratos 4 y 3. Esto ya lo intuíamos pero se consideró importante hacerlo para darle solidez a las afirmaciones que hemos hecho.

tabla <- table(vivienda1$estrato, vivienda1$precio_cat)
res_ac <- CA(tabla, graph = FALSE)
fviz_ca_biplot(res_ac, repel = TRUE) +
  ggtitle("Análisis de Correspondencia: Estrato vs Categorías de Precio") +
  theme_minimal()

Recomendaciones al Gerente

Debido a que el informe arroja que las viviendas menos apetecidas se encuentran en la zona centro y Oriente, sería recomendable generar ofertas atratcivas para no perder la inversión en estos sectores.

Para los aspirantes a las viviendas de la Zona Oeste se podría realizar una inversión para brindarle más lujo a las viviendas, de esa manera no solo comprarían por las razones que analizamos anteriormente, sino que además al aumenta la percepción de “lujo” podrían estar dispuestos a pagar un poco más.