1. Introducción

Entre 2022 y 2023, el mercado inmobiliario en Cali experimentó un crecimiento significativo, impulsado por el incremento poblacional, la inversión extranjera directa y el desarrollo de nuevos proyectos residenciales y comerciales. En 2022, las ventas en el sector alcanzaron los 6,700 millones de pesos, aunque en 2023 se observó una leve disminución a 6,100 millones de pesos. A pesar de esta ligera caída, se proyecta que el sector continuará expandiéndose en los próximos años, consolidándose como un pilar fundamental de la economía regional.

Este informe tiene como objetivo ofrecer un análisis detallado del mercado inmobiliario en Cali, centrándose en los precios de las viviendas en diferentes zonas de la ciudad, los tipos de viviendas más ofertadas y las características más relevantes de la oferta disponible. Se busca proporcionar una visión comprensiva de las dinámicas actuales y futuras del sector, facilitando decisiones informadas en un mercado en constante evolución.

2. Analisis descriptivo:

Al abordar los datos utilizados para analizar el mercado inmobiliario en Cali, se implementaron varias técnicas para garantizar la precisión y la fiabilidad de los resultados obtenidos. Primero, se realizó un proceso de limpieza de datos para identificar y corregir errores tipográficos, inconsistencias en el formato y entradas faltantes que podrían distorsionar los análisis. Además, se identificaron y trataron los datos atípicos, que son valores significativamente alejados del resto de los datos, los cuales podrían ser el resultado de errores de registro o de eventos únicos.

Para abordar los datos faltantes, se emplearon diferentes técnicas según la naturaleza y la cantidad de información faltante. En casos donde los datos faltantes eran mínimos y no afectaban significativamente el análisis, se optó por eliminar las entradas incompletas. Sin embargo, en situaciones donde la ausencia de datos podría influir en los resultados, se aplicaron métodos de imputación, como la mediana siempre y cuando la distribución fuese no normal.

Cargar y ver datos de la libreria.

#cargar datos de viviendas en cali - y ver la tabla#
data(vivienda_faltantes)
head (vivienda_faltantes)

Observar los datos faltantes por variable.

colSums(is.na(vivienda_faltantes)) %>%
  as.data.frame()
gg_miss_var(vivienda_faltantes) # grafico de datos faltantes

Como se muestra en la figura siguiente, las variables “piso” y “parqueadero” son las que presentan el mayor número de datos faltantes. Para determinar el tipo de imputación adecuado, se realizó un análisis de la distribución de las variables numéricas. La prueba utilizada fue la curtosis, que evalúa la “altitud” y el “ancho” de las colas de la distribución en comparación con una distribución normal.

Tipo de Viviendas mas ofertadas en Cali.

t_vivienda = table(vivienda_faltantes$tipo)
print("Tipo de viviendas más ofertadas en Cali:")
## [1] "Tipo de viviendas más ofertadas en Cali:"
t_vivienda
## 
## Apartamento APARTAMENTO        apto        casa        Casa        CASA 
##        5032          61          13          14        3195          12
ggplot(data = vivienda_faltantes, aes(x = tipo)) +
  geom_bar() +
  theme_minimal() +
  labs(title = "Tipos de Viviendas mas ofertadas en Cali",
       x = "Tipo de Vivienda",
       y = "Frecuencia")

Como no estan estandarizados los terminos se procede a ello de la siguiente forma:

# Función para estandarizar los valores de 'tipo'
estandarizar_tipo = function(tipo) {
  tipo = tolower(tipo)  # Convertir a minúsculas
  tipo = dplyr::recode(tipo,  # Reemplazar valores específicos
                        "apartamento" = "Apartamento",
                        "apto" = "Apartamento",
                        "casa" = "Casa",
                        "CASA" = "Casa",
                        "APARTAMENTO" = "Apartamento")
  return(tipo)
}

# Aplicar la función para unificar la columna 'tipo'#
vivienda_faltantes = vivienda_faltantes %>%
  mutate(tipo = estandarizar_tipo(tipo))
# Ver los resultados
table(vivienda_faltantes$tipo)
## 
## Apartamento        Casa 
##        5106        3221

Se observa la cantidad de viviendas por zona.

