1. Informe

1.1. Introducción

El mercado de viviendas urbanas es un sector complejo y dinámico que se encuentra en constante evolución. Para tener éxito en este mercado, las empresas inmobiliarias necesitan comprender en profundidad las tendencias del mercado, las necesidades de los clientes y la competencia.

En este informe, se presenta un análisis del mercado de viviendas urbanas en Cali. El análisis se basa en información detallada sobre diversas propiedades residenciales disponibles en el mercado de la ciudad de Cali. Se aplicarán diversas técnicas estadísticas para poder extraer conclusiones relevantes para el cliente.

2. Preprocesamiento de información

Se realiza un primer análisis exploratorio de la base de datos. Este análisis se estructurará en la identificación de las columnas y dimensiones de la tabla, lo segundo será identificar los tipos de datos de las columnas y seleccionar cuáles serán relevantes para el análisis. Lo tercero será realizar la identificación de valores perdidos dentro de las columnas seleccionadas para el análisis y brindar posibles propuestas para poder solucionar los datos faltantes. Con la realización de estos pasos se puede determinar la relación de algunas variables con respecto a los objetivos del presente informe.

2.1 Cargue de base de datos

head(vivienda)
cat("Cantidad columnas: ", ncol(vivienda), "Cantidad filas: ", nrow(vivienda))
## Cantidad columnas:  13 Cantidad filas:  8322

2.2 Selección de datos relevantes

Según los objetivos del estudio, se identifican como variables relevantes zona, barrio, tipo como variables categóricas. Para análisis de variables numéricas se selecciona principalmente la variable preciom, como variables para análisis de caraterísticas se seleccionan piso, estrato, areaconst, banios, habitac. A continuación se realiza una verificación de datos categóricos relevantes:

# Vector con los nombres de las columnas a analizar
columnas_interes <- c("zona", "piso", "tipo", "estrato")

# Bucle for para iterar sobre cada columna
for (columna in columnas_interes) {
  valores_unicos <- unique(vivienda[[columna]])
  print(paste0("Valores únicos para ", columna, ": ", paste0(valores_unicos, collapse = ", ")))
}
## [1] "Valores únicos para zona: Zona Oriente, Zona Sur, Zona Norte, Zona Oeste, Zona Centro, NA"
## [1] "Valores únicos para piso: NA, 02, 01, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12"
## [1] "Valores únicos para tipo: Casa, Apartamento, NA"
## [1] "Valores únicos para estrato: 3, 4, 5, 6, NA"
# Generando vector para resumir las columnas seleccionadas
specific_columns <- c("preciom", "parqueaderos", "areaconst", "banios", "habitaciones")
summary(vivienda[, specific_columns])
##     preciom        parqueaderos      areaconst          banios      
##  Min.   :  58.0   Min.   : 1.000   Min.   :  30.0   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.: 1.000   1st Qu.:  80.0   1st Qu.: 2.000  
##  Median : 330.0   Median : 2.000   Median : 123.0   Median : 3.000  
##  Mean   : 433.9   Mean   : 1.835   Mean   : 174.9   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 2.000   3rd Qu.: 229.0   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :10.000   Max.   :1745.0   Max.   :10.000  
##  NA's   :2        NA's   :1605     NA's   :3        NA's   :3       
##   habitaciones   
##  Min.   : 0.000  
##  1st Qu.: 3.000  
##  Median : 3.000  
##  Mean   : 3.605  
##  3rd Qu.: 4.000  
##  Max.   :10.000  
##  NA's   :3

2.3 Limpieza de datos

Se evidencia que vienen valores de id con vacíos, para lo cual es necesario quitarlos de la base de datos:

faltantes_id = sum(is.na(vivienda$id))
cat("Cantidad de id faltantes:", faltantes_id)
## Cantidad de id faltantes: 3
## Se remueven los id vacios de la tabla:
vivienda = subset(vivienda, !is.na(id))
cat("Nueva cantidad filas: ", nrow(vivienda))
## Nueva cantidad filas:  8319

