Introducción

La dinámica del mercado inmobiliario en las ciudades se ha vuelto cada vez más compleja debido a la multiplicidad de factores que influyen en la oferta y la demanda de vivienda. La ciudad de Cali, no es ajena a esta realidad. Entender las características del mercado inmobiliario de esta ciudad es esencial para poder ofrecer una oferta adecuada y alineada con las necesidades y preferencias de los potenciales compradores.

En este contexto, el presente trabajo tiene como objetivo realizar una evaluación detallada de la oferta inmobiliaria urbana en Cali utilizando modelos multivariados. Se analizaron diversos factores como la zona, el número de pisos, la cantidad de baños, el precio, el área, el número de parqueaderos y habitaciones, entre otros, para identificar patrones y segmentar el mercado en clústeres que permiten una mejor comprensión de las tendencias actuales.

Para lograr estos objetivos, se emplearon enfoques estadísticos avanzados y de machine learning, incluyendo técnicas como el análisis de componentes principales (PCA), el clustering y el análisis de correspondencia. Este último fue fundamental para explorar y visualizar las asociaciones entre las diferentes variables categóricas, ofreciendo una perspectiva más profunda sobre cómo interactúan ciertos atributos de las propiedades. Estos métodos permitieron no solo reducir la dimensionalidad de los datos, sino también agrupar las viviendas en categorías similares, facilitando la identificación de patrones en la oferta inmobiliaria.

Preprocesamiento de datos

Antes de llevar a cabo los análisis mencionados, es fundamental realizar un preprocesamiento de los datos para asegurar que la base esté preparada para los distintos procesos. Este paso inicial incluye una exploración exhaustiva de la base de datos con el objetivo de conocer las variables presentes y determinar la mejor manera de alistarlas para los análisis posteriores.

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")=
##   .. cols(
##   ..   id = col_double(),
##   ..   zona = col_character(),
##   ..   piso = col_character(),
##   ..   estrato = col_double(),
##   ..   preciom = col_double(),
##   ..   areaconst = col_double(),
##   ..   parqueaderos = col_double(),
##   ..   banios = col_double(),
##   ..   habitaciones = col_double(),
##   ..   tipo = col_character(),
##   ..   barrio = col_character(),
##   ..   longitud = col_double(),
##   ..   latitud = col_double()
##   .. )
##  - 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

Después de la exploración inicial, se procede a preparar la base de datos para los análisis. El primer paso en este proceso es definir las variables categóricas como datos de tipo factor.

vivienda$tipo =tolower(vivienda$tipo)
vivienda$barrio =tolower(vivienda$barrio)
vivienda$piso <- as.numeric(vivienda$piso)
vivienda$zona    <- as.factor(vivienda$zona)
vivienda$estrato <- as.factor(vivienda$estrato)
vivienda$tipo    <- as.factor(vivienda$tipo)
vivienda$barrio  <- as.factor(vivienda$barrio)
summary(vivienda)
##        id                 zona           piso        estrato    
##  Min.   :   1   Zona Centro : 124   Min.   : 1.000   3   :1453  
##  1st Qu.:2080   Zona Norte  :1920   1st Qu.: 2.000   4   :2129  
##  Median :4160   Zona Oeste  :1198   Median : 3.000   5   :2750  
##  Mean   :4160   Zona Oriente: 351   Mean   : 3.771   6   :1987  
##  3rd Qu.:6240   Zona Sur    :4726   3rd Qu.: 5.000   NA's:   3  
##  Max.   :8319   NA's        :   3   Max.   :12.000              
##  NA's   :3                          NA's   :2638                
##     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   apartamento:5100   valle del lili:1009   Min.   :-76.59  
##  1st Qu.: 3.000   casa       :3219   ciudad jardín : 518   1st Qu.:-76.54  
##  Median : 3.000   NA's       :   3   pance         : 412   Median :-76.53  
##  Mean   : 3.605                      la flora      : 368   Mean   :-76.53  
##  3rd Qu.: 4.000                      santa teresita: 263   3rd Qu.:-76.52  
##  Max.   :10.000                      (Other)       :5749   Max.   :-76.46  
##  NA's   :3                           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

Posteriormente, se eliminan las filas que contienen principalmente datos nulos.

nulos_por_fila <- apply(vivienda, 1, function(x) sum(is.na(x)))
nulos_df <- data.frame(fila = 1:nrow(vivienda), nulos = nulos_por_fila)
nulos_df <- nulos_df[order(-nulos_df$nulos),]
top_5_nulos <- head(nulos_df, 5)
print(top_5_nulos)
##      fila nulos
## 8320 8320    13
## 8321 8321    13
## 8322 8322    12
## 28     28     2
## 29     29     2
elim <- head(top_5_nulos,3)
filas <- elim$fila
vivienda <- vivienda[-filas,]
summary(vivienda)
##        id                 zona           piso        estrato     preciom      
##  Min.   :   1   Zona Centro : 124   Min.   : 1.000   3:1453   Min.   :  58.0  
##  1st Qu.:2080   Zona Norte  :1920   1st Qu.: 2.000   4:2129   1st Qu.: 220.0  
##  Median :4160   Zona Oeste  :1198   Median : 3.000   5:2750   Median : 330.0  
##  Mean   :4160   Zona Oriente: 351   Mean   : 3.771   6:1987   Mean   : 433.9  
##  3rd Qu.:6240   Zona Sur    :4726   3rd Qu.: 5.000            3rd Qu.: 540.0  
##  Max.   :8319                       Max.   :12.000            Max.   :1999.0  
##                                     NA's   :2635                              
##    areaconst       parqueaderos        banios        habitaciones   
##  Min.   :  30.0   Min.   : 1.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000  
##  Median : 123.0   Median : 2.000   Median : 3.000   Median : 3.000  
##  Mean   : 174.9   Mean   : 1.835   Mean   : 3.111   Mean   : 3.605  
##  3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000  
##  Max.   :1745.0   Max.   :10.000   Max.   :10.000   Max.   :10.000  
##                   NA's   :1602                                      
##           tipo                 barrio        longitud         latitud     
##  apartamento:5100   valle del lili:1009   Min.   :-76.59   Min.   :3.333  
##  casa       :3219   ciudad jardín : 518   1st Qu.:-76.54   1st Qu.:3.381  
##                     pance         : 412   Median :-76.53   Median :3.416  
##                     la flora      : 368   Mean   :-76.53   Mean   :3.418  
##                     santa teresita: 263   3rd Qu.:-76.52   3rd Qu.:3.452  
##                     el caney      : 209   Max.   :-76.46   Max.   :3.498  
##                     (Other)       :5540