ggplot(data = vivienda_faltantes, aes(x = zona, fill = tipo)) +
  geom_bar(position = "dodge") +
  labs(title = "Distribución de Tipos de Vivienda por Zona",
       x = "Zona",
       y = "Cantidad",
       fill = "Tipo de Vivienda") +
  theme_minimal()

En el mercado inmobiliario de Cali, los tipos de viviendas más ofertadas son apartamentos y casas. Según los datos recopilados, se ofertan un total de 5,106 apartamentos y 3,221 casas. Este predominio de apartamentos se debe a la alta demanda en las zonas urbanas, mientras que las casas unifamiliares siguen siendo una opción popular para quienes buscan más espacio y privacidad.

Observar la distribución de la variable piso para determinar la imputación de N/A.

Como se muestra en la figura siguiente, las variables “piso” y “parqueadero” son las que presentan el mayor número de datos faltantes. Para determinar el tipo de imputación adecuado, se realizó un análisis de la distribución de las variables numéricas. La prueba utilizada fue la curtosis, que evalúa la “altitud” y el “ancho” de las colas de la distribución en comparación con una distribución normal.

# Calcular la curtosis del piso con el fin de determinar su distribución e imputacion#
curtosis = kurtosis(vivienda_faltantes$piso, na.rm = TRUE)
print(paste("Curtosis de 'preciom':", curtosis))
## [1] "Curtosis de 'preciom': 1.04803518541756"
ggplot(vivienda_faltantes, aes(x = piso)) +
  geom_histogram(bins = 10, fill = "orange", color = "black") +
  theme_minimal() +
  labs(title = paste("Distribucion de Precios de Viviendas (Curtosis =", round(curtosis, 2), ")"),
       x = "Precio (en millones)",
       y = "Frecuencia")
## Warning: Removed 2641 rows containing non-finite outside the scale range
## (`stat_bin()`).

Un valor de curtosis de 1.05 para precio explica que la distribución es platicúrtica. Es decir, la distribución es menos pronunciada en el centro y tiene colas más cortas y menos destacadas en comparación con una distribución normal.

Imputación por mediana para la varibale piso.

Al no seguir una distribución normal, se imputan los datos faltantes a traves de la mediana.

# Calculo de la mediana#
mediana_piso = median(vivienda_faltantes$piso, na.rm = TRUE)
mediana_piso
## [1] 3
# Imputar los valores con la mediana#
vivienda_faltantes$piso[is.na(vivienda_faltantes$piso)] = mediana_piso
summarytools::freq(vivienda_faltantes$piso)
## Frequencies  
## vivienda_faltantes$piso  
## Type: Numeric  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##           1    861     10.34          10.34     10.34          10.34
##           2   1450     17.41          27.74     17.41          27.74
##           3   3738     44.87          72.62     44.87          72.62
##           4    607      7.29          79.90      7.29          79.90
##           5    568      6.82          86.72      6.82          86.72
##           6    245      2.94          89.66      2.94          89.66
##           7    207      2.48          92.15      2.48          92.15
##           8    211      2.53          94.68      2.53          94.68
##           9    146      1.75          96.43      1.75          96.43
##          10    130      1.56          98.00      1.56          98.00
##          11     84      1.01          99.00      1.01          99.00
##          12     83      1.00         100.00      1.00         100.00
##        <NA>      0                               0.00         100.00
##       Total   8330    100.00         100.00    100.00         100.00

Bajo la anterior imputación se procede a observar de nuevo los datos faltantes del archivo.

colSums(is.na(vivienda_faltantes)) %>%
  as.data.frame()

el anterior muestra un cantidad predominante para la variable parqueadero parquea, por lo anterior, se procede a determinar bajo el mismo condicionante anterior.

Observar la distribución de la variable parquea para determinar la imputación de N/A.

#Curtosis de los parqueaderos#
curtosis_parquea = kurtosis(vivienda_faltantes$parquea, na.rm = TRUE)
print(paste("Curtosis de 'parquea':", curtosis_parquea))
## [1] "Curtosis de 'parquea': 8.29065224012304"
ggplot(vivienda_faltantes, aes(x = parquea)) +
  geom_histogram(bins = 10, fill = "red", color = "black") +
  theme_minimal() +
  labs(title = paste("Distribucion de Precios de Viviendas (Curtosis =", round(curtosis_parquea, 2), ")"),
       x = "Precio (en millones)",
       y = "Frecuencia")
