Unidad 1. Métodos estadisticos para la toma de desiciones Evaluación de la oferta inmobiliaria urbana

Contexto

La actividad se va a desarrollar en el concepto de Una destacada empresa del sector inmobiliario en una metrópoli está interesada en obtener un conocimiento profundo del mercado de viviendas urbanas para tomar decisiones estratégicas más fundamentadas. La compañía cuenta con una amplia base de datos que incluye información detallada sobre diversas propiedades residenciales disponibles en el mercado. Es necesario llevar a cabo un análisis integral de estos datos para identificar patrones, relaciones y segmentaciones significativas que faciliten la mejora en la toma de decisiones relacionadas con la compra, venta y valoración de propiedades.

Metodología

En el proceso de indentificar la toma de desiciones relacionadas con la valorizacion de las propiedades de finca raiz para compra y venta se van a realizar los siguientes pasos:

  1. Análisis descriptivo de las variables de la base de datos Vivienda suministrada.

  2. Imputación de Datos Faltantes: Se imputaron los datos faltantes utilizando la mediana de cada columna y la mode en los ddatos categoricos.

  3. Eliminación de Variables con Más Faltantes: Se eliminaron las columnas que tienen más del 50% de datos faltantes.

  4. Selección de Columnas Numéricas: Se seleccionaron solo las columnas numéricas para el análisis inicial.

  5. Escalado de Características: Se escalaron las características para que tengan media 0 y desviación estándar 1.

  6. Análisis de Componentes Principales (PCA): Se realizó el PCA.

  7. Análisis de Conglomerados: Se calculó la matriz de distancias y se realizó el clustering jerárquico, generando un dendrograma.

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.

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.

Desarrollo y resultados

# Instalar paquetes

knitr::opts_chunk$set(echo = TRUE)
library(paqueteMODELOS)
## Cargando paquete requerido: boot
## Cargando paquete requerido: broom
## Cargando paquete requerido: GGally
## Cargando paquete requerido: ggplot2
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Cargando paquete requerido: gridExtra
## Cargando paquete requerido: knitr
## Cargando paquete requerido: summarytools
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.2
## 
## Adjuntando el paquete: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
library(textshape)
## Warning: package 'textshape' was built under R version 4.4.2
## 
## Adjuntando el paquete: 'textshape'
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following object is masked from 'package:gridExtra':
## 
##     combine
library(mice)
## Warning: package 'mice' was built under R version 4.4.2
## 
## Adjuntando el paquete: 'mice'
## The following object is masked from 'package:stats':
## 
##     filter
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
library(factoextra)
## Warning: package 'factoextra' was built under R version 4.4.2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(FactoMineR)
## Warning: package 'FactoMineR' was built under R version 4.4.2
library(corrr)
## Warning: package 'corrr' was built under R version 4.4.2
library(ca)
## Warning: package 'ca' was built under R version 4.4.2

Cargue de datos

library(paqueteMODELOS)
library(paqueteMODELOS)
data("vivienda")

Análisis descriptivo

data(vivienda)
str(vivienda)
## 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>
head(vivienda)
## # 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>
summary(vivienda)
##        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

Para el analisis se identifican las variables, y se observa la base de datos tiene la variable piso como númerica, por lo cual, se va a realizar una modificación a númerica

Tabla 1. Descripcion de las variables y los datos faltantes

# Calcula la cantidad de NA's por columna
na_counts <- colSums(is.na(vivienda))

# Filtra SOLO las variables que tienen al menos un NA
variables_con_na <- names(na_counts[na_counts > 0])

# Crea la tabla SOLO con las variables que tienen NA's
tabla_faltantes <- vivienda %>%
  select(all_of(variables_con_na)) %>% # Selecciona solo las columnas con NAs
  summarise(across(everything(), ~sum(is.na(.)))) %>%
  pivot_longer(everything(), names_to = "variable", values_to = "cantidad_faltante") %>%
  mutate(porcentaje_faltante = (cantidad_faltante / nrow(vivienda)) * 100) %>% # Calcula el porcentaje
  arrange(desc(cantidad_faltante))