También se realiza la verificación de datos duplicados dentro de la tabla provista:

duplicates <- duplicated(vivienda)
n_duplicates <- sum(duplicates)
cat("Cantidad de filas duplicadas: ", n_duplicates)
## Cantidad de filas duplicadas:  0
## Se remueven los duplicados encontrados:
vivienda <- unique(vivienda)
cat("Nueva cantidad filas: ", nrow(vivienda))
## Nueva cantidad filas:  8319

Con lo identificado en la selección de variables se realiza la limpieza de las columnas de tipo, zona y barrio. Adicionalmente, al haber identificado un valor de “APTO” en la columna zona, esta se unifica con “APARTAMENTO” para normalizar los datos.

upper_cols <- c("zona", "barrio", "tipo")

for (col in upper_cols) {
  vivienda[[col]] <- toupper(vivienda[[col]])
}

head(vivienda[, upper_cols])

2.4 Identificación de faltantes

## uses visdat
vis_miss(vivienda)

Haciendo uso de la librería visdat se logra identificar que dentro de la tabla dispuesta, después de la limpieza se tiene 3.9 % de datos faltantes. Todos distribuidos en las columnas parqueaderos y piso.

## uses visdat
md.pattern(vivienda, rotate.names = TRUE)

##      id zona estrato preciom 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
##       0    0       0       0         0      0            0    0      0        0
##      latitud parqueaderos piso     
## 4808       1            1    1    0
## 1909       1            1    0    1
## 876        1            0    1    1
## 726        1            0    0    2
##            0         1602 2635 4237

2.5 Estrategias para mitigar datos faltantes

La primera posible solución para poder trabajar con los datos de la forma más completa posible puede ser precindir de usar las variables parqueaderos y piso. Esta estrategia tiene como ventaja que puede ser de las más rápidas de implementar, así como reducir la dimensionalidad de los datos, con lo que aplicar algunos algoritmos puede llegar a ser más eficiente. Así mismo, la reducción de información dentro de la tabla puede generar eliminación de información importante, lo que puede conllevar a presentar sesgo en el resultado.

2.5.1 Análisis de faltantes en la variable PARQUEADEROS

vivienda %>% count(parqueaderos)

Entendiendo el contexto, y que no existen valores de 0 dentro de la información que tenemos, es prudente asumir que existen viviendas que no tienen parqueaderos, por lo cual se procede a inputar el valor de 0 en los missing values

vivienda <- vivienda %>% mutate(parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos))

2.5.2 Análisis de faltantes en la variable PISO

Se supone una correlación entre la variable piso con la variable tipo de vivienda. En las variables casa, no tendría sentido tener el piso en el que se encuentra ubicada la casa (como sí lo tendría en un apartamento). Así que se propone realizar la revisión. A menos de que se trate de la cantidad de pisos de una vivienda. Se procede a reemplazar los valores de la mediana por tipo

# Convertir a numérico
vivienda$piso <- as.numeric(vivienda$piso)

# reemplazar valores por mediana por tipo: CASA
only_casas <- subset(vivienda, vivienda$tipo == "CASA")
median_casa_piso = median(only_casas$piso)
vivienda <- vivienda %>% mutate(piso = ifelse(is.na(piso) & tipo == "CASA", median_casa_piso, piso))

# reemplazar valores por mediana por tipo: CASA
only_apto <- subset(vivienda, vivienda$tipo == "APARTAMENTO")
median_apt_piso = median(only_apto$piso)
vivienda <- vivienda %>% mutate(piso = ifelse(is.na(piso) & tipo == "APARTAMENTO", median_apt_piso, piso))

verificación de dataset completo:

## uses visdat
vis_miss(vivienda)

2.6 Correlación de variables:

Encoding de variables para hallar matrices de correlación:

vivienda$zona_num <- as.integer(factor(vivienda$zona))
vivienda$tipo_num <- as.integer(factor(vivienda$tipo))
vivienda$barrio_num <- as.integer(factor(vivienda$barrio))

