1. Introducción

El mercado inmobiliario de la ciudad de Cali ha experimentado un notable crecimiento en los últimos años, impulsado por diversos factores como el aumento de la población, la considerable inversión extranjera y el desarrollo de nuevos proyectos inmobiliarios. Este informe se propone realizar un análisis estadístico exhaustivo, recopilando información importante sobre las viviendas, incluyendo sus precios, ubicaciones y características.

2. Objetivo

El objetivo del informe es identificar tendencias, patrones y características relevantes del mercado inmobiliario en Cali. Se pretende ofrecer una visión diferente para la toma de decisiones estratégicas tanto para B&C como para las personas interesadas.

3. Métodos

Realizaremos la limpieza de datos, la eliminación de duplicados y el tratamiento de valores faltantes en el informe

Descripción variables base de datos

Id Registro unico que define la vivienda

Zona: Zona Centro,Zona Norte,Zona Oeste,Zona Oriente,Zona Sur

Piso Ubicación de la vivienda en el eje y de una propiedad horizontal

Estrato Variable con escala de medición ordinal,los estrato de las viviendas son 3,4,5,6

Preciom Precio de la vivienda

areaconstu: Area Construida

Parquea: Parqueaderos que tiene la vivienda

banios: Número de baños que tiene la vivienda

habitac: Número de habitaciones que tiene la vivienda

Tipo: Tipo de la vivienda, casa o apartamento

Barrio: Barrio donde esta posicionada la vivienda

Longitud: Posición de las líneas que representan la distancia este-oeste en la superficie de la Tierra

Latitud: Posición de lass líneas que representan la distancia norte-sur en la superficie de la Tierra

Número total de registros iniciales 8328

El primer paso es evidenciar como se encuentran cada una de las variables, si existen valores duplicados, teniendo en consideración que el ID deberia ser un numero unico y no se deberia encontrar

Porcentaje de datos faltantes y patrones

porce<-apply(vivienda_faltantes, 2, function(x) c(sum(is.na(x)), sum(is.na(x))/length(x)*100))
pander(porce[-1,])
Table continues below
id zona piso estrato preciom areaconst parquea banios
0.03601 0.03601 31.7 0.03601 0.02401 0.03601 19.28 0.03601
habitac tipo barrio longitud latitud
0.03601 0.03601 0.03601 0.03601 0.03601

La variable que presente mayor número de datos faltantes es piso (31,7%), seguido por Parquea (19,28%) estas dos variables son las que presentan el mayor número de datos faltantes a comparación de las demas.

Revisión de los valores de cada variable

Variable zona

## [1] "Zona Oeste"   "Zona Sur"     "Zona Norte"   "Zona Oriente" "Zona Centro" 
## [6] NA

variable piso

##  [1]  4  1 NA  2  6  8  3  9  5 12 11  7 10

variable estrato

## [1]  6  5  3  4 NA

variable parquea

##  [1]  2  1  4  8 NA  3  6  5 10  7  9

variable banios

##  [1]  2  1  4  8 NA  3  6  5 10  7  9

variable habitac

##  [1]  2  4  5  3 10  6  8  7  0  9  1 NA

variable habitac

## [1] "Apartamento" "Casa"        "APARTAMENTO" "casa"        "CASA"       
## [6] "apto"        NA

Este es un problema muy comun cuando trabajos con datos categoricos y es la inconsistencia ya que los datos pueden tener abreviaciones, capitalización etc, en estos casos si es coherente la solución consta en homologar estos datos a uno solo

Capitalizamos los valores

base_viviendas <- base_viviendas %>%
  mutate(tipo = str_to_title(base_viviendas$tipo))

Convertimos apto a apartamento

base_viviendas <- base_viviendas %>%
  mutate(tipo = str_replace(tipo, "Apto", "Apartamento"))

variable tipo con el proceso de Limpieza

