Evaluación de la oferta inmobiliaria urbana

[Problema]

Problema

Una empresa inmobiliaria líder en una gran ciudad está buscando comprender en profundidad el mercado de viviendas urbanas para tomar decisiones estratégicas más informadas. La empresa posee una base de datos extensa que contiene información detallada sobre diversas propiedades residenciales disponibles en el mercado. Se requiere realizar un análisis holístico de estos datos para identificar patrones, relaciones y segmentaciones relevantes que permitan mejorar la toma de decisiones en cuanto a la compra, venta y valoración de propiedades.

Se incluyen las librerías a utilizar

library(devtools)
library(knitr)
devtools::install_github("dgonxalex80/paqueteMODELOS", force = TRUE)
## ggplot2 (3.4.4 -> 3.5.0) [CRAN]
## magick  (2.8.2 -> 2.8.3) [CRAN]
## 
##   There is a binary version available but the source version is later:
##         binary source needs_compilation
## ggplot2  3.4.4  3.5.0             FALSE
## 
## package 'magick' successfully unpacked and MD5 sums checked
## 
## The downloaded binary packages are in
##  C:\Users\cdela\AppData\Local\Temp\Rtmp6ZferS\downloaded_packages
## ── R CMD build ─────────────────────────────────────────────────────────────────
##   
  
  
✔  checking for file 'C:\Users\cdela\AppData\Local\Temp\Rtmp6ZferS\remotes52182e7b1996\dgonxalex80-paqueteMODELOS-796f588/DESCRIPTION'
## 
  
  
  
─  preparing 'paqueteMODELOS':
##    checking DESCRIPTION meta-information ...
  
✔  checking DESCRIPTION meta-information
## 
  
  
  
─  checking for LF line-endings in source and make files and shell scripts
## 
  
─  checking for empty or unneeded directories
## ─  building 'paqueteMODELOS_0.1.0.tar.gz'
## 
  
   
## 
library(paqueteMODELOS)
library(mice)
library(ggplot2)
library(tidyverse)

Limpieza de datos

Cargamos y examinamos los datos

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>

Mostramos data faltante (rotando los nombres para mejor visualización)

md.pattern(vivienda, rotate.names = TRUE)

##      preciom id zona estrato areaconst banios habitaciones 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

Creamos vector missing_data que contiene el número de valores faltantes por cada variable de vivienda

missing_data <-sapply(vivienda, function(x) sum(is.na(x))) 
missing_data
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3

Para visualizarlo mejor, usaremos la función kable del paquete knitr

kable(missing_data, format = "markdown", align = "c", col.names = "Missing Data")
Missing Data
id 3
zona 3
piso 2638
estrato 3
preciom 2
areaconst 3
parqueaderos 1605
banios 3
habitaciones 3
tipo 3
barrio 3
longitud 3
latitud 3

Podemos observar que “piso” y “parqueaderos” son los atributos con mayor número de datos faltantes, con 2638 y 1605 respectivamente.

percent_missing_data <- function(x) {sum(is.na(x))/length(x)*100} 
kable(apply(vivienda, 2, percent_missing_data), format = "markdown", align = "c", col.names = "Percent Missing Data")
Percent Missing Data
id 0.0360490
zona 0.0360490
piso 31.6991108
estrato 0.0360490
preciom 0.0240327
areaconst 0.0360490
parqueaderos 19.2862293
banios 0.0360490
habitaciones 0.0360490
tipo 0.0360490
barrio 0.0360490
longitud 0.0360490
latitud 0.0360490

Podemos observar que el porcentaje de valores faltantes en piso es de 31.69% mientras que de parqueadero es el 19.28%.

Decidimos eliminar esas variables con datos faltantes al igual que las filas con datos faltantes, sin afectar la bd original.

vivienda_clean_columns <- vivienda[, !names(vivienda) %in% c("id","piso", "parqueaderos")]

vivienda_clean <- vivienda_clean_columns[complete.cases(vivienda_clean_columns), ]

vivienda_clean

Confirmamos que no tengamos datos faltantes.

md.pattern(vivienda_clean, rotate.names = TRUE)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      zona estrato preciom areaconst banios habitaciones tipo barrio longitud
## 8319    1       1       1         1      1            1    1      1        1
##         0       0       0         0      0            0    0      0        0
##      latitud  
## 8319       1 0
##            0 0

Estandarizamos variables numéricas, restandole la media y dividiendo por la desviación estandar:

vivienda_cp <- vivienda_clean %>% select_if(is.numeric) %>% scale()
head (vivienda_cp)
##         estrato    preciom  areaconst      banios habitaciones   longitud
## [1,] -1.5872276 -0.5595498 -0.7339949 -0.07793773    1.6406840  0.9728466
## [2,] -1.5872276 -0.3465670 -0.3842568 -0.77811479   -0.4147626  0.9331875
## [3,] -1.5872276 -0.2552886  0.3152194 -0.77811479    0.2703863  0.7607566
## [4,] -0.6156201 -0.1031580  0.7349051  1.32241640   -0.4147626 -0.6549016
## [5,]  0.3559875 -0.5291236 -0.5940997 -0.77811479   -0.4147626  0.8682385
## [6,]  0.3559875 -0.5899759 -0.6150839 -0.07793773   -0.4147626  0.6670691
##         latitud
## [1,]  0.3793708
## [2,]  0.3763219
## [3,]  0.4225243
## [4,]  0.4070454
## [5,]  0.9678065
## [6,] -1.1242009
prcomp(vivienda_cp)
## Standard deviations (1, .., p=7):
## [1] 1.7649945 1.2197986 0.9408170 0.8090903 0.6534211 0.4902176 0.4357175
## 
## Rotation (n x k) = (7 x 7):
##                     PC1         PC2         PC3         PC4         PC5
## estrato       0.3596495 -0.46160840  0.29253122 -0.24988384 -0.47695341
## preciom       0.4947460 -0.06764679  0.25051923 -0.25237458  0.22943062
## areaconst     0.4556538  0.26756925  0.02809938 -0.12641620  0.64147176
## banios        0.4903682  0.18623745 -0.06882523 -0.01680433 -0.39400870
## habitaciones  0.2974110  0.54943309 -0.32731570  0.32355603 -0.33736244
## longitud     -0.2672367  0.46217187 -0.01581921 -0.82481589 -0.17733905
## latitud      -0.1338978  0.40072976  0.85950759  0.26942892 -0.08880096
##                       PC6         PC7
## estrato       0.501853021  0.17381740
## preciom      -0.219896274 -0.72332891
## areaconst     0.292270190  0.45509101
## banios       -0.652112622  0.37328490
## habitaciones  0.431734658 -0.31247031
## longitud      0.052076162 -0.01578471
## latitud      -0.003114001  0.04739724