Con el dataframe codificado se procede a realizar una matriz de correlación para hallar la relación entre los valores

library(psych)
enc_vivienda <- vivienda[, !names(vivienda) %in% c("barrio", "zona", "tipo")]
correlacion <- round(cor(enc_vivienda), 1)
corPlot(correlacion, number.cex = 0.5)
## Warning in axis(2, at = at2, labels = lab2, las = ylas, ...): "number.cex" is
## not a graphical parameter
## Warning in axis(xaxis, at = at1, labels = lab1, las = xlas, line = line, :
## "number.cex" is not a graphical parameter
## Warning in text.default(rx, ry, rv, cex = 1.5 * cex, ...): "number.cex" is not
## a graphical parameter
## Warning in axis(4, at = at2, labels = labels, las = 2, ...): "number.cex" is
## not a graphical parameter

3. Análisis de Componentes Principales

El Análisis de Componentes Principales (ACP) es una técnica estadística utilizada en el análisis de datos. Su principal objetivo es reducir la dimensionalidad de un conjunto de datos mientras se conserva la mayor cantidad posible de varianza.

## Se seleccionan las variables que se van a utilizar dentro del ACP
selected_vars <- c("piso", "estrato", "preciom", "areaconst", "parqueaderos", "banios","habitaciones", "zona_num", "tipo_num")
selected_vivienda <- vivienda[, selected_vars]

## Reescalar las variables seleccionadas
scale_vivienda <- scale(selected_vivienda)

Generación de los componentes principales haciendo uso de la librería PCA

library(FactoMineR)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
res.ACP <- PCA(scale_vivienda, graph = FALSE)

## Verficación de valores propios
res.ACP$eig
##        eigenvalue percentage of variance cumulative percentage of variance
## comp 1  3.7134103              41.260114                          41.26011
## comp 2  1.8575851              20.639835                          61.89995
## comp 3  1.0235814              11.373126                          73.27308
## comp 4  0.7766444               8.629382                          81.90246
## comp 5  0.4603713               5.115237                          87.01770
## comp 6  0.4264659               4.738510                          91.75621
## comp 7  0.3180017               3.533352                          95.28956
## comp 8  0.2396958               2.663287                          97.95284
## comp 9  0.1842441               2.047156                         100.00000

3.1 Eigen Values

Visualización de información de acumulación de la varianza dentro de los componentes generados

fviz_eig(res.ACP, addlabels = TRUE)

Se procede a realizar el círculo de correlación

3.2 Circulo de correlación

Permite realizar la identificación de cómo se correlaciónan las variables estudiadas y cuál es su relación con respectoa los datos.

##Circulo de correlación (nube de variables)
fviz_pca_var(res.ACP,
             col.var = "contrib", # Color by contributions to the PC
             gradient.cols = c("#FF7F00",  "#034D94"),
             repel = TRUE     # Avoid text overlapping
)