unique_valores <- unique(base_viviendas$tipo            )
print(unique_valores)
## [1] "Apartamento" "Casa"        NA

Para la variable de Barrio se identifican algunos caracteres mal comprendidos por ejemplo √© que en la mayoria de casos hace referencia a una e y en otros casos este √∫ que hace referencia a una u de igual manera se procede a capitalizar y retirar las tildes ya que se puede presentar el caso de ambos registros iguales pero con una tilde de diferencia, quizas sean problemas de condificación pero para este caso no fue posible resolverlos asi por lo que se opto por desarrollar una regla.

base_viviendas <- base_viviendas
base_viviendas <- base_viviendas %>%
  mutate(barrio = str_to_title(base_viviendas$barrio    ))
base_viviendas <- base_viviendas %>%
  mutate(barrio = gsub("√∫", "u", barrio)) %>%
  mutate(barrio = gsub("é", "e", barrio)) %>%
  mutate(barrio = tolower(barrio))%>%
  mutate(barrio = iconv(barrio, to = "ASCII//TRANSLIT"))

Para la columna Id haremos una revisión de duplicado dado que el Id se entiende como un valor unico, lo ideal es que no se presente el caso donde existan duplicados.

duplicados <- base_viviendas[duplicated(base_viviendas$id), ]

if (nrow(duplicados) > 0) {
  print("Se encontraron duplicados en la columna ID:")
  print(duplicados)
} else {
  print("No se encontraron duplicados en la columna ID.")
}
## [1] "Se encontraron duplicados en la columna ID:"
## # A tibble: 10 × 13
##       id zona        piso estrato preciom areaconst parquea banios habitac tipo 
##    <dbl> <chr>      <dbl>   <dbl>   <dbl>     <dbl>   <dbl>  <dbl>   <dbl> <chr>
##  1    NA <NA>          NA      NA      NA        NA      NA     NA      NA <NA> 
##  2    NA <NA>          NA      NA     330        NA      NA     NA      NA <NA> 
##  3  8309 Zona Oeste     5       4     150        56      NA      1       2 Apar…
##  4  8310 Zona Oeste     1       6    1600       463       4      6       3 Apar…
##  5  8313 Zona Oeste     7       6     525       137       2      3       3 Apar…
##  6  8314 Zona Oeste     7       6    1400       210       3      4       3 Apar…
##  7  8315 Zona Oeste    NA       6     620       167       2      4       4 Apar…
##  8  8316 Zona Norte     7       5     400       220       1      4       4 Apar…
##  9  8317 Zona Oeste    NA       6    1100       290       4      4       3 Apar…
## 10  8318 Zona Norte    NA       4     580       295       2      5       5 Casa 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Con este analisis encontramos dos puntos importantes la presencia de registros totalmente nulos asi mismo como la presencia de duplicados, procedemos a eliminar estos registros duplicados asumiendo que son valores escritos dos veces en la tabla, sin embargo la presencia de estos valores siempre es ideal hablarlo con el experto en la información y encontrar sus causas

Eliminamos los valores faltantes en la columna Id

base_viviendas <- base_viviendas %>%
  distinct(id, .keep_all = TRUE)

base_viviendas <- base_viviendas[!is.na(base_viviendas$id), ]

Ahora tenemos un gran dilema, y es qué hacer con los datos faltantes que presentan las diversas variables en la tabla. ¿Los definimos como NA, los imputamos, o los simulamos con algún método estadístico? Para este caso en concreto, como no hemos aprendido la simulación de valores faltantes, nos quedan dos opciones: categorizar el valor como un NA o realizar una imputación. Personalmente, prefiero dejar el valor categorizado como NA, ya que los datos faltantes pueden tener un significado propio y no deben ser imputados ni eliminados. En lugar de eso, se puede considerar tratar los datos faltantes como una categoría adicional en el análisis o modelo, ya que esto permite una traza de la información origen y un entendimiento con el área de negocio de la pérdida de información. Sin embargo, hay excepciones.

