El presente informe permitirá identificar patrones, relaciones y segmentaciones relevantes que permitirán mejorar la toma de decisiones en cuanto a la compra y venta, así como la valoración de propiedades por parte de una empresa inmobiliaria.

Como primer punto será necesario la utilización de 2 librerías:

  1. factoextra:
    • factoextra es un paquete de R diseñado para extraer y visualizar los resultados de análisis de datos multivariados, especialmente aquellos que involucran técnicas de reducción de dimensionalidad y agrupamiento.
    • Funciona bien en conjunto con otros paquetes como FactoMineR, que proporciona funciones para realizar análisis de componentes principales (PCA), análisis factorial y más.
    • Con factoextra, puedes crear visualizaciones informativas, como gráficos de dispersión, biplots y varios tipos de mapas de factores, que permiten comprender la estructura de tus datos después de aplicar algoritmos de reducción de dimensionalidad o agrupamiento.
  2. dplyr:
    • dplyr es un paquete fundamental para la manipulación y transformación de datos en R.
    • Proporciona un conjunto de funciones que simplifican el proceso de filtrar, resumir, ordenar y mutar data frames.

En resumen, factoextra se enfoca principalmente en extraer y visualizar resultados de análisis de reducción de dimensionalidad y agrupamiento, mientras que dplyr se enfoca en tareas de manipulación y transformación de datos.

## Loading required package: ggplot2
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Habiendo realizado la instalación de las librerías, será necesario guardar los datos a analizar en una variable para comenzar a trabajar con ella, una vez hecho esto podemos observar los primeros 5 registros de nuestros datos de la siguiente manera:

##     id       zona piso estrato preciom areaconst parquea banios habitac
## 1 8312 Zona Oeste    4       6    1300       318       2      4       2
## 2 8311 Zona Oeste    1       6     480       300       1      4       4
## 3 8307 Zona Oeste   NA       5    1200       800       4      7       5
## 4 8296   Zona Sur    2       3     220       150       1      2       4
## 5 8297 Zona Oeste   NA       5     330       112       2      4       3
##          tipo           barrio longitud latitud
## 1 Apartamento         arboleda   -76576    3454
## 2        Casa        normandía   -76571    3454
## 3        Casa       miraflores   -76568    3455
## 4        Casa        el guabal   -76565    3417
## 5        Casa bella suiza alta   -76565    3408

Ahora observamos el tipo de dato y la cantidad de valores faltantes de cada columna.

## 'data.frame':    8322 obs. of  13 variables:
##  $ id       : int  8312 8311 8307 8296 8297 8298 8299 8300 8286 8287 ...
##  $ zona     : chr  "Zona Oeste" "Zona Oeste" "Zona Oeste" "Zona Sur" ...
##  $ piso     : int  4 1 NA 2 NA NA 2 NA NA 2 ...
##  $ estrato  : int  6 6 5 3 5 5 6 5 5 5 ...
##  $ preciom  : int  1300 480 1200 220 330 1350 305 480 275 285 ...
##  $ areaconst: num  318 300 800 150 112 390 125 280 74 120 ...
##  $ parquea  : int  2 1 4 1 2 8 2 4 1 2 ...
##  $ banios   : int  4 4 7 2 4 10 3 4 2 4 ...
##  $ habitac  : int  2 4 5 4 3 10 3 4 3 3 ...
##  $ tipo     : chr  "Apartamento" "Casa" "Casa" "Casa" ...
##  $ barrio   : chr  "arboleda" "normandía" "miraflores" "el guabal" ...
##  $ longitud : num  -76576 -76571 -76568 -76565 -76565 ...
##  $ latitud  : num  3454 3454 3455 3417 3408 ...
##        id      zona      piso   estrato   preciom areaconst   parquea    banios 
##         3         3      2638         3         2         3      1605         3 
##   habitac      tipo    barrio  longitud   latitud 
##         3         3         3         3         3

Borramos las columnas ID (ya que se trata únicamente de un número identificador de cada registro por lo que no será utilizado en el análisis). Eliminamos las columnas Barrio, Longitud y Latitud debido a que estaremos utilizando la columna Zona, la cual nos brindará la misma información de manera generalizada. Borramos algunas columnas como Piso y Parqueadero debido a que contienen muchos valores faltantes.

