1. Introducción

El mercado inmobiliario en Colombia ha presentado un constante crecimiento en los últimos años a razón de múltiples factores sociales, económicos y tecnológicos, en donde se evidencia que la demanda de inmuebles ha crecido en la mayoría de las ciudades capitales del país.

En la ciudad de Cali, Valle del Cauca, se presenta un mercado inmobiliario dinámico con alta variedad de oportunidades que se ajustan a la diversidad turística, gastronómica, cultural y social, y que a su vez se apalanca en un sector industrial con alto potencial de crecimiento. Debido al incremento de la población, a la inversión extranjera, al desarrollo empresarial y al despliegue de nuevos proyectos inmobiliarios, la ciudad logró en el año 2023 un valor aproximado de $18 mil millones de pesos en transacciones inmobiliarias (https://occidente.co/empresario/el-mercado-inmobiliario-de-cali-crecera-en-2024), permitiendo proyectar un 2024 con mayores operaciones y con múltiples retos.

En este informe se analizan datos presentados por la empresa B&C sobre el mercado inmobiliario en Cali, con el fin de presentar un reporte estadístico descriptivo que la empresa podría utilizar como herramienta facilitadora en la toma de decisiones sobre su negocio.


2. Objetivos

En este reporte se presenta un análisis estadístico descriptivo, basado en datos de ventas de viviendas en Cali, desde un enfoque de mercado inmobiliario para que la empresa B&C pueda tomar decisiones y desarrollar estrategias relacionadas a su negocio.


3. Metodología

En esta actividad se utilizó una metodología dividida en dos secciones: Transformación de datos y Análisis descriptivo.

En la transformación de datos se iniciará con la validación de datos faltantes y así poder determinar qué hacer con ellos. Posteriormente se procede con un proceso de imputación de datos sobre las variables que presenten ausencia de información. Al final se realizará una limpieza de datos en las variables categóricas.

En el Análisis Descriptivo se emplearon tablas, gráficos e indicadores para explicar y resumir características y generalidades en cada una de las variables.

Para la realización de este informe se utiliza la herramienta R en la transformación de los datos y en la descripción analítica.

3.1. Transformación de datos

  • Importación de datos

La base de datos viviendas_faltantes, utilizados para este estudio, son importados de la librería paqueteMETODOS. La siguiente es una muestra aleatoria de la base de datos.

muestra <- vivienda_faltantes %>% sample_n(5)
kable(muestra, "html") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
id zona piso estrato preciom areaconst parquea banios habitac tipo barrio longitud latitud
4061 Zona Norte 7 4 300 126 2 4 4 Apartamento versalles -76.52953 3.45926
691 Zona Sur 5 3 120 48 NA 1 2 APARTAMENTO ciudad universitaria -76501.00000 3449.00000
2374 Zona Norte NA 5 320 143 2 2 3 Apartamento versalles -76.52000 3451.00000
3023 Zona Sur 5 5 320 125 2 4 4 Apartamento el caney -76.52340 3.38056
3267 Zona Sur 1 3 130 55 NA 1 3 Apartamento el limonar -76.52525 3.39984

Se importó un dataframe con 13 variables: 3 cualitativas, 9 cuantitativas y 1 como identificador del registro. Las tres variables cualitativas son nominales, y la variable identificador puede ser descartada para el análisis.

Inicialmente el dataframe cuenta con 8330 registros.

  • Validación de datos faltantes

Iniciamos haciendo un conteo de los datos faltantes en todo el dataframe y los agrupamos para cada variable.

faltantes <- colSums(is.na(vivienda_faltantes)) %>%
                 as.data.frame() 
conteo <- kable(faltantes, "html", caption = "Conteo de faltantes") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
conteo
Conteo de faltantes
.
id 3
zona 3
piso 2641
estrato 3
preciom 2
areaconst 3
parquea 1606
banios 3
habitac 3
tipo 3
barrio 3
longitud 3
latitud 3

Lo cual también podemos observar gráficamente para dimensionar las cantidades de manera visual en la siguiente gráfica.

gg_miss_var(vivienda_faltantes) 

A partir de la tabla conteo de faltantes podemos identificar que hay 3 registros que no presentan información, los cuales serán eliminados de la base de datos, y realizamos un nuevo conteo sobre los datos faltantes para cada variable.

viviendas_1 <- subset(vivienda_faltantes, !is.na(id))
viviendas_1 <- viviendas_1 %>%
    mutate_at(vars(tipo,barrio), toupper)
viviendas_1$tipo[viviendas_1$tipo == "APTO"] <- "APARTAMENTO"
faltantes_2 <- colSums(is.na(viviendas_1)) %>%
                 as.data.frame()

Ahora se tiene un dataframe con 8327 registros, en donde únicamente las variables piso y parquea (# parqueadero) presentan datos faltantes. La variable piso presenta 2638 registros faltantes, que corresponde al 32% de los registros, y la variable parquea presenta 1603 registros faltantes, correspondientes al 19% del total de los registros. Dado a que cada una de estas variables presenta un alto porcentaje de datos faltantes no sería adecuado eliminar los registros.

  • Imputación de datos

Para determinar cómo imputar los datos de la variable piso y paquea realizamos un análisis exploratorio de los datos para identificar características particulares que nos ayuden a determinar cúal sería la manera más acertada para realizar el proceso.

  1. Variable Piso

Para la variable piso no es posible imputar por cero. La media y la mediana son similares pero no representan datos atípicos como es el caso del dato máximo presentado en el resumen de la variable.

resumen_piso <- summarytools::descr(viviendas_1$piso)
kable(resumen_piso, "html", caption = "Resumen de Piso") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Resumen de Piso
piso
Mean 3.7723677
Std.Dev 2.6150132
Min 1.0000000
Q1 2.0000000
Median 3.0000000
Q3 5.0000000
Max 12.0000000
MAD 1.4826000
IQR 3.0000000
CV 0.6932021
Skewness 1.2773189
SE.Skewness 0.0324671
Kurtosis 1.0480352
N.Valid 5689.0000000
Pct.Valid 68.3199231

En las siguientes gráficas podemos observar las frecuencias para cada dato de la variable y los datos atípicos

par(mfrow = c(1, 2))

histograma <- hist(viviendas_1$piso, 
  las=1,
  main = "Distribucion de Piso",
  xlab="Piso",
  ylab = "frecuencia",
  col =c("lightblue")
)

box <- boxplot(viviendas_1$piso,  
  names = "Piso", 
  main = "Diagrama de Caja - Piso", 
  col = c("lightblue"), 
  xlab="Piso",
  ylab = "Valores",
  border = "black"
)

Al identificar que los datos atípicos tienen una frecuencia relevante, y que no hay un dato específico que pueda reemplazar los valores nulos, se imputarán los datos a partir de comparaciones de las variables zona, estrato, banios, habitac y tipo.

Este proceso se realizará creando dos dataframe, uno con los registros que tienen datos de piso y otro con registro que no tienen datos de piso. Se utiliza la función left_join para comprar cada uno de estos dataframes en las variables zona, estrato, banios, habitac y tipo, generando un nuevo dataframe con los registros que coincidan en estas variables. Ya que el dataframe de coincidencias es many-to-many, en la comparación se obtendrán varios registros para cada registro comparado, por lo que se deberá realizar otra validación adicional. Para la validación adicional se utiliza la variable preciom, ya que al estudiar la correlación de ésta variable con la variable piso, se presenta una menor correlación negativa que la que hay entre piso y areaconst, que era la otra opción a tener en cuenta para una segunda comparación.

cor.test(viviendas_1$piso, viviendas_1$areaconst)
## 
##  Pearson's product-moment correlation
## 
## data:  viviendas_1$piso and viviendas_1$areaconst
## t = -16.041, df = 5687, p-value < 0.00000000000000022
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.2327879 -0.1830634
## sample estimates:
##        cor 
## -0.2080601
cor.test(viviendas_1$piso, viviendas_1$preciom)
## 
##  Pearson's product-moment correlation
## 
## data:  viviendas_1$piso and viviendas_1$preciom
## t = -1.111, df = 5687, p-value = 0.2666
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.04070185  0.01125977
## sample estimates:
##         cor 
## -0.01473099
viviendas_1_sinpiso <- subset(viviendas_1, is.na(piso))
viviendas_1_conpiso <- subset(viviendas_1, !is.na(piso))
matched_rows <- left_join(viviendas_1_sinpiso, viviendas_1_conpiso, by = c("zona", "estrato", "banios", "habitac", "tipo"))
no_pisos <- viviendas_1_sinpiso
for (i in 1:nrow(no_pisos)) {
  row <- no_pisos[i, ]
  matched <- matched_rows[matched_rows$id.x==row$id,]
  if (nrow(matched)>0) {
    matched$diferencia = 0
    for (j in 1:nrow(matched)) {
      row_match <- matched[j, ]
      diff <- abs(row$preciom-row_match$preciom.y)
      matched[j, ]$diferencia = diff
    }
    final_match <- matched[matched$diferencia==min(matched$diferencia),]
    piso = final_match[1,]$piso.y
    no_pisos[i, ]$piso = piso
  } 
}

En la comparación con la variable preciom, se toma el registro que tenga mayor cercanía en el dato de precio en el registro sin información de piso, y así se obtiene un dato de piso aproximado.

nuevos_pisos <- subset(no_pisos,!is.na(piso))
for (i in 1:nrow(nuevos_pisos)) {
    row <- nuevos_pisos[i, ]
    viviendas_1[viviendas_1$id==row$id,]$piso = row$piso
}
viviendas_2 <- viviendas_1[!is.na(viviendas_1$piso),]

Finalmente quedaron 193 registros sin datos de piso, los cuales corresponden al 2.3% del total de registros. Esta cantidad es factible a eliminar, dejando una nueva base de datos con 8134 registros.

  1. Variable Parqueadero

El proceso de imputación de datos para la variable parquea es idéntico al proceso utilizado en la imputación de datos de la variable piso.

resumen_parquea <- summarytools::descr(viviendas_2$parquea)
kable(resumen_parquea, "html", caption = "Resumen de Piso") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Resumen de Piso
parquea
Mean 1.8322279
Std.Dev 1.1160784
Min 1.0000000
Q1 1.0000000
Median 2.0000000
Q3 2.0000000
Max 10.0000000
MAD 1.4826000
IQR 1.0000000
CV 0.6091373
Skewness 2.3022317
SE.Skewness 0.0300670
Kurtosis 8.1413843
N.Valid 6634.0000000
Pct.Valid 81.5588886

En este caso la media y la mediana tampoco representan una buena aproximación de los datos de la variable, y esto también lo vemos en la distribución y en los datos atípicos.

par(mfrow = c(1, 2))

histograma <- hist(viviendas_2$parquea, 
  las=1,
  main = "Distribución de Parqueadero",
  xlab="Parqueadero",
  ylab = "Frecuencia",
  col =c("lightblue")
)

box <- boxplot(viviendas_1$piso,  
  names = "Piso", 
  main = "Diagrama de Caja - Parqueadero", 
  col = c("lightblue"), 
  xlab="Parqueadero",
  ylab = "Valores",
  border = "black"
)

Aquí se realiza el mismo proceso de crear dos dataframe, uno con los registro que tiene datos de parqueadero y otro con los que no tienen datos de parqueadero, validando coincidencias en las mismas variables: zona, estrato, banios, habitac y tipo.

De igual manera la validación adicional se hace con la variable preciom, que en este caso sí presenta una mayor correlación con la variable parquea, en comparación a la correlación que hay entre parquea y areaconst.

cor.test(viviendas_2$parquea, viviendas_2$areaconst)
## 
##  Pearson's product-moment correlation
## 
## data:  viviendas_2$parquea and viviendas_2$areaconst
## t = 59.601, df = 6632, p-value < 0.00000000000000022
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5746966 0.6060444
## sample estimates:
##       cor 
## 0.5905932
cor.test(viviendas_2$parquea, viviendas_2$preciom)
## 
##  Pearson's product-moment correlation
## 
## data:  viviendas_2$parquea and viviendas_2$preciom
## t = 78.093, df = 6632, p-value < 0.00000000000000022
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.6793813 0.7044612
## sample estimates:
##       cor 
## 0.6921301
viviendas_2_sinparque <- subset(viviendas_2, is.na(parquea))
viviendas_2_conparque <- subset(viviendas_2, !is.na(parquea))
matched_parquea <- left_join(viviendas_2_sinparque, viviendas_2_conparque, by = c("zona", "estrato", "banios", "habitac", "tipo"))
no_parquea <- viviendas_2_sinparque
for (i in 1:nrow(no_parquea)) {
  row <- no_parquea[i, ]
  matched_p <- matched_parquea[matched_parquea$id.x==row$id,]
  if (nrow(matched_p)>0) {
    matched_p$diferencia = 0
    for (j in 1:nrow(matched_p)) {
      row_match <- matched_p[j, ]
      diff <- abs(row$preciom-row_match$preciom.y)
      matched_p[j, ]$diferencia = diff
    }
    final_match <- matched_p[matched_p$diferencia==min(matched_p$diferencia),]
    parquea = final_match[1,]$parquea.y
    no_parquea[i, ]$parquea = parquea
  } 
}

Igualmente se toma el registro que tenga mayor cercanía en el dato de precio en el registro sin información de parqueadero para obtener el dato de parquea.

nuevos_parquea <- subset(no_parquea,!is.na(parquea))
for (i in 1:nrow(nuevos_parquea)) {
    row <- nuevos_parquea[i, ]
    viviendas_2[viviendas_2$id==row$id,]$parquea = row$parquea
}
viviendas_3 <- viviendas_2[!is.na(viviendas_2$parquea),]

De esta forma resultaron 114 registros sin datos de parqueadero, los cuales corresponden al 1.4% del total de registros. Se elimina ese porcentaje de registros sin datos de parqueadero obteniendo una base de datos final con 8020 registros completos, habiendo eliminado el 3.7% del total inicial.

  • Limpieza de datos categóricos

La variable zona está limpia, presentando 5 zonas diferentes: Zona Oeste, Zona Sur, Zona Oriente, Zona Norte y Zona Centro. A esta variable no se le realiza ningún procesamiento. La variable tipo únicamente presenta algunas distinciones con respecto a la categoría APARTAMENTO, por lo que se realiza una limpieza comparativa y se llevan todos los datos a mayúsculas.

La variable barrio si presenta muchas inconsistencias en las categorías presentadas, por lo que en primera instancia se eliminan las posibles espacios al inicio y al final del texto, se llevan todos los registros a mayúsculas y se eliminan los acentos, con el fin de estandarizar la información.

viviendas_3$barrio <- trimws(viviendas_3$barrio)
viviendas_3$barrio <- chartr("áéíóúüñÁÉÍÓÚÜÑ", "aeiouunAEIOUUN", viviendas_3$barrio)

Por medio de un análisis exploratorio manual de datos se determinan los barrios que más se repiten en los datos, así se modifican registros que presenten carácteres extraños o diferencias sencillas en el conjunto de barrios que presentan mayor frecuencias, como por ejemplo una modificación a realizar sería en las categorías VALLE DE LILI a VALLE DEL LILI.

viviendas_3[viviendas_3$barrio=='VALLE DE LILI',]$barrio <- 'VALLE DEL LILI'
viviendas_3[viviendas_3$barrio=='FLORA',]$barrio <- 'LA FLORA'
viviendas_3[viviendas_3$barrio=='EL CANEY',]$barrio <- 'CANEY'
viviendas_3[viviendas_3$barrio=='CANEY ESPECIAL',]$barrio <- 'CANEY'
viviendas_3[viviendas_3$barrio=='CRISTALES',]$barrio <- 'LOS CRISTALES'
viviendas_3[viviendas_3$barrio=='NORMANDIA WEST POINT',]$barrio <- 'NORMANDIA'
viviendas_3[viviendas_3$barrio=='REFUGIO',]$barrio <- 'EL REFUGIO'
viviendas_3[viviendas_3$barrio=='SANTA MONICA NORTE',]$barrio <- 'SANTA MONICA'
viviendas_3[viviendas_3$barrio=='SANTA MONICA ALTA',]$barrio <- 'SANTA MONICA'
viviendas_3[viviendas_3$barrio=='MELéNDEZ',]$barrio <- 'MELENDEZ'
viviendas_3[viviendas_3$barrio=='PAMPA LINDA',]$barrio <- 'PAMPALINDA'
viviendas_3[viviendas_3$barrio=='BELLA SUIZA ALTA',]$barrio <- 'BELLA SUIZA'
viviendas_3[viviendas_3$barrio=='JUANAMB√∫',]$barrio <- 'JUANAMBU'
viviendas_3[viviendas_3$barrio=='EL INGENIO 3',]$barrio <- 'EL INGENIO'
viviendas_3[viviendas_3$barrio=='EL INGENIO II',]$barrio <- 'EL INGENIO'
viviendas_3[viviendas_3$barrio=='EL INGENIO III',]$barrio <- 'EL INGENIO'
viviendas_3[viviendas_3$barrio=='EL INGENIO I',]$barrio <- 'EL INGENIO'

Finalmente se transformaron las variables latitud y longitud para que tengan una estructura del dato válida, generando así un nuevo dataframe de calidad.

viviendas_4 <- viviendas_3
umbral_1 <- -77
for (i in 1:nrow(viviendas_4)) {
    if (viviendas_4[i,'longitud']<umbral_1) {
        viviendas_4[i,'longitud'] <- viviendas_4[i,'longitud']/1000
    } 
}
umbral_2 <- 4
for (i in 1:nrow(viviendas_4)) {
    if (viviendas_4[i,'latitud']>umbral_2) {
        viviendas_4[i,'latitud'] <- viviendas_4[i,'latitud']/1000
    } 
}

muestra_2 <- viviendas_4 %>% sample_n(5)
kable(muestra_2, "html") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
id zona piso estrato preciom areaconst parquea banios habitac tipo barrio longitud latitud
7570 Zona Oeste 1 6 330 84 2 2 2 APARTAMENTO LOS CRISTALES -76.55057 3.44166
1351 Zona Norte 2 4 210 72 1 2 3 APARTAMENTO PRADOS DEL NORTE -76.51537 3.47156
4147 Zona Sur 1 5 350 87 2 4 3 CASA LA HACIENDA -76.53000 3.39300
5983 Zona Sur 2 5 650 500 2 7 7 CASA SANTA ANITA -76.54000 3.40300
3457 Zona Sur 3 5 320 98 2 2 3 APARTAMENTO MULTICENTRO -76.52645 3.43402

3.2. Análisis Descriptivo

Las tres variables categóricas están en escala nominal. La variable zona, tipo y barrio se pueden resumir en las siguientes tablas de frecuencia.

zonas_freq <- viviendas_4 %>%
  group_by(zona) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))