print(tabla_faltantes)
## # A tibble: 13 × 3
##    variable     cantidad_faltante porcentaje_faltante
##    <chr>                    <int>               <dbl>
##  1 piso                      2638             31.7   
##  2 parqueaderos              1605             19.3   
##  3 id                           3              0.0360
##  4 zona                         3              0.0360
##  5 estrato                      3              0.0360
##  6 areaconst                    3              0.0360
##  7 banios                       3              0.0360
##  8 habitaciones                 3              0.0360
##  9 tipo                         3              0.0360
## 10 barrio                       3              0.0360
## 11 longitud                     3              0.0360
## 12 latitud                      3              0.0360
## 13 preciom                      2              0.0240

Descripción de los datos

La base de datos presenta 8,322 registros y 13 columnas. Las variables observadas fueron:

Variables numéricas: piso, estrato, preciom (precio en millones), areaconst (área construida), parqueaderos, banios, habitaciones, longitud, latitud.

Variables categóricas: zona, tipo, barrio.

Datos faltantes: Algunas columnas tienen valores nulos (piso, parqueaderos, etc.), lo que podría afectar los análisis, por eso se toma la desicion de imputar las que tienen menos del 20% de pérdida y de eliminar las que superan esta pedirda. Tambiés se eliminó la variabe id ya que no aporta al analisis.

# 1. Eliminar la variable "piso y ID"
vivienda <- vivienda %>%
  select(-piso)

vivienda <- vivienda %>%
  select(-id)

# 2. Imputar valores numéricos con la media
vivienda <- vivienda %>%
  mutate(across(where(is.numeric), ~ifelse(is.na(.), mean(., na.rm = TRUE), .)))

# Verificar los cambios
summary(vivienda)
##      zona              estrato         preciom         areaconst     
##  Length:8322        Min.   :3.000   Min.   :  58.0   Min.   :  30.0  
##  Class :character   1st Qu.:4.000   1st Qu.: 220.0   1st Qu.:  80.0  
##  Mode  :character   Median :5.000   Median : 330.0   Median : 123.0  
##                     Mean   :4.634   Mean   : 433.9   Mean   : 174.9  
##                     3rd Qu.:5.000   3rd Qu.: 540.0   3rd Qu.: 229.0  
##                     Max.   :6.000   Max.   :1999.0   Max.   :1745.0  
##   parqueaderos        banios        habitaciones        tipo          
##  Min.   : 1.000   Min.   : 0.000   Min.   : 0.000   Length:8322       
##  1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000   Class :character  
##  Median : 1.835   Median : 3.000   Median : 3.000   Mode  :character  
##  Mean   : 1.835   Mean   : 3.111   Mean   : 3.605                     
##  3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000                     
##  Max.   :10.000   Max.   :10.000   Max.   :10.000                     
##     barrio             longitud         latitud     
##  Length:8322        Min.   :-76.59   Min.   :3.333  
##  Class :character   1st Qu.:-76.54   1st Qu.:3.381  
##  Mode  :character   Median :-76.53   Median :3.416  
##                     Mean   :-76.53   Mean   :3.418  
##                     3rd Qu.:-76.52   3rd Qu.:3.452  
##                     Max.   :-76.46   Max.   :3.498
# Calcular frecuencias para variables categóricas
table(vivienda$zona)
## 
##  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur 
##          124         1920         1198          351         4726
table(vivienda$tipo)
## 
## Apartamento        Casa 
##        5100        3219
# Histograma de la variable Precio
ggplot(vivienda, aes(x = preciom)) +
  geom_histogram(binwidth = 50, fill = "blue", color = "black") +
  labs(title = "Distribución de Precios", x = "Precio", y = "Frecuencia")

# Boxplot de la variable Área Construida
ggplot(vivienda, aes(y = areaconst)) +
  geom_boxplot(fill = "orange", color = "black") +
  labs(title = "Boxplot de Área Construida", y = "Área Construida")

Análisis de componentes principales

str(vivienda)
## spc_tbl_ [8,322 × 11] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ 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>
# Convertir variables categóricas en FACTORES
vivienda$zona <- as.factor(vivienda$zona)
vivienda$estrato <- as.factor(vivienda$estrato)
vivienda$tipo <- as.factor(vivienda$tipo)
vivienda$barrio <- as.factor(vivienda$barrio)
vivienda_numeric <- vivienda[sapply(vivienda, is.numeric)]
md.pattern(vivienda_numeric)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      preciom areaconst parqueaderos banios habitaciones longitud latitud  
## 8322       1         1            1      1            1        1       1 0
##            0         0            0      0            0        0       0 0
# Realizar el PCA
pca_result <- prcomp(vivienda_numeric, scale. = TRUE)


# Resumen del PCA
summary(pca_result)
## Importance of components:
##                           PC1    PC2    PC3    PC4     PC5     PC6     PC7
## Standard deviation     1.8047 1.1128 0.9200 0.8525 0.64077 0.57489 0.43646
## Proportion of Variance 0.4653 0.1769 0.1209 0.1038 0.05865 0.04721 0.02721
## Cumulative Proportion  0.4653 0.6422 0.7631 0.8669 0.92557 0.97279 1.00000
# Scree plot
screeplot(pca_result, type = "lines", main = "Scree Plot")

# Biplot
biplot(pca_result, main = "Biplot of PCA")

# Visualización con ggplot2
pca_data <- as.data.frame(pca_result$x)
pca_data$ID <- rownames(pca_data)  # Add an ID column for labeling

ggplot(pca_data, aes(x = PC1, y = PC2)) +
  geom_point(aes(color = ID), size = 2) +
  labs(title = "PCA of Vivienda Dataset", x = "Principal Component 1", y = "Principal Component 2") +
  theme_minimal()

prcomp(vivienda_numeric)
## Standard deviations (1, .., p=7):
## [1] 344.38690046  99.06645846   1.38820957   0.79425941   0.74474686
## [6]   0.04204346   0.01577032
## 
## Rotation (n x k) = (7 x 7):
##                        PC1           PC2           PC3           PC4
## preciom       9.499401e-01  3.124218e-01 -0.0004161614  0.0024643321
## areaconst     3.124106e-01 -9.499174e-01 -0.0074257181  0.0003769577
## parqueaderos  1.879510e-03 -7.374523e-04  0.0335527569 -0.7321236699
## banios        2.863544e-03 -2.816101e-03  0.5468928721 -0.5562807713
## habitaciones  1.298375e-03 -6.406422e-03  0.8364968723  0.3930624744
## longitud     -1.686869e-05 -2.070588e-05  0.0001855085  0.0011329214
## latitud      -1.381126e-05 -2.097684e-05 -0.0001810464  0.0064053169
##                        PC5           PC6           PC7
## preciom       0.0003867844  3.929456e-06  1.856633e-05
## areaconst    -0.0011208300 -1.580882e-05 -9.373124e-06
## parqueaderos  0.6803384489  1.914086e-03 -9.996347e-04
## banios       -0.6256211301  6.302431e-03  1.496836e-03
## habitaciones  0.3817260093 -4.001333e-03 -1.178572e-03
## longitud      0.0024257259  8.811462e-02  9.961067e-01
## latitud       0.0039699192  9.960805e-01 -8.812922e-02
# Seleccionar solo las variables numéricas para PCA
vivienda_numeric <- vivienda[, sapply(vivienda, is.numeric)]

# Identificar las filas completas (sin valores faltantes)
filas_completas <- complete.cases(vivienda_numeric)

# Eliminar filas con valores faltantes del dataset original
vivienda <- vivienda[filas_completas, ]

# Realizar PCA
pca_result <- prcomp(vivienda_numeric[filas_completas, ], scale. = TRUE)

# Visualizar los resultados de PCA con fviz_pca_biplot
fviz_pca_biplot(pca_result, 
                repel = TRUE,
                habillage = vivienda$tipo, # Usar la variable 'tipo' para colorear
                col.var = "#034A94", # Color de las variables
                col.ind = c("#DEDEDE", "#034A94")) # Color de los individuos
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

Elección de componenetes principales

data("vivienda")
data(vivenda)
## Warning in data(vivenda): data set 'vivenda' not found
# Escalar las columnas numéricas
vivienda_numeric <- scale(vivienda_numeric[, 2:5])