borrar <- c("id","barrio","latitud","longitud","piso","parquea")
vivienda <- my_data[ ,!(names(my_data) %in% borrar)]
colSums(is.na(vivienda))
##      zona   estrato   preciom areaconst    banios   habitac      tipo 
##         3         3         2         3         3         3         3

Luego de realizar estos pasos, podemos observar que aún existen missings en nuestros datos.Por lo que es necesario borrar las filas que contienen registros con missings:

vivienda <- vivienda[complete.cases(vivienda),]
colSums(is.na(vivienda))
##      zona   estrato   preciom areaconst    banios   habitac      tipo 
##         0         0         0         0         0         0         0

Ahora podemos ver que no existen datos faltantes. Para realizar el primer paso de éste análisis, emplearemos un análisis de componentes principales.

El Análisis de Componentes Principales (PCA, por sus siglas en inglés) es una técnica de análisis multivariado utilizada en estadísticas y ciencia de datos para reducir la dimensionalidad de conjuntos de datos complejos, manteniendo al mismo tiempo la mayor parte de la variabilidad presente en los datos originales. PCA se utiliza principalmente para simplificar la estructura de datos de alta dimensionalidad al transformar las variables originales en un nuevo conjunto de variables no correlacionadas llamadas componentes principales. El objetivo principal del PCA es encontrar un conjunto de componentes principales que sean combinaciones lineales de las variables originales y que expliquen la mayor cantidad posible de variabilidad en los datos. Los componentes principales están ordenados en función de la cantidad de varianza que explican, y el primer componente principal captura la mayor parte de la varianza total.

El Análisis de Componente Principales sólo es aplicable en variables numéricas, realizando una exploración de nuestros datos podemos encontrar que actualmente no todos nuestros datos son de tipo numérico.

str(vivienda)
## 'data.frame':    8319 obs. of  7 variables:
##  $ zona     : chr  "Zona Oeste" "Zona Oeste" "Zona Oeste" "Zona Sur" ...
##  $ estrato  : int  6 6 5 3 5 5 6 5 5 5 ...
##  $ preciom  : int  1300 480 1200 220 330 1350 305 480 275 285 ...
##  $ areaconst: num  318 300 800 150 112 390 125 280 74 120 ...
##  $ banios   : int  4 4 7 2 4 10 3 4 2 4 ...
##  $ habitac  : int  2 4 5 4 3 10 3 4 3 3 ...
##  $ tipo     : chr  "Apartamento" "Casa" "Casa" "Casa" ...

Por lo cual, será necesario implementar una mutación de datos.

# Realizamos un remplazo de los valores del campo Zona mediante la función mutate
vivienda <- vivienda %>% mutate(zona = replace(zona, 
                                               zona == "Zona Centro", 1))
vivienda <- vivienda %>% mutate(zona = replace(zona, 
                                               zona == "Zona Norte", 2))
vivienda <- vivienda %>% mutate(zona = replace(zona, 
                                               zona == "Zona Oeste", 3))
vivienda <- vivienda %>% mutate(zona = replace(zona, 
                                               zona == "Zona Oriente",
                                               4))
vivienda <- vivienda %>% mutate(zona = replace(zona, 
                                               zona == "Zona Sur", 5))
vivienda$zona <- as.numeric(vivienda$zona)

# Realizamos un remplazo de los valores del campo Tipo mediante la función mutate

vivienda <- vivienda %>% mutate(tipo = replace(tipo, 
                                               tipo == "Casa", 1))
vivienda <- vivienda %>% mutate(tipo = replace(tipo, 
                                               tipo == "Apartamento", 2))
vivienda$tipo <- as.numeric(vivienda$tipo)
## Warning: NAs introduced by coercion

Ahora todas nuestras variables son de tipo numérico, sin embargo, tenemos algunos missings en las columnas Zona y Tipo.