zonas_freq$freq_rel <- zonas_freq$conteo / sum(zonas_freq$conteo)

tipo_freq <- viviendas_4 %>%
  group_by(tipo) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))
tipo_freq$freq_rel <- tipo_freq$conteo / sum(tipo_freq$conteo)

barrio_freq <- viviendas_4 %>%
  group_by(barrio) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))
barrio_freq$freq_rel <- barrio_freq$conteo / sum(barrio_freq$conteo)

kable(zonas_freq, "html", caption = "Frecuencias Zona") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Frecuencias Zona
zona conteo freq_rel
Zona Sur 4652 0.5800499
Zona Norte 1811 0.2258105
Zona Oeste 1159 0.1445137
Zona Oriente 320 0.0399002
Zona Centro 78 0.0097257
kable(tipo_freq, "html", caption = "Frecuencias Tipo") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Frecuencias Tipo
tipo conteo freq_rel
APARTAMENTO 5032 0.6274314
CASA 2988 0.3725686
kable(head(barrio_freq, 15), "html", caption = "Frecuencia de barrios") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Frecuencia de barrios
barrio conteo freq_rel
VALLE DEL LILI 1009 0.1258105
CIUDAD JARDIN 537 0.0669576
PANCE 408 0.0508728
LA FLORA 368 0.0458853
CANEY 301 0.0375312
EL INGENIO 263 0.0327930
SANTA TERESITA 261 0.0325436
LOS CRISTALES 232 0.0289277
LA HACIENDA 166 0.0206983
NORMANDIA 163 0.0203242
EL LIMONAR 133 0.0165835
ACOPI 132 0.0164589
PRADOS DEL NORTE 125 0.0155860
EL REFUGIO 120 0.0149626
AGUACATAL 103 0.0128429