# Realizar el PCA
res.pca <- prcomp(vivienda_numeric)

# Visualizar la varianza explicada
library(factoextra)
fviz_eig(res.pca, addlabels = TRUE)

Se observa como el primer componente explica el 56% de la variabilidad contenida en la base de datos, y entre las cuatro, un porcentaje alto.

fviz_pca_var(res.pca,
col.var = "contrib", # Color by contributions to the PC
gradient.cols = c("#FF7F00",  "#034D94"),
repel = TRUE     # Avoid text overlapping
)

Al visualizar las variables en el plano de los componentes principales permite identificar el sentido y la caracterización de los componentes asociado al área de construcción, parqueaderos y precio.

datos<- rbind(vivienda_numeric[98,], # ok
vivienda_numeric[12,],
vivienda_numeric[6,],
vivienda_numeric[250,])

datos <- as.data.frame(datos)
rownames(datos) = c("Vivienda 98","Vivienda 12","Vivienda 6","Vivienda 250")
datos
##               areaconst parqueaderos      banios habitaciones
## Vivienda 98  -0.8110835    0.0000000 -0.77825510   -0.4148373
## Vivienda 12   1.2597393    1.1525734  1.32265485    0.9557075
## Vivienda 6   -0.6151949   -0.8264234 -0.07795178   -0.4148373
## Vivienda 250  0.4551967    1.1525734  0.62235154   -0.4148373
casos1 <- rbind(res.pca$x[98,1:2],res.pca$x[12,1:2]) # CP1
rownames(casos1) = c("98","299")
casos1 <- as.data.frame(casos1)

casos2 <- rbind(res.pca$x[6,1:2], res.pca$x[250,1:2]) # CP2
rownames(casos2) = c("6","190")
casos2 <- as.data.frame(casos2)

fviz_pca_ind(res.pca, col.ind = "#DEDEDE", gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07")) +
geom_point(data = casos1, aes(x = PC1, y = PC2), color = "red", size = 3) +
geom_point(data = casos2, aes(x = PC1, y = PC2), color = "blue", size = 3)

# Visualizar los resultados de PCA con fviz_pca_biplot
fviz_pca_biplot(pca_result, 
                repel = TRUE,
                habillage = vivienda$tipo, # Usar la variable 'tipo' para colorear
                col.var = "#034A94", # Color de las variables
                col.ind = c("#DEDEDE", "#034A94")) # Color de los individuos
## Warning: Removed 3 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

Analisis de conglomerados

# Análisis de Conglomerados
# Seleccionar variables numéricas relevantes para el análisis de clúster
vivienda_numeric <- vivienda[, c("preciom", "areaconst", "estrato", "habitaciones", "banios")]

# Identificar las filas completas (sin valores faltantes)
filas_completas <- complete.cases(vivienda_numeric)

# Eliminar filas con valores faltantes del dataset original
vivienda <- vivienda[filas_completas, ]

# Estandarizar los datos
vivienda_scaled <- scale(vivienda_numeric[filas_completas, ])

# Método del codo para determinar el número óptimo de clústeres
wss <- (nrow(vivienda_scaled) - 1) * sum(apply(vivienda_scaled, 2, var))
for (i in 2:15) wss[i] <- sum(kmeans(vivienda_scaled, centers = i)$withinss)
## Warning: did not converge in 10 iterations
# Graficar el método del codo
plot(1:15, wss, type = "b", xlab = "Número de Clústeres", ylab = "Suma de Cuadrados Dentro del Clúster", main = "Método del Codo")

# Aplicar k-means con el número óptimo de clústeres (por ejemplo, 4)
set.seed(123)
kmeans_result <- kmeans(vivienda_scaled, centers = 4)

# Añadir los clústeres al dataset original
vivienda$Cluster <- as.factor(kmeans_result$cluster)

# Visualizar los clústeres en un gráfico
ggplot(vivienda, aes(x = areaconst, y = preciom, color = Cluster)) +
  geom_point() +
  labs(title = "Análisis de Conglomerados de Propiedades Residenciales", x = "Área Construida", y = "Precio")