str(vivienda)
## 'data.frame':    8319 obs. of  7 variables:
##  $ zona     : num  3 3 3 5 3 5 5 3 5 5 ...
##  $ estrato  : int  6 6 5 3 5 5 6 5 5 5 ...
##  $ preciom  : int  1300 480 1200 220 330 1350 305 480 275 285 ...
##  $ areaconst: num  318 300 800 150 112 390 125 280 74 120 ...
##  $ banios   : int  4 4 7 2 4 10 3 4 2 4 ...
##  $ habitac  : int  2 4 5 4 3 10 3 4 3 3 ...
##  $ tipo     : num  2 1 1 1 1 1 2 2 2 2 ...

Procedemos a quitarlos del dataset.

vivienda <- vivienda[complete.cases(vivienda),]
#revisamos cuántos valores NA existen actualmente
colSums(is.na(vivienda))
##      zona   estrato   preciom areaconst    banios   habitac      tipo 
##         0         0         0         0         0         0         0

Observamos la cantidad de filas que obtuvimos del preprocesamiento

## [1] 8219

Detallamos la correlación de las variables:

correlacion <- round(cor(vivienda), 1)
correlacion
##           zona estrato preciom areaconst banios habitac tipo
## zona       1.0     0.1     0.0       0.0    0.1     0.0 -0.1
## estrato    0.1     1.0     0.6       0.3    0.4    -0.1  0.1
## preciom    0.0     0.6     1.0       0.7    0.7     0.3 -0.3
## areaconst  0.0     0.3     0.7       1.0    0.6     0.5 -0.5
## banios     0.1     0.4     0.7       0.6    1.0     0.6 -0.4
## habitac    0.0    -0.1     0.3       0.5    0.6     1.0 -0.5
## tipo      -0.1     0.1    -0.3      -0.5   -0.4    -0.5  1.0
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
## (as 'lib' is unspecified)
## corrplot 0.92 loaded

Hallamos la matriz de correlación

install.packages("Hmisc")
## Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
## (as 'lib' is unspecified)
library("Hmisc")
## 
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:dplyr':
## 
##     src, summarize
## The following objects are masked from 'package:base':
## 
##     format.pval, units
res2 <- rcorr(as.matrix(correlacion))
res2
##            zona estrato preciom areaconst banios habitac  tipo
## zona       1.00   -0.18   -0.30     -0.24  -0.18   -0.16 -0.10
## estrato   -0.18    1.00    0.60      0.24   0.28   -0.27 -0.01
## preciom   -0.30    0.60    1.00      0.88   0.86    0.51 -0.70
## areaconst -0.24    0.24    0.88      1.00   0.88    0.75 -0.89
## banios    -0.18    0.28    0.86      0.88   1.00    0.80 -0.88
## habitac   -0.16   -0.27    0.51      0.75   0.80    1.00 -0.88
## tipo      -0.10   -0.01   -0.70     -0.89  -0.88   -0.88  1.00
## 
## n= 7 
## 
## 
## P
##           zona   estrato preciom areaconst banios habitac tipo  
## zona             0.7061  0.5092  0.6059    0.6919 0.7295  0.8321
## estrato   0.7061         0.1554  0.5969    0.5461 0.5602  0.9856
## preciom   0.5092 0.1554          0.0092    0.0123 0.2460  0.0774
## areaconst 0.6059 0.5969  0.0092            0.0091 0.0498  0.0072
## banios    0.6919 0.5461  0.0123  0.0091           0.0301  0.0096
## habitac   0.7295 0.5602  0.2460  0.0498    0.0301         0.0083
## tipo      0.8321 0.9856  0.0774  0.0072    0.0096 0.0083

Podemos observar un resultado más detallado en el siguiente gráfico de la matriz de correlación:

Observamos que el area construida y cantidad de baños está mucho más correlacionado positivamente al precio de las viviendas.

Luego de estandarizar nuestros datos para que las variables no dominen de acuerdo a sus escalas y de hallar la matriz de correlación de las variables originales para entender cómo están relacionadas entre sí, procedemos a realizar el cálculo del análisis de componentes principales.

PCA <- prcomp(vivienda, scale = TRUE, center = TRUE)

Del siguiente gráfico podemos deducir que las componentes más contribuyentes son PC1, PC2, PC3, PC4 en ese orden.

plot(PCA, type = "l")

print(PCA)
## Standard deviations (1, .., p=7):
## [1] 1.7966656 1.2153838 1.0025429 0.7174243 0.5890131 0.4916427 0.4317360
## 
## Rotation (n x k) = (7 x 7):
##                   PC1         PC2          PC3         PC4         PC5
## zona       0.04484504 -0.09953272  0.983041650  0.03400220 -0.13999773
## estrato    0.24636645 -0.66798737  0.003434856 -0.07494024  0.48877321
## preciom    0.44854765 -0.35366216 -0.125583203  0.19263157 -0.27874190
## areaconst  0.48453661  0.05572751 -0.086561067  0.33947781 -0.53964330
## banios     0.49081807 -0.05338208  0.012663948 -0.37941931  0.18096051
## habitac    0.37412255  0.43499464  0.039949480 -0.63274923 -0.04832285
## tipo      -0.34706154 -0.47289920 -0.092720120 -0.54455955 -0.58095408
##                    PC6         PC7
## zona       0.013160504  0.02797432
## estrato   -0.481703150 -0.12857463
## preciom    0.218251540  0.70393127
## areaconst -0.293886855 -0.51166038
## banios     0.666107692 -0.36835095
## habitac   -0.435974078  0.27647050
## tipo       0.003578401 -0.11511760

Con la función summary podemos obtener más detalle del PCA

summary(PCA)
## Importance of components:
##                           PC1    PC2    PC3     PC4     PC5     PC6     PC7
## Standard deviation     1.7967 1.2154 1.0025 0.71742 0.58901 0.49164 0.43174
## Proportion of Variance 0.4611 0.2110 0.1436 0.07353 0.04956 0.03453 0.02663
## Cumulative Proportion  0.4611 0.6722 0.8157 0.88928 0.93884 0.97337 1.00000

En el contexto del Análisis de Componentes Principales (PCA), “Desviación Estándar” (Standard Deviation), “Proporción de Varianza” (Proportion of Variance) y “Proporción Acumulada” (Cumulative Proportion) son conceptos importantes que te ayudan a comprender la importancia de cada componente principal y la cantidad de información retenida al reducir la dimensionalidad de tus datos.

Desviación Estándar:

La desviación estándar mide la dispersión o la extensión de un conjunto de datos alrededor del valor medio (promedio). En PCA, la desviación estándar de un componente principal indica cuánta variación o “extensión” existe en los datos proyectados en ese componente. Una desviación estándar más alta para un componente implica que los puntos de datos tienen un rango más amplio cuando se proyectan en ese componente, lo que sugiere que captura más información de los datos originales.

Proporción de Varianza:

La proporción de varianza explica cuánta de la varianza total en los datos originales es capturada por un componente principal específico. Se calcula dividiendo la varianza de un componente principal específico entre la varianza total de todas las variables originales. Una proporción de varianza más alta para un componente indica que retiene una cantidad mayor de información de los datos originales.

Proporción Acumulada:

La proporción acumulada es la suma de las proporciones de varianza de los componentes principales que vienen antes de un componente específico. Representa la cantidad acumulativa de varianza explicada a medida que consideras más y más componentes principales en orden. Una proporción acumulada más alta indica cuánta de la variabilidad total se captura al incluir cierto número de componentes principales. Esto es especialmente útil para decidir cuántos componentes principales retener. Una práctica común es retener suficientes componentes para capturar una parte significativa de la varianza total (por ejemplo, 95% o 99%). En resumen, estos conceptos te ayudan a evaluar la importancia de cada componente principal en términos de la información que contiene. Al realizar un PCA, a menudo se elige retener cierto número de componentes principales en función de su proporción de varianza y proporción acumulada. Esto te permite reducir la dimensionalidad de tus datos mientras preservas una porción sustancial de la variabilidad de los datos originales.

Para detallar un poco más la información que nos proporcionan las componentes principales, estudiamos las PC1 y PC2:

#creamos un nuevo dataset
vivienda_2 <- vivienda
pc1 <- apply(PCA$rotation[,1]*vivienda_2,1,sum) 
pc2 <- apply(PCA$rotation[,2]*vivienda_2,1,sum) 