## Warning: Removed 1606 rows containing non-finite outside the scale range
## (`stat_bin()`).

Una curtosis de 8.29 para parqueaderos indica que la variable es leptocúrtica. Esto significa que la distribución tiene colas más pesadas y un pico más alto en comparación con una distribución normal. Al no seguir una distribución normal, se imputan los datos faltantes a traves de la mediana.

# Calcular la mediana del 'parqueadero' para cada.#
mediana_parquea = median(vivienda_faltantes$parquea, na.rm = TRUE)
# Imputar los valores vacíos con la mediana#
vivienda_faltantes$parquea[is.na(vivienda_faltantes$parquea)] = mediana_parquea
summarytools::freq(vivienda_faltantes$parquea)
## Frequencies  
## vivienda_faltantes$parquea  
## Type: Numeric  
## 
##               Freq   % Valid   % Valid Cum.   % Total   % Total Cum.
## ----------- ------ --------- -------------- --------- --------------
##           1   3156    37.887         37.887    37.887         37.887
##           2   4084    49.028         86.915    49.028         86.915
##           3    521     6.255         93.169     6.255         93.169
##           4    386     4.634         97.803     4.634         97.803
##           5     68     0.816         98.619     0.816         98.619
##           6     68     0.816         99.436     0.816         99.436
##           7     18     0.216         99.652     0.216         99.652
##           8     17     0.204         99.856     0.204         99.856
##           9      4     0.048         99.904     0.048         99.904
##          10      8     0.096        100.000     0.096        100.000
##        <NA>      0                              0.000        100.000
##       Total   8330   100.000        100.000   100.000        100.000

Observo los datos faltantes de nuevo, para cesiorar la realización de la imputación.

#mirar datos faltantes#
colSums(is.na(vivienda_faltantes)) %>%
  as.data.frame()

Eliminación de las filas con valores nulos.

#elimino filas con valores nulos#
vivienda_faltantes <- na.omit(vivienda_faltantes)
#mirar datos faltantes#
colSums(is.na(vivienda_faltantes)) %>%
  as.data.frame()

La tabla anterior refleja los datos para las variables previamente expuestas, imputados mediante la mediana, dado que se trataba de variables numéricas que no seguían una distribución normal. Para las demás variables con datos faltantes, se procedió a eliminar las filas correspondientes, ya que, dado que los datos faltantes eran mínimos (con un máximo de tres datos faltantes por variable), esta eliminación no tiene un efecto significativo en el análisis.

Precios promedio de las viviendas de acuerdo a la zona.

#Precio promedio de las viviendas en diferentes zonas de Cali#
p_pzona = aggregate(vivienda_faltantes$preciom, 
                    by=list(vivienda_faltantes$zona), FUN=mean, na.rm=TRUE)
colnames(p_pzona) = c("Zona", "Precio_Promedio")
print("Precios promedio de las viviendas por zona:")
## [1] "Precios promedio de las viviendas por zona:"
p_pzona

::: {align=“justify”} La Zona Oeste la más costosa y la Zona Oriente la más económica. La Zona Sur presenta la mayor actividad inmobiliaria con el mayor número de ofertas, mientras que la Zona Centro tiene la menor cantidad de ofertas. No hay una relación directa clara entre el precio promedio y el número de ofertas, ya que zonas con precios altos como la Zona Oeste tienen una cantidad considerable de ofertas, y la Zona Norte también muestra una alta oferta pese a tener un precio intermedio.:::

Tipos de vivienda mas ofertadas en Cali (Estandarizado).

# Gráfico de tipos de viviendas más ofertadas#
ggplot(data = vivienda_faltantes, aes(x = tipo)) +
  geom_bar() +
  theme_minimal() +
  labs(title = "Tipos de Viviendas mas ofertadas en Cali",
       x = "Tipo de Vivienda",
       y = "Frecuencia")

Distribución de Precios de Viviendas por Zona en Cali.

#Gráfico de precios por zona#
ggplot(data = vivienda_faltantes, aes(x = zona, y = preciom)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Distribucion de Precios de Viviendas por Zona en Cali",
       x = "Zona",
       y = "Precio (en millones)")

La Zona Oeste la más costosa y la Zona Oriente la más económica. La Zona Sur presenta la mayor actividad inmobiliaria con el mayor número de ofertas, mientras que la Zona Centro tiene la menor cantidad de ofertas. No hay una relación directa clara entre el precio promedio y el número de ofertas, ya que zonas con precios altos como la Zona Oeste tienen una cantidad considerable de ofertas, y la Zona Norte también muestra una alta oferta pese a tener un precio intermedio.

