Como una agencia de bienes raíces líder en la ciudad de Cali, fundada por Sandra Milena hace 10 años y con un equipo de ocho agentes de bienes raíces, B&C está en una posición única para aprovechar las oportunidades que ofrece el creciente mercado de bienes raíces.
En los últimos años, el mercado de bienes raíces en la ciudad ha experimentado un crecimiento significativo, impulsado por factores como el aumento de la población, la inversión extranjera directa y el desarrollo de nuevos proyectos inmobiliarios. Como se vio en los años 2021 y 2022, en los cuales se superaron las ventas del sector sobre los $6000 millones. Se espera que el sector siga creciendo, representando oportunidades para un desarrollo dinámico en la economía regional.
Para ayudar a la empresa B&C a entender mejor el mercado, se realizó un análisis exploratorio sobre datos de las viviendas en toda la ciudad, incluyendo variables como el precio, ubicación y diferentes características (pisos, baños, habitaciones, entre otros). Se espera con ello generar información útil que sirva a la empresa en la toma de decisiones, como puede ser definir su nicho de mercado, desarrollar estrategias de marketing, establecer precios de venta u ofrecer servicios personalizados a sus clientes.
Realizar un análisis exploratorio sobre los datos de vivienda recolectados en la ciudad de Cali para generar información útil en la toma de decisiones estratégicas de la agencia de bienes raíces B&C.
Determinar si hay correlación entre alguna de las variables del conjunto de datos con la variable precio.
Analizar la distribución geográfica de las viviendas y su relación con el precio y otras variables relevantes.
Generar visualizaciones que sean de utilidad para la interpretación de los datos y la toma de decisiones.
Después de cargar los datos en R, se puede aplicar la función
summary() para obtener un primer diagnóstico univariado.
Este diagnóstico incluye medidas de tendencia central, medidas de
dispersión y una revisión de frecuencias, entre otros aspectos, para
validar los datos del dataset. A continuación, es necesario realizar una
depuración, limpieza y transformación de los datos. Esto implica revisar
los datos faltantes y duplicados y determinar su posible tratamiento.
También es importante evaluar si los valores de las diferentes variables
son correctos en el contexto de los datos y decidir si deben ser
transformados, dejados como están o eliminados para continuar con el
análisis. Una vez que los datos han sido depurados, se puede realizar un
segundo resumen mostrado en tablas y construir gráficos univariados como
diagramas de barras, círculos e histogramas para empezar a generar
información. Luego, se puede avanzar a un análisis bivariado en el que
se definen algunas variables para ser cruzadas entre ellas y seguir
generando información que relacione el comportamiento de las variables.
Para esto, se pueden revisar temas como la correlación entre variables,
la dispersión y construir boxplots para comprender la distribución de
los datos entre diferentes grupos. Finalmente, se pueden dar respuestas
a preguntas concretas planteadas al inicio del estudio y generar
conclusiones sobre los resultados obtenidos.
library(tidyverse)
library(skimr)
library(stringi)
library(highcharter)
| Name | data |
| Number of rows | 8330 |
| Number of columns | 13 |
| _______________________ | |
| Column type frequency: | |
| character | 3 |
| numeric | 10 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| zona | 3 | 1 | 8 | 12 | 0 | 5 | 0 |
| tipo | 3 | 1 | 4 | 11 | 0 | 6 | 0 |
| barrio | 3 | 1 | 4 | 29 | 0 | 436 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id | 3 | 1.00 | 4163.99 | 2403.93 | 1.00 | 2082.50 | 4164.00 | 6245.50 | 8319.00 | ▇▇▇▇▇ |
| piso | 2641 | 0.68 | 3.77 | 2.62 | 1.00 | 2.00 | 3.00 | 5.00 | 12.00 | ▇▃▁▁▁ |
| estrato | 3 | 1.00 | 4.63 | 1.03 | 3.00 | 4.00 | 5.00 | 5.00 | 6.00 | ▅▆▁▇▆ |
| preciom | 2 | 1.00 | 434.24 | 329.02 | 58.00 | 220.00 | 330.00 | 540.00 | 1999.00 | ▇▂▁▁▁ |
| areaconst | 3 | 1.00 | 174.99 | 142.95 | 30.00 | 80.00 | 123.00 | 229.00 | 1745.00 | ▇▁▁▁▁ |
| parquea | 1606 | 0.81 | 1.84 | 1.13 | 1.00 | 1.00 | 2.00 | 2.00 | 10.00 | ▇▁▁▁▁ |
| banios | 3 | 1.00 | 3.11 | 1.43 | 0.00 | 2.00 | 3.00 | 4.00 | 10.00 | ▇▇▃▁▁ |
| habitac | 3 | 1.00 | 3.61 | 1.46 | 0.00 | 3.00 | 3.00 | 4.00 | 10.00 | ▂▇▂▁▁ |
| longitud | 3 | 1.00 | -21845.13 | 34503.90 | -76576.00 | -76506.00 | -76.54 | -76.52 | -76.46 | ▃▁▁▁▇ |
| latitud | 3 | 1.00 | 970.37 | 1539.16 | 3.33 | 3.39 | 3.45 | 3367.00 | 3497.00 | ▇▁▁▁▃ |
El resúmen evidencia que se cuenta con 13 variables, de las cuales 10
son numéricas y 3 son de tipo carácter. De aquí que estas 3 variables se
puedan entender como categóricas. Ahora se revisa en profundidad los
diferentes valores únicos que poseen estas 3 variables usando la función
unique(). Adicional se realiza una revisión de valores
duplicados:
## id zona piso estrato preciom areaconst parquea banios habitac
## 8311 8309 Zona Oeste 5 4 150 56 NA 1 2
## 8312 8310 Zona Oeste 1 6 1600 463 4 6 3
## 8313 8313 Zona Oeste 7 6 525 137 2 3 3
## 8314 8314 Zona Oeste 7 6 1400 210 3 4 3
## 8315 8315 Zona Oeste NA 6 620 167 2 4 4
## 8316 8316 Zona Norte 7 5 400 220 1 4 4
## 8317 8317 Zona Oeste NA 6 1100 290 4 4 3
## 8318 8318 Zona Norte NA 4 580 295 2 5 5
## 8320 NA <NA> NA NA NA NA NA NA NA
## 8321 NA <NA> NA NA NA NA NA NA NA
## 8322 NA <NA> NA NA 330 NA NA NA NA
## 8323 8309 Zona Oeste 5 4 150 56 NA 1 2
## 8324 8310 Zona Oeste 1 6 1600 463 4 6 3
## 8325 8313 Zona Oeste 7 6 525 137 2 3 3
## 8326 8314 Zona Oeste 7 6 1400 210 3 4 3
## 8327 8315 Zona Oeste NA 6 620 167 2 4 4
## 8328 8316 Zona Norte 7 5 400 220 1 4 4
## 8329 8317 Zona Oeste NA 6 1100 290 4 4 3
## 8330 8318 Zona Norte NA 4 580 295 2 5 5
## tipo barrio longitud latitud
## 8311 Apartamento aguacatal -76.57049 3.45445
## 8312 Apartamento santa rita -76.57076 3.45332
## 8313 Apartamento normandía -76.57941 3.45531
## 8314 Apartamento normandía -76.57941 3.45531
## 8315 Apartamento normandía -76.58530 3.45971
## 8316 Apartamento granada -76.58732 3.46148
## 8317 Apartamento normandía -76.58744 3.46124
## 8318 Casa la flora -76.58876 3.46348
## 8320 <NA> <NA> NA NA
## 8321 <NA> <NA> NA NA
## 8322 <NA> <NA> NA NA
## 8323 Apartamento aguacatal -76.57049 3.45445
## 8324 Apartamento santa rita -76.57076 3.45332
## 8325 Apartamento normandía -76.57941 3.45531
## 8326 Apartamento normandía -76.57941 3.45531
## 8327 Apartamento normandía -76.58530 3.45971
## 8328 Apartamento granada -76.58732 3.46148
## 8329 Apartamento normandía -76.58744 3.46124
## 8330 Casa la flora -76.58876 3.46348
El resultado del diagnóstico final del set de datos es el siguiente:
Se realiza entonces la respectiva depuración, cuyo código queda registrado en anexos.
Se inicia con la interpretación del resumen de los datos depurados
| Name | unique_data |
| Number of rows | 8322 |
| Number of columns | 13 |
| _______________________ | |
| Column type frequency: | |
| character | 3 |
| numeric | 10 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| zona | 0 | 1 | 8 | 12 | 0 | 5 | 0 |
| tipo | 0 | 1 | 4 | 11 | 0 | 2 | 0 |
| barrio | 0 | 1 | 4 | 29 | 0 | 385 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id | 0 | 1.00 | 4161.50 | 2402.50 | 1.00 | 2081.25 | 4161.50 | 6241.75 | 8319.00 | ▇▇▇▇▇ |
| piso | 2637 | 0.68 | 3.77 | 2.61 | 1.00 | 2.00 | 3.00 | 5.00 | 12.00 | ▇▃▁▁▁ |
| estrato | 0 | 1.00 | 4.63 | 1.03 | 3.00 | 4.00 | 5.00 | 5.00 | 6.00 | ▅▆▁▇▆ |
| preciom | 0 | 1.00 | 434.00 | 328.69 | 58.00 | 220.00 | 330.00 | 540.00 | 1999.00 | ▇▂▁▁▁ |
| areaconst | 0 | 1.00 | 174.97 | 142.95 | 30.00 | 80.00 | 123.00 | 229.00 | 1745.00 | ▇▁▁▁▁ |
| parquea | 1602 | 0.81 | 1.84 | 1.13 | 1.00 | 1.00 | 2.00 | 2.00 | 10.00 | ▇▁▁▁▁ |
| banios | 0 | 1.00 | 3.11 | 1.43 | 0.00 | 2.00 | 3.00 | 4.00 | 10.00 | ▇▇▃▁▁ |
| habitac | 0 | 1.00 | 3.61 | 1.46 | 0.00 | 3.00 | 3.00 | 4.00 | 10.00 | ▂▇▂▁▁ |
| longitud | 0 | 1.00 | -76.53 | 0.02 | -76.59 | -76.54 | -76.53 | -76.52 | -76.46 | ▁▅▇▂▁ |
| latitud | 0 | 1.00 | 3.42 | 0.04 | 3.33 | 3.38 | 3.42 | 3.45 | 3.50 | ▃▇▅▇▅ |
El set de datos finalmente se consolida con 8322 filas y 13 columnas. Contiene 3 variables de tipo carácter que serán tratadas como variables categóricas, y 10 variables numéricas. De estas últimas, 2 son continuas, 5 son discretas y serán tratadas como variables de tipo factor cuando sea necesario, y finalmente la variable id que se considera una llave, y la latitud y longitud que sirven para establecer la ubicación de las diferentes viviendas en grillas o mapas.
En cuanto a las variables categóricas, la variable zona tiene 5 valores únicos que corresponden al sur, norte, este, oeste y centro de la ciudad. La variable tipo solo tiene los valores de casa y apartamento. Finalmente, la variable barrios tiene cerca de 385 barrios distintos, aunque se presume que son menos debido a problemas en los nombres de las diferentes categorías que deben ser revisados en un futuro.
Respecto a las variables numéricas, la variable piso tiene un 32% de datos faltantes. Su media es de 3.77 y su desviación estándar es de 2.61, por lo que se espera que los apartamentos y las casas se encuentren entre 1 y 5 pisos. Esta variable muestra un máximo de 12 pisos. En cuanto a la variable estrato, entre el 25% y el 75% de las viviendas se encuentran entre el estrato 4 y 5, siendo estos los estratos más comunes en el conjunto de datos. El histograma de los precios es asimétrico, por lo que se generaliza que las viviendas en la ciudad de Cali rondan los 330 millones. Sin embargo, hay que tener en cuenta que la desviación es de 328 millones, por lo que el rango de precios es bastante amplio. Algo similar ocurre con el área construida, cuyo histograma también es asimétrico. La mediana es de 123 metros cuadrados, pero la desviación es alta con aproximadamente 143 metros cuadrados, por lo que las áreas en la ciudad también tienen un alto grado de variabilidad. Respecto a los parqueaderos, lo común es tener entre 1 y 2 por vivienda; para los baños entre 2 y 4 por vivienda; y finalmente entre 3 y 4 habitaciones por vivienda.
El gráfico muestra que la zona sur tiene la mayor oferta de viviendas, con el 56.8% de todo el stock, seguida por la zona norte y la zona oeste. En cuanto al tipo de vivienda, los apartamentos son los más ofertados con un 61.3%, mientras que las casas representan el 38.7%. Además, el estrato socioeconómico predominante en la oferta de viviendas es el estrato 5, seguido muy de cerca por los estratos 4 y 6.
La gráfica muestra los 10 barrios con la mayor cantidad de oferta de viviendas en la ciudad. El barrio con la mayor oferta es Valle del Lili, que representa el 12.2% del total de la oferta registrada en el conjunto de datos. Esto indica que Valle del Lili es un lugar popular para buscar viviendas en la ciudad.
La gráfica muestra los 10 barrios con mayor cantidad de oferta de viviendas en la ciudad. El barrio con mayor oferta es Valle del Lili, con el 12.2% del total de la oferta registrada en el conjunto de datos.
En cuanto al precio de las viviendas en general del stock ofertado, se puede observar que estos precios se concentran por debajo de los 500 millones, como se muestra en el histograma. La mayor cantidad de viviendas se encuentran en el rango de precios entre 250 y 330 millones. En cuanto al área construida, esta suele encontrarse por debajo de los 500 metros cuadrados y se generaliza entre los 120 metros cuadrados.
El gráfico muestra la relación entre los 10 barrios con más viviendas y la variable zona, permitiendo determinar que la zona sur es donde se encuentran los barrios con la mayor oferta de viviendas en la ciudad.
Es interesante analizar las dos variables continuas con mayor profundidad, ya que se considera que estas dos variables pueden ser determinantes a la hora de que un cliente decida comprar o no una vivienda. Estudiar la variable precio permitirá comprender qué características hacen que una vivienda cueste más que otra. La variable área permite establecer si una compra es mejor que otra, para ello resulta interesante revisar la dispersión de los datos que genera el cruce de ambas variables.
El gráfico de dispersión muestra una cierta linealidad que indica que, en general, a mayor precio, mayor es el área de la vivienda. Esto sugiere que existe una relación positiva entre el precio y el área de las viviendas.
Para entender mejor la relación anterior, se revisa por zonas. Se muestra que el centro tiene menos predios y también menos dispersión en los precios. Las demás zonas tienen datos atípicos. La zona oriente tiene menos dispersión en los precios y permite conseguir, en mayor medida, predios con precios por debajo de los 500 millones y áreas que pueden superar los 250 metros cuadrados. La zona oeste es la más costosa, ya que tiene una gran cantidad de predios con áreas por debajo de los 250 metros cuadrados y precios que pueden llegar hasta los 2000 millones. Las zonas sur y norte son las más variables y en ellas se encuentran toda clase de precios y áreas.
Un análisis conjunto de ambas gráficas muestra que hay 5 viviendas con precios por debajo de los 300 millones y áreas por encima de los 870 metros cuadrados. Tres de ellas están ubicadas en el oriente, una en el sur y una en el norte.
La gráfica muestra que la zona oriente tiene precios más similares entre las viviendas que se ofrecen allí. Incluso, se podría afirmar, por la ubicación del 75% de sus viviendas, que es la zona más económica de la ciudad, seguida de la zona centro. Por otro lado, las zonas sur y norte muestran una variación más alta en los precios, siendo la zona sur en general un poco más costosa que la zona norte. Ambas zonas tienen precios atípicos que superan los 1000 millones. Por último, la zona oeste es la más costosa, ya que la mediana y el 75% de las viviendas se encuentran por encima de los 500 millones.
El boxplot muestra que el 75% de las viviendas, independientemente de la zona, suelen tener áreas por metro cuadrado por debajo de los 250 metros. Además, se puede observar que las viviendas con áreas cercanas o superiores a los 500 metros cuadrados se consideran datos atípicos.
| preciom | areaconst | estrato | banios | habitac | |
|---|---|---|---|---|---|
| preciom | 1.0000000 | 0.6873507 | 0.6098514 | 0.6690851 | 0.2639510 |
| areaconst | 0.6873507 | 1.0000000 | 0.2743417 | 0.6484788 | 0.5168984 |
La tabla muestra que existen correlaciones positivas tanto para la variable precio como para la variable área. Sin embargo, la correlación más fuerte se encuentra entre estas dos variables (0.687), seguida de la correlación entre el precio y el número de baños de la vivienda (0.669). Luego, encontramos la correlación entre el precio y el estrato (0.609) y, por último, la correlación entre el precio y el número de habitaciones (0.263).
##
## 3 4 5 6
## Zona Centro 1.26 0.17 0.05 0.01
## Zona Norte 6.87 4.90 9.25 2.07
## Zona Oeste 0.65 1.01 3.48 9.26
## Zona Oriente 4.09 0.10 0.02 0.01
## Zona Sur 4.59 19.42 20.25 12.53
La tabla evidencia el cruce entre la variable zona y estrato.
La zona sur de la ciudad es conocida por tener una gran oferta de viviendas, lo que se refleja en la diversidad de estratos socioeconómicos presentes en esta área. Aunque todas las zonas de la ciudad ofrecen viviendas para todos los estratos, es en el norte donde se encuentra la mayor cantidad de viviendas de estrato 3.
Según el análisis univariado, una buena oferta de vivienda se caracteriza por tener un precio inferior a 300 millones, un área construida superior a 100 metros cuadrados, 3 o más habitaciones y baños, al menos un parqueadero y estar ubicada en un estrato igual o inferior a 4. Estas viviendas existen y se pueden encontrar fácilmente mediante el filtrado de la base de datos.
La zona sur es la que presenta la mayor oferta de viviendas, siendo los apartamentos el tipo de vivienda más ofertado. Las variables que tienen una alta relación con el precio son la zona, el área construida, el número de baños y el estrato.
En cuanto al precio, la zona oriente es la que presenta los precios más bajos. Sin embargo, en las demás zonas se pueden encontrar viviendas con una amplia variedad de precios. Esto permite establecer filtros personalizados según las preferencias de los clientes. Por ejemplo, una persona soltera podría estar interesada en un apartamento en la zona sur de la ciudad, con un precio entre 100 y 200 millones, estrato 3 o 4 y con parqueadero para su vehículo. El análisis de los datos muestra que existen viviendas con estas características, lo que permite a la empresa B&C ofrecer asesorías personalizadas según el tipo de cliente y sus preferencias.
Cabe destacar que en el set de datos no se encuentran viviendas con estratos inferiores a 3.
Se adjunta código usado para el cargue, la revisión inicial de los datos, se agregan los problemas detectados en las diferentes variables, la limpieza y la transformación de los datos.
##################################################################
# Librerías
##################################################################
library(tidyverse)
library(skimr)
library(stringi)
##################################################################
# Cargue de datos
##################################################################
data <- read.csv("C:/Users/Andre/OneDrive/Escritorio/vivienda_faltantes.csv", fileEncoding = "UTF-8")
##################################################################
# Exploración de datos
##################################################################
# Revisión inicial
names(data) # Nombres de variables
summary(data) # Resumen de datos
skim(data) # Resumen de datos más detallado
# Revisión de valores de categorias por variable
cols <- names(data)
resultados <- lapply(data[cols], function(x) sort(unique(x)))
# Revisión de duplicados en id (Se revisa en id pues se supone que estos son códigos únicos no se pueden repetir)
duplicated_rows <- duplicated(data$id) | duplicated(data$id, fromLast = TRUE)
duplicated_data <- data[duplicated_rows, ]
duplicated_data
##################################################################
# Problemas visibles del set de datos
##################################################################
# NA en id
# Baños y Habitaciones 0
# Valores atípicos de longitud y latitud
# Categorías de $tipo sin estandarizar
# Barrios con caracteres extraños producto de los acentos y la codificación
# Barrios sin estandarizar
# Registros duplicados
##################################################################
# Limpieza de datos
##################################################################
# Quitando filas con NA en id
data_no_na <- data[!is.na(data$id), ]
# Quitado de duplicados
duplicated_rows <- duplicated(data_no_na$id)
unique_data <- data[!duplicated_rows, ]
# Revisión del resultado de la limpieza
skim(unique_data)
##################################################################
# Transformación de datos
##################################################################
# Depuracion caracteres extraños para la variable barrio
unique_data$barrio <- stri_replace_all_fixed(unique_data$barrio, c("√∫", "√©"), c("ú", "é"), vectorize_all = FALSE)
# Depuracion quitar acentos y dejar solo palabras en minisculas variables barrio y tipo
col1 <- c("barrio", "tipo")
unique_data[col1] <- lapply(unique_data[col1], function(x) tolower(iconv(x, from = "UTF-8", to = "ASCII//TRANSLIT")))
# Revision del resultado de las variables transformadas
cols <- names(unique_data)
resultados <- lapply(unique_data[cols], function(x) sort(unique(x)))
# Unificacion categorias variable tipo
unique_data$tipo <- ifelse(unique_data$tipo %in% c("apartamento", "apto"), "apartamento", "casa")
# Correccion de cordenadas
unique_data$longitud <- ifelse(unique_data$longitud < -1000, unique_data$longitud/1000, unique_data$longitud)
unique_data$latitud <- ifelse(unique_data$latitud > 100, unique_data$latitud/1000, unique_data$latitud)
# Revision final de las trasnformaciones realizadas
skim(unique_data)