Luego, se realizan ajustes a las variables de tipo carácter (char) para asegurarse de que no existan errores de digitación en la base de datos.

mapeo <- c("á" = "a", "é" = "e", "í" = "i", "ó" = "o", "ú" = "u", "ü" = "u")
vivienda$barrio = chartr(paste(names(mapeo), collapse = ""), paste(mapeo, collapse = ""), vivienda$barrio)
rm(mapeo)
vivienda$barrio <- gsub("√∫", "u", vivienda$barrio)
vivienda$barrio <- gsub("é", "e", vivienda$barrio)
vivienda$barrio <- gsub("ó", "o", vivienda$barrio)
vivienda$barrio <- gsub("ò", "a", vivienda$barrio)
vivienda$barrio <- gsub("í", "i", vivienda$barrio)

Se da inicio a la imputación de datos faltantes. Para ello, primero se asigna el piso uno a todas las viviendas de tipo casa que presentan datos faltantes en la variable “piso”.

vivienda$piso = ifelse(is.na(vivienda$piso) & vivienda$tipo == "casa",1,vivienda$piso)

en este punto se procede a verificar la cantidad de datos faltantes actuales a travez de la siguiente grafica

captured_output <- capture.output({
  md.pattern(vivienda[, 1:13], plot = TRUE, rotate.names = TRUE)
})

En este punto, se procede a realizar la imputación de la variable “parqueqderos” mediante un código que determina la moda de los parqueaderos por zonas y asigna la cantidad en función del precio de la vivienda. La hipótesis es que las viviendas con parqueadero deben tener un precio mayor en comparación con las que no lo tienen.

vivienda$NAPISO = ifelse(is.na(vivienda$piso),1,0)
vivienda$NAPARQUEA = ifelse(is.na(vivienda$parqueaderos),1,0)
vivienda$costo_metro_m = vivienda$preciom / vivienda$areaconst


# Calcular la moda y la cantidad de valores faltantes en parquea
df_resultado_estrato_parquea <- vivienda %>% 
  group_by(barrio, tipo,estrato) %>%
  reframe(nv_parqueo = sum(NAPARQUEA, na.rm = TRUE),  
          N_MODE  = Mode(parqueaderos, na.rm =TRUE))

df_resultado_estrato_parquea <- df_resultado_estrato_parquea %>% 
  rename(barrio1=barrio,
         tipo1=tipo,
         estrato1=estrato)

df_resultado_estrato_parquea$N_MODE[is.na(df_resultado_estrato_parquea$N_MODE)] <- 0
df_resultado_estrato_parquea$COSTO_METRO_CUADRADO <- NA
m<-max(df_resultado_estrato_parquea$N_MODE)
for(a in 1:m){
  for (i in 1:nrow(df_resultado_estrato_parquea)) {
    filtros <- filter(vivienda,
                      barrio == df_resultado_estrato_parquea[[i, "barrio1"]]
                      ,tipo == df_resultado_estrato_parquea[[i, "tipo1"]]
                      ,estrato == df_resultado_estrato_parquea[[i, "estrato1"]]
                      ,parqueaderos == df_resultado_estrato_parquea[[i, "N_MODE"]]
    )
    if (nrow(filtros) > 0) {
      filtros$costo_metro<-filtros$preciom / filtros$areaconst
      val=sd(filtros$costo_metro)/mean(filtros$costo_metro)
      #print(paste("entro", val))
      costo_medio  <-ifelse(val>0.20,median(filtros$costo_metro, na.rm=TRUE),mean(filtros$costo_metro, na.rm=TRUE)) 
      df_resultado_estrato_parquea[i, "COSTO_METRO_CUADRADO"] <- costo_medio
    }
  }
  df_resultado_estrato_parquea<- filter(df_resultado_estrato_parquea,
                                        !is.na(COSTO_METRO_CUADRADO))
  vivienda$NAPARQUEA = ifelse(is.na(vivienda$parqueaderos),1,0)
  for (i in 1:nrow(df_resultado_estrato_parquea)) {
    vivienda$parqueaderos <- ifelse(vivienda$barrio == df_resultado_estrato_parquea[[i, "barrio1"]] &
                                           vivienda$tipo == df_resultado_estrato_parquea[[i, "tipo1"]] &
                                           vivienda$estrato == df_resultado_estrato_parquea[[i, "estrato1"]] &
                                           vivienda$costo_metro_m >= df_resultado_estrato_parquea[[i, "COSTO_METRO_CUADRADO"]] &
                                           vivienda$NAPARQUEA == 1,
                                         df_resultado_estrato_parquea[[i, "N_MODE"]],
                                         vivienda$parqueaderos)
  }
  df_resultado_estrato_parquea$N_MODE = ifelse(df_resultado_estrato_parquea$N_MODE == 0, 0 ,df_resultado_estrato_parquea$N_MODE-1)
}



df_resultado_estrato_parquea <- vivienda %>% 
  group_by(zona, tipo,estrato) %>%
  reframe(nv_parqueo = sum(NAPARQUEA, na.rm = TRUE),  
          N_MODE  = Mode(parqueaderos, na.rm =TRUE))  
df_resultado_estrato_parquea <- df_resultado_estrato_parquea %>% 
  rename(zona1=zona,
         tipo1=tipo,
         estrato1=estrato)
df_resultado_estrato_parquea$N_MODE[is.na(df_resultado_estrato_parquea$N_MODE)] <- 0
df_resultado_estrato_parquea$COSTO_METRO_CUADRADO <- NA
m<-max(df_resultado_estrato_parquea$N_MODE)
for(a in 1:m){
  for (i in 1:nrow(df_resultado_estrato_parquea)) {
    filtros <- filter(vivienda,
                      zona == df_resultado_estrato_parquea[[i, "zona1"]]
                      ,tipo == df_resultado_estrato_parquea[[i, "tipo1"]]
                      ,estrato == df_resultado_estrato_parquea[[i, "estrato1"]]
                      ,parqueaderos == df_resultado_estrato_parquea[[i, "N_MODE"]]
    )
    if (nrow(filtros) > 0) {
      filtros$costo_metro<-filtros$preciom / filtros$areaconst
      val=sd(filtros$costo_metro)/mean(filtros$costo_metro)
      #print(paste("entro", val))
      costo_medio  <-ifelse(val>0.20,median(filtros$costo_metro, na.rm=TRUE),mean(filtros$costo_metro, na.rm=TRUE)) 
      df_resultado_estrato_parquea[i, "COSTO_METRO_CUADRADO"] <- costo_medio
    }
  }
  df_resultado_estrato_parquea<- filter(df_resultado_estrato_parquea,
                                        !is.na(COSTO_METRO_CUADRADO))
  vivienda$NAPARQUEA = ifelse(is.na(vivienda$parqueaderos),1,0)
  for (i in 1:nrow(df_resultado_estrato_parquea)) {
    vivienda$parqueaderos <- ifelse(vivienda$zona == df_resultado_estrato_parquea[[i, "zona1"]] &
                                           vivienda$tipo == df_resultado_estrato_parquea[[i, "tipo1"]] &
                                           vivienda$estrato == df_resultado_estrato_parquea[[i, "estrato1"]] &
                                           vivienda$costo_metro_m >= df_resultado_estrato_parquea[[i, "COSTO_METRO_CUADRADO"]] &
                                           vivienda$NAPARQUEA == 1,
                                         df_resultado_estrato_parquea[[i, "N_MODE"]],
                                         vivienda$parqueaderos)
  }
  
  df_resultado_estrato_parquea$N_MODE = ifelse(df_resultado_estrato_parquea$N_MODE == 0, 0 ,df_resultado_estrato_parquea$N_MODE-1)
}

##asigno 0 a los demas 
vivienda$parqueaderos <- ifelse(is.na(vivienda$parqueaderos),0,vivienda$parqueaderos)
rm(df_resultado_estrato_parquea,a,costo_medio,i,m,val,filtros)


captured_output <- capture.output({
  md.pattern(vivienda[, 1:13], plot = TRUE, rotate.names = TRUE)
})

En este punto, se han imputado todos los datos de parqueadero; sin embargo, aún se observan valores nulos para la variable “piso”. Por lo tanto, se realiza un proceso similar al de imputación del parqueadero, identificando las zonas por latitud y longitud. Se asume que, en dos apartamentos situados en el mismo edificio, los de los pisos superiores tienen un costo mayor.

## tratamiento de piso 
# Calcular la moda y la cantidad de valores faltantes en piso
#rm(df_resultado_piso)
vivienda$todos = 1
filtro<-filter(vivienda,tipo == "apartamento")
df_resultado_piso <- filtro %>% 
  group_by(latitud, longitud, estrato) %>%
  summarise(nv_piso = sum(NAPISO),
            num_total = sum(todos))
## `summarise()` has grouped output by 'latitud', 'longitud'. You can override
## using the `.groups` argument.
df_resultado_piso$DIF <- df_resultado_piso$nv_piso - df_resultado_piso$num_total
df_resultado_piso <- filter(df_resultado_piso,DIF<0)
df_resultado_piso <- filter(df_resultado_piso,nv_piso!=0)
#print(sum(df_resultado_piso$nv_piso))
for(a in 1:nrow(df_resultado_piso)){
  filtro<- filter(vivienda,
                  latitud == df_resultado_piso[[a,"latitud"]] &
                    longitud == df_resultado_piso[[a,"longitud"]] &
                    tipo == "apartamento" &
                    estrato == df_resultado_piso[[a,"estrato"]]
  )
  listadepisos=sort(unique(filtro$piso))
  elmax=max(listadepisos)
  for(i in listadepisos){
    #print(paste("iter ", a," piso ",i))
    filtroa<-filter(vivienda,
                    piso==i &
                      latitud == df_resultado_piso[[a,"latitud"]] &
                      longitud == df_resultado_piso[[a,"longitud"]] &
                      tipo == "apartamento" &
                      estrato == df_resultado_piso[[a,"estrato"]])
    if(nrow(filtroa)>0){
      val = sd(filtroa$costo_metro_m)/mean(filtroa$costo_metro_m)
      cost= ifelse(val>0.20 | is.na(val) ,median(filtroa$costo_metro_m),mean(filtroa$costo_metro_m))
      
      vivienda$piso <- ifelse(vivienda$costo_metro_m<=cost &
                                          vivienda$NAPISO == 1 &
                                          vivienda$latitud == df_resultado_piso[[a,"latitud"]] &
                                          vivienda$longitud == df_resultado_piso[[a,"longitud"]] &
                                          vivienda$tipo == "apartamento" &
                                          vivienda$estrato == df_resultado_piso[[a,"estrato"]]
                                        ,
                                        ifelse(i==1,1,i-1),vivienda$piso) 
      vivienda$NAPISO = ifelse(is.na(vivienda$piso),1,0)
      #print(sum(vivienda$NAPISO))
      if(i==elmax){
        #print(paste("elmax ",elmax))
        vivienda$piso <-ifelse(vivienda$costo_metro_m>cost &
                                           vivienda$NAPISO == 1 &
                                           i==elmax &
                                           vivienda$latitud == df_resultado_piso[[a,"latitud"]] &
                                           vivienda$longitud == df_resultado_piso[[a,"longitud"]] &
                                           vivienda$tipo == "apartamento" &
                                           vivienda$estrato == df_resultado_piso[[a,"estrato"]]
                                         ,
                                         i+1,vivienda$piso)
        vivienda$NAPISO = ifelse(is.na(vivienda$piso),1,0)
        #print(sum(vivienda$NAPISO))
      }
    }
  }
}


vivienda$todos = 1
filtro<-filter(vivienda,tipo == "apartamento")
df_resultado_piso <- filtro %>% 
  group_by(barrio, estrato) %>%
  summarise(nv_piso = sum(NAPISO),
            num_total = sum(todos))
## `summarise()` has grouped output by 'barrio'. You can override using the
## `.groups` argument.
df_resultado_piso$DIF <- df_resultado_piso$nv_piso - df_resultado_piso$num_total
df_resultado_piso <- filter(df_resultado_piso,DIF<0)
df_resultado_piso <- filter(df_resultado_piso,nv_piso!=0)
#print(sum(df_resultado_piso$nv_piso))
for(a in 1:nrow(df_resultado_piso)){
  filtro<- filter(vivienda,
                  barrio == df_resultado_piso[[a,"barrio"]] &
                    tipo == "apartamento" &
                    estrato == df_resultado_piso[[a,"estrato"]]
  )
  listadepisos=sort(unique(filtro$piso))
  elmax=max(listadepisos)
  for(i in listadepisos){
    #print(paste("iter ", a," piso ",i))
    filtroa<-filter(vivienda,
                    piso==i &
                      barrio == df_resultado_piso[[a,"barrio"]] &
                      tipo == "apartamento" &
                      estrato == df_resultado_piso[[a,"estrato"]])
    if(nrow(filtroa)>0){
      val = sd(filtroa$costo_metro_m)/mean(filtroa$costo_metro_m)
      cost= ifelse(val>0.20 | is.na(val) ,median(filtroa$costo_metro_m),mean(filtroa$costo_metro_m))
      
      vivienda$piso <- ifelse(vivienda$costo_metro_m<=cost &
                                          vivienda$NAPISO == 1 &
                                          vivienda$barrio == df_resultado_piso[[a,"barrio"]] &
                                          vivienda$tipo == "apartamento" &
                                          vivienda$estrato == df_resultado_piso[[a,"estrato"]]
                                        ,
                                        ifelse(i==1,1,i-1),vivienda$piso) 
      #print("aca1")
      vivienda$NAPISO = ifelse(is.na(vivienda$piso),1,0)
      #print(sum(vivienda$NAPISO))
      if(i==elmax){
        #print(paste("elmax ",elmax))
        vivienda$piso <-ifelse(vivienda$costo_metro_m>cost &
                                           vivienda$NAPISO == 1 &
                                           i==elmax &
                                           vivienda$barrio == df_resultado_piso[[a,"barrio"]] &
                                           vivienda$tipo == "apartamento" &
                                           vivienda$estrato == df_resultado_piso[[a,"estrato"]]
                                         ,
                                         i+1,vivienda$piso)
        vivienda$NAPISO = ifelse(is.na(vivienda$piso),1,0)
        #print(sum(vivienda$NAPISO))
      }
    }
  }
}


rm(filtro,filtroa,df_resultado_piso,cost,elmax,elmin,listadepisos,a,i,val)
## Warning in rm(filtro, filtroa, df_resultado_piso, cost, elmax, elmin,
## listadepisos, : objeto 'elmin' no encontrado
vivienda$piso<-ifelse(is.na(vivienda$piso),1,vivienda$piso)
vivienda <- subset(vivienda, select = -c(NAPISO,NAPARQUEA,costo_metro_m,todos))

captured_output <- capture.output({
  md.pattern(vivienda[, 1:13], plot = TRUE, rotate.names = TRUE)
})

Al finalizar estos procesos, se ha completado la imputación de los datos y la base de datos está lista para realizar los respectivos análisis.

Análisis de Componentes Principales (PCA)

En este punto, se realiza un análisis de componentes principales (PCA) con el objetivo de reducir la dimensionalidad de los datos. Para ello, primero se normalizan las variables numéricas, y luego se llevan a cabo los diferentes procesos del PCA.

rm(elim,top_5_nulos,filas,nulos_por_fila)
vivienda_normalizada <- scale(vivienda[, c(3, 5:9)])
vivienda_normalizada <- as.data.frame(vivienda_normalizada)
row.names(vivienda_normalizada) <- vivienda$id
prcomp(vivienda_normalizada)
## Standard deviations (1, .., p=6):
## [1] 1.7859444 1.0801856 0.8616258 0.6398383 0.5514201 0.4332962
## 
## Rotation (n x k) = (6 x 6):
##                     PC1         PC2         PC3         PC4        PC5
## piso          0.1179789 -0.74788297  0.62880855  0.06749209 -0.1477085
## preciom      -0.4700814 -0.32071206 -0.19581705  0.30978866  0.2208753
## areaconst    -0.4819893  0.08404774 -0.02956444  0.61311674 -0.4557695
## parqueaderos -0.4103275 -0.35258478 -0.37135471 -0.65144988 -0.3539325
## banios       -0.4887511  0.01178265  0.24020757 -0.13214321  0.7132220
## habitaciones -0.3543226  0.45419815  0.60809328 -0.28585817 -0.2961698
##                      PC6
## piso         -0.07056096
## preciom       0.70218803
## areaconst    -0.41965490
## parqueaderos -0.14055542
## banios       -0.42087574
## habitaciones  0.35909702

En la primera componente principal (PC1), las variables con mayor carga son el numero de baños y el área de construcción, con cargas negativas altas de -0.489 y -0.482, respectivamente, lo que indica que estas variables influyen significativamente en esta componente. En contraste, la variable con menor carga es el piso, con una carga baja de 0.118, sugiriendo una influencia menor en esta componente.

Para la segunda componente principal (PC2), el piso tiene la carga más alta en valor absoluto, con una carga negativa de -0.748, lo que sugiere una fuerte influencia del piso en esta componente. Por otro lado, la variable con la carga más baja es el área de construcción, con una carga muy baja de 0.084, indicando una mínima influencia en esta componente.

res.pca <- prcomp(vivienda_normalizada)
fviz_eig(res.pca, addlabels = TRUE)

En la figura se observa que las dos primeras componentes explican el 72.6% de la varianza de los datos, por lo que se opta por utilizar únicamente estas dos componentes.

Finalmente, se procede a crear un gráfico de representación de los vectores de las componentes principales para visualizar gráficamente la información contenida en la tabla.

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

El Análisis de Componentes Principales (PCA) ha demostrado ser efectivo para reducir la dimensionalidad del conjunto de datos, explicando el 72.6% de la varianza total con las dos primeras componentes. Esto simplifica el análisis al enfocarse en las variables más influyentes y facilita la visualización y la interpretación de patrones clave en los datos.

Análisis de Conglomerados

Con el objetivo de identificar segmentos de similitud en las viviendas, se realiza un análisis de conglomerados utilizando la misma base de datos normalizada que se usó para el PCA. Para este análisis, se decide emplear la distancia de Minkowski. Se obtienen los valores de la prueba cutree para los 6 primeros clusters. Aunque la organización en dos clusters presenta el valor más alto en esta prueba, no se forman grupos grandes. Por lo tanto, se opta por utilizar el segundo valor más alto y se configuran 4 clusters.

# deu <- dist(vivienda_normalizada, method = "euclidean")
# dma <- dist(vivienda_normalizada, method = "manhattan")
dmi <- dist(vivienda_normalizada, method = "minkowski")


hc_emp <- hclust(dmi, method = 'complete')

for(a in 2:6){
  cluster_assigments <- cutree(hc_emp, k = a )
  sil <- silhouette(cluster_assigments, dist(vivienda_normalizada))
  sil_avg <- mean(sil[,3])
  cat("Coeficiente de Silhouette promedio k =", a, ": ", sil_avg,"\n")  
}
## Coeficiente de Silhouette promedio k = 2 :  0.7080745 
## Coeficiente de Silhouette promedio k = 3 :  0.4041728 
## Coeficiente de Silhouette promedio k = 4 :  0.4198973 
## Coeficiente de Silhouette promedio k = 5 :  0.4132474 
## Coeficiente de Silhouette promedio k = 6 :  0.3761095

Con la configuración descrita anteriormente, se obtienen los siguientes grupos:

cluster_assigments <- cutree(hc_emp, k = 4)
assigned_cluster <- vivienda_normalizada %>% mutate(cluster = as.factor(cluster_assigments))
table(assigned_cluster$cluster)
## 
##    1    2    3    4 
## 7161  658  493    7
assigned_cluster$rownames <- rownames(assigned_cluster)
names(assigned_cluster)[names(assigned_cluster) == "cluster"] <- "cluster_id"
merged_df <- merge(vivienda, assigned_cluster, by.x = "id", by.y = "rownames")
merged_df <- merged_df[, -c(12:19)]
split_dfs <- split(merged_df, merged_df$cluster_id)

# Describir estadísticamente cada subdata frame
descriptions <- lapply(split_dfs, function(df) {
  list(
    Summary = summary(df),
    Description = describe(df)
  )
})

