El reto principal consisten en realizar un análisis integral y multidimensional de la base de datos para obtener una comprensión del mercado inmobiliario urbano. Se requiere aplicar diversas técnicas de análisis de datos, incluyendo:

Análisis de Componentes Principales: Reducir la dimensionalidad del conjunto de datos y visualizar la estructura de las variables en componentes principales para identificar características clave que influyen en la variación de precios y oferta del mercado.

Análisis de Conglomerados: Agrupar las propiedades residenciales en segmentos homogéneos con características similares para entender las dinámicas de las ofertas específicas en diferentes partes de la ciudad y en diferentes estratos socioeconómicos.

Análisis de Correspondencia : Examinar la relación entre las variables categóricas (tipo de vivienda, zona y barrio), para identificar patrones de comportamiento de la oferta en mercado inmobiliario.

Visualización de resultados: Presentar gráficos, mapas y otros recursos visuales para comunicar los hallazgos de manera clara y efectiva a la dirección de la empresa.

se hace llamado de los datos “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> 

Identificar campos vacios en la base de datos

# Para encontrar valores vacíos en todo el conjunto de datos

library(mice)
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>
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
vivienda2 <- data.frame(vivienda)
md.pattern(vivienda2,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
          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 

Se hace imputación de las variables parqueadero y piso. Se omiten aquellos registros que no esten completos. Quedando 8319 registros para el analisis

#imputar parqueaderos
vivienda2$parqueaderos[is.na(vivienda2$parqueaderos)] <-0

#imputar piso
vivienda2$piso[is.na(vivienda2$piso)] <-0

#imputar datos faltantes
vivienda2 <-na.omit(vivienda2)


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

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

El siguiente paso es convertir los registros de los campor piso,estrato,preciom,areaconst, banios, habitaciones en datos numericos.

#convertir en numerico los campos piso, preciom, aream , baños, habitaciones.

vivienda2$piso <-as.numeric(vivienda2$piso)
vivienda2$estrato <-as.numeric(vivienda2$estrato)
vivienda2$preciom <-as.numeric(vivienda2$preciom)
vivienda2$areaconst <-as.numeric(vivienda2$areaconst)
vivienda2$banios <-as.numeric(vivienda2$banios)
vivienda2$habitaciones <-as.numeric(vivienda2$habitaciones)

str(vivienda2)
'data.frame':   8319 obs. of  13 variables:
 $ id          : num  1147 1169 1350 5992 1212 ...
 $ zona        : chr  "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
 $ piso        : num  0 0 0 2 1 1 1 1 2 2 ...
 $ estrato     : num  3 3 3 4 5 5 4 5 5 5 ...
 $ preciom     : num  250 320 350 400 260 240 220 310 320 780 ...
 $ areaconst   : num  70 120 220 280 90 87 52 137 150 380 ...
 $ parqueaderos: num  1 1 2 3 1 1 2 2 2 2 ...
 $ banios      : num  3 2 2 5 2 3 2 3 4 3 ...
 $ habitaciones: num  6 3 4 3 3 3 3 4 6 3 ...
 $ tipo        : chr  "Casa" "Casa" "Casa" "Casa" ...
 $ barrio      : chr  "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
 $ longitud    : num  -76.5 -76.5 -76.5 -76.5 -76.5 ...
 $ latitud     : num  3.43 3.43 3.44 3.44 3.46 ...
 - attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
  ..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"

Se crea una base de datos solo con datos numericos, en este se incluye los campos: -preciom -estrato -piso -areaconst -parqueaderos -barrio -habitaciones

Con el fin de evitar que las variables que tiene una escala con valores más grandes afecten las estimaciones realizadas (sesgos) se realiza la estandarización de las variables antes de proceder a realizar el proceso de estimación de los componentes principales.

vivienda2 <- vivienda2[,c("preciom","estrato","piso","areaconst","parqueaderos","banios","habitaciones")]
vivienda2= scale(vivienda2[,1:7])
head(vivienda2) # primeros 6 registros
     preciom    estrato       piso  areaconst parqueaderos      banios
1 -0.5595498 -1.5872276 -0.9255638 -0.7339949   -0.3875522 -0.07793773
2 -0.3465670 -1.5872276 -0.9255638 -0.3842568   -0.3875522 -0.77811479
3 -0.2552886 -1.5872276 -0.9255638  0.3152194    0.4168506 -0.77811479
4 -0.1031580 -0.6156201 -0.2071010  0.7349051    1.2212534  1.32241640
5 -0.5291236  0.3559875 -0.5663324 -0.5940997   -0.3875522 -0.77811479
6 -0.5899759  0.3559875 -0.5663324 -0.6150839   -0.3875522 -0.07793773
  habitaciones
1    1.6406840
2   -0.4147626
3    0.2703863
4   -0.4147626
5   -0.4147626
6   -0.4147626
prcomp(vivienda2)
Standard deviations (1, .., p=7):
[1] 1.8501262 1.1819381 0.9307555 0.6769022 0.6518585 0.4906918 0.4357238

Rotation (n x k) = (7 x 7):
                     PC1        PC2         PC3         PC4        PC5
preciom       0.47660417  0.1683184 -0.11405920 -0.01219188  0.3592424
estrato       0.32632057  0.5210267 -0.24346981 -0.54797999 -0.1712473
piso         -0.05624508  0.5051515  0.84823031  0.04601540  0.1397874
areaconst     0.44355148 -0.2205624  0.03079337  0.20603152  0.6496296
parqueaderos  0.40706306  0.2474119 -0.08106990  0.72275475 -0.4872424
banios        0.46672129 -0.1339445  0.17622708 -0.33001544 -0.2103781
habitaciones  0.28798060 -0.5632233  0.41187588 -0.15400738 -0.3440872
                     PC6          PC7
preciom       0.21541366 -0.745587739
estrato      -0.45625352  0.158093347
piso         -0.02062750  0.008965092
areaconst    -0.28414812  0.456572351
parqueaderos -0.04430878  0.069078491
banios        0.68056871  0.341805824
habitaciones -0.44623648 -0.298267787

Se elabora matriz de correlación.

#install.packages("FactoMineR")
#library("FactoMineR")
library(ggcorrplot)
corr_matrix <- cor(vivienda2)
ggcorrplot(corr_matrix)

Componentes Principales.

library(tidyverse)

#install.packages("factoextra")
library(factoextra)


# Hacer PCA

r_pca <-prcomp(vivienda2)

#mostrar valores
#fviz_pca(r_pca,addlabels =TRUE)
fviz_eig(r_pca,addlabels =TRUE)

En este caso el primer componente principal explica el 48,9% de la variabilidad contenida en la base de datos y entre los tres primeros se casi el 80% de los datos (81.3), lo cual indicaría que con dos variable (CP1.CP2) que se obtiene mediante una combinación lineal de las variables se puede resumir gran parte de la variabilidad que contiene la base de datos.

CP1: Preciom, estrato CP2:Piso, areaconst

#library(cont)
fviz_pca_var(r_pca,
             col.var = "contrib", # Color by contributions to the pc
             gradient.cols=c("#FF7F00","#034D94"),
             repel = TRUE
)

Al visualizar las variables en el plano de los componentes principales permite identificar el sentido y la caracterización de los componentes.

fviz_pca_biplot(r_pca,label="Var",repel = TRUE)

informacion <- rbind(
  vivienda[200,],
  vivienda[1500,],
  vivienda[5000,],
  vivienda[8300,]
)

informacion<-as.data.frame(informacion)
rownames(informacion)=c("Vivienda 200","Vivienda 1500","Vivienda 5000","Vivienda 8300")
informacion
                id       zona piso estrato preciom areaconst parqueaderos
Vivienda 200  7617 Zona Oeste   05       6     950       217            3
Vivienda 1500 3635   Zona Sur   03       6     530       150            2
Vivienda 5000 7761   Zona Sur   01       5     350       176            1
Vivienda 8300 3490   Zona Sur   05       5     240        68            1
              banios habitaciones        tipo        barrio  longitud latitud
Vivienda 200       5            4 Apartamento     aguacatal -76.55081 3.45659
Vivienda 1500      4            3        Casa ciudad jardín -76.52753 3.35735
Vivienda 5000      2            3        Casa    pampalinda -76.55187 3.40384
Vivienda 8300      2            3 Apartamento      zona sur -76.52673 3.37677
casos1 <- rbind(r_pca$x[200,1:2],r_pca$x[1500,1:2]) # CP1
rownames(casos1) = c("200","1500")
casos1 <- as.data.frame(casos1)

casos2 <- rbind(r_pca$x[5000,1:2], r_pca$x[8300,1:2]) # CP2
rownames(casos2) = c("5000","8300")
casos2 <- as.data.frame(casos2)

fviz_pca_ind(r_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)

datavivienda<- rbind(vivienda[200,],
vivienda[500,],
vivienda[5000,],
vivienda[8300,])
datavivienda
# A tibble: 4 × 13
     id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
  <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
1  7617 Zona O… 05          6     950       217            3      5            4
2  7703 Zona O… <NA>        6     750       220            2      4            3
3  7761 Zona S… 01          5     350       176            1      2            3
4  3490 Zona S… 05          5     240        68            1      2            3
# ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
#library(factoextra)

#fviz_pca_biplot(r_pca, 
#repel = TRUE,

#col.var = "#034A94", # Variables color
#col.ind = c("#DEDEDE")  # Individuals color
#)
#habillage = vivienda2$preciom,

Analisis de conglomerados

library(tidyverse)
#library(NbClust)
# distancia euclidiana
dist_emp <- dist(vivienda2, method = 'euclidean')

# Cluster jerarquico con el método complete
hc_emp <- hclust(dist_emp, method = 'complete')

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

library(cluster)

fviz_nbclust(vivienda2,clara,method = "silhouette")+theme_classic()

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

La matriz de distancia se puede calcular usando la función dist (). Esta función calcula y devuelve la matriz de distancia calculada utilizando la medida de distancia especificada para calcular las distancias entre las filas de una matriz de datos. Para ejecutar esta función, asegúrese de que el paquete de estadísticas esté cargado usando la función require ().

El argumento x especifica una matriz numérica, un marco de datos o un objeto “dist”. El método del segundo argumento especifica la medida de distancia que se utilizará. El método debe ser uno de los siguientes:

“euclidiana”, “máximo”, “manhattan”, “canberra”, “binario” o “minkowski”

Calcule los resultados del objeto dist_emp como una matriz utilizando la función as.matrix () y especifique el número de filas y columnas que se imprimirán entre corchetes.

#dendograma <- hclust(dist_emp, method = "average")
#grp <- cutree(dendograma, k = 4)
#grp

# Imprimi la matriz de distancias
output = as.matrix(dist_emp)[1:7, 1:7]
round(output, digits = 3)
      1     2     3     4     5     6     7
1 0.000 2.210 2.051 3.550 2.940 2.854 2.542
2 2.210 0.000 1.270 3.126 1.996 2.123 1.428
3 2.051 1.270 0.000 2.681 2.434 2.547 1.755
4 3.550 3.126 2.681 0.000 3.166 2.772 2.834
5 2.940 1.996 2.434 3.166 0.000 0.703 1.295
6 2.854 2.123 2.547 2.772 0.703 0.000 1.465
7 2.542 1.428 1.755 2.834 1.295 1.465 0.000

El método “dist” de as.matrix() y as.dist() se puede utilizar para la conversión entre objetos de la clase “dist” y matrices de distancia convencionales.

d = as.dist(output)
d
          1         2         3         4         5         6
2 2.2097029                                                  
3 2.0514514 1.2704690                                        
4 3.5496871 3.1260947 2.6810844                              
5 2.9395131 1.9956184 2.4338183 3.1656366                    
6 2.8539548 2.1231837 2.5474208 2.7716020 0.7031296          
7 2.5415399 1.4279160 1.7549518 2.8340236 1.2948155 1.4645720
library(factoextra)

dist_emp <- dist(vivienda2, method = "euclidean")
dendograma <- hclust(dist_emp, method = "average")
# plot(dendograma, cex = 0.6, hang = -1) 
barplot(sort(dendograma$height, decreasing = TRUE), horiz = TRUE, 
main = "Agregaciones (distancias euclidianas)",
col = "lightblue", ylab = "Nodo", xlab = "Peso", xlim = c(0, 2.5))

library(tidyverse)
library(cluster)
# distancia euclidiana
dist_emp <- dist(vivienda2, method = 'euclidean')

# Cluster jerarquico con el método complete
hc_emp <- hclust(dist_emp, method = 'complete')

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

# Calcular el coeficiente de Silhouette
sil <- silhouette(cluster_assigments, dist(vivienda2))
sil_avg <- mean(sil[,3])

# Imprimir el coeficiente de Silhouette promedio
cat("Coeficiente de Silhouette promedio k=2 : ", sil_avg)
Coeficiente de Silhouette promedio k=2 :  0.5382836

El agrupamiento jerárquico es un análisis de agrupamiento sobre un conjunto de diferencias y métodos para analizarlo. Dicho agrupamiento se realiza mediante el uso de la hclust() función en el paquete de estadísticas .

El argumento despecifica una estructura de disimilitud producida por la dist() función. El segundo argumento es el method que especifica el método de aglomeración que se utilizará.

En el ejercicio se identifican dos cluster, con un coeficiente de Silhouette 0,5382.

Analisis de Correspondencia

vivienda3 <- vivienda[,c("tipo","zona","barrio")]


vivienda3$tipo <- as.factor(vivienda3$tipo)

vivienda3$zona <- as.factor(vivienda3$zona)

vivienda3$barrio <- as.factor(vivienda3$barrio)

Para el analisis de correspondencia se utilizan los campos tipo y zona, se eliminan los campos vacios.

library(mice)
md.pattern(vivienda3, rotate.names = TRUE)

     tipo zona barrio  
8319    1    1      1 0
3       0    0      0 3
        3    3      3 9
library(mice)
vivienda3 <- na.omit(vivienda3)
grafico <-md.pattern(vivienda3, rotate.names = TRUE)
 /\     /\
{  `---'  }
{  O   O  }
==>  V <==  No need for mice. This data set is completely observed.
 \  \|/  /
  `-----'

library(FactoMineR)
tabla <- table(vivienda3$tipo, vivienda3$zona)
tabla
             
              Zona Centro Zona Norte Zona Oeste Zona Oriente Zona Sur
  Apartamento          24       1198       1029           62     2787
  Casa                100        722        169          289     1939

Con la información suministrada en la tabla, se puede concluir que tanto en apartamentos y casas, la mayor concentración esta en el sur de la ciudad.

chisq.test(tabla)

    Pearson's Chi-squared test

data:  tabla
X-squared = 690.93, df = 4, p-value < 2.2e-16

El resultado indica que se rechaza la hipótesis de independencia de las variables (p-value: 0.0000), indicando grado tipo de relación entre ellas.

library(FactoMineR)
library(factoextra)
library(gridExtra)
library(dplyr)
resultados_ac <- CA(tabla)

valores_prop <-resultados_ac$eig