Las frecuencias de esta variables también podemos observarlas gráficamente de la siguiente manera.

PieChart(zona, hole=0 ,values="%", data=viviendas_4, fill="blues", main="Porcentajes Zona", values_size=1)

PieChart(tipo, hole=0 ,values="%", data=viviendas_4, fill="blues", main="Porcentajes Tipo", values_size=1)

barrio_freq_1 <- viviendas_4 %>%
  group_by(barrio) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))
barrio_freq_2 <- barrio_freq_1[barrio_freq_1$conteo>70,]
barrio_freq_3 <- barrio_freq_1[barrio_freq_1$conteo<=70,]
barrio_freq_t <- rbind(barrio_freq_2, c("OTROS", sum(barrio_freq_3$conteo)))
barrio_freq_t$conteo <- as.integer(barrio_freq_t$conteo)


ggplot(barrio_freq_t, aes(x = barrio, y = conteo, fill = barrio)) +
geom_bar(stat = "identity", width = 0.7) +
geom_text(aes(label = conteo), vjust = -0.5, size = 3) + 
labs(title = "Distribucion por Barrios",
x = "Barrio",
y = "Conteo") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
theme(legend.position = "none") 

Claramente se puede observar que el mayor mercado inmobiliario se da en en la Zona Sur, específicamente en los barrios Valle del Lili y Ciudad Jardín, y el tipo vivienda más ofertada son los apartamentos con un 63% de los registros.

Ahora podemos analizar la variable preciom con la variable categórica tipo para comparar a detalle los valores de las viviendas para estas dos categorías.

ggplot(viviendas_4, aes(y=preciom, x=tipo))+
geom_jitter(color="#034A94", size=1, alpha=0.9) +
aes(color=paleta6)+
labs(title = "Tipo de vivienda - Precio de venta",
y= "Precio (millones)",
x= "Tipo")

ggplot(viviendas_4, aes(x = tipo, y = preciom, fill = tipo)) +
geom_boxplot() +
labs(title = "Distribucion deprecio por tipo de vivienda",
x = "Tipo",
y = "Precio (millones)") +
scale_fill_manual(values = c("#f4d35e", "#ee964b")) +
theme_minimal()

max(viviendas_4[viviendas_4$tipo=="CASA",]$preciom)
max(viviendas_4[viviendas_4$tipo=="APARTAMENTO",]$preciom)
min(viviendas_4[viviendas_4$tipo=="APARTAMENTO",]$preciom)
min(viviendas_4[viviendas_4$tipo=="CASA",]$preciom)
mean(viviendas_4[viviendas_4$tipo=="CASA",]$preciom)
mean(viviendas_4[viviendas_4$tipo=="APARTAMENTO",]$preciom)

En este punto resulta interesante observar que aunque el rango de precios para los dos tipos de viviendas son similares, CASA(min:77-max:1999) y APARTAMENTO(min:58-max:1950), la mayoría de las casa se venden en un valor superior a la mayoría de los apartamentos, ya que el promedio del precio de las casas es aproximadamente 539 millones y el promedio del precio de los apartamento es de alrededor de 368 millones.

El mismo análisis podría realizarse para las zonas, e identificar como es el comportamiento de los precios en cada una de ellas.