# Verificar los resultados
for (i in seq_along(descriptions)) {
  cat("Cluster ID:", names(descriptions)[i], "\n")
  print(descriptions[[i]]$Summary)
  print(descriptions[[i]]$Description)
  cat("\n\n")
}
## Cluster ID: 1 
##        id                 zona          piso.x       estrato    preciom.x     
##  Min.   :   1   Zona Centro :  95   Min.   : 1.000   3:1231   Min.   :  58.0  
##  1st Qu.:1984   Zona Norte  :1732   1st Qu.: 1.000   4:1975   1st Qu.: 200.0  
##  Median :3939   Zona Oeste  :1061   Median : 3.000   5:2470   Median : 300.0  
##  Mean   :4064   Zona Oriente: 262   Mean   : 3.621   6:1485   Mean   : 364.8  
##  3rd Qu.:6184   Zona Sur    :4011   3rd Qu.: 5.000            3rd Qu.: 450.0  
##  Max.   :8318                       Max.   :14.000            Max.   :1850.0  
##   areaconst.x  parqueaderos.x     banios.x     habitaciones.x 
##  Min.   : 30   Min.   :0.000   Min.   : 0.00   Min.   :0.000  
##  1st Qu.: 75   1st Qu.:1.000   1st Qu.: 2.00   1st Qu.:3.000  
##  Median :108   Median :1.000   Median : 3.00   Median :3.000  
##  Mean   :139   Mean   :1.376   Mean   : 2.78   Mean   :3.243  
##  3rd Qu.:178   3rd Qu.:2.000   3rd Qu.: 4.00   3rd Qu.:4.000  
##  Max.   :932   Max.   :5.000   Max.   :10.00   Max.   :7.000  
##           tipo         barrio          cluster_id
##  apartamento:4999   Length:7161        1:7161    
##  casa       :2162   Class :character   2:   0    
##                     Mode  :character   3:   0    
##                                        4:   0    
##                                                  
##                                                  
##                vars    n    mean      sd median trimmed     mad min  max range
## id                1 7161 4064.14 2419.87   3939 4040.73 3097.15   1 8318  8317
## zona*             2 7161    3.89    1.34      5    4.00    0.00   1    5     4
## piso.x            3 7161    3.62    2.94      3    3.11    2.97   1   14    13
## estrato*          4 7161    2.59    1.00      3    2.61    1.48   1    4     3
## preciom.x         5 7161  364.79  238.35    300  327.64  177.91  58 1850  1792
## areaconst.x       6 7161  139.01   89.90    108  123.64   62.27  30  932   902
## parqueaderos.x    7 7161    1.38    0.80      1    1.37    1.48   0    5     5
## banios.x          8 7161    2.78    1.12      3    2.71    1.48   0   10    10
## habitaciones.x    9 7161    3.24    0.95      3    3.19    0.00   0    7     7
## tipo*            10 7161    1.30    0.46      1    1.25    0.00   1    2     1
## barrio*          11 7161  204.31  106.98    213  208.59  143.81   1  361   360
## cluster_id*      12 7161    1.00    0.00      1    1.00    0.00   1    1     0
##                 skew kurtosis    se
## id              0.09    -1.21 28.60
## zona*          -0.54    -1.44  0.02
## piso.x          1.29     1.00  0.03
## estrato*       -0.14    -1.04  0.01
## preciom.x       1.74     3.92  2.82
## areaconst.x     1.84     4.66  1.06
## parqueaderos.x  0.59     0.96  0.01
## banios.x        0.62     0.30  0.01
## habitaciones.x  0.51     2.16  0.01
## tipo*           0.86    -1.26  0.01
## barrio*        -0.14    -1.23  1.26
## cluster_id*      NaN      NaN  0.00
## 
## 
## Cluster ID: 2 
##        id                 zona         piso.x      estrato   preciom.x     
##  Min.   :   2   Zona Centro : 27   Min.   :1.000   3:213   Min.   : 150.0  
##  1st Qu.:1903   Zona Norte  :144   1st Qu.:1.000   4:136   1st Qu.: 370.0  
##  Median :4574   Zona Oeste  : 42   Median :2.000   5:192   Median : 500.0  
##  Mean   :4210   Zona Oriente: 85   Mean   :1.758   6:117   Mean   : 635.2  
##  3rd Qu.:6206   Zona Sur    :360   3rd Qu.:2.000           3rd Qu.: 750.0  
##  Max.   :8319                      Max.   :7.000           Max.   :1999.0  
##   areaconst.x     parqueaderos.x     banios.x      habitaciones.x  
##  Min.   :  70.0   Min.   :0.000   Min.   : 0.000   Min.   : 3.000  
##  1st Qu.: 249.2   1st Qu.:1.000   1st Qu.: 4.000   1st Qu.: 6.000  
##  Median : 344.5   Median :2.000   Median : 5.000   Median : 7.000  
##  Mean   : 399.2   Mean   :2.005   Mean   : 5.246   Mean   : 7.036  
##  3rd Qu.: 479.0   3rd Qu.:3.000   3rd Qu.: 6.000   3rd Qu.: 8.000  
##  Max.   :1440.0   Max.   :8.000   Max.   :10.000   Max.   :10.000  
##           tipo        barrio          cluster_id
##  apartamento:  8   Length:658         1:  0     
##  casa       :650   Class :character   2:658     
##                    Mode  :character   3:  0     
##                                       4:  0     
##                                                 
##                                                 
##                vars   n    mean      sd median trimmed     mad min  max range
## id                1 658 4210.16 2350.98 4573.5 4264.32 2821.39   2 8319  8317
## zona*             2 658    3.92    1.36    5.0    4.08    0.00   1    5     4
## piso.x            3 658    1.76    0.89    2.0    1.65    1.48   1    7     6
## estrato*          4 658    2.32    1.11    2.0    2.28    1.48   1    4     3
## preciom.x         5 658  635.24  379.78  500.0  577.56  252.04 150 1999  1849
## areaconst.x       6 658  399.19  219.20  344.5  369.99  155.67  70 1440  1370
## parqueaderos.x    7 658    2.00    1.59    2.0    1.84    1.48   0    8     8
## banios.x          8 658    5.25    1.57    5.0    5.18    1.48   0   10    10
## habitaciones.x    9 658    7.04    1.62    7.0    7.02    1.48   3   10     7
## tipo*            10 658    1.99    0.11    2.0    2.00    0.00   1    2     1
## barrio*          11 658   81.43   48.56   77.0   80.72   60.05   1  168   167
## cluster_id*      12 658    2.00    0.00    2.0    2.00    0.00   2    2     0
##                 skew kurtosis    se
## id             -0.24    -1.19 91.65
## zona*          -0.77    -1.03  0.05
## piso.x          1.04     1.20  0.03
## estrato*        0.13    -1.36  0.04
## preciom.x       1.35     1.18 14.81
## areaconst.x     1.32     1.72  8.55
## parqueaderos.x  1.01     1.11  0.06
## banios.x        0.39     0.44  0.06
## habitaciones.x  0.05    -0.78  0.06
## tipo*          -8.88    77.02  0.00
## barrio*         0.11    -1.20  1.89
## cluster_id*      NaN      NaN  0.00
## 
## 
## Cluster ID: 3 
##        id                 zona         piso.x       estrato   preciom.x   
##  Min.   :   4   Zona Centro :  2   Min.   : 1.000   3:  7   Min.   : 190  
##  1st Qu.:4588   Zona Norte  : 44   1st Qu.: 1.000   4: 18   1st Qu.: 870  
##  Median :5651   Zona Oeste  : 95   Median : 2.000   5: 88   Median :1150  
##  Mean   :5491   Zona Oriente:  2   Mean   : 2.018   6:380   Mean   :1158  
##  3rd Qu.:6641   Zona Sur    :350   3rd Qu.: 2.000           3rd Qu.:1400  
##  Max.   :8310                      Max.   :13.000           Max.   :1950  
##   areaconst.x    parqueaderos.x      banios.x      habitaciones.x  
##  Min.   : 50.0   Min.   : 1.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:300.0   1st Qu.: 3.000   1st Qu.: 4.000   1st Qu.: 4.000  
##  Median :367.0   Median : 4.000   Median : 5.000   Median : 4.000  
##  Mean   :378.5   Mean   : 4.195   Mean   : 5.053   Mean   : 4.288  
##  3rd Qu.:450.0   3rd Qu.: 5.000   3rd Qu.: 6.000   3rd Qu.: 5.000  
##  Max.   :630.0   Max.   :10.000   Max.   :10.000   Max.   :10.000  
##           tipo        barrio          cluster_id
##  apartamento: 93   Length:493         1:  0     
##  casa       :400   Class :character   2:  0     
##                    Mode  :character   3:493     
##                                       4:  0     
##                                                 
##                                                 
##                vars   n    mean      sd median trimmed     mad min  max range
## id                1 493 5491.27 1714.11   5651 5618.35 1497.43   4 8310  8306
## zona*             2 493    4.33    1.09      5    4.53    0.00   1    5     4
## piso.x            3 493    2.02    1.71      2    1.67    1.48   1   13    12
## estrato*          4 493    3.71    0.61      4    3.84    0.00   1    4     3
## preciom.x         5 493 1158.03  378.63   1150 1152.08  400.30 190 1950  1760
## areaconst.x       6 493  378.50  103.19    367  374.73  123.06  50  630   580
## parqueaderos.x    7 493    4.19    1.56      4    4.05    1.48   1   10     9
## banios.x          8 493    5.05    1.17      5    5.00    1.48   0   10    10
## habitaciones.x    9 493    4.29    1.12      4    4.17    1.48   0   10    10
## tipo*            10 493    1.81    0.39      2    1.89    0.00   1    2     1
## barrio*          11 493   31.49   17.55     39   30.91   25.20   1   66    65
## cluster_id*      12 493    3.00    0.00      3    3.00    0.00   3    3     0
##                 skew kurtosis    se
## id             -0.80     0.84 77.20
## zona*          -1.16    -0.29  0.05
## piso.x          3.57    15.18  0.08
## estrato*       -2.28     5.34  0.03
## preciom.x       0.15    -0.74 17.05
## areaconst.x     0.25    -0.51  4.65
## parqueaderos.x  1.28     2.26  0.07
## banios.x        0.19     1.80  0.05
## habitaciones.x  0.94     3.10  0.05
## tipo*          -1.59     0.52  0.02
## barrio*         0.05    -1.38  0.79
## cluster_id*      NaN      NaN  0.00
## 
## 
## Cluster ID: 4 
##        id                 zona       piso.x  estrato   preciom.x     
##  Min.   : 315   Zona Centro :0   Min.   :1   3:2     Min.   : 200.0  
##  1st Qu.:2170   Zona Norte  :0   1st Qu.:1   4:0     1st Qu.: 877.5  
##  Median :5016   Zona Oeste  :0   Median :1   5:0     Median :1500.0  
##  Mean   :3746   Zona Oriente:2   Mean   :1   6:5     Mean   :1215.0  
##  3rd Qu.:5432   Zona Sur    :5   3rd Qu.:1           3rd Qu.:1625.0  
##  Max.   :5684                    Max.   :1           Max.   :1800.0  
##   areaconst.x   parqueaderos.x      banios.x     habitaciones.x 
##  Min.   :1250   Min.   : 0.000   Min.   :1.000   Min.   :2.000  
##  1st Qu.:1432   1st Qu.: 2.500   1st Qu.:3.500   1st Qu.:3.000  
##  Median :1500   Median : 4.000   Median :5.000   Median :3.000  
##  Mean   :1507   Mean   : 4.143   Mean   :4.143   Mean   :3.714  
##  3rd Qu.:1593   3rd Qu.: 5.000   3rd Qu.:5.000   3rd Qu.:4.500  
##  Max.   :1745   Max.   :10.000   Max.   :6.000   Max.   :6.000  
##           tipo      barrio          cluster_id
##  apartamento:0   Length:7           1:0       
##  casa       :7   Class :character   2:0       
##                  Mode  :character   3:0       
##                                     4:7       
##                                               
##                                               
##                vars n    mean      sd median trimmed    mad  min  max range
## id                1 7 3745.57 2252.79   5016 3745.57 990.38  315 5684  5369
## zona*             2 7    4.71    0.49      5    4.71   0.00    4    5     1
## piso.x            3 7    1.00    0.00      1    1.00   0.00    1    1     0
## estrato*          4 7    3.14    1.46      4    3.14   0.00    1    4     3
## preciom.x         5 7 1215.00  682.39   1500 1215.00 222.39  200 1800  1600
## areaconst.x       6 7 1506.57  162.20   1500 1506.57 148.26 1250 1745   495
## parqueaderos.x    7 7    4.14    3.13      4    4.14   1.48    0   10    10
## banios.x          8 7    4.14    1.68      5    4.14   1.48    1    6     5
## habitaciones.x    9 7    3.71    1.38      3    3.71   1.48    2    6     4
## tipo*            10 7    2.00    0.00      2    2.00   0.00    2    2     0
## barrio*          11 7    3.29    1.38      4    3.29   1.48    1    5     4
## cluster_id*      12 7    4.00    0.00      4    4.00   0.00    4    4     0
##                 skew kurtosis     se
## id             -0.52    -1.74 851.47
## zona*          -0.75    -1.60   0.18
## piso.x           NaN      NaN   0.00
## estrato*       -0.75    -1.60   0.55
## preciom.x      -0.69    -1.62 257.92
## areaconst.x    -0.16    -1.33  61.30
## parqueaderos.x  0.56    -0.78   1.18
## banios.x       -0.74    -0.96   0.63
## habitaciones.x  0.43    -1.45   0.52
## tipo*            NaN      NaN   0.00
## barrio*        -0.43    -1.45   0.52
## cluster_id*      NaN      NaN   0.00

