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 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.
data(vivienda_faltantes)
head(vivienda_faltantes)
## # A tibble: 6 × 13
## id zona piso estrato preciom areaconst parquea banios habitac tipo
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 8312 Zona Oeste 4 6 1300 318 2 4 2 Apart…
## 2 8311 Zona Oeste 1 6 480 300 1 4 4 Casa
## 3 8307 Zona Oeste NA 5 1200 800 4 7 5 Casa
## 4 8296 Zona Sur 2 3 220 150 1 2 4 Casa
## 5 8297 Zona Oeste NA 5 330 112 2 4 3 Casa
## 6 8298 Zona Sur NA 5 1350 390 8 10 10 Casa
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
cat("Cantidad columnas: ", ncol(vivienda_faltantes), "Cantidad filas: ", nrow(vivienda_faltantes))
## Cantidad columnas: 13 Cantidad filas: 8330
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:
unique_values <- unique(vivienda_faltantes$zona)
print(paste0("Valores únicos para zona: ", paste0(unique_values, collapse = ", ")))
## [1] "Valores únicos para zona: Zona Oeste, Zona Sur, Zona Norte, Zona Oriente, Zona Centro, NA"
unique_values <- unique(vivienda_faltantes$tipo)
print(paste0("Valores únicos para tipo: ", paste0(unique_values, collapse = ", ")))
## [1] "Valores únicos para tipo: Apartamento, Casa, APARTAMENTO, casa, CASA, apto, NA"
# Generando vector para resumir las columnas seleccionadas
specific_columns <- c("preciom", "piso", "estrato", "areaconst", "banios", "habitac")
summary(vivienda_faltantes[, specific_columns])
## preciom piso estrato areaconst
## Min. : 58.0 Min. : 1.000 Min. :3.000 Min. : 30
## 1st Qu.: 220.0 1st Qu.: 2.000 1st Qu.:4.000 1st Qu.: 80
## Median : 330.0 Median : 3.000 Median :5.000 Median : 123
## Mean : 434.2 Mean : 3.772 Mean :4.634 Mean : 175
## 3rd Qu.: 540.0 3rd Qu.: 5.000 3rd Qu.:5.000 3rd Qu.: 229
## Max. :1999.0 Max. :12.000 Max. :6.000 Max. :1745
## NA's :2 NA's :2641 NA's :3 NA's :3
## banios habitac
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 2.000 1st Qu.: 3.000
## Median : 3.000 Median : 3.000
## Mean : 3.112 Mean : 3.605
## 3rd Qu.: 4.000 3rd Qu.: 4.000
## Max. :10.000 Max. :10.000
## NA's :3 NA's :3
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_faltantes$id))
cat("Cantidad de id faltantes:", faltantes_id)
## Cantidad de id faltantes: 3
## Se remueven los id vacios de la tabla:
vivienda_faltantes = subset(vivienda_faltantes, !is.na(id))
cat("Nueva cantidad filas: ", nrow(vivienda_faltantes))
## Nueva cantidad filas: 8327
También se realiza la verificación de datos duplicados dentro de la tabla provista:
duplicates <- duplicated(vivienda_faltantes)
n_duplicates <- sum(duplicates)
cat("Cantidad de filas duplicadas: ", n_duplicates)
## Cantidad de filas duplicadas: 8
## Se remueven los duplicados encontrados:
vivienda_faltantes <- unique(vivienda_faltantes)
cat("Nueva cantidad filas: ", nrow(vivienda_faltantes))
## 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.
vivienda_faltantes$zona <- toupper(vivienda_faltantes$zona)
vivienda_faltantes$barrio <- toupper(vivienda_faltantes$barrio)
vivienda_faltantes$tipo <- toupper(vivienda_faltantes$tipo)
vivienda_faltantes$tipo <- ifelse(vivienda_faltantes$tipo=='CASA','CASA','APARTAMENTO')
unique_values <- unique(vivienda_faltantes$zona)
print(paste0("Valores únicos para zona: ", paste0(unique_values, collapse = ", ")))
## [1] "Valores únicos para zona: ZONA OESTE, ZONA SUR, ZONA NORTE, ZONA ORIENTE, ZONA CENTRO"
unique_values <- unique(vivienda_faltantes$tipo)
print(paste0("Valores únicos para tipo: ", paste0(unique_values, collapse = ", ")))
## [1] "Valores únicos para tipo: APARTAMENTO, CASA"
## uses visdat
vis_miss(vivienda_faltantes)
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_faltantes, rotate.names = TRUE)
## id zona estrato preciom areaconst banios habitac 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 parquea 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
Asi mismo, utilizando la librería mice se logra
distinguir en qué categorías se encuentran mezclados los datos perdidos.
Para el total de filas (8.319 filas) después de limpieza, se logra
identificar que hay 1.602 (19,25 %) datos perdidos para la variable
parqueaderos. Mientras que para la variable
piso existen un total de 2.635 (31,67 %) filas sin
datos.
En cuanto a la combinación de datos perdidos entre las dos variables
identificadas, se resalta que hay 1.909 (22,94 %) filas con datos
perdidos únicamente para la variable piso, hay 876 (10,53%)
filas con datos perdidos únicamente para parqueaderos y 726
(8,72 %) filas que no tienen datos para ninguna de las dos columnas.
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.
vivienda_faltantes <- vivienda_faltantes[, -c("parqueaderos", "piso")]
La segunda solución puede ser implementar una imputación por alguna medida de tendencia central. Esta también es una medida simple de implementar y puede ayudar a conservar la dimensionalidad del conjunto de datos. Como contra puede que si los valores faltantes no son aleatorios o dependen de alguna categorización puede guiar a tener sesgo en los resultados.
median_value <- median(vivienda_faltantes$piso) Se
calcula la mediana y se imputa
vivienda_faltantes$piso[is.na(vivienda_faltantes$piso)] <- median_value
También se puede intentar implementar una imputación por regresión. Esta técnica puede llegar a ser más sofisticada ya que tiene en cuenta la interacción entre variables. Sin embargo, este tipo de técnicas puede llegar a ser menos eficiente (tomar más tiempo de ejecución) que la imputación por medidas de tendencia central para conjuntos de datos grandes.
Después de realizar la limpieza de los datos se procede a analizar los puntos propuestos dentro de los objetivos del escrito. Entre ellos, se puede realizar la verificación de los precios en general para las viviendas de cali. Lo primero será realizar un box plot por zona y precio en millones de pesos.
ggplot(vivienda_faltantes, aes(x = preciom, y = zona)) +
geom_boxplot() + labs(title = "Precio en millones según zona",
x = "Precio (millones de pesos)",
y = "Zona") +
theme_bw()
Antes de sacar conclusiones del gráfico, también puede ser útil realizar la visualización de la cantidad de propiedades por zona que están ofertadas en la base de datos:
ggplot(vivienda_faltantes, aes(x = zona)) +
geom_bar() +
labs(title = "Cantidad de viviendas por zona",
x = "Zona",
y = "Cantidad ofertada")
Como se puede observar en la gráfica, la zona sur es la zona que más viviendas tiene en oferta, seguida por la zona norte y la zona oeste. Sin embargo, la zona sur presenta una asimetría a la derecha desde la media con respecto a sus precios, aunque se encuentran más concentrados con respecto a la mediana que en la zona oeste. También es importante resaltar que sólo en la zona oeste la mediana de precios se encuentra después de los 500 millones de pesos, esta zona posee gran variabilidad en la columna de precio, el resto de zonas tiene su mediana rondando por los 250 millones de pesos.
Aquí también surge la necesidad de realizar un análisis de precio por
metro cuadrado. Para articular este ejercicio se declara la variable
preciom2 que va a ser el resultado de la división de
preciom por areaconst. La variable se declara
de la siguiente manera:
vivienda_faltantes$preciom2 <- vivienda_faltantes$preciom / vivienda_faltantes$areaconst
summary(vivienda_faltantes$preciom2)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1461 1.9167 2.6400 2.7222 3.3795 9.4681
Y se realiza la correspondiente gráfica por zona nuevamente para poder identificar precios anómalos dentro de la base de datos:
ggplot(vivienda_faltantes, aes(x = preciom2, y = zona)) +
geom_boxplot() + labs(title = "Precio x m2 en millones según zona",
x = "Precio x m2 (millones de pesos)",
y = "Zona") +
theme_bw()
Llama la atención un precio de 9.46 millones de pesos por metro cuadrado en la zona norte, que se encuentra además muy alejado de los demás datos dentro de la distribución.
En la base de datos usada, luego de limpieza, sólo se tienen dos tipos de viviendas: apartamentos y casas. La distribución de las cantidades ofertadas se analiza a continuación en el gráfico de barras:
ggplot(vivienda_faltantes, aes(x = tipo, fill = tipo)) +
geom_bar() +
labs(title = "Tipo de viviendas más ofertadas",
x = "Tipo de viviendas",
y = "Cantidad ofertada")
Allí se puede evidenciar que se ofertan más apartamentos (más de 5.000 unidades) con respecto a casas (más de 3.000 unidades) en la data prevista para Cali. Así mismo, para complementar el análisis de precio, se presenta un análisis de cambios de precio con respecto al tipo en el siguiente gráfico de bigotes y cajas:
ggplot(vivienda_faltantes, aes(x = preciom, y = tipo)) +
geom_boxplot() + labs(title = "Precio en millones según tipo de vivienda",
x = "Precio (millones de pesos)",
y = "Tipo de vivienda") +
theme_bw()
En el anterior gráfico se puede observar que se tienen outliers en precio que van desde los 1.250 millones de pesos hasta casi 2.000 millones de pesos para el caso de las casas y desde aproximadamente los 800 millones de pesos hasta los 2.000 millones de pesos también para los apartamentos.
ggplot(vivienda_faltantes, aes(x = preciom2, y = tipo)) +
geom_boxplot() + labs(title = "Precio x m2 en millones según tipo de vivienda",
x = "Precio x m2 (millones de pesos)",
y = "Tipo de vivienda") +
theme_bw()
Como principal hallazgo destacado, se encuentra en este gráfico que las casas tienen un menor precio por metro cuadrado que los apartamentos, encontrandose su mediana por debajo de los 2,5 millones de pesos x m2, mientras que los apartamentos tienen una mediana por encima de los 2,5 millones de pesos.
Este apartado pretende mostrar las características principales de las
viviendas en Cali, así como su correlación con el precio para oferta. De
lo encontrado en la base de datos, se realizará una tabla dinámica que
contiene la información agrupada en promedios de preciom,
preciom2, banios y areaconst como
variables numéricas relevantes para el análisis de las características
de las viviendas en oferta.
vivienda_faltantes %>%
group_by(zona, tipo) %>%
summarize(
preciom_mean = mean(preciom),
preciom2_mean = mean(preciom2),
banios_mean = mean(banios),
areaconst_mean = mean(areaconst)
)
## `summarise()` has grouped output by 'zona'. You can override using the
## `.groups` argument.
## # A tibble: 10 × 6
## # Groups: zona [5]
## zona tipo preciom_mean preciom2_mean banios_mean areaconst_mean
## <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 ZONA CENTRO APARTAMEN… 187. 1.95 2.12 95.1
## 2 ZONA CENTRO CASA 339. 1.70 3.01 218.
## 3 ZONA NORTE APARTAMEN… 285. 2.81 2.31 98.6
## 4 ZONA NORTE CASA 446. 1.85 3.56 265.
## 5 ZONA OESTE APARTAMEN… 668. 3.85 3.39 172.
## 6 ZONA OESTE CASA 736. 2.39 4.26 343.
## 7 ZONA ORIENTE APARTAMEN… 153. 1.70 1.73 94.2
## 8 ZONA ORIENTE CASA 245. 1.39 2.97 213.
## 9 ZONA SUR APARTAMEN… 297. 2.99 2.49 97.5
## 10 ZONA SUR CASA 612. 2.34 4.17 282.
También se permite realizar un análisis por estrato, dado que se entiende el estrato como una variable que puede afectar directamente la valoración de una vivienda. En este caso se toman las variables de zona y tipo como índice, la variable estrato como columna, y como variable objetivo se toma el precio x m2, agregado en promedios.
dcast(vivienda_faltantes, zona + tipo ~ estrato, value.var = "preciom2", fun.aggregate = mean)
## zona tipo 3 4 5 6
## 1 ZONA CENTRO APARTAMENTO 1.687162 2.196739 2.5970172 NaN
## 2 ZONA CENTRO CASA 1.618190 2.129628 5.0387597 2.500000
## 3 ZONA NORTE APARTAMENTO 1.982170 2.619976 3.2505142 3.732418
## 4 ZONA NORTE CASA 1.741728 1.832592 1.8555119 2.284297
## 5 ZONA OESTE APARTAMENTO 2.166269 2.741639 3.5526116 4.106758
## 6 ZONA OESTE CASA 2.217560 2.171362 2.0538018 2.885407
## 7 ZONA ORIENTE APARTAMENTO 1.568097 3.011905 1.7500000 6.367925
## 8 ZONA ORIENTE CASA 1.383509 1.702931 0.9866667 NaN
## 9 ZONA SUR APARTAMENTO 2.088322 2.711098 3.0142868 3.977286
## 10 ZONA SUR CASA 1.644637 2.018603 2.2415438 2.942569
En esta sección se van a explorar los hallazgos más relevantes del trabajo expuesto en los resultados. Los mayores puntos y más relevantes se encuentran enfocados a las características de las viviendas que se trataron en el punto 4.3 de este reporte.
El análisis de viviendas en Cali revela una serie de hallazgos importantes. En cuanto a la oferta, se observa que hay una mayor cantidad de apartamentos disponibles en el mercado que de casas. Sin embargo, las casas tienen un precio por metro cuadrado menor que los apartamentos, con una mediana inferior a 2,5 millones de pesos frente a una superior a 2,5 millones de pesos para los apartamentos.
En cuanto a las características, las casas tienden a tener un área construida mayor que los apartamentos, llegando a ser hasta el doble o triple en algunas zonas. Además, las casas tienen un promedio de baños superior al de los apartamentos, con valores que alcanzan los 4,2 baños en las zonas sur y oeste.
En términos de precios por zona, las casas son más costosas que los apartamentos, con un promedio del doble. Sin embargo, al analizar los precios por metro cuadrado, se observa que los apartamentos tienen un valor superior en todas las zonas, lo que indica que la mayor área construida de las casas compensa en parte su precio más elevado.
Al analizar el comportamiento del precio por metro cuadrado en diferentes estratos, se observa que en los estratos 3 y 4, los valores son similares para casas y apartamentos, independientemente de la zona. Sin embargo, en los estratos 5 y 6 se encuentran algunos datos curiosos. Por ejemplo, en la zona oriente, el precio por metro cuadrado de una casa estrato 5 es menor que el de los estratos 3 y 4. Además, en la misma zona, los apartamentos de estrato 6 tienen el precio por metro cuadrado más alto de todas las categorías.