Así mismo, se tiene lo siguiente:

  • Tienen una correlación positiva por su cercanía e igual dirección:(Parqueaderos, Preciom), (banios, areaconstr), (habitaciones, tipo vivienda)
  • No hay correlación lineal entre las variables: (estrato, habitaciones), (estrato, tipo vivienda), (piso, preciom)
  • banios y areaconst tienen una mayor contribución y relación en la definición del componente 2
  • Variables que están altamente correlacionadas negativamente: (piso, habitaciones, (piso, tipo vivienda)

4. Análisis de conglomerados

El análisis de conglomerados busca agrupar las propiedades residenciales en clusters. Esto significa que las propiedades dentro de cada grupo compartirán características similares, como ubicación, tamaño, precio y otras. Al identificar estos grupos, se puede entender mejor las dinámicas de la data y el comportamiento que tienen las agrupaciones.

fviz_nbclust(scale_vivienda, kmeans, method = "wss")

Usando el elbow method, no se tiene una curva con una relación acentuada en algún cluster específico, así que se decide usar el más central, eligiendo realizar el análisis con cuatro clusters y se procede a realizar la visualización para poder hacer la verificación de resultados

cluster_generation <- kmeans(scale_vivienda, centers = 4, nstart = 25)

fviz_cluster(cluster_generation, data = scale_vivienda,
             geom = "point", stand = FALSE,
             ellipse = TRUE, show.clust.cent = TRUE)

Se encuentra que:

Los cluster encontrados tienen una distribución en cuanto a frecuencia muy similar. El primer cluster tiene 2219 observaciones, el segundo tiene 1785, mientras que el tercero tiene 1026 observaciones y el último tiene 3289.

## Se asigna el número del cluster a cada uno de los registros del dataframe
selected_vivienda$cluster <- as.factor(cluster_generation$cluster)

## Se halla la cantidad y distribución de los cluster
cat("Distribución de la cantidad de filas por cluster:")
## Distribución de la cantidad de filas por cluster:
print(table(selected_vivienda$cluster))
## 
##    1    2    3    4 
## 1785 3289 2219 1026
cat("\n")
## Se genera información relevante para cada uno de los cluster
interest_vars <- c("preciom", "banios", "estrato", "piso", "areaconst")
for (clus in 1:4) {
  cat("Resulados para cluster: ", clus)
  cat("\n")
  cluster_info <- subset(selected_vivienda, selected_vivienda$cluster == clus)
  print(summary(cluster_info[, interest_vars]))
  cat("\n\n")
}
## Resulados para cluster:  1
##     preciom         banios         estrato          piso          areaconst    
##  Min.   : 190   Min.   :0.000   Min.   :4.00   Min.   : 1.000   Min.   : 50.0  
##  1st Qu.: 397   1st Qu.:3.000   1st Qu.:5.00   1st Qu.: 4.000   1st Qu.:115.0  
##  Median : 530   Median :3.000   Median :6.00   Median : 4.000   Median :143.2  
##  Mean   : 604   Mean   :3.529   Mean   :5.65   Mean   : 5.222   Mean   :163.1  
##  3rd Qu.: 720   3rd Qu.:4.000   3rd Qu.:6.00   3rd Qu.: 7.000   3rd Qu.:196.0  
##  Max.   :1801   Max.   :6.000   Max.   :6.00   Max.   :12.000   Max.   :932.0  
## 
## 
## Resulados para cluster:  2
##     preciom          banios         estrato           piso       
##  Min.   : 58.0   Min.   :0.000   Min.   :3.000   Min.   : 1.000  
##  1st Qu.:149.0   1st Qu.:2.000   1st Qu.:4.000   1st Qu.: 3.000  
##  Median :210.0   Median :2.000   Median :4.000   Median : 4.000  
##  Mean   :215.4   Mean   :2.036   Mean   :4.191   Mean   : 4.016  
##  3rd Qu.:270.0   3rd Qu.:2.000   3rd Qu.:5.000   3rd Qu.: 5.000  
##  Max.   :900.0   Max.   :4.000   Max.   :6.000   Max.   :12.000  
##    areaconst     
##  Min.   : 35.00  
##  1st Qu.: 60.00  
##  Median : 74.00  
##  Mean   : 80.02  
##  3rd Qu.: 90.00  
##  Max.   :605.00  
## 
## 
## Resulados para cluster:  3
##     preciom           banios          estrato           piso       
##  Min.   :  80.0   Min.   : 0.000   Min.   :3.000   Min.   : 1.000  
##  1st Qu.: 260.0   1st Qu.: 3.000   1st Qu.:3.000   1st Qu.: 2.000  
##  Median : 350.0   Median : 3.000   Median :4.000   Median : 2.000  
##  Mean   : 367.9   Mean   : 3.349   Mean   :4.076   Mean   : 2.074  
##  3rd Qu.: 450.0   3rd Qu.: 4.000   3rd Qu.:5.000   3rd Qu.: 2.000  
##  Max.   :1500.0   Max.   :10.000   Max.   :6.000   Max.   :10.000  
##    areaconst     
##  Min.   :  30.0  
##  1st Qu.: 135.0  
##  Median : 190.0  
##  Mean   : 207.7  
##  3rd Qu.: 260.0  
##  Max.   :1365.0  
## 
## 
## Resulados para cluster:  4
##     preciom           banios          estrato           piso       
##  Min.   : 255.0   Min.   : 0.000   Min.   :3.000   Min.   : 1.000  
##  1st Qu.: 680.0   1st Qu.: 4.000   1st Qu.:5.000   1st Qu.: 2.000  
##  Median : 880.0   Median : 5.000   Median :6.000   Median : 2.000  
##  Mean   : 981.2   Mean   : 5.317   Mean   :5.489   Mean   : 2.274  
##  3rd Qu.:1250.0   3rd Qu.: 6.000   3rd Qu.:6.000   3rd Qu.: 2.000  
##  Max.   :1999.0   Max.   :10.000   Max.   :6.000   Max.   :12.000  
##    areaconst     
##  Min.   : 128.0  
##  1st Qu.: 300.0  
##  Median : 395.5  
##  Mean   : 428.8  
##  3rd Qu.: 500.0  
##  Max.   :1745.0

5. Análisis de Correspondencia

El análisis de correspondencia busca identificar y visualizar las relaciones entre las variables categóricas de un conjunto de datos. En el contexto de los datos, se espera que este análisis permita descubrir cómo se distribuyen los diferentes tipos de vivienda en las distintas zonas y estratos

cat_vivienda <- table(vivienda$zona, vivienda$estrato)
cat_vivienda
##               
##                   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(cat_vivienda)
## 
##  Pearson's Chi-squared test
## 
## data:  cat_vivienda
## X-squared = 3830.4, df = 12, p-value < 2.2e-16

Un p-valor menor a 0.05 en un análisis de correspondencia indica que la relación entre las categorías de tipo de vivienda y zona. Esto significa que existe una fuerte asociación entre estas variables. En otras palabras, la distribución de los tipos de vivienda no es uniforme en todas las zonas y barrios, lo que te permite identificar patrones específicos en el mercado inmobiliario.

 cat_vivienda_AC <- CA(cat_vivienda, graph = TRUE)

Se encuentra lo siguiente:

El análisis nos muestra una relación con respect a los estratos 4 y 5 con la Zona Sur, así como una relación entre el estrato 6 y la Zona Oeste.

fviz_screeplot(cat_vivienda_AC, addlabels = TRUE)

6. Conclusiones

Cada una de las técnicas empleadas ha aportado valiosa información para comprender la estructura y las dinámicas del mercado inmobiliario. El ACP ha permitido reducir la dimensionalidad de los datos y destacar las variables más influyentes, mientras que el análisis de conglomerados ha facilitado la identificación de segmentos homogéneos de propiedades. Por su parte, el análisis de correspondencias ha revelado relaciones significativas entre las variables categóricas, enriqueciendo la comprensión de la distribución espacial de la oferta inmobiliaria.

Haciendo uso de la reducción de la dimensionalidad se logra la visualización de datos multidimensionales, identificación de patrones, mejora de la eficiencia en modelos.Mientras tranto el análisis de Correspondencias sirve para encontrar las relaciones entre variables categóricas, visualización de tablas de contingencia, identificación de asociaciones y patrones, segmentación de datos. El Análisis de Conglomerados identifica grupos homogéneos, segmentación de datos para estrategias personalizadas, descubrimiento de patrones ocultos. En conjunto, estos resultados proporcionan una base sólida para la toma de decisiones estratégicas en el sector inmobiliario, permitiendo una mejor comprensión de las preferencias de los consumidores, la identificación de oportunidades de mercado y la optimización de las estrategias de marketing y ventas.

Se llevaron a cabo diversos análisis exploratorios con el objetivo de identificar patrones y relaciones subyacentes en los datos del mercado inmobiliario. A continuación, se presentan las principales conclusiones obtenidas: