Evaluación de la oferta inmobiliaria

Análisis de componentes principales

El gráfico generado con md.pattern() del paquete mice en R muestra el patrón de datos faltantes en el dataset vivienda_num. Se observa que la mayoría de las variables tienen datos completos, representados en azul, mientras que algunas presentan valores faltantes, indicados en rosa. Existen distintos patrones de ausencia de datos, con un grupo significativo de observaciones que carecen de información en variables específicas. La última columna a la derecha indica la cantidad de registros que siguen cada patrón de completitud o ausencia de datos. Este análisis es clave para decidir estrategias de manejo de datos faltantes, como imputación o eliminación de registros con información incompleta.

# veridicamos datos faltes
library(mice)
md.pattern(vivienda_num)

##      preciom id estrato areaconst banios habitaciones longitud latitud
## 6717       1  1       1         1      1            1        1       1
## 1602       1  1       1         1      1            1        1       1
## 1          1  0       0         0      0            0        0       0
## 2          0  0       0         0      0            0        0       0
##            2  3       3         3      3            3        3       3
##      parqueaderos     
## 6717            1    0
## 1602            0    1
## 1               0    8
## 2               0    9
##              1605 1628

Para el tratamiento de estos datos faltantes, se eliminan los registros con pocos datos en las variables y los parqueaderos vacios se pasan a cero

#Según el resultado, hay 1 registro que solo tiene una variable, 2 que no tienen ninguna, 1602 que no tienen información del parqueadero.
# se eliminarán las filas con pocas variables, se imputará el núero de parqueaderos a "0"

# Ver cuántos valores faltantes tiene cada fila
num_na_por_fila <- rowSums(is.na(vivienda_num))

# Filtrar el dataset: conservar solo las filas con al menos 2 valores no NA
vivienda_num_limpio <- vivienda_num[num_na_por_fila < (ncol(vivienda_num) - 1), ]


#pasamos los parqueaderos vacios a cero
vivienda_num_limpio$parqueaderos[is.na(vivienda_num_limpio$parqueaderos)] <- 0

# se verifica
md.pattern(vivienda_num_limpio)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      id estrato preciom areaconst parqueaderos banios habitaciones longitud
## 8319  1       1       1         1            1      1            1        1
##       0       0       0         0            0      0            0        0
##      latitud  
## 8319       1 0
##            0 0

El gráfico generado tras reemplazar los valores faltantes en la variable “parqueaderos” por 0 muestra que el dataset vivienda_num_limpio ya no contiene datos faltantes. La función md.pattern() del paquete mice confirma que todas las variables tienen valores completos, lo que se evidencia en el mensaje “No need for mice. This data set is completely observed”. Además, el gráfico indica que en las 8319 observaciones, todas las columnas están completamente llenas (representadas en azul), sin ninguna celda en rosa. Esto significa que ya no es necesario aplicar técnicas de imputación para manejar valores faltantes.

Realizamos el escalado de las variables numericas

#eliminasmos las columnas id, longitud y latitud ya que su estandarización no nos dan información relevante sobre el dataset
viviendaZ= scale(vivienda_num_limpio[,2:7])
head(viviendaZ) # primeros 6 registros
##         estrato    preciom  areaconst parqueaderos      banios habitaciones
## [1,] -1.5872276 -0.5595498 -0.7339949   -0.3875522 -0.07793773    1.6406840
## [2,] -1.5872276 -0.3465670 -0.3842568   -0.3875522 -0.77811479   -0.4147626
## [3,] -1.5872276 -0.2552886  0.3152194    0.4168506 -0.77811479    0.2703863
## [4,] -0.6156201 -0.1031580  0.7349051    1.2212534  1.32241640   -0.4147626
## [5,]  0.3559875 -0.5291236 -0.5940997   -0.3875522 -0.77811479   -0.4147626
## [6,]  0.3559875 -0.5899759 -0.6150839   -0.3875522 -0.07793773   -0.4147626

se aplica el analisis de componentes princiales

#se aplica el Análisis de componentes principales
prcomp(viviendaZ)
## Standard deviations (1, .., p=6):
## [1] 1.8480703 1.1222455 0.6779600 0.6592367 0.4909833 0.4357909
## 
## Rotation (n x k) = (6 x 6):
##                    PC1        PC2        PC3         PC4         PC5        PC6
## estrato      0.3306904 -0.5816950  0.5551021  0.07749341  0.46143880 -0.1587620
## preciom      0.4781754 -0.1883533 -0.0359721 -0.36062553 -0.21967777  0.7458339
## areaconst    0.4417250  0.2388377 -0.2878930 -0.61783592  0.27859384 -0.4534506
## parqueaderos 0.4096354 -0.2607395 -0.6565763  0.57091588  0.04760414 -0.0700729
## banios       0.4668745  0.1921461  0.3633148  0.18500105 -0.67876421 -0.3437503
## habitaciones 0.2847775  0.6813261  0.2111757  0.34936005  0.44521273  0.2997240
#elección del número de componentes principales
library(factoextra)

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

El Scree Plot presentado muestra el porcentaje de varianza explicada por cada componente principal en un Análisis de Componentes Principales (PCA) aplicado al conjunto de datos de viviendas. En el gráfico, se observa que el primer componente principal (PC1) explica el 56.9% de la variabilidad, mientras que el segundo componente (PC2) explica un 21% adicional, sumando un 78% de varianza explicada con solo dos dimensiones. A partir del tercer componente, la contribución a la varianza es significativamente menor (7.7% en PC3 y 7.2% en PC4), lo que sugiere que estos componentes aportan información marginal. El punto de codo en el gráfico indica que dos componentes son suficientes para capturar la mayor parte de la estructura de los datos, lo que permite reducir la dimensionalidad sin perder demasiada información.

se genral el graífco de coordenadas

#según el gráfico de el PC1 y PC2 explican el 77.9% de la variabilidad total, por lo que se elegirán estos componentes para reducir la dimencionalidad a solo 2 componentes

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 gráfico de variables del Análisis de Componentes Principales (PCA) muestra la relación entre las variables originales y los dos primeros componentes principales, que explican el 77.9% de la variabilidad total de los datos. Se observa que el primer componente principal (PC1), que representa el 56.9% de la varianza, está fuertemente influenciado por el precio del metro cuadrado (preciom), el estrato socioeconómico (estrato), el número de parqueaderos (parqueaderos) y el área construida (areaconst), lo que sugiere que este componente refleja principalmente el tamaño y nivel socioeconómico de las viviendas. Por otro lado, el segundo componente principal (PC2), que explica el 21% de la varianza, está dominado por la variable habitaciones, indicando que este factor también es diferenciador en la clasificación de las viviendas. Además, se observa una fuerte correlación entre precio, área construida y número de baños, lo que indica que las viviendas más grandes y con más baños tienden a ser más costosas. Dado que estos dos componentes capturan la mayor parte de la información del conjunto de datos, se justifica la reducción de la dimensionalidad a solo dos dimensiones para facilitar el análisis y la visualización sin perder información relevante.

se buscan los valores mas altos y bajos en PC1 y PC2

pca_coords <- res.pca$x  # Obtener coordenadas de los individuos en los PCs

# Identificar los valores más altos y más bajos en PC1 y PC2
vivienda_max_PC1 <- which.max(pca_coords[, 1])  # Mayor en PC1
vivienda_min_PC1 <- which.min(pca_coords[, 1])  # Menor en PC1
vivienda_max_PC2 <- which.max(pca_coords[, 2])  # Mayor en PC2
vivienda_min_PC2 <- which.min(pca_coords[, 2])  # Menor en PC2

# Obtener los índices de los clientes extremos
vivienda_extremos <- c(vivienda_max_PC1, vivienda_min_PC1, vivienda_max_PC2, vivienda_min_PC2)
vivienda_extremos
## [1] 1762 4480 8073 2926
#Para explicar el sentido de los ejes, se escogen 4 casos extremos
datos<- rbind(vivienda_num_limpio[1762,], 
              vivienda_num_limpio[4480,],
              vivienda_num_limpio[8073,],
              vivienda_num_limpio[2926,])