ggplot(viviendas_4, aes(y=preciom, x=zona))+
geom_jitter(color="#034A94", size=1, alpha=0.9) +
aes(color=paleta6)+
labs(title = "Zona - Precio de venta",
y= "Precio (millones)",
x= "Zona")

ggplot(viviendas_4, aes(x = zona, y = preciom, fill = zona)) +
geom_boxplot() +
labs(title = "Distribucion del precio por zona",
x = "Zona",
y = "Precio (millones)") +
scale_fill_manual(values = c("#f95738","#ee964b", "#f4d35e", "#faf0ca", "#0d3b66")) +
theme_minimal()

max(viviendas_4[viviendas_4$zona=="Zona Oeste",]$preciom)
min(viviendas_4[viviendas_4$zona=="Zona Oeste",]$preciom)
mean(viviendas_4[viviendas_4$zona=="Zona Oeste",]$preciom)
max(viviendas_4[viviendas_4$zona=="Zona Sur",]$preciom)
min(viviendas_4[viviendas_4$zona=="Zona Sur",]$preciom)
mean(viviendas_4[viviendas_4$zona=="Zona Sur",]$preciom)

Un enfoque importante podría ser que la Zona Oeste presenta los más altos precios de las viviendas, con un promedio de precio de 681 millones, bastante superior al promedio del precio en la Zona Sur, que es de 424 millones.

Debido a que la zona abarca múltiples barrios, sería importante observar la distribución de precios en los 5 barrios con más mercado, tratando de abarcar la Zona Sur y la Zona Oeste, que son las zonas de mayor interés. Para este caso se escoge VALLE DEL LILI, CIUDAD JARDIN, LA FLORA, SANTA TERESITA Y LOS CRISTALES.

viviendas_barrio <- subset(viviendas_4, barrio == "VALLE DEL LILI" | barrio == "CIUDAD JARDIN" | barrio == "LA FLORA" | barrio == "SANTA TERESITA" | barrio == "LOS CRISTALES")
ggplot(viviendas_barrio, aes(y=preciom, x=barrio))+
geom_jitter(color="#034A94", size=1, alpha=0.9) +
aes(color=paleta6)+
labs(title = "Barrios - Precio de venta",
y= "Precio (millones)",
x= "Barrio")

ggplot(viviendas_barrio, aes(x = barrio, y = preciom, fill = barrio)) +
geom_boxplot() +
labs(title = "Distribucion del precio por barrio",
x = "Barrio",
y = "Precio (millones)") +
scale_fill_manual(values = c("#f95738","#ee964b", "#f4d35e", "#faf0ca", "#0d3b66")) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
theme_minimal()

Es de resaltar que aunque Valle del Lili presenta un amplio número de ventas de inmuebles, barrios como Ciudad Jardín y Santa Teresita presentan precios de venta mucho más altos.

Tomando la variable preciom como base de comparación, debido a que estamos en un análisis descriptivo de mercado, también sería de ayuda encontrar la relación que existe entre esta y las variables estrato, cantidad de baños, cantidad de habitaciones y cantidad de parqueaderos. Primero revisaremos la distribución de frecuencias de estas variables cuantitativas discretas.

hist_par = ggplot(viviendas_4, aes(x = parquea)) +
geom_histogram(bins = 10, fill = "lightblue", color = "white", alpha = 1) +
labs(title = "Distribucion de Parqueaderos",
  x = "Parqueaderos",
  y = "Frecuencia") +
theme_minimal() +
facet_wrap(~ tipo) 

hist_ban = ggplot(viviendas_4, aes(x = banios)) +
geom_histogram(bins = 11, fill = "lightblue", color = "white", alpha = 1) +
labs(title = "Distribucion de Banios",
  x = "Banios",
  y = "Frecuencia") +
theme_minimal() +
facet_wrap(~ tipo) 

hist_habi = ggplot(viviendas_4, aes(x = habitac)) +
geom_histogram(bins = 11, fill = "lightblue", color = "white", alpha = 1) +
labs(title = "Distribucion de Habitaciones",
  x = "Habitaciones",
  y = "Frecuencia") +
theme_minimal() +
facet_wrap(~ tipo) 

hist_estra = ggplot(viviendas_4, aes(x = estrato)) +
geom_histogram(bins = 7, fill = "lightblue", color = "white", alpha = 1) +
labs(title = "Distribucion de Estrato",
  x = "Estrato",
  y = "Frecuencia") +
theme_minimal() +
facet_wrap(~ tipo) 

ggplotly(hist_par)
ggplotly(hist_ban)
ggplotly(hist_habi)
ggplotly(hist_estra)

Para la cantidad de parqueaderos se observa que al menos debe haber 1 parqueadero y que hay alta preferencia para tener dos parqueaderos. La cantidad de baños de mayor frecuencia está entre 2 a 4. Inmuebles con 3 habitaciones son los más comunes. Y los estratos en los que más se ofrecen inmuebles es en el 4 y 5.

También podemos ver la correlación que hay entre estas variables y la variable preciom referente al precio del inmueble.

viviendas_num <- subset(viviendas_4, select = c("preciom", "areaconst", "estrato", "parquea", "banios", "habitac"))
matriz_cor <- cor(viviendas_num)
kable(matriz_cor, "html", caption = "Tabla de correlaciones de variables") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Tabla de correlaciones de variables
preciom areaconst estrato parquea banios habitac
preciom 1.0000000 0.6901782 0.6226467 0.6881231 0.6979638 0.2739627
areaconst 0.6901782 1.0000000 0.2924573 0.5755525 0.6703331 0.5367047
estrato 0.6226467 0.2924573 1.0000000 0.4436682 0.4616654 -0.0622357
parquea 0.6881231 0.5755525 0.4436682 1.0000000 0.5811723 0.2775658
banios 0.6979638 0.6703331 0.4616654 0.5811723 1.0000000 0.5838974
habitac 0.2739627 0.5367047 -0.0622357 0.2775658 0.5838974 1.0000000