Dispersión del precio por zona.

paleta6=c("#447270", "#6B9493", "#F6E271", "#F6B916", "#F69312", "#BC6C25")
p1=ggplot(vivienda_faltantes, aes(y=preciom, x=zona))+
  geom_jitter(color="#034A94", size=1, alpha=0.9) +
  aes(color=paleta6)+
  labs(title = "Dispercion del precio por zona",
       y= "precio",
       x= "zona")+ 
  ylim(0,1000)


p1
## Warning: Removed 575 rows containing missing values or values outside the scale range
## (`geom_point()`).

Conteo de viviendas por zona.

conteo_por_zona = vivienda_faltantes %>%
  count(zona)

# Muestra el conteo por zona
print(conteo_por_zona)
## # A tibble: 5 × 2
##   zona             n
##   <chr>        <int>
## 1 Zona Centro    124
## 2 Zona Norte    1922
## 3 Zona Oeste    1204
## 4 Zona Oriente   351
## 5 Zona Sur      4726

La mayor cantidad de viviendas se encuentra en la zona sur, la segunda zona con mayor cantidad de viviendas es la zona norte.

Relación entre Area Construida y Precio de Viviendas en Cali.

#Gráfico de dispersión de precio vs área construida#
ggplot(data = vivienda_faltantes, aes(x = areaconst, y = preciom)) +
  geom_point() +
  theme_minimal() +
  labs(title = "Relacion entre Area Construida y Precio de Viviendas en Cali",
       x = "Area Construida (m2)",
       y = "Precio (en millones)")

La dispersión de precios de las viviendas muestra una variabilidad en los estratos. El estrato 5, con 2,751 viviendas, presenta la mayor dispersión, seguido por el estrato 4 con 2,131 viviendas. En el estrato 6 se encuentran 1,992 viviendas, mientras que el estrato 3 cuenta con 1,453 viviendas. En cuanto a la distribución de barrios, su distribución por estratos es el siguiente: el estrato 3 alberga 262 barrios, el estrato 4 tiene 171 barrios, el estrato 5 incluye 169 barrios y el estrato 6 cuenta con 84 barrios.

#Datos atipicos##

# Calcular estadísticas descriptivas por zona#
estadisticas_por_zona <- vivienda_faltantes %>%
  group_by(zona) %>%
  summarise(
    media = mean(preciom, na.rm = TRUE),
    mediana = median(preciom, na.rm = TRUE),
    desviacion = sd(preciom, na.rm = TRUE),
    q1 = quantile(preciom, 0.25, na.rm = TRUE),
    q3 = quantile(preciom, 0.75, na.rm = TRUE),
    IQR = q3 - q1
  )

estadisticas_por_zona

La columna atipico contiene un valor lógico (TRUE/FALSE) que indica si el valor de preciom es un valor atípico (outlier) según la regla del rango intercuartílico (IQR). Un valor se considera atípico si es menor que q1 - 1.5 * IQR o mayor que q3 + 1.5 * IQR. La Zona Oeste la más costosa y la Zona Oriente la más económica. La Zona Sur presenta la mayor actividad inmobiliaria con el mayor número de ofertas, mientras que la Zona Centro tiene la menor cantidad de ofertas. No hay una relación directa clara entre el precio promedio y el número de ofertas, ya que zonas con precios altos como la Zona Oeste tienen una cantidad considerable de ofertas, y la Zona Norte también muestra una alta oferta pese a tener un precio intermedio.

# Detectar atípicos usando el IQR#
vivienda_faltantes_atipicos = vivienda_faltantes %>%
  left_join(estadisticas_por_zona, by = "zona") %>%
  mutate(
    atipico = preciom < (q1 - 1.5 * IQR) | preciom > (q3 + 1.5 * IQR)
  )

# Ver datos atípicos
datos_atipicos <- vivienda_faltantes_atipicos %>%
  filter(atipico)
datos_atipicos
# Gráfico de cajas con atípicos#
ggplot(vivienda_faltantes, aes(x = zona, y = preciom)) +
  geom_boxplot(outlier.colour = "red", outlier.shape = 16) +
  theme_minimal() +
  labs(title = "Distribucion de Precios por Zona con Datos Atipicos",
       x = "Zona",
       y = "Precio (en millones)")