datos <- as.data.frame(datos)
rownames(datos) = c("vivienda 1762","vivienda 4480","vivienda 8073","vivienda 2926")
datos
##                 id estrato preciom areaconst parqueaderos banios habitaciones
## vivienda 1762 5684       6    1800      1586           10      4            5
## vivienda 4480 7522       3     148        87            0      0            0
## vivienda 8073  534       3     370      1440            1      4           10
## vivienda 2926 5908       5     950       280           10      0            0
##                longitud latitud
## vivienda 1762 -76.53798 3.35961
## vivienda 4480 -76.55000 3.37500
## vivienda 8073 -76.49815 3.46343
## vivienda 2926 -76.53932 3.39698
casos1 <- rbind(res.pca$x[1762,1:2],res.pca$x[4480,1:2]) # CP1
rownames(casos1) = c("1762","4480")
casos1 <- as.data.frame(casos1)

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

se ubican los extremos de los componentes en el gráfico de componentes principales

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)

vivienda_num_limpio[c(1762, 4480, 8073, 2926), ]
## # A tibble: 4 × 9
##      id estrato preciom areaconst parqueaderos banios habitaciones longitud
##   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>    <dbl>
## 1  5684       6    1800      1586           10      4            5    -76.5
## 2  7522       3     148        87            0      0            0    -76.6
## 3   534       3     370      1440            1      4           10    -76.5
## 4  5908       5     950       280           10      0            0    -76.5
## # ℹ 1 more variable: latitud <dbl>
#PC1 representa precio, área, parqueaderos y baños, mientras que PC2 represneta estrato y habitaciones. 
res.pca
## Standard deviations (1, .., p=6):
## [1] 1.8480703 1.1222455 0.6779600 0.6592367 0.4909833 0.4357909
## 
## Rotation (n x k) = (6 x 6):
##                    PC1        PC2        PC3         PC4         PC5        PC6
## estrato      0.3306904 -0.5816950  0.5551021  0.07749341  0.46143880 -0.1587620
## preciom      0.4781754 -0.1883533 -0.0359721 -0.36062553 -0.21967777  0.7458339
## areaconst    0.4417250  0.2388377 -0.2878930 -0.61783592  0.27859384 -0.4534506
## parqueaderos 0.4096354 -0.2607395 -0.6565763  0.57091588  0.04760414 -0.0700729
## banios       0.4668745  0.1921461  0.3633148  0.18500105 -0.67876421 -0.3437503
## habitaciones 0.2847775  0.6813261  0.2111757  0.34936005  0.44521273  0.2997240

El gráfico muestra la distribución de las viviendas en el espacio definido por los dos primeros componentes principales (PCA), que explican el 77.9% de la variabilidad total de los datos. Cada punto representa una vivienda proyectada en este nuevo espacio, donde la mayoría se agrupa en una región central, indicando que comparten características similares. Sin embargo, se destacan algunos casos extremos en rojo y azul, que corresponden a viviendas con características significativamente diferentes del resto. La Dimensión 1 (56.9%) parece estar relacionada con factores como el precio, el área construida y el estrato, mientras que la Dimensión 2 (21%) parece diferenciar las viviendas en función del número de habitaciones y otras características secundarias. La vivienda ubicada en el extremo derecho (1762) sugiere una propiedad con un alto impacto en las variables asociadas a Dim 1, mientras que las viviendas en los extremos superior (8073) e inferior (2926) en Dim 2 podrían diferenciarse por su distribución en términos de número de habitaciones o estructura interna.

Análisis de agrupamiento

Para este análisis se vueve a generar la limpieza y escalamietno de la base de datos, ahora incluyendo variables categóricas

library(paqueteMODELOS)
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>
vivienda$longitud <- as.character(vivienda$longitud)
vivienda$latitud <- as.character(vivienda$latitud)
vivienda$id <- as.character(vivienda$id)


# Seleccionar solo las columnas numéricas
vivienda_num <- vivienda[sapply(vivienda, is.numeric)]
# Escalar solo las variables numéricas
vivienda_num_scaled <- as.data.frame(scale(vivienda_num))

# Unir las variables categóricas con las numéricas escaladas
vivienda_scaled <- cbind(vivienda_num_scaled, vivienda[sapply(vivienda, is.character)])
vivienda_scaled$longitud <- as.numeric(vivienda_scaled$longitud)
vivienda_scaled$latitud <- as.numeric(vivienda_scaled$latitud)
                         