Dados los resultados anteriores, se procede a realizar una descripción de cada uno de los clusters:

Cluster ID: 1

  • Zona Predominante: Predomina en la Zona Sur con un 56% de los casos.
  • Piso: La media del piso es 3, con valores que van desde el piso 1 hasta el 5.
  • Estrato: El estrato predominante es el 5, con un 39.41%.
  • Número de Baños: Rango desde 0 hasta 10, con una media de 3.
  • Número de Parqueaderos: Rango desde 0 hasta 5, con una media de 1.38 (aprox 1).
  • Número de Habitaciones: Rango desde 0 hasta 7, con una media de 3.24 (aprox 3).
  • Tipo de Propiedad: Predomina el apartamento con un 69.8% de los casos.
  • Área: Rango desde 30 hasta 932 m², con una media de 139 m².
  • Precio de la Propiedad: Rango desde 58 hasta 1850, con una media de 364.8.

Cluster ID: 2

  • Zona Predominante: Predomina en la Zona Sur con un 54.6% de los casos.
  • Piso: La media del piso es 1.76 (aprox 2), con valores que van desde el piso 1 hasta el 7.
  • Estrato: El estrato predominante es el 5, con un 29.2%.
  • Número de Baños: Rango desde 0 hasta 10, con una media de 5.25 (aprox 5).
  • Número de Parqueaderos: Rango desde 0 hasta 8, con una media de 2.
  • Número de Habitaciones: Rango desde 3 hasta 10, con una media de 7.
  • Tipo de Propiedad: Predomina la casa con un 98.8% de los casos.
  • Área: Rango desde 70 hasta 1440 m², con una media de 399.2 m².
  • Precio de la Propiedad: Rango desde 150 hasta 1999, con una media de 635.2.

Cluster ID: 3

  • Zona Predominante: Predomina en la Zona Sur con un 71% de los casos.
  • Piso: La media del piso es 2, con valores que van desde el piso 1 hasta el 13.
  • Estrato: El estrato predominante es el 6, con un 77.08%.
  • Número de Baños: Rango desde 0 hasta 10, con una media de 5.05 (aprox 5).
  • Número de Parqueaderos: Rango desde 1 hasta 10, con una media de 4.20 (aprox 4).
  • Número de Habitaciones: Rango desde 0 hasta 10, con una media de 4.29 (aprox 4).
  • Tipo de Propiedad: Predomina la casa con un 81.13% de los casos.
  • Área: Rango desde 50 hasta 630 m², con una media de 378.5 m².
  • Precio de la Propiedad: Rango desde 190 hasta 1950, con una media de 1158.

Cluster ID: 4

  • Zona Predominante: Predomina en la Zona Sur con un 71.4% de los casos.
  • Piso: Todas las viviendas están en el piso 1.
  • Estrato: El estrato predominante es el 6, con un 71.4%.
  • Número de Baños: Rango desde 1 hasta 6, con una media de 4.14 (aprox 4).
  • Número de Parqueaderos: Rango desde 0 hasta 10, con una media de 4.14 (aprox 4).
  • Número de Habitaciones: Rango desde 2 hasta 6, con una media de 3.71 (aprox 4).
  • Tipo de Propiedad: Todas las propiedades son de tipo casa.
  • Área: Rango desde 1250 hasta 1745 m², con una media de 1507 m².
  • Precio de la Propiedad: Rango desde 200 hasta 1800, con una media de 1215.

Análisis de Correspondencia

Después de los análisis anteriores, se procede a realizar un análisis de correspondencia para identificar relaciones entre las variables categóricas. Primero, se decide realizar asociaciones entre pares de variables, obteniendo los siguientes resultados.

variables zona y estrato

En primer lugar, se analiza la asociación entre las variables zona y estrato.