vivienda_2$pc1 <- pc1
vivienda_2$pc2 <- pc2
head(vivienda_2)
##   zona estrato preciom areaconst banios habitac tipo        pc1         pc2
## 1    3       6    1300       318      4       2    2  740.82472 -446.635321
## 2    3       6     480       300      4       4    1  383.40647    6.357497
## 3    3       5    1200       800      7       5    1  890.08921  278.678922
## 4    5       3     220       150      2       4    1   35.66657   21.657755
## 5    3       5     330       112      4       3    1 -103.34895 -168.866781
## 6    5       5    1350       390     10      10    1  166.58068 -398.106492
#PCA SCORE
pca_scores <- data.frame(PC1 = pc1, PC2 = pc2)

ggplot(pca_scores, aes(x = PC1, y = PC2)) +
  geom_point() +
  labs(x = "PC1", y = "PC2", title = "PCA Scatter Plot")

# Hallamos la proporción de varianza explicada para cada componente
variance_explained <- PCA$sdev^2 / sum(PCA$sdev^2)

# Imprimimos la varianza explicada
print(variance_explained)
## [1] 0.46114392 0.21102255 0.14358460 0.07352823 0.04956235 0.03453036 0.02662800
#Graficamos la varianza explicada
plot(variance_explained, type = "b", xlab = "Principal Component", ylab = "Variance Explained",
     main = "Scree Plot de varianza explicada para cada componente")

# Cumulative variance explained plot
cumulative_variance <- cumsum(variance_explained)
plot(cumulative_variance, type = "b", xlab = "Number of Principal Components", ylab = "Cumulative Variance Explained",
     main = "Cumulative Variance Explained")

# Biplot visualization
biplot(PCA, scale = 0)

fviz_screeplot(PCA, addlabels = TRUE, ylim = c(0, 100))

fviz_contrib(PCA, choice = "var", axes = 1, top = 10) # Dim1

fviz_contrib(PCA, choice = "var", axes = 2, top = 10) # Dim2

Cabe resaltar que en la gráfica Biplot no se logra detallar mayor información debido a que se está tomando todos y cada uno de los registros así como las variables del dataset. Sin embargo, se logra apreciar la distribución de las variables estudiadas.

Mediante el scree plot de nuestro PCA podemos observar que la componente principal con mayor varianza explicada es PC1. La varianza explicada ayuda a comprender cuánta información se conserva al considerar cada componente. Es decir, el componente principal captura el 46% de la información presente en los datos.Esto significa que al considerar solo este componente principal, estaríamos reteniendo el 46% de la variación total que estaba presente en las variables originales.

Es decir, que al considerar las primeras cuatro componentes principales estamos capturando el 89% de la variación total de las variables originales.

Ahora bien, teniendo en cuenta las dimensiones 1 y 2, podemos deducir que las variables que nos dan información de la cantidad de baños, área construida, precio y cantidad de habitaciónes son las que tienen mayor influencia en esa dimensión, y por lo tanto, tendrán más influencia sobre los registros de viviendas que se ubiquen en esa dimensión, por otra parte, las variables estrato, tipo y cantidad de habitaciones llevan la delantera a nivel de influencia en la dimensión 2.

Esto nos podría indicar que la cantidad de habitaciones posiblemente sea influyente para predecir el precio de una vivienda, ya que esta variable es influyente en dos dimensiones.

Pasamos a la clusterización.

Los métodos Elbow, Silhouette o Gap_stat nos ayudan a calcular la cantidad de clusters que son necesarios para agrupar nuestros datos en segmentos homogéneos, sin embargo, podemos detallar en las siguientes gráficas cómo quedarían clusterizados los datos para una cantidad de clusters: 2,3 y 4.

df_clusters <- vivienda

k2 <- kmeans(df_clusters, centers = 2, nstart = 25)


#plotear los cluster
fviz_cluster(k2, data = df_clusters)

fviz_cluster(k2, data = df_clusters, ellipse.type = "euclid",repel = FALSE,star.plot = TRUE) #ellipse.type= "t", "norm", "euclid"

fviz_cluster(k2, data = df_clusters, ellipse.type = "norm")