Análisis de componentes principales

library(factoextra)
acp.pca <- prcomp(vivienda_cp)
fviz_eig(acp.pca, addlabels = TRUE, barfill = "green", col = "red", labelsize = 14, ggtitle = "Valores propios de PCA", xlab = "Dimensión", ylab = "Porcentaje de varianza")

Podemos observar que que el primer componente corresponde al 44.5% de variabilidad, el segundo 21.3%, 12.6% el tercero y 9.4% el cuarto. Realizaremos el análisis de componentes principales con estos primeros 4 componente pues suman el 87.8% de variabilidad.

Dimensión 1:

fviz_contrib(acp.pca,axes = 1, choice = "var", barfill = "green", col = "red",addlabels = TRUE)

Dimensión 2:

fviz_contrib(acp.pca,axes = 2, choice = "var", barfill = "green", col = "red",addlabels = TRUE)

Dimensión 3:

fviz_contrib(acp.pca,axes = 3, choice = "var", barfill = "green", col = "red",addlabels = TRUE)

Dimensión 4:

fviz_contrib(acp.pca,axes = 4, choice = "var", barfill = "green", col = "red",addlabels = TRUE)

fviz_pca_var(acp.pca,
col.var = "contrib", 
gradient.cols = c("green",  "red"),
repel = TRUE,
bg = "blue",
theme = "minimal",
legend.title = "Contribución",
arrowsize = 1
)

La Dim1 explica el 44.5% de la variabilidad, mientras que la Dim2 explica el 21.3%. Las variables como “areaconst”, “banios”, “precio” y “estrato” están fuertemente asociadas con la Dim1, mientras que “latitud” y “longitud” están más asociadas con la Dim2.

Se observa que la latitud no contribuye tanto, como si lo hace banios, areaconst.

filas<- rbind(vivienda_cp[110,], 
vivienda_cp[1060,],
vivienda_cp[19,],
vivienda_cp[470,])

filas <- as.data.frame(filas)
rownames(filas) = c("Fila 114","Fila 106","Fila 19","Fila 470")

print(filas)
##             estrato    preciom  areaconst      banios habitaciones    longitud
## Fila 114  0.3559875  0.5662165  1.9240146 -0.07793773   -2.4702091 -0.07495879
## Fila 106 -1.5872276 -1.0615807 -0.7899530 -1.47829186   -0.4147626  2.01260527
## Fila 19   0.3559875 -0.1487972 -0.5031678 -0.77811479   -0.4147626 -0.75433674
## Fila 470  1.3275950  1.4181477  0.5250622  1.32241640    0.2703863 -1.74696425
##              latitud
## Fila 114 -0.66546026
## Fila 106  1.36369315
## Fila 19   0.14906237
## Fila 470  0.03179738
casos1 <- rbind(acp.pca$x[110,1:4],acp.pca$x[1060,1:4]) # CP1
rownames(casos1) = c("110","1060")
casos1 <- as.data.frame(casos1)

casos2 <- rbind(acp.pca$x[019,1:4], acp.pca$x[470,1:4]) # CP2
rownames(casos2) = c("019","470")
casos2 <- as.data.frame(casos2)

fviz_pca_ind(acp.pca, col.ind = "gray", gradient.cols = c("green", "orange", "purple")) +
geom_point(data = casos1, aes(x = PC1, y = PC2), color = "red", size = 5) +
geom_point(data = casos2, aes(x = PC1, y = PC2), color = "blue", size = 5)

Obtenemos el número de elementos en el conjunto de datos

length(vivienda_clean$tipo)
## [1] 8319

Obtenemos el número de filas de la matriz

nrow(acp.pca$x)
## [1] 8319
fviz_pca_biplot(
  acp.pca, 
  habillage = vivienda_clean$tipo,
  repel = TRUE,
  col.var =  "red",
  col.ind = c("green", "blue")
) 

De la anterior gráfica podemos analizar que el tipo de vivienda apartamento, tiene variables con mayor peso explicativo relacionado con metro cuadrado y estrato. Mientras que el tipo de vivienda casa las variables con mayor peso son habitaciones, areaconst, y banios

fviz_pca_biplot(
  acp.pca, 
  repel = TRUE,
  habillage = vivienda_clean$zona,
  col.var = "red",
  col.ind = c("green", "purple")
)

Con respecto a la zona, podemos ver que influye en la variación. Por ejemplo:

  • Zona norte priman habitacion, longitud y latitud.

  • Zona sur: areaconst

  • Zona oeste: banios, preciom

Análisis de conglomerados

Análisis de correspondencia

Conclusión