# Tabla de contingencia para las variables zona y estrato
tabla_zona_estrato <- table(vivienda$zona, vivienda$estrato)
print(tabla_zona_estrato)
##               
##                   3    4    5    6
##   Zona Centro   105   14    4    1
##   Zona Norte    572  407  769  172
##   Zona Oeste     54   84  290  770
##   Zona Oriente  340    8    2    1
##   Zona Sur      382 1616 1685 1043
chisq.test(tabla_zona_estrato)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_zona_estrato
## X-squared = 3830.4, df = 12, p-value < 2.2e-16
resultados_ac <- CA(tabla_zona_estrato)

La prueba de chi-cuadrado muestra un valor p de significancia inferior a 0.05, lo que indica que las variables están correlacionadas.

La gráfica revela que la Zona Sur está estrechamente relacionada con los estratos 4 y 5, mientras que las zonas Oriente y Centro se asocian principalmente con el estrato 3 y, en menor medida, con los otros estratos.

Variables Zona y Tipo

Posteriormente, se procede a analizar las variables zona y tipo, obteniendo los siguientes resultados:

# Tabla de contingencia para las variables zona y tipo
tabla_zona_tipo <- table(vivienda$zona, vivienda$tipo)
print(tabla_zona_tipo)
##               
##                apartamento casa
##   Zona Centro           24  100
##   Zona Norte          1198  722
##   Zona Oeste          1029  169
##   Zona Oriente          62  289
##   Zona Sur            2787 1939
chisq.test(tabla_zona_tipo)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_zona_tipo
## X-squared = 690.93, df = 4, p-value < 2.2e-16
resultados_ac_2 <- CA(tabla_zona_tipo)
##filas
coords_rows <- as.data.frame(resultados_ac_2$row$coord)
coords_rows$category <- rownames(coords_rows)
names(coords_rows)[1] <- "V1"
ggplot(coords_rows, aes(x = V1, y = 0, label = category)) + 
  geom_point(color = 'blue', size = 4) +
  geom_text(angle = 90, hjust = 1.1, vjust = -0.5) +
  #geom_text(vjust = -0.5) +
  theme_minimal() +
  labs(title = "Coordenadas de las Filas en la Dimensión 1",
       x = "Coordenada Dimensión 1",
       y = "") +
  theme(axis.text.y = element_blank(),
        axis.ticks.y = element_blank())

De la prueba chi-cuadrado se deduce que las variables tienen correlación. Sin embargo, dado que la variable tipo solo contiene dos categorías, la reducción de la dimensionalidad resulta en una sola dimensión, lo que genera la gráfica presentada.

La gráfica muestra que las categorías Zona Oriente y Zona Centro tienen una fuerte correlación con la categoría casa. En contraste, las Zonas Sur y Norte no presentan una alta correlación con ninguna de las categorías de la variable tipo. Por otro lado, la Zona Oeste muestra una fuerte relación con la categoría apartamento.

Variables Estrato y Tipo

Finalmente, se procede a analizar las variables estrato y tipo, obteniendo los siguientes resultados:

# Tabla de contingencia para las variables estrato y tipo
tabla_estrato_tipo <- table(vivienda$estrato, vivienda$tipo)
print(tabla_estrato_tipo)
##    
##     apartamento casa
##   3         639  814
##   4        1404  725
##   5        1766  984
##   6        1291  696
chisq.test(tabla_estrato_tipo)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_estrato_tipo
## X-squared = 224.33, df = 3, p-value < 2.2e-16
resultados_ac_3 <- CA(tabla_estrato_tipo)

# filas
coords_rows <- as.data.frame(resultados_ac_3$row$coord)
coords_rows$category <- rownames(coords_rows)
names(coords_rows)[1] <- "V1"
print(names(coords_rows))
## [1] "V1"       "category"
ggplot(coords_rows, aes(x = V1, y = 0, label = category)) + 
  geom_point(color = 'blue', size = 4) +
  geom_text(angle = 90, hjust = 1.1, vjust = -0.5) +
  #geom_text(vjust = -0.5) +
  theme_minimal() +
  labs(title = "Coordenadas de las Filas en la Dimensión 1",
       x = "Coordenada Dimensión 1",
       y = "") +
  theme(axis.text.y = element_blank(),
        axis.ticks.y = element_blank())

Nuevamente, la prueba chi-cuadrado muestra que existe correlación entre las variables categóricas. Sin embargo, debido al reducido número de categorías en la variable tipo, se genera una sola dimensión, que está representada en la gráfica mostrada en pantalla. En esta gráfica, se observa que el estrato 3 tiene una alta correlación con la categoría casa, mientras que los estratos 4, 5 y 6 muestran una mayor presencia de apartamentos.

Análisis de las Tres Variables

Para finalizar, se realiza el análisis conjunto de las tres variables, obteniendo los siguientes resultados.

vivienda_cat = subset(vivienda, select = c(tipo,zona,estrato)) 
tipo = vivienda_cat$tipo
zona = vivienda_cat$zona
estrato = as.factor(vivienda_cat$estrato)
viv_cat <- cbind(vivienda_cat,tipo,zona,estrato)
viv_cat[,1:3] <- NULL
summary(viv_cat)
##           tipo                zona      estrato 
##  apartamento:5100   Zona Centro : 124   3:1453  
##  casa       :3219   Zona Norte  :1920   4:2129  
##                     Zona Oeste  :1198   5:2750  
##                     Zona Oriente: 351   6:1987  
##                     Zona Sur    :4726
uni.mca <- MCA(viv_cat, graph = FALSE)
fviz_mca_biplot(uni.mca, 
                repel = TRUE, 
                ggtheme = theme_grey(), 
                invisible = "ind") + 
  labs(title ="Representación de las categorías")

var_coord <- uni.mca$var$coord

# Obtener la contribución de cada variable a las dimensiones
var_contrib <- uni.mca$var$contrib

# # Ver los resultados
# print(var_coord)
# print(var_contrib)

En la gráfica se puede representar la relación entre las tres variables, mostrando comportamientos similares a los descritos en los análisis individuales de pares de variables. Se observa que las casas en los estratos 4 y 5, así como las zonas Norte y Sur, presentan la mayor relación entre las distintas categorías.

Conclusiones

Segmentación del Mercado: El clustering ha identificado varios segmentos clave en el mercado inmobiliario de Cali, que reflejan diferentes necesidades y características de los compradores.

Dimensionalidad Reducida: El PCA ha permitido reducir la complejidad del conjunto de datos y ha destacado las principales variables que explican la variabilidad en los precios de las viviendas.

Análisis de Correspondencia: El análisis de correspondencia ha proporcionado una comprensión más profunda de las relaciones entre las variables categóricas.