fviz_cluster(k2, data = df_clusters, ellipse.type = "norm",palette = "Set2", ggtheme = theme_minimal())

vivienda_clusters <- df_clusters

# Agregamos los resultados del clustering al conjunto de datos original
vivienda_clusters$cluster <- as.factor(k2$cluster)

# Visualizamos la distribución de las viviendas en cada clúster
table(vivienda_clusters$cluster)
## 
##    1    2 
## 1390 6829
# 3 clusters

k3 <- kmeans(df_clusters, centers = 3, nstart = 25)


#plotear los cluster
fviz_cluster(k3, data = df_clusters)

fviz_cluster(k3, data = df_clusters, ellipse.type = "euclid",repel = FALSE,star.plot = TRUE) #ellipse.type= "t", "norm", "euclid"

fviz_cluster(k3, data = df_clusters, ellipse.type = "norm")

fviz_cluster(k3, data = df_clusters, ellipse.type = "norm",palette = "Set2", ggtheme = theme_minimal())

vivienda_clusters_3 <- df_clusters

# Agregamos los resultados del clustering al conjunto de datos original
vivienda_clusters_3$cluster <- as.factor(k2$cluster)


# 4 clusters

k4 <- kmeans(df_clusters, centers = 4, nstart = 25)


#plotear los cluster
fviz_cluster(k4, data = df_clusters)

fviz_cluster(k4, data = df_clusters, ellipse.type = "euclid",repel = FALSE,star.plot = TRUE) #ellipse.type= "t", "norm", "euclid"

fviz_cluster(k4, data = df_clusters, ellipse.type = "norm")

fviz_cluster(k4, data = df_clusters, ellipse.type = "norm",palette = "Set2", ggtheme = theme_minimal())

vivienda_clusters_4 <- df_clusters

# Agregamos los resultados del clustering al conjunto de datos original
vivienda_clusters_4$cluster <- as.factor(k2$cluster)

Podemos observar cómo hemos obtenido una agrupación más homogénea con un número de clusters igual a 2.

Ahora para aplicar un dendograma sería necesario tener mucho menos cantidad de datos, ya que es necesario tener únicamente cientos de datos. Nuestro dataset tiene poco más de ocho mil registros.

Ahora realizamos un análisis de correspondencia entre las variables categóricas y numéricas de nuestro dataset.

El Análisis de Correspondencia es similar al Análisis de Componentes Principales (PCA), pero en lugar de trabajar con variables numéricas, se aplica a variables categóricas. Esta técnica busca encontrar patrones de asociación y dependencia entre categorías en las variables categóricas y las representa en un espacio de menor dimensionalidad.

library(dplyr)
library(mice)
## 
## Attaching package: 'mice'
## The following object is masked from 'package:stats':
## 
##     filter
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
library(FactoMineR)
library(factoextra)
vivienda_3 <- sample_n(my_data, 4000)

# Crear una tabla de contingencia para las variables categóricas
tabla <- table(vivienda_3$zona, vivienda_3$estrato)

# Mostrar la tabla cruzada Zona vs. Estrato
print(tabla)
##               
##                  3   4   5   6
##   Zona Centro   56   3   1   1
##   Zona Norte   278 176 364  92
##   Zona Oeste    28  40 131 375
##   Zona Oriente 143   3   1   0
##   Zona Sur     203 808 793 502
resultados_ac <- CA(tabla)

valores_prop <- resultados_ac$eig

# Gráfica ScreePlot
fviz_screeplot(resultados_ac, addlabels = TRUE, ylim = c(0, 80)) +
  ggtitle("Scree Plot del Análisis de Correspondencia Zona vs. Estrato") +
  ylab("Porcentaje de Varianza Explicado") +
  xlab("Ejes")

Mediante los gráficos anteriormente presentados podemos observar la correlación entre las variables estudiadas y cómo claramente se observa una correlación en cuanto a la zona y estrato de las viviendas. Además de que en la zona sur y oeste se encuentran las viviendas con mayor estrato en general.

Este análisis sin duda apoyará la gestión de compra, venta y valoración de viviendas por parte de la inmobiliaria.