# Imputar datos atípicos con la mediana#
vivienda_faltantes_imputados = vivienda_faltantes_atipicos %>%
  mutate(preciom = ifelse(atipico, mediana, preciom))

Outliers imputados con mediana

ggplot(vivienda_faltantes_imputados, aes(x = zona, y = preciom)) +
  geom_boxplot(outlier.colour = "red", outlier.shape = 16) +
  theme_minimal() +
  labs(title = "Distribucion de Precios por Zona con Datos Atipicos arreglados con la mediana",
       x = "Zona",
       y = "Precio (en millones)")

Finalmente, los datos atípicos fueron imputados con la mediana para suavizar la distribución y mejorar la calidad del análisis. Sin embargo, es importante destacar que este proceso no se aplicó al análisis descriptivo, ya que los datos atípicos pueden reflejar características particulares de las viviendas, como su estado físico o ubicación geográfica, que pueden justificar un valor más alto y son relevantes para comprender las dinámicas del mercado inmobiliario en Cali.

Dispersión de precios por zona - viviendas imputadas.

paleta7=c("#447270", "#6B9493", "#F6E271", "#F6B916", "#F69312", "#BC6C25")
p3=ggplot(vivienda_faltantes_imputados, aes(y=preciom, x=estrato))+
  geom_jitter(color="#447270", size=1, alpha=0.9) +
  aes(color=paleta7)+
  labs(title = " ",
       y= "precio",
       x= "Estrato")+ 
  ylim(0,1500)

p3
## Warning: Removed 21 rows containing missing values or values outside the scale range
## (`geom_point()`).

Distribución de barrios por estrato.

conteo_barrios_por_estrato = vivienda_faltantes_imputados %>%
  group_by(estrato) %>%
  summarise(
    cantidad_barrios = n_distinct(barrio)  # Contar barrios únicos por estrato
  )
print(conteo_barrios_por_estrato)
## # A tibble: 4 × 2
##   estrato cantidad_barrios
##     <dbl>            <int>
## 1       3              262
## 2       4              171
## 3       5              169
## 4       6               84

Distribución de viviendas por estrato.

vivienda_faltantes_imputados$estrato = as.factor(vivienda_faltantes_imputados$estrato)
conteo=vivienda_faltantes_imputados %>%
  count(estrato)
conteo

En cuanto a la distribución de barrios, su distribución por estratos es el siguiente: el estrato 3 alberga 262 barrios, el estrato 4 tiene 171 barrios, el estrato 5 incluye 169 barrios y el estrato 6 cuenta con 84 barrios.

p2 = ggplot(vivienda_faltantes_imputados, aes(y = preciom, x = estrato)) +
  geom_jitter(color = "#447270", size = 1, alpha = 0.9) +
  geom_text(data = conteo , aes(label = n, x = estrato, y = 0), vjust = -0.5, color = "black") +
  labs(title = " ",
       y = "Precio",
       x = "Estrato") +
  ylim(0, 1500)
p2
## Warning: Removed 21 rows containing missing values or values outside the scale range
## (`geom_point()`).

3. Discución y conclusiones

El mercado inmobiliario en Cali revela una significativa variabilidad en precios según la zona, con el Oeste siendo la más cara y el Oriente la más económica, mientras que la Zona Sur muestra una alta actividad inmobiliaria. Los apartamentos predominan en el mercado, con una oferta mucho mayor que la de las casas, reflejando una alta demanda en áreas urbanas y ofreciendo oportunidades para desarrollos residenciales. La relación directa entre el tamaño de la vivienda y su precio subraya la importancia de ofrecer opciones amplias para captar a quienes están dispuestos a pagar más. En cuanto a los estratos, el estrato 5 presenta la mayor dispersión de precios y el estrato 3 tiene el mayor número de barrios, indicando una alta fragmentación en ese estrato.

Por otro lado, la variabilidad en el número de barrios por estrato indica diferencias en la concentración de propiedades. El estrato 3, con su gran cantidad de barrios, puede indicar un mercado más fragmentado y competitivo, mientras que los estratos superiores, con menos barrios, pueden reflejar una mayor concentración de propiedades y, posiblemente, una demanda más homogénea.