Para el caso de la variable “Parquea”, es curioso que ningún valor se asignara como 0. ¿Será que el valor faltante es la ausencia de parqueadero en la vivienda? Podríamos asumir esta conclusión que a priori tiene lógica; sin embargo, es muy importante dialogarlo con el experto en el negocio y los datos.

Para las variables “Piso”, “Estrato” y “Baños”, se dejó en la variable la presencia de DatosFaltantes con el acrónimo de “SinInformación”.

base_viviendas <- base_viviendas %>%
  mutate(estrato = ifelse(is.na(estrato), "SinInformacion", estrato),
         piso = ifelse(is.na(piso), "SinInformacion", piso))
base_viviendas <- base_viviendas %>%
  mutate(parquea = ifelse(is.na(parquea), 0, parquea))

Datos Faltantes despues de las imputaciones

porce_imp<-apply(base_viviendas, 2, function(x) c(sum(is.na(x)), sum(is.na(x))/length(x)*100))
pander(porce_imp[-1,])
Table continues below
id zona piso estrato preciom areaconst parquea banios habitac
0 0 0 0 0 0 0 0 0
tipo barrio longitud latitud
0 0 0 0

4. RESULTADOS

4. Presentación análisis de mercado grupo B&C

4.1 Distribución por precio de las viviendas en la ciudad de Cali

percentiles <- quantile(base_viviendas$preciom, probs = c(0.25, 0.75))
rango_bajo <- percentiles[1]
rango_alto <- percentiles[2]

base_viviendas$categoria_precio <- ifelse(base_viviendas$preciom < rango_bajo, "Bajo",
                               ifelse(base_viviendas$preciom > rango_alto, "Alto", "Medio"))

En este mapa podemos apreciar la distribución en cuestión de longitud y latitud de las viviendas en la ciudad Cali, el color define si esta vivienda se encuentra en la categoria baja, media o alta con base en su precio.

city <- get_map(c(-76.5, 3.43), zoom=12)
## ℹ <https://maps.googleapis.com/maps/api/staticmap?center=3.43,-76.5&zoom=12&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx>
mapa <- ggmap(city)
mapa <- mapa + geom_point(data=base_viviendas, aes(x=longitud, y=latitud, color=categoria_precio), size=base_viviendas$preciom*0.0002)
mapa + scale_color_manual(values=c("Bajo"="red", "Medio"="blue","Alto"="green"))
## Warning: Removed 2630 rows containing missing values (`geom_point()`).

4.1 Distribución por tipo de las viviendas en la ciudad de Cali

mapa <- mapa + geom_point(data=base_viviendas, aes(x=longitud, y=latitud, color=tipo), size=base_viviendas$preciom*0.0002)
mapa + scale_color_manual(values=c("Casa"="red", "Apartamento"="blue")) # Specify colors for each category
## Warning: Removed 2630 rows containing missing values (`geom_point()`).
## Removed 2630 rows containing missing values (`geom_point()`).

Tipo de viviendas mas ofertadas en Cali

## >>> suggestions
## PieChart(tipo, hole=0)  # traditional pie chart
## PieChart(tipo, values="%")  # display %'s on the chart
## PieChart(tipo)  # bar chart
## Plot(tipo)  # bubble plot
## Plot(tipo, values="count")  # lollipop plot 
## 
## --- tipo --- 
## 
##                Apartamento   Casa      Total 
## Frequencies:          5099   3220       8319 
## Proportions:         0.613  0.387      1.000 
## 
## Chi-squared test of null hypothesis of equal probabilities 
##   Chisq = 424.407, df = 1, p-value = 0.000

En este grafico podemos apreciar que las viviendas mas ofertadas por los proyectos inmobiliarios son los apartamentos con 5.099 registros que significan el 61,3% mientras las casas con 3.220 equivalen al 38,7%

Sin embargo es importante apreciar si el valor medio de estos tipos de vivienda tiene alguna diferencia

promedio_por_tipo <- base_viviendas %>%
  group_by(tipo) %>%
  summarize(Promedio = mean(preciom))

pander(promedio_por_tipo)
tipo Promedio
Apartamento 367
Casa 539.9
promedio_casa <- promedio_por_tipo$Promedio[promedio_por_tipo$tipo == "Casa"]
promedio_apartamento <- promedio_por_tipo$Promedio[promedio_por_tipo$tipo == "Apartamento"]
# Calcula el porcentaje de aumento de la casa sobre el apartamento
porcentaje_aumento <- ((promedio_casa - promedio_apartamento) / promedio_apartamento) * 100


print(porcentaje_aumento)
## [1] 47.10503

Podemos apreciar que en promedio la venta de una casa representa un 47% mas a la venta de un apartamento

Zona de las viviendas

library(lessR)
data=base_viviendas
PieChart(zona, hole = 0, values = "%", data = base_viviendas, fill = "blues", main = "",values_size =.8)

## >>> suggestions
## PieChart(zona, hole=0)  # traditional pie chart
## PieChart(zona, values="%")  # display %'s on the chart
## PieChart(zona)  # bar chart
## Plot(zona)  # bubble plot
## Plot(zona, values="count")  # lollipop plot 
## 
## --- zona --- 
## 
##         zona  Count   Prop 
## --------------------------- 
##  Zona Centro    124   0.015 
##   Zona Norte   1920   0.231 
##   Zona Oeste   1198   0.144 
## Zona Oriente    351   0.042 
##     Zona Sur   4726   0.568 
## --------------------------- 
##        Total   8319   1.000 
## 
## Chi-squared test of null hypothesis of equal probabilities 
##   Chisq = 8266.682, df = 4, p-value = 0.000

Como podemos apreciar en el anterior graficos las Zona sur es la ubicación donde se encuentran mas viviendas, seguido por la zona Norte.

Como en el anterior punto evaluaremos el precio promedio de una viviendas en estas zonas

promedio_por_zona <- base_viviendas %>%
  group_by(zona) %>%
  summarize(Promedio_Precio = mean(preciom),
            Promedio_AreaConstruida = mean(areaconst)) %>%
  arrange(desc(Promedio_Precio))

pander(promedio_por_zona)
zona Promedio_Precio Promedio_AreaConstruida
Zona Oeste 677.6 196.4
Zona Sur 426.5 173.3
Zona Norte 345.6 161.1
Zona Centro 309.7 194
Zona Oriente 228.5 192.3

Como podemos apreciar la Zona Oeste es donde se comercializan las viviendas a un precio promedio mas alto, y la zona Oriente a un precio promedio mas bajo, quizas esto puede deberse en que en la Zona Oeste se encuentran las viviendas con un promedio de area construida mas grande.

Estrato de viviendas

library(ggplot2)
library(plotly)


# Crea el gráfico de barras con etiquetas de recuento
ggplot(base_viviendas, aes(x = estrato)) +
  geom_bar(fill = "skyblue", color = "black") +
  geom_text(stat='count', aes(label=..count..), vjust=-0.5) +
  labs(title = "Distribución de estratos",
       x = "Estrato",
       y = "Número de viviendas")

Podemos ver como se distribuyen los estratos de las viviendas de cali

promedio_por_estrato <- base_viviendas %>%
  group_by(estrato) %>%
  summarize(Promedio_Precio = mean(preciom),
            Promedio_AreaConstruida = mean(areaconst)) %>%
  arrange(desc(Promedio_Precio))

pander(promedio_por_estrato)
estrato Promedio_Precio Promedio_AreaConstruida
6 800.3 248.4
5 410.3 173.3
4 275.1 131.2
3 210.3 141.7

Como era de esperar las viviendas de estrato mas alta oscilan los precios mas altos asi mismo tienen un promedio de area contruida mas elevado que los demas estratos, ampliaremos un poco las caracteristicas de estas viviendas.

Numero de parqueaderos por estrato

library(ggplot2)

ggplot(base_viviendas, aes(x = "", y = parquea)) +
  geom_boxplot() +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(median_parquea = median(parquea),
                        q1_parquea = quantile(parquea, 0.25),
                        q3_parquea = quantile(parquea, 0.75)),
            aes(label = paste("Mediana:", median_parquea), x = 1, y = median_parquea), vjust = -1) +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(q1_parquea = quantile(parquea, 0.25)),
            aes(label = paste("Q1:", q1_parquea), x = 1, y = q1_parquea), vjust = 1) +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(q3_parquea = quantile(parquea, 0.75)),
            aes(label = paste("Q3:", q3_parquea), x = 1, y = q3_parquea), vjust = -1) +
  facet_wrap(~ estrato, scales = "free") +
  labs(x = "", y = "Número de parqueaderos", title = "Boxplot de número de parqueaderos por estrato")

Numero de Habitaciones por estrato

Si intentamos apreciar los pratones en las habitaciones, hay evidencia de que podrian existir datos mal capturados en el momento, ya que se encontraron apartamentos y casas que el número de habitaciones es igual a 0, este valor es muy dificil de imputar si no se hace un analisis estadistico más riguroso,

library(ggplot2)

ggplot(base_viviendas, aes(x = "", y = habitac)) +
  geom_boxplot() +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(median_parquea = median(habitac),
                        q1_parquea = quantile(habitac, 0.25),
                        q3_parquea = quantile(habitac, 0.75)),
            aes(label = paste("Mediana:", median_parquea), x = 1, y = median_parquea), vjust = -1) +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(q1_parquea = quantile(habitac, 0.25)),
            aes(label = paste("Q1:", q1_parquea), x = 1, y = q1_parquea), vjust = 1) +
  geom_text(data = base_viviendas %>%
              group_by(estrato) %>%
              summarize(q3_parquea = quantile(habitac, 0.75)),
            aes(label = paste("Q3:", q3_parquea), x = 1, y = q3_parquea), vjust = -1) +
  facet_wrap(~ estrato, scales = "free") +
  labs(x = "", y = "Número de habitaciones", title = "Boxplot de número de habitaciones por estrato")

Barrios de la ciudad de Cali

# Crea el gráfico de barras con etiquetas de recuento
library(ggplot2)
library(dplyr)

# Contar las observaciones por barrio


barrio_counts <- base_viviendas %>%
  filter(!is.na(barrio)) %>%
  group_by(barrio) %>%
  count() %>%
  left_join(base_viviendas %>%
              group_by(barrio) %>%
              summarise(preciom_promedio = mean(preciom, na.rm = TRUE)), by = "barrio") %>%
  arrange(desc(n))



# Filtrar los primeros 10 barrios con mayor número de viviendas
top_10_barrios <-  head(barrio_counts, 20)

# Crear el gráfico de barras
ggplot(top_10_barrios, aes(x = reorder(barrio, -n), y = n)) +
  geom_bar(stat = "identity", fill = "skyblue", color = "black") +
  geom_text(aes(label = n), vjust = -0.5) +
  labs(title = "Top 20 Barrios por Número de Viviendas",
       x = "Barrio",
       y = "Número de Viviendas") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Como la extención del numero de barrios es muy grande alrededor de 436, no es posible graficarlos todos, por ende se opto por graficar los 10 primero barrios con mas viviendas en Cali.

Tipo de viviendas por barrio

# Luego, creamos el gráfico apilado
barrio_tipo<- base_viviendas %>%
  filter(!is.na(barrio)) %>%
  group_by(barrio,tipo) %>%
  count() %>%
  arrange(desc(n))

Barrio_Tipo_filtrado <- barrio_tipo %>%
  filter(barrio %in% top_10_barrios$barrio)
ggplot(Barrio_Tipo_filtrado, aes(x = reorder(barrio, -n), y = n, fill = tipo)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = n), vjust = -0.5, position = position_stack(vjust = 0.5)) +
  labs(title = "Top 20 Barrios por Número de Viviendas y Tipo de Vivienda",
       x = "Barrio",
       y = "Número de Viviendas") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Precio promedio de una vivienda en los barrios mas frecuentes

library(ggplot2)

# Ordenar el dataframe por precio promedio de manera descendente
top_10_barrios <- top_10_barrios[order(-top_10_barrios$preciom_promedio), ]

# Crear el gráfico de barras
ggplot(top_10_barrios, aes(x = reorder(barrio, -preciom_promedio), y = preciom_promedio)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  geom_text(aes(label = sprintf("$%.2f", preciom_promedio)), 
            vjust = -0.5, size = 3, color = "black") +
  labs(x = "Barrio", y = "Precio Promedio", title = "Precio Promedio por Barrio") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

5. Discusión

Se evidencia que la oferta de viviendas en Cali se encuentra distribuida por toda la ciudad, con una amplia disponibilidad tanto de apartamentos como de casas. Se encuentran disponibles viviendas de todas las características en cada uno de los sectores y estratos, lo cual es una gran ventaja, ya que permite segmentar la oferta de vivienda según las necesidades y capacidades de cada cliente que esté buscando un hogar que se ajuste a sus requerimientos, tales como el número de habitaciones, baños, parqueaderos y, por supuesto, el precio.

Esta diversidad de oferta facilita también la creación de modelos que pueden predecir los precios de nuevas viviendas que se incorporen al mercado inmobiliario, teniendo en cuenta sus características específicas.

6. Conclusiones

  1. Con base en la conclusión sobre la distribución de viviendas en la ciudad de Cali, podemos extraer diversos análisis:

1.1 El precio más elevado de las viviendas se localiza mayormente en el sur y oeste de la ciudad de Cali. En todos los sectores, excepto en los mencionados anteriormente, es posible encontrar viviendas con una amplia gama de precios, adecuados para diferentes grupos de poder adquisitivo.

1.2 Se observa que en toda la ciudad de Cali hay tanto apartamentos como casas en todos los sectores. No obstante, en el centro de la ciudad se destacan más las casas. Una correlación interesante es que, mientras las viviendas más costosas en el sur de la ciudad son casas, las viviendas más caras en el oeste son apartamentos. Esto puede influir en la estrategia de búsqueda dependiendo de las preferencias del comprador.

1.3 El precio promedio de las casas es un 47% superior al de los apartamentos, lo que podría ayudar a economizar para ciertos clientes.

1.4 Las zonas donde se capturó la mayoría de viviendas fueron el sur y el oeste. El sur se caracteriza por tener el segundo precio más alto en viviendas y un promedio de área construida más bajo que otras zonas. Por otro lado, el oeste tiene el promedio más alto de viviendas con precio elevado y la mayor área construida, lo que lo hace ideal para familias numerosas.

1.5 En cuanto al estrato, se evidencia que hay viviendas de todos los estratos en el inventario. Sin embargo, varían considerablemente tanto en su valor como en su área construida. Cada estrato puede definir las necesidades de las personas en un momento dado. Por ejemplo, si un cliente necesita un parqueadero debido a que posee un vehículo, basándonos en los datos, sabríamos que la mayoría de viviendas en el estrato 3 no cuentan con parqueaderos. Sin embargo, en el estrato 4, la mayoría sí los tiene. Aunque en el estrato 3 también hay viviendas con parqueaderos, son un poco más difíciles de encontrar. Con un análisis más detallado, podemos orientar al cliente de manera más estratégica según sus necesidades y capacidades.