str(vivienda_scaled)
## 'data.frame':    8322 obs. of  13 variables:
##  $ estrato     : num  -1.587 -1.587 -1.587 -0.616 0.356 ...
##  $ preciom     : num  -0.56 -0.347 -0.255 -0.103 -0.529 ...
##  $ areaconst   : num  -0.734 -0.384 0.315 0.735 -0.594 ...
##  $ parqueaderos: num  -0.742 -0.742 0.147 1.035 -0.742 ...
##  $ banios      : num  -0.0779 -0.7781 -0.7781 1.3224 -0.7781 ...
##  $ habitaciones: num  1.641 -0.415 0.27 -0.415 -0.415 ...
##  $ id          : chr  "1147" "1169" "1350" "5992" ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  NA NA NA "02" ...
##  $ 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 ...
# verificamos datos faltes
library(mice)
md.pattern(vivienda_scaled)

##      preciom estrato areaconst banios habitaciones id zona tipo barrio longitud
## 4808       1       1         1      1            1  1    1    1      1        1
## 1909       1       1         1      1            1  1    1    1      1        1
## 876        1       1         1      1            1  1    1    1      1        1
## 726        1       1         1      1            1  1    1    1      1        1
## 1          1       0         0      0            0  0    0    0      0        0
## 2          0       0         0      0            0  0    0    0      0        0
##            2       3         3      3            3  3    3    3      3        3
##      latitud parqueaderos piso     
## 4808       1            1    1    0
## 1909       1            1    0    1
## 876        1            0    1    1
## 726        1            0    0    2
## 1          0            0    0   12
## 2          0            0    0   13
##            3         1605 2638 4275
#Según el resultado, hay 1 registro que solo tiene una variable, 2 que no tienen ninguna, 1602 que no tienen información del parqueadero.
# se eliminarán las filas con pocas variables, se imputará el núero de parqueaderos a "0"

# Ver cuántos valores faltantes tiene cada fila
num_na_por_fila <- rowSums(is.na(vivienda_scaled))

# Filtrar el dataset: conservar solo las filas con al menos 2 valores no NA
vivienda_scaled_limpio <- vivienda_scaled[num_na_por_fila < (ncol(vivienda_scaled) - 1), ]


#pasamos los parqueaderos vacios a cero
vivienda_scaled_limpio$parqueaderos[is.na(vivienda_scaled_limpio$parqueaderos)] <- 0
vivienda_scaled_limpio$piso[is.na(vivienda_scaled_limpio$piso)] <- 1


# se verifica
md.pattern(vivienda_scaled_limpio)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      estrato preciom areaconst parqueaderos banios habitaciones id zona piso
## 8319       1       1         1            1      1            1  1    1    1
##            0       0         0            0      0            0  0    0    0
##      tipo barrio longitud latitud  
## 8319    1      1        1       1 0
##         0      0        0       0 0
head(vivienda_scaled_limpio) # primeros 6 registros
##      estrato    preciom  areaconst parqueaderos      banios habitaciones   id
## 1 -1.5872276 -0.5595420 -0.7339949   -0.7424551 -0.07793773    1.6406840 1147
## 2 -1.5872276 -0.3465477 -0.3842568   -0.7424551 -0.77811479   -0.4147626 1169
## 3 -1.5872276 -0.2552644  0.3152194    0.1465058 -0.77811479    0.2703863 1350
## 4 -0.6156201 -0.1031256  0.7349051    1.0354668  1.32241640   -0.4147626 5992
## 5  0.3559875 -0.5291143 -0.5940997   -0.7424551 -0.77811479   -0.4147626 1212
## 6  0.3559875 -0.5899698 -0.6150839   -0.7424551 -0.07793773   -0.4147626 1724
##           zona piso        tipo      barrio  longitud latitud
## 1 Zona Oriente    1        Casa 20 de julio -76.51168 3.43382
## 2 Zona Oriente    1        Casa 20 de julio -76.51237 3.43369
## 3 Zona Oriente    1        Casa 20 de julio -76.51537 3.43566
## 4     Zona Sur   02        Casa  3 de julio -76.54000 3.43500
## 5   Zona Norte   01 Apartamento       acopi -76.51350 3.45891
## 6   Zona Norte   01 Apartamento       acopi -76.51700 3.36971
#distribución de individuos por distancias
library(tidyverse)

# distancia euclidiana
dist_viv <- dist(vivienda_scaled_limpio, method = 'euclidean')

# Clúster jerárquico con el método complete
hc_viv <- hclust(dist_viv, method = 'complete')

# Determinamos a dónde pertenece cada observación
cluster_assigments <- cutree(hc_viv, k = 4)

assigned_cluster<-cluster_assigments

# asignamos los clusters
assigned_cluster <- vivienda_scaled_limpio %>% mutate(cluster = as.factor(cluster_assigments))

media_estrato <- mean(vivienda$estrato, na.rm = TRUE)
sd_estrato <- sd(vivienda$estrato, na.rm = TRUE)

assigned_cluster$estrato_original <- (assigned_cluster$estrato * sd_estrato) + media_estrato


# gráfico de puntos
ggplot(assigned_cluster, aes(x = zona, y = preciom, color = cluster)) +
  geom_point(size = 4) +
  geom_text(aes(label = cluster), vjust = -.8) + # Agregar etiquetas del clúster
  theme_classic()

# gráfico de puntos
ggplot(assigned_cluster, aes(x = estrato_original, y = preciom, color = cluster)) +
  geom_point(size = 4) +
  geom_text(aes(label = cluster), vjust = -.8) + # Agregar etiquetas del clúster
  theme_classic()

# gráfico de puntos
ggplot(assigned_cluster, aes(x = estrato_original, y = zona, color = cluster)) +
  geom_point(size = 4) +
  geom_text(aes(label = cluster), vjust = -.8) + # Agregar etiquetas del clúster
  theme_classic()

plot(hc_viv, cex = 0.6, main = "Dendograma de viviendas", las=1,
     ylab = "Distancia euclidiana", xlab = "Grupos")
rect.hclust(hc_viv, k = 4, border = 2:5)

#medias de clusteres para entender mejor la relación de colores
assigned_cluster %>%
  group_by(cluster) %>%
  summarise(
    promedio_precio = mean(preciom, na.rm = TRUE),
    promedio_areaconst = mean(areaconst, na.rm = TRUE),
    promedio_parqueaderos = mean(parqueaderos, na.rm = TRUE),
    promedio_banios = mean(banios, na.rm = TRUE),
    promedio_habitaciones = mean(habitaciones, na.rm = TRUE)
  )
## # A tibble: 4 × 6
##   cluster promedio_precio promedio_areaconst promedio_parqueaderos
##   <fct>             <dbl>              <dbl>                 <dbl>
## 1 1               -0.478             -0.295                -0.261 
## 2 2                0.461              0.346                 0.340 
## 3 3                0.0554             0.0861                0.0264
## 4 4                0.379              0.0920                0.123 
## # ℹ 2 more variables: promedio_banios <dbl>, promedio_habitaciones <dbl>
#dendograma
plot(hc_viv, cex = 0.6, main = "Dendograma de viviendas", las=1,
     ylab = "Distancia euclidiana", xlab = "Grupos")
rect.hclust(hc_viv, k = 4, border = 2:5)

Gracias a los valores médios de precio, area construida, parqueaderos, nuemro de baños y número de habitaciones de cada uno de los clusteres generados, es posible observar que el cluster 1 es que el que esta albergando las viviendas de menos precio, menor área construida, menos parqueaderos y menos cantidad de baños; el cluster 2 representa a las más costosas y con maypr área; el cluster numero 3 representa a las que estan muy cerca de la media en las distribuciones de cada variable, y por último, el cluster 4 representa viviendas con alto precio pero poca área construida, pocos baños, parqueaderos y habitaciones.

De esta manera, podemos acercarnos a análisis como: * en la zona sur de la ciudad estan las viviendas más costosas y más grandes (cluster 2). * en la zona oriente encontramos las mas pequeñas y económicas. * en la zona oeste estan las viviendas con características especiales que tienen un alto valor comercial pero baja cantidad de área, habitaciones, baños y parqueaderos. * en la zona norte se encuentran la mayoría de viviendas con características promedio y algúnas de las más económicas. * en la zona centro se encuentran viviendas de los clusteres 1, 2 y 3 pero con costos más bajos.

Adicionalmente, segúin la gráfica que nos relaciona los clusteres con los estratos y el precio, se obtiene la siguiente información:

  • el estrato 6 esta representado principalmente por el cluster 4, viviendas exclusivas con características especiales y alto costo.
  • En el estrato 5 encontramos viviendas de todos los clústeres, resaltando los clusteres 4 y 2.
  • en el estrato 4 se encuentran la mayoría de las viviendas del cluster 3, que representan viviendas promedio.
  • por último, en el estrato 3 estan ubicadas las viviendas más económicas y más pequeñas.

Análisis de correspondencia

library(paqueteMODELOS)
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")=
##   .. 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>
vivienda$longitud <- as.character(vivienda$longitud)
vivienda$latitud <- as.character(vivienda$latitud)
vivienda$id <- as.character(vivienda$id)


# Seleccionar solo las columnas numéricas
vivienda_num <- vivienda[sapply(vivienda, is.numeric)]
# Escalar solo las variables numéricas
vivienda_num_scaled <- as.data.frame(scale(vivienda_num))

# Unir las variables categóricas con las numéricas escaladas
vivienda_scaled <- cbind(vivienda_num_scaled, vivienda[sapply(vivienda, is.character)])
vivienda_scaled$longitud <- as.numeric(vivienda_scaled$longitud)
vivienda_scaled$latitud <- as.numeric(vivienda_scaled$latitud)

str(vivienda_scaled)
## 'data.frame':    8322 obs. of  13 variables:
##  $ estrato     : num  -1.587 -1.587 -1.587 -0.616 0.356 ...
##  $ preciom     : num  -0.56 -0.347 -0.255 -0.103 -0.529 ...
##  $ areaconst   : num  -0.734 -0.384 0.315 0.735 -0.594 ...
##  $ parqueaderos: num  -0.742 -0.742 0.147 1.035 -0.742 ...
##  $ banios      : num  -0.0779 -0.7781 -0.7781 1.3224 -0.7781 ...
##  $ habitaciones: num  1.641 -0.415 0.27 -0.415 -0.415 ...
##  $ id          : chr  "1147" "1169" "1350" "5992" ...
##  $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr  NA NA NA "02" ...
##  $ 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 ...
# verificamos datos faltes
library(mice)
md.pattern(vivienda_scaled)

##      preciom estrato areaconst banios habitaciones id zona tipo barrio longitud
## 4808       1       1         1      1            1  1    1    1      1        1
## 1909       1       1         1      1            1  1    1    1      1        1
## 876        1       1         1      1            1  1    1    1      1        1
## 726        1       1         1      1            1  1    1    1      1        1
## 1          1       0         0      0            0  0    0    0      0        0
## 2          0       0         0      0            0  0    0    0      0        0
##            2       3         3      3            3  3    3    3      3        3
##      latitud parqueaderos piso     
## 4808       1            1    1    0
## 1909       1            1    0    1
## 876        1            0    1    1
## 726        1            0    0    2
## 1          0            0    0   12
## 2          0            0    0   13
##            3         1605 2638 4275
#Según el resultado, hay 1 registro que solo tiene una variable, 2 que no tienen ninguna, 1602 que no tienen información del parqueadero.
# se eliminarán las filas con pocas variables, se imputará el núero de parqueaderos a "0"

# Ver cuántos valores faltantes tiene cada fila
num_na_por_fila <- rowSums(is.na(vivienda_scaled))

# Filtrar el dataset: conservar solo las filas con al menos 2 valores no NA
vivienda_scaled_limpio <- vivienda_scaled[num_na_por_fila < (ncol(vivienda_scaled) - 1), ]


#pasamos los parqueaderos vacios a cero
vivienda_scaled_limpio$parqueaderos[is.na(vivienda_scaled_limpio$parqueaderos)] <- 0
vivienda_scaled_limpio$piso[is.na(vivienda_scaled_limpio$piso)] <- 1

media_estrato <- mean(vivienda$estrato, na.rm = TRUE)
sd_estrato <- sd(vivienda$estrato, na.rm = TRUE)

vivienda_scaled_limpio$estrato_original <- (vivienda_scaled_limpio$estrato * sd_estrato) + media_estrato

# se verifica
md.pattern(vivienda_scaled_limpio)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      estrato preciom areaconst parqueaderos banios habitaciones id zona piso
## 8319       1       1         1            1      1            1  1    1    1
##            0       0         0            0      0            0  0    0    0
##      tipo barrio longitud latitud estrato_original  
## 8319    1      1        1       1                1 0
##         0      0        0       0                0 0
head(vivienda_scaled_limpio) # primeros 6 registros
##      estrato    preciom  areaconst parqueaderos      banios habitaciones   id
## 1 -1.5872276 -0.5595420 -0.7339949   -0.7424551 -0.07793773    1.6406840 1147
## 2 -1.5872276 -0.3465477 -0.3842568   -0.7424551 -0.77811479   -0.4147626 1169
## 3 -1.5872276 -0.2552644  0.3152194    0.1465058 -0.77811479    0.2703863 1350
## 4 -0.6156201 -0.1031256  0.7349051    1.0354668  1.32241640   -0.4147626 5992
## 5  0.3559875 -0.5291143 -0.5940997   -0.7424551 -0.77811479   -0.4147626 1212
## 6  0.3559875 -0.5899698 -0.6150839   -0.7424551 -0.07793773   -0.4147626 1724
##           zona piso        tipo      barrio  longitud latitud estrato_original
## 1 Zona Oriente    1        Casa 20 de julio -76.51168 3.43382                3
## 2 Zona Oriente    1        Casa 20 de julio -76.51237 3.43369                3
## 3 Zona Oriente    1        Casa 20 de julio -76.51537 3.43566                3
## 4     Zona Sur   02        Casa  3 de julio -76.54000 3.43500                4
## 5   Zona Norte   01 Apartamento       acopi -76.51350 3.45891                5
## 6   Zona Norte   01 Apartamento       acopi -76.51700 3.36971                5
library(FactoMineR)

tabla_zona_estrato<-table(vivienda_scaled_limpio$zona, vivienda_scaled_limpio$estrato_original)
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
library(FactoMineR)
library(factoextra)
library(gridExtra)
resultados_ac <- CA(tabla_zona_estrato)

El análisis de correspondecia resalata lo que se concluyó en los análisis de clusteres:

  • en la zona sur se encuentran los estratos 4 y 5 principalmente.
  • en la zona oeste estan casi la totalidad de las viviendas estrato 6.
  • en la zona centro y oriente estan las viviendas estrato 3.

Conclusiones y recomendaciones

Conclusiones Clave:

Tendencias de Precio y Ubicación:

  • Se identifican patrones de precios basados en la ubicación, donde ciertas zonas presentan una mayor valorización en el tiempo.
  • La relación entre precio y características del inmueble (tamaño, número de habitaciones, parqueaderos) es significativa, lo que sugiere que la demanda se inclina por ciertas especificaciones.

Factores Determinantes en la Valoración Inmobiliaria:

  • Los estratos socioeconómicos influyen en la percepción de valor de los inmuebles.
  • La disponibilidad de parqueaderos y el número de habitaciones tienen un impacto en la decisión de compra de los clientes.

Demanda y Rentabilidad:

  • Se identifican segmentos de clientes con alta disposición de compra en ciertas zonas, lo que permite focalizar esfuerzos de marketing e inversión.
  • El análisis de la varianza explicada en el PCA muestra que pocos componentes pueden resumir gran parte de la variabilidad en los datos, lo que facilita la segmentación y predicción de tendencias del mercado.

Datos Faltantes y su Impacto en el Análisis:

  • Se observó que los datos faltantes en la variable “parqueaderos” podrían afectar la precisión del análisis. La estrategia de imputación adoptada (asignar ceros) permitió limpiar el dataset y garantizar la completitud de la información sin afectar significativamente la distribución.

Recomendaciones Estratégicas:

Optimización de la Inversión en Propiedades:

  • Enfocar la inversión en zonas con tendencia de valorización constante y alta demanda.
  • Priorizar inmuebles con características que maximizan la rentabilidad, como mayor número de habitaciones y parqueaderos.

Segmentación y Estrategia de Marketing:

  • Desarrollar campañas específicas para cada segmento de clientes identificados.
  • Utilizar análisis de componentes principales (PCA) para mejorar la personalización de las ofertas inmobiliarias y optimizar la publicidad digital.

Estrategia de Precios Dinámicos:

  • Implementar modelos predictivos que ajusten los precios en función de la oferta y demanda en tiempo real.
  • Evaluar factores como el estrato y la ubicación para definir precios competitivos que atraigan a compradores potenciales.

Gestión de Datos y Mejora en la Calidad de Información:

  • Establecer estrategias de recolección de datos más eficientes para evitar valores faltantes en variables clave.
  • Implementar sistemas de gestión de información que permitan un análisis más preciso y en tiempo real.