Es de destacar que exista correlación significativa entre el precio y el área construida. De igual forma es importante la correlación entre el precio y el estrato, el número de parqueaderos y el número de baños. También podríamos observar correlaciones entre área construída con número de baños y número de parqueaderos. Se puede observar que la variable cantidad de habitaciones no tiene mucha correlación con el resto de variables.

Enfocándonos en las variables área construida y precio, podríamos ver las dispersiones segmentadas por tipo (Casa y Apartamento) y por Zonas.

ggplot(viviendas_4, aes(x = areaconst, y = preciom)) +
  geom_point(position = position_jitter(width = 0.2), color = "#034A94") +
  facet_wrap(~ tipo) + 
  stat_smooth(method = "loess" , formula =y ~ x) +
  labs(title = "Dispercion precio - area - tipo", x = "Area Construida", y = "Precio (millones)") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  theme_minimal()

En la dispersión de datos área construída y precio para la variable tipo, se observa en general un comportamiento natural de que a mayor área construida mayor precio de venta, y eso se puede ver en el crecimiento casi lineal de la mayoría de los registros. Pero también se ven datos atípicos de viviendas con precios bajos para áreas construidas grandes.

ggplot(viviendas_4, aes(x = areaconst, y = preciom)) +
  geom_point(position = position_jitter(width = 0.2), color = "#034A94") +
  facet_wrap(~ zona) +  
  stat_smooth(method = "loess" , formula =y ~ x) +
  labs(title = "Dispercion de precio - area - zona", x = "Area Construida", y = "Precio (millones)") +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  theme_minimal()

Y en la segmentación por zonas el comportamiento también podría considerarse natural de que a mayor área construida mayor precio de venta. Aquí también resaltan los datos atípicos, principalmente en la Zona Norte y en la Zona Orientes. Esos datos atípicos pueden estudiarse a detalle dentro de un contexto socioeconómico y cultural de la zona y el barrio.

Ahora podemos incluir detalles de la variable piso determinada por el tipo, ya que asumimos que para los Apartamentos la variable indica el piso en el cual se encuentra el apartamento y para la Casa indica la cantidad de pisos que tiene la casa.

piso_freq <- viviendas_4[viviendas_4$tipo=='APARTAMENTO',] %>%
  group_by(piso) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))

ggplot(piso_freq, aes(x = piso, y = conteo, fill = piso)) +
geom_bar(stat = "identity", width = 0.7) +
geom_text(aes(label = conteo), vjust = -0.5, size = 3) + 
labs(title = "Frecuencia por Piso - Apartamentos",
x = "Piso",
y = "Conteo") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
theme(legend.position = "none") 

De las frecuencias de piso en los apartamentos podemos ver que el mayor comercio se da para los apartamentos ubicados entre los pisos 2 a 5.

piso_freq <- viviendas_4[viviendas_4$tipo=='CASA',] %>%
  group_by(piso) %>%
  summarize(conteo = n()) %>%
  arrange(desc(conteo))

ggplot(piso_freq, aes(x = piso, y = conteo, fill = piso)) +
geom_bar(stat = "identity", width = 0.7) +
geom_text(aes(label = conteo), vjust = -0.5, size = 3) + 
labs(title = "Frecuencia por Piso - Casa",
x = "Piso",
y = "Conteo") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
theme(legend.position = "none") 

De las frecuencias de cantidad de pisos en las casas podemos ver que el mayor comercio se da para las casa que tienen 2 y 3 pisos.

Ahora podríamos comparar ciertas variables para tratar de identificar cómo se distribuyen las ventas entre las zonas, los estratos y los tipos de vivienda.

conteo <- table(viviendas_4$tipo,viviendas_4$zona)
barplot(conteo, main="Numero de viviendas por zona y tipo",  
xlab="Zonas",
col=c("#0d3b66","#f95738"),
legend = rownames(conteo))

En la Zona Sur se observa un buen mercado tanto de Casas como de Apartamentos, en la Zona Norte y Zona Oeste predomina el mercado de Apartamentos.

conteo <- table(viviendas_4$estrato,viviendas_4$zona)
barplot(conteo, main="Numero de viviendas por zona y estrato",  
xlab="Zonas",
col=c("#f95738","#ee964b", "#f4d35e", "#0d3b66"),
legend = rownames(conteo))

En la Zona Sur hay un alto comerció en los estratos 4 y 5, y en la Zona Oeste hay más comercio en el estrato 6.

conteo <- table(viviendas_4$tipo,viviendas_4$estrato)
barplot(conteo, main="Numero de viviendas por tipo y estrato",  
xlab="Estrato",
col=c("#0d3b66","#f95738"),
legend = rownames(conteo))

El estrato 3 es el único que presenta mayor número de viviendas en venta de tipo Casa, en el resto de estratos predomina el comercio de Apartamentos.

Para complementar el análisis segmentado de viviendas por zona, estrato y tipo, podemos revisar indicadores de centro de precio y área construída para cada zona, estrato y tipo.

calcular_moda <- function(x) {
  tabla_frec <- table(x)
  moda <- as.numeric(names(tabla_frec)[which.max(tabla_frec)])
  return(moda)
}

resumen <- viviendas_4 %>%
              group_by(zona, tipo, estrato) %>%
              summarize(promedio_precio  = mean(preciom),
                        mediana_precio = median(preciom),
                        moda_precio = calcular_moda(preciom),
                        promedio_area = mean(areaconst),
                        mediana_area = median(areaconst),
                        moda_area = calcular_moda(areaconst)) %>%
              arrange(zona)

#datatable(resumen) 
kable(resumen, "html", caption = "Indicadores de centro agrupados por zona - tipo - estrato") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Indicadores de centro agrupados por zona - tipo - estrato
zona tipo estrato promedio_precio mediana_precio moda_precio promedio_area mediana_area moda_area
Zona Centro APARTAMENTO 3 153.0000 120.0 120 86.95200 84.0 84
Zona Centro APARTAMENTO 4 208.6667 166.0 150 99.83333 108.0 120
Zona Centro CASA 3 309.9394 287.5 350 196.52273 167.0 150
Zona Centro CASA 4 450.0000 450.0 450 228.00000 228.0 228
Zona Norte APARTAMENTO 3 119.1329 119.0 120 60.23565 59.0 60
Zona Norte APARTAMENTO 4 209.7300 190.0 160 82.70717 73.0 60
Zona Norte APARTAMENTO 5 347.8704 320.0 320 112.50425 100.0 100
Zona Norte APARTAMENTO 6 649.1429 592.5 950 176.77616 163.0 227
Zona Norte CASA 3 228.8929 200.0 170 155.37694 120.0 120
Zona Norte CASA 4 408.5342 380.0 330 250.45103 259.0 120
Zona Norte CASA 5 540.3106 480.0 350 322.13333 296.0 300
Zona Norte CASA 6 799.0000 780.0 850 378.71290 304.0 298
Zona Oeste APARTAMENTO 3 149.8000 128.0 98 67.44000 60.0 60
Zona Oeste APARTAMENTO 4 232.4561 170.0 165 85.47281 64.0 61
Zona Oeste APARTAMENTO 5 494.8261 420.5 350 142.31443 117.5 110
Zona Oeste APARTAMENTO 6 782.4965 660.0 1400 193.48471 180.0 125
Zona Oeste CASA 3 487.2857 400.0 299 252.44357 256.5 55
Zona Oeste CASA 4 451.5500 395.0 395 285.10000 268.0 114
Zona Oeste CASA 5 693.2857 650.0 1200 368.70408 300.0 487
Zona Oeste CASA 6 1012.3922 920.0 1200 383.58235 370.0 400
Zona Oriente APARTAMENTO 3 114.8163 113.0 113 80.69265 62.0 60
Zona Oriente APARTAMENTO 4 262.5000 262.5 240 87.00000 87.0 84
Zona Oriente APARTAMENTO 5 105.0000 105.0 105 60.00000 60.0 60
Zona Oriente CASA 3 239.5918 230.0 350 212.82397 179.0 90
Zona Oriente CASA 4 265.0000 265.0 265 162.00000 162.0 162
Zona Sur APARTAMENTO 3 138.6615 127.0 115 66.39169 60.0 60
Zona Sur APARTAMENTO 4 203.2138 188.0 150 75.51382 70.0 60
Zona Sur APARTAMENTO 5 292.2556 280.0 250 101.42870 90.0 90
Zona Sur APARTAMENTO 6 593.6182 580.0 650 149.34848 136.0 130
Zona Sur CASA 3 291.8025 270.0 350 202.28025 196.0 200
Zona Sur CASA 4 387.2961 350.0 450 218.06529 200.0 200
Zona Sur CASA 5 527.6209 470.0 450 261.89950 240.0 300
Zona Sur CASA 6 995.6551 900.0 850 379.69162 330.0 300

También podemos analizar valores máximos y mínimos de precio para zonas y tipos de inmuebles

max_min <- viviendas_4 %>%
              group_by(zona, tipo) %>%
              summarize(min_precio = min(preciom),
                        max_precio  = max(preciom),
                        rango_precio = range(preciom)) %>%
              arrange(zona)

kable(max_min, "html", caption = "Rango de precios en millones para zonas y tipos") %>%
  kable_styling("striped", full_width = FALSE) %>%
  row_spec(0, bold = TRUE)
Rango de precios en millones para zonas y tipos
zona tipo min_precio max_precio rango_precio
Zona Centro APARTAMENTO 100 310 210
Zona Centro CASA 148 780 632
Zona Norte APARTAMENTO 65 1580 1515
Zona Norte CASA 89 1600 1511
Zona Oeste APARTAMENTO 85 1950 1865
Zona Oeste CASA 135 1999 1864
Zona Oriente APARTAMENTO 58 290 232
Zona Oriente CASA 80 700 620
Zona Sur APARTAMENTO 75 1750 1675
Zona Sur CASA 77 1900 1823

Así pudimos ver diferentes indicadores y visualizaciones estadísticas del conjunto de datos sobre el comercio inmobiliario en cali, caracterizando y resaltando comportamientos y valores desde una perspectiva de analitica descriptiva.

4. Resultados y Discusión

Basándonos en el análisis estadístico descriptivo realizado en este informe, en esta sección nos enfocaremos en perspectivas y percepciones alrededor de la exploración, comprensión y visualización de datos de comercio de viviendas para poder inferir conclusiones dentro del mercado inmobiliario en la ciudad de Cali, con el objetivo de brindar enfoques específicos y de utilidad en la toma de decisiones de negocio a la empresa B&C.

Uno de los primeros enfoques que nos ofrece el análisis descriptivo es la identificación de las zonas en las cuales se podría concentrar el mercado de la empresa. Se identifica que hay tres zonas predominantes en el mercado: la Zona Sur, la Zona Norte y la Zona Oeste, abarcando el 95% del mercado. De las tres zonas se podría elegir la Zona Sur y la Zona Oeste porque juntas representan el 72% del mercado y porque ofrecen características de precios favorables dentro de la oferta inmobiliaria. En la distribución de precios se observa que lo precios de las viviendas en la Zona Oeste son mayores que en el resto de las zonas presentando un promedio de 681 millones en precios de inmuebles en general. Por otra parte, lo que caracteriza a la Zona Sur es la gran cantidad de transacciones inmobiliarias, el amplio rango de precios que presenta (que van desde los 75 millones hasta los 1900 millones) y el amplio mercado para los dos tipos de inmueble y para cada estrato.

Ahora podemos hacer un enfoque más detallado con respecto a los barrios dentro de las zonas de preferencia: Zona Sur y Zona Oeste. Para la Zona Sur resaltamos la importancia de barrios como Valle del Lili y Ciudad Jardín; Valle del Lili con un amplio número de ventas de inmuebles con el 12.6% del total de registros evaluados, y Ciudad Jardín con muchas opciones de precios que van desde 180 millones hasta 1900 millones para los dos tipos de inmuebles.

Para la Zona Oeste se resaltan los barrios Santa Teresita y Los Cristales, en donde ambos representan el 6.2% del total de registros, teniendo a Santa Teresita como el barrio con el promedio de precio más alto: 847.2 millones de pesos. Otros dos barrios a tener en cuenta son Pance en Zona Sur y La Flora en Zona Norte.

En el estrato 5 es en donde se presenta mayor comercio con el 33.8% del total, seguido por el estrato 4 con el 25.7% y por el estrato 6 con el 24.2%. El estrato 3 es el único que presenta mayor número de viviendas de tipo Casa, en el resto de los estratos hay mayor comercio de Apartamentos.

En la Zona Sur hay un amplio mercado para todos los estratos estudiados, con mayor actividad en los estratos 4 y 5. En la Zona Oeste hay un mayor mercado en el estrato 6. En la Zona Norte resaltan los estratos 3 y 5 en cuanto a ventas de vivienda.

Con esta información se podrá indicar a detalle para cada zona qué estratos abarcar con mayor esfuerzo y qué tipo de vivienda ofertar. Esto se puede contrastar con la información de Indicadores de centro de precios de vivienda para Zona, Tipo y Estratos mostrados en el análisis descriptivo.

Del total de registros de viviendas el tipo Apartamento presenta el 63% y el tipo Casa presenta el 37%. Los dos tipos de vivienda tienen fuerte influencia en el mercado inmobiliario en Cali, aunque las vivienda de tipo Casa presentan una distribución y un promedio de precio mayor. Los de tipo Apartamento tienen un promedio de precio de 368 millones y los de tipo Casa tienen un promedio de precio de 539 millones.

Los rangos de precios son similares para ambos tipos: Apartamento va desde 58 millones hasta 1950 millones, y Casa va desde 77 millones hasta 1999 millones, por lo que en los dos casos hay mucha variabilidad de precios a ofertar.

En el tipo Casa se observa más variabilidad entre el área construida y el precio de venta, que aunque en general se tiene que a mayor área construida mayor precio, pueden haber casas muy grandes (aprox 1500 m2) con un precio de venta muy bajo, y pueden haber casas pequeñas (aprox 150 m2) con un precio de venta muy alto. En estos casos atípicos hay que revisar a detalle la ubicación geográfica y la condición socioeconómica en la que se encuentre la vivienda.

En la Zona Sur se puede realizar un esfuerzo igual en la venta de viviendas tipo Casa y tipo Apartamento, pero en las zonas Zona Oeste y Zona Norte se recomienda enfocarse en el comercio de viviendas de tipo Apartamento.

Para el comercio de viviendas de tipo Casa se recomienda enfocarse en el estrato 3 y 5, y para el comercio de viviendas de tipo Apartamento se recomienda trabajarlo en los estratos 4, 5 y 6.

En las características de las viviendas se relaciona la cantidad de parqueaderos, cantidad de baños, cantidad de habitaciones, cantidad de pisos para las casas y piso en el que se ubica un apartamento.

Para los parqueaderos se observa que al menos debe haber 1 parqueadero, y que hay alta preferencia por viviendas con dos parqueaderos para tipo Casa y Apartamento.

La mitad de comercio de los apartamentos tienen 2 baños, con el 49.3%, y el 23.8% tienen 3 baños, por lo que el mercado de apartamentos se moverá en esa cantidad de baños. Con respecto a las casas, hay preferencia por viviendas con 3, 4 y 5 baños.

El 67% de los apartamentos tienen 3 habitaciones, representando ampliamente la mayoría del comercio en ese tipo de viviendas. El 33.2% de las casas tienen 4 baños, siendo la mayoría, pero hay un importante porcentaje de casas que tienen 3 y 5 baños.

En cuanto a la variable piso referente a las viviendas de tipo Apartamento, se ve que hay mayor preferencia en el mercado de apartamentos que están entre el segundo y el quinto piso, con 2978 viviendas vendidas con esa característica. En cuanto a la cantidad de pisos para viviendas de tipo Casa, el 47.9% de las casas en venta tienen 2 pisos, indicando alta preferencia por esa característica en las casas.

En general se puede observar la correlación entre el precio de una vivienda y el área construída, y la afectación de la variable área construída con respecto a la cantidad de habitaciones, baños y parqueaderos.

Las decisiones en el comercio de inmuebles basados en tipo de vivienda se pueden basar en los indicadores de centro agrupados por zona, tipo y estrato, para las variables precio y área construída (Tabla de análisis descriptivo). Para un detalle mayor, las decisiones se pueden apalancar en los rangos de precios para zonas y tipos de vivienda.

5. Conclusiones

Con este análisis descriptivo de datos se propuso presentar enfoques y perspectivas del mercado inmobiliario en la ciudad de Cali, para que la empresa B&C pueda generar lineamientos de negocio de cara a la toma de decisiones basadas en datos. En este caso se proporcionó un resumen conciso de las principales características alrededor de los datos analizados dentro de un contexto de mercado inmobiliario, identificando tendencias, patrones y anomalías, utilizando técnicas estadísticas como medidas de tendencia central, dispersión y distribución, tablas de frecuencia y visualizaciones para datos cuantitativos y cualitativos.

Podríamos resaltar la necesidad e importancia de un análisis exploratorio de datos dentro del contexto de negocio trabajado. Con este análisis se conoce el conjunto de datos a trabajar y se identifican los procesos de limpieza y transformación necesarios para obtener una base de datos de calidad que inducirá en la certeza de los resultados.

Es importante contar con conocimientos estadísticos y herramientas computacionales en la realización del análisis. En este caso la transformación y el análisis de datos se realizó con R y RStudio, las cuales son herramientas demasiado robustas en temas de analitica de datos, que permitieron calcular valores, identificar patrones, explorar datos y crear visualizaciones interactivas para interpretar y reportar la información.

Los resultados del análisis fueron interpretaciones tomadas a partir de la exploración y descripción de los datos, junto con una idea de negocio contextualizada en el mercado inmobiliario en Cali, Valle del Cauca.