1 Problema

Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.

Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.

Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:

Características Vivienda 1 Vivienda 2
Tipo Casa Apartamento
Área construida 200 300
Parqueaderos 1 3
Banos 2 3
Habitaciones 4 5
Estrato 4 o 5 5 o 6
Zona Norte Sur
Crédito preaprobado 350 millones 850 millones

2 Objetivos

2.1 Objetivo General:

Realizar un análisis de regresión multiple para determinar los componentes del precio de las viviendas del mercado inmobiliario de Cali.

2.2 Objetivos Específicos:

  • Realizar un análisis exploratorio inicial de los datos de las viviendas (Casas y Apartamentos) del mercado inmobiliario de Cali.
  • Realizar un análisi de regresión multiple que determine el precio en función de las variables de cada tipo de vivienda.
  • Realizar una validación de supuestos de los modelos construídos
  • Realizar una prueba predictiva de los modelos donde se use el 30% de los datos para probar la validez y desempeño del modelo.
  • Realizar las predicciones para estudiar el caso planteado.

3 Análisis Exploratorio de datos

Para el análisis exploratorio de las variables que describen las características de los tipos de vivienda en Cali en función del precio, se realizaron diagramas de dispersión para las variables continuas como el precio y el área construida y para las variables continuas se construyeron diagramas de cajas del precio por cada una de las categorías analizadas como: el estrato, número de habitaciones, número de pisos, número de parqueaderos y número de baños.

3.1 Casas

3.1.1 Área Construida

El diagrama de dispersión entre el área construida y el precio mostró que hay una relación directa entre estas dos variables. El mayor valor del precio fue de 1650 millones de pesos para un área 1500 millones.

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)


# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~Area_Construida, 
              y = ~Precio, 
              type = 'scatter', 
              mode = 'markers') %>%
  layout(
    xaxis = list(title = "Área Construida"),
    yaxis = list(title = "Precio")
  )

p1

Figura 1. Diagrama de dispersión de la relación del área construida vs el precio para las casas ubicadas en el norte de Cali.

3.1.2 Estrato

Para la relación entre el estrato y el precio se encontró que en el estrato 3 se presentan los menores precios para las casas distribuidas en el norte de Cali con una mediana de 235 millones y el estrato 6 se presentan los mayores valores de precios con una mediana de 1250 millones, sin embargo las distribuciones de precios entre los estratos medios parecen sobreponerse con medianas para el estrato 4 de 272 millones y para el estrato 5 con 415 millones.

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~Estrato , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "Estrato"),
    yaxis = list(title = "Precio")
  )

p1

Figura 2. Diagrama de cajas representando la relación del estrato vs el precio para las casas ubicadas en el norte de Cali.

3.1.3 Número de habitaciones

No se observó una tendencia clara entre el precio y el número de habitaciones (Figura 3). Se encontró que el mayor precio se presentó para una casa que tiene solo tres habitaciones con un valor de 1650 millones, sin embargo esta misma categoría presentó la menor mediana de todas (Figura 3).

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~No.Habitaciones , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Habitaciones"),
    yaxis = list(title = "Precio")
  )

p1

Figura 3. Diagrama de cajas de la relación del número de habitaciones vs el precio para las casas ubicadas en el norte de Cali.

3.1.4 Número de pisos

En esta relación se pudo observar que hay una tendencia al aumento de precios con el aumento del número de pisos. Sin embargo, hay muchos datos que son atípicos y el mayor precio del conjunto se presenta para las casas que tiene un piso con un precio de 1650 millones (Figura 4).

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~No.Pisos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Pisos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 4. Diagrama de cajas de la relación del número de pisos vs el precio para las casas ubicadas en el norte de Cali.

3.1.5 Número de parqueaderos

Basados en las medianas de los precios frente al número de parqueaderos, se puede observar una tendencia al aumento de los precios con el aumento del número de parqueaderos (Figura 5).

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)



# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~No.Parqueaderos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Parqueaderos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 5. Diagrama de cajas de la relación del número de parqueaderos vs el precio para las casas ubicadas en el norte de Cali.

3.1.6 Número de baños

En cuanto a la relación entre el número de baños de las casas y el precio de estas, se puede observar una tendencia general del aumento de las medianas del precio con el aumento de número de baños, exceptuando cuando las casas tienen alrededor de 8 baños con una media mucho menor de 470 millones que cuando presenta siete baños con un precio de 785 millones (Figura 6).

library(plotly)
casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)
# Crear gráficos individuales
p1 <- plot_ly(data = casas, x = ~No.Banos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Banos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 6. Diagrama de cajas de la relación del número de Banos vs el precio para las casas ubicadas en el norte de Cali.

3.2 Apartamentos

3.2.1 Área Construida

Se puede observar que los puntos dispersos indicaron una tendencia positiva entre ambas variables, sugiriendo que a medida que el precio de las propiedades aumentó, el área construida también tendió a incrementarse. Sin embargo, se observaron algunas dispersiones notables en las que los precios elevados correspondieron a áreas construidas relativamente pequeñas y viceversa. Estas observaciones sugirieron la presencia de posibles outliers o variaciones en el mercado que podrían estar influenciando esta relación (Figura 8).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)
apartamentos$Area_Construida<-as.numeric(apartamentos$Area_Construida)


# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~Area_Construida, y = ~Precio , type = 'scatter', mode = 'markers') %>%
  layout(
    xaxis = list(title = "Precio"),
    yaxis = list(title = "Área Construida")
  )

p1

Figura 7.Diagrama de dispersión de la relación del área construida vs el precio para las apartamentos ubicadas en el norte de Cali..

3.2.2 Estrato

Se observó que, en general, el precio de las propiedades aumentó con el estrato. Los diagramas de cajas revelaron que la mediana del precio fue progresivamente mayor a medida que el estrato aumentó. Además, la dispersión de los precios dentro de cada estrato también mostró una tendencia creciente, especialmente en el estrato 6, donde se presentaron varios valores atípicos por encima del tercer cuartil, indicando que en este estrato hubo una mayor variabilidad en los precios.

Los datos sugirieron que los apartamentos en estratos más altos no solo tienden a ser más costosas, sino que también exhiben una mayor diversidad en los precios, lo que podría estar relacionado con diferencias en características como la ubicación, el tamaño o las comodidades ofrecidas (Figura 8).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~Estrato , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "Estrato"),
    yaxis = list(title = "Precio")
  )

p1

Figura 8. Diagrama de cajas de la relación del estrato vs el precio para los apartamentos ubicadas en el norte de Cali.

3.2.3 Número de habitaciones

En esta gráfica se observó una tendencia de las medianas del precio a aumentar con el número de habitaciones, sin embargo los apartamentos que aparecen en la categoría de cero habitaciones se sale de esta tendencia (Figura 9). Cada una de estas categorías presentaron una variación de precios amplia pero la categoría de 3 habitaciones es la que mayor variabilidad presentó (Figura 9).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~No.Habitaciones , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Habitaciones"),
    yaxis = list(title = "Precio")
  )

p1

Figura 9 Diagrama de cajas de la relación del número de habitaciones vs el precio para los apartamentos ubicados en el norte de Cali.

3.2.4 Nivel del piso

Esta categoría se refiere al número de piso en el que se encuentra el apartamento. Para esta categoría parece no existir una relación clara entre el precio y el número del piso en el que se ubica el apartamento (Figura 10).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)


# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~No.Pisos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Pisos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 10. Diagrama de cajas de la relación del número de pisos vs el precio para los apartamentos ubicados en el norte de Cali.

3.2.5 Número de parqueaderos

Se observó que el número de parqueaderos asignados al apartamento si influye en el precio de este con una tendencia al aumento (Figura 11).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)


# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~No.Parqueaderos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Parqueaderos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 11. Diagrama de cajas de la relación del número de parqueaderos vs el precio para los apartamentos ubicados en el norte de Cali.

3.2.6 Número de baños

También se observó que hay una relación directa entre el precio de los apartamentos y el número de baños, sin embargo existe una categoría de cero para esta variable lo cuál no tiene mucho sentido práctico (Figura 12).

library(plotly)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)

# Crear gráficos individuales
p1 <- plot_ly(data = apartamentos, x = ~No.Banos , y = ~Precio, 
              type = 'box') %>%
  layout(
    xaxis = list(title = "No. Banos"),
    yaxis = list(title = "Precio")
  )

p1

Figura 12 Diagrama de cajas de la relación del número de Banos vs el precio para las casas ubicadas en el norte de Cali.

4 Modelación

4.1 Casas

4.1.1 Ejecución de modelo

El modelo de regresión lineal múltiple fue ajustado para predecir el precio de las casas utilizando como variables predictoras el área construida, el estrato, el número de habitaciones, el número de parqueaderos y el número de baños.

\(Precio = Area.Construida + Estrato + No.Habitaciones + No.Parqueaderos + No.Baños\)

library(caret)

casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)
casas$No.Parqueaderos<-as.integer(casas$No.Parqueaderos)
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Estrato<-as.factor(casas$Estrato)

modelo <- lm(Precio ~ Area_Construida + Estrato + No.Habitaciones + No.Parqueaderos + No.Banos, data = casas)


# Mostrar el resumen del modelo
summary(modelo)
## 
## Call:
## lm(formula = Precio ~ Area_Construida + Estrato + No.Habitaciones + 
##     No.Parqueaderos + No.Banos, data = casas)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -470.76  -48.39  -13.96   37.49  546.23 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      60.94030   16.67335   3.655 0.000295 ***
## Area_Construida   0.40854    0.03877  10.537  < 2e-16 ***
## Estrato4         33.28387   17.73052   1.877 0.061291 .  
## Estrato5        114.07611   22.12116   5.157 4.14e-07 ***
## Estrato6        634.00436   39.87847  15.898  < 2e-16 ***
## No.Habitaciones  -3.38578    3.77851  -0.896 0.370813    
## No.Parqueaderos  34.03396    6.82782   4.985 9.63e-07 ***
## No.Banos         31.67447    5.34640   5.924 7.28e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 103 on 363 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.7391, Adjusted R-squared:  0.7341 
## F-statistic: 146.9 on 7 and 363 DF,  p-value: < 2.2e-16
print(varImp(modelo))
##                    Overall
## Area_Construida 10.5366236
## Estrato4         1.8772076
## Estrato5         5.1568768
## Estrato6        15.8984125
## No.Habitaciones  0.8960635
## No.Parqueaderos  4.9846013
## No.Banos         5.9244526

4.1.2 Resumen de los resultados.

Coeficientes: El área construida tiene un coeficiente estimado de 0.41 (p < 2e-16), indicando un aumento en el precio con el aumento del área. Los estratos 4, 5 y 6 muestran coeficientes positivos, siendo el estrato 6 el de mayor impacto en el precio (634.00, p < 2e-16).

El estrato 4 es marginalmente significativo (p = 0.061).

El número de parqueaderos (coeficiente 34.03, p < 0.001) y el número de baños (coeficiente 31.67, p < 0.001) son predictores significativos y positivos del precio.

El número de habitaciones no resultó significativo (p = 0.370), indicando que su efecto sobre el precio no es considerable en este modelo.

Bondad de ajuste: El valor de R-cuadrado ajustado es 0.7341, lo que indica que aproximadamente el 73.4% de la variabilidad en el precio puede ser explicada por el modelo. El valor F del modelo es 146.9 (p < 2.2e-16), lo que sugiere que el modelo en su conjunto es altamente significativo.

El modelo demuestra que el área construida, el estrato, el número de parqueaderos y el número de baños son predictores significativos del precio de las casas, mientras que el número de habitaciones no presenta un efecto significativo.

4.1.3 Validación de los supuestos

4.1.3.1 Normalidad

Residuales: Los residuales del modelo presentan una distribución simétrica, con valores mínimos y máximos de -470.76 y 546.23, respectivamente. El primer cuartil (1Q) es -48.39, la mediana es -13.96, y el tercer cuartil (3Q) es 37.49, lo que sugiere una leve asimetría hacia valores residuales positivos.

# Normalidad prueba de Shapiro
coeficientes <- summary(modelo)$coefficients
shapiro.test(modelo$residuals)
## 
##  Shapiro-Wilk normality test
## 
## data:  modelo$residuals
## W = 0.86649, p-value < 2.2e-16

4.1.3.2 Homocedasticidad

Según la prueba de Breusch-Pagan esta indica la presencia de heterocedasticidad .

# Comprobar homocedasticidad (prueba de Breusch-Pagan)
library(lmtest)
bptest(modelo)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo
## BP = 85.212, df = 7, p-value = 1.186e-15

4.1.3.3 Indenpendencia de los residuos

El valor del estadístico Durbin-Watson cercano a 2 indica una autocorrelación positiva leve. Sin embargo, el p-valor bajo sugiere que la autocorrelación de los errores es significativa, lo cual puede indicar que los errores no son independientes.

# Comprobar la independencia de los residuos (prueba de Durbin-Watson)
dwtest(modelo)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 1.7528, p-value = 0.005205
## alternative hypothesis: true autocorrelation is greater than 0

4.1.3.4 Multicolinealidad

Residuals vs Fitted: Este gráfico muestra una ligera tendencia no lineal en los residuales, lo que sugiere que podría haber una relación no capturada por el modelo. También se observan algunos puntos con valores de residuales altos, lo que indica posibles valores atípicos.

Q-Q Plot: Los residuales estandarizados no siguen completamente la línea de normalidad, especialmente en los extremos, lo que indica que los residuales no son perfectamente normales.

Scale-Location: Este gráfico muestra una dispersión creciente de los residuales a medida que aumenta el valor ajustado, lo que sugiere heterocedasticidad (varianza no constante de los errores).

Residuals vs Leverage: Muestra algunos puntos con alta influencia (alto leverage y residuales estandarizados altos), lo que indica que algunos valores tienen un impacto significativo en el modelo.

par(mfrow=c(2,2))
plot(modelo)

4.1.3.5 Análisis de VIF (Varianza Inflación Factor)

Los resultados de la prueba de Multicolinealidad no muestras valores alto que indiquen que haya multicolinealidad entre las variables analizadas.

# 4.4. Comprobar la multicolinealidad (VIF)
library(car)
vif(modelo)
##                     GVIF Df GVIF^(1/(2*Df))
## Area_Construida 1.378636  1        1.174154
## Estrato         1.527540  3        1.073162
## No.Habitaciones 2.044903  1        1.430001
## No.Parqueaderos 1.201060  1        1.095929
## No.Banos        2.105593  1        1.451066

4.1.4 Predicciones

Las predicciones de precios basados en la variables del modelo para una casa de 200 m^2 con 1 parqueaderos, 2 baños y 4 habitaciones en el estrato 4 y en el estrato 5 está entre los 259 y los 340 millones de pesos cuyos costos están acorde con el presupuesto de los clientes.

4.1.4.1 Predicciones para el estrato 4

# 4.4. Comprobar la multicolinealidad (VIF)
predict(modelo,list(Area_Construida  = 200, No.Parqueaderos = 1, No.Banos = 2,
                      No.Habitaciones = 4, Estrato = "4")) # Estrato 4
##        1 
## 259.7711

4.1.4.2 Predicciones para el estrato 5

# 4.4. Comprobar la multicolinealidad (VIF)
predict(modelo,list(Area_Construida  = 200, No.Parqueaderos = 1, No.Banos = 2,
                      No.Habitaciones = 4, Estrato = "5")) # Estrato 5
##        1 
## 340.5634

4.1.5 Partición de los datos

Para explorar una mejora en el desempeño del modelo se proceso un análisis con una partición de un 70% de entrenamiento y un 30% de testeo junto a una validación cruzada de k-fold con un \(k=10\).

Para las Casas, esta aproximación mejoró el desempeño del modelo y pasó de un \(R^{2}=0.7391\) a un \(R^{2}=0.7551\).

library(caret)

casas<-read.csv2("casaEspacial_df.csv", header = TRUE, sep=",")
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Precio<-as.numeric(casas$Precio)
casas$No.Parqueaderos<-as.integer(casas$No.Parqueaderos)
casas$Area_Construida<-as.numeric(casas$Area_Construida)
casas$Estrato<-as.factor(casas$Estrato)

casas <- na.omit(casas)

# 5. Partición de los datos en entrenamiento (70%) y prueba (30%)
set.seed(123) # Para reproducibilidad
train_index <- sample(1:nrow(casas), 0.7 * nrow(casas))
casas_train <- casas[train_index, ]
casas_test <- casas[-train_index, ]




control <- trainControl(method = "cv", number = 10) # 10-fold cross-validation
formula_model <- Precio ~ Area_Construida + Estrato + No.Habitaciones + No.Parqueaderos + No.Banos
modelo_cv <- train(formula_model, data = casas_train, method = "lm", trControl = control)

summary(modelo_cv)
## 
## Call:
## lm(formula = .outcome ~ ., data = dat)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -266.75  -49.33  -13.99   37.45  515.92 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      91.60011   18.04210   5.077 7.48e-07 ***
## Area_Construida   0.59682    0.04996  11.947  < 2e-16 ***
## Estrato4         32.67102   18.97088   1.722 0.086271 .  
## Estrato5         95.94330   22.73538   4.220 3.42e-05 ***
## Estrato6        537.42803   40.24118  13.355  < 2e-16 ***
## No.Habitaciones  -4.00237    3.91920  -1.021 0.308133    
## No.Parqueaderos  16.63093    7.51987   2.212 0.027896 *  
## No.Banos         19.16979    5.71880   3.352 0.000926 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 92.52 on 251 degrees of freedom
## Multiple R-squared:  0.7557, Adjusted R-squared:  0.7489 
## F-statistic: 110.9 on 7 and 251 DF,  p-value: < 2.2e-16
print(varImp(modelo_cv))
## lm variable importance
## 
##                 Overall
## Estrato6        100.000
## Area_Construida  88.580
## Estrato5         25.935
## No.Banos         18.898
## No.Parqueaderos   9.651
## Estrato4          5.683
## No.Habitaciones   0.000
predicciones <- predict(modelo_cv, newdata = casas_test)

# Mostrar las primeras predicciones
head(predicciones)
##        3        6        8       12       15       17 
## 324.5669 192.5583 217.6962 157.1708 404.8900 218.4684
# Comparar las predicciones con los valores reales
comparacion <- data.frame(Real = casas_test$Precio, Predicho = predicciones)
head(comparacion)
##    Real Predicho
## 3   500 324.5669
## 6   180 192.5583
## 8   200 217.6962
## 12   85 157.1708
## 15  320 404.8900
## 17  215 218.4684

4.1.6 Mapeo de las ofertas de las casas

Con la información obtenida se presentó la distribución espacial (Figura 12) de las casas del norte que pueden ser obtenidas por los clientes en el estrato 4 y 5, con precios inferiores a las 350 (Casas E4 y Casas E5) y con las características deseadas (Area_Construida >= 200, No. Baños == 2, No. Parqueaderos==1, No.Habitaciones == 4), se encontraron 11 casas 7 en el estrato 4 y 4 en el estrato 5.

require(sf)
require(leaflet)
require(leaflet.extras)
require(dplyr)

#Carga de capas espaciales
Barrios<-sf::read_sf("Cali_WGS84.gpkg", layer='Barrios')
zonas<-sf::read_sf("Zonas.gpkg",   layer='Zonas_Precio_Total')
crs <- sf::st_crs("+proj=longlat +datum=WGS84 +no_defs")

#Carga de datos iniciales
datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
#Filtración de lso registros de las casas del 
Casas_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Casa")
casasNorte<-subset(Casas_Imputadas, Zona == "Zona Norte")

datos_casas_sin.na<- subset(Casas_Imputadas, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial_Casas <- sf::st_as_sf(datos_casas_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial_Casas <- sf::st_set_crs(Viviendas_Espacial_Casas, crs)
Casas_joined_sf <- sf::st_join(Viviendas_Espacial_Casas,Barrios , join = st_within)
zona_Norte<-dplyr::filter(zonas, Zonas_IDE == "Zona Norte")
casas_NorteEspacial<-sf::st_intersection(zona_Norte, Casas_joined_sf)


E4 <- casas_NorteEspacial %>%
  filter( Estrato == "4", Precio < 350)


E5 <- casas_NorteEspacial %>%
  filter( Estrato == "5", Precio < 350)
  
ofertas4 <- casas_NorteEspacial %>%
  filter(Area_Construida >= 200, Estrato == "4",No.Banos >= 2, No.Parqueaderos>=1, No.Habitaciones >= 4, Precio < 350)


ofertas5 <- casas_NorteEspacial %>%
  filter(Area_Construida >= 200, Estrato == "5", No.Banos >= 2, No.Parqueaderos>=1, No.Habitaciones >= 4, Precio < 350)
  


ZonalabelsMediana <- sprintf(
  "<strong></strong><strong>%s</strong>",
  zonas$Zonas_IDE 
) %>% lapply(htmltools::HTML)

pal1 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(E4$Precio))
pal2 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(E5$Precio))
pal3 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(ofertas4$Precio))
pal4 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(ofertas5$Precio))



preciosLabels1 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  E4$Barrio, E4$Zona, E4$Precio
) %>% lapply(htmltools::HTML)
preciosLabels2 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  E5$Barrio, E5$Zona, E5$Precio
) %>% lapply(htmltools::HTML)
preciosLabels3 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  ofertas4$id, ofertas4$Area_Construida, ofertas4$Precio
) %>% lapply(htmltools::HTML)
preciosLabels4 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  ofertas5$id, ofertas5$Area_Construida, ofertas5$Precio
) %>% lapply(htmltools::HTML)

map <-leaflet::leaflet(height=700, width = 800)%>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels)%>%
  setView(lng = -76.49478, lat = 3.45, zoom = 13)%>%
  
  addPolygons(data = zonas, 
              opacity = 1,
              color = "white",
              label = ZonalabelsMediana,
              group = "Zona",
              stroke = 0.2)%>%
  # addPolygons(data = Barrios, group = "Barrios", color = "grey",stroke = 0.2, opacity = 1)%>%
  addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE)%>%
  addLayersControl(overlayGroups = c( "Zona","Casas E4", "Casas E5", "Ofertas E4", "Ofertas E5"),    options = layersControlOptions(collapsed = FALSE))%>%
  addScaleBar(position = c("topright", "bottomright", "bottomleft", "topleft"),options = scaleBarOptions(metric = TRUE,
                                                                                                         imperial = FALSE, updateWhenIdle = TRUE))%>%
  addMapPane("ames_lines", zIndex = 430) %>%
   
   addCircles(data = E4, 
               color = ~pal1(Precio),  
           radius = ~Precio/10,
           group ="Casas E4",
             label = preciosLabels1,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
  addCircles(data = E5, 
               color = ~pal2(Precio),  
           radius = ~Precio/10,
           group ="Casas E5",
             label = preciosLabels2,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
  
   addCircles(data = ofertas4, 
               color = ~pal3(Precio),  
           radius = ~Precio/10,
           group ="Ofertas E4",
             label = preciosLabels3,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
     addCircles(data = ofertas5, 
               color = ~pal4(Precio),  
           radius = ~Precio/10,
           group ="Ofertas E5",
             label = preciosLabels4,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
  
   addLegend( position = "bottomleft", pal = pal1, values = na.omit(E5$Precio),
            title = 'Precios',
            opacity = 1)%>%
 
  addMiniMap(tiles = "CartoDB.Voyager")

map

Figura 12. Mapa de la distribución de las casas de la zona norte de Cali que están dentro del conjunto de datos de C&A.

4.2 Apartamentos

El modelo de regresión lineal múltiple fue ajustado para predecir el precio de los apartamentos utilizando como variables predictoras el área construida, el estrato, el número de habitaciones, el número de parqueaderos y el número de baños.

\(Precio=Area.Construida+Estrato+No.Habitaciones+No.Parqueaderos+No.Baños\)

library(caret)
apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)
apartamentos$Area_Construida<-as.numeric(apartamentos$Area_Construida)
apartamentos$Estrato<-as.factor(apartamentos$Estrato)
modelo <- lm(Precio ~ Area_Construida + Estrato + No.Habitaciones + No.Parqueaderos + No.Banos, data = apartamentos)


summary(modelo)
## 
## Call:
## lm(formula = Precio ~ Area_Construida + Estrato + No.Habitaciones + 
##     No.Parqueaderos + No.Banos, data = apartamentos)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1335.39   -41.60    -1.78    40.14   829.89 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     -47.29826   12.15017  -3.893 0.000101 ***
## Area_Construida   1.61474    0.05061  31.909  < 2e-16 ***
## Estrato4         25.41023    8.47920   2.997 0.002752 ** 
## Estrato5         47.71849    8.64608   5.519 3.71e-08 ***
## Estrato6        183.60669   10.24594  17.920  < 2e-16 ***
## No.Habitaciones -24.62444    3.62465  -6.794 1.33e-11 ***
## No.Parqueaderos  71.31824    4.30789  16.555  < 2e-16 ***
## No.Banos         43.86262    3.25399  13.480  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 104.6 on 2855 degrees of freedom
## Multiple R-squared:  0.7874, Adjusted R-squared:  0.7869 
## F-statistic:  1511 on 7 and 2855 DF,  p-value: < 2.2e-16
print(varImp(modelo))
##                   Overall
## Area_Construida 31.908669
## Estrato4         2.996772
## Estrato5         5.519091
## Estrato6        17.919941
## No.Habitaciones  6.793599
## No.Parqueaderos 16.555273
## No.Banos        13.479662

4.2.1 Resumen de los resultados

El modelo de regresión lineal múltiple explica una proporción considerable de la variabilidad en el precio de los apartamentos. El área construida, el estrato, el número de parqueaderos y el número de baños tienen efectos positivos y significativos sobre el precio. Por otro lado, el número de habitaciones tiene un efecto negativo inesperado, que podría ser resultado de alguna interacción no modelada o un posible sesgo en los datos.

Coefficients: El coeficiente del intercepto es -47.30 (p < 0.001), lo que indica que cuando todas las variables predictoras son cero, el precio estimado sería negativo, lo cual no es interpretable en un contexto real, pero esto suele suceder en modelos lineales cuando el rango de las variables predictoras no incluye cero. Cada aumento de una unidad en el área construida se asocia con un incremento de 1.61 unidades en el precio, siendo este efecto altamente significativo (p < 2e-16).

Estrato:

  • Estrato 4: El efecto de vivir en un estrato 4 en comparación con el estrato de referencia es un aumento de 25.41 unidades en el precio, con significancia (p = 0.002).
  • Estrato 5: El aumento asociado es de 47.72 unidades, altamente significativo (p < 0.001).
  • Estrato 6: Este estrato muestra el impacto más grande, con un incremento de 183.61 unidades en el precio (p < 2e-16).

Número de Habitaciones: Sorprendentemente, el número de habitaciones tiene un efecto negativo sobre el precio (-24.62 unidades, p < 0.001), lo que podría indicar una correlación negativa o un efecto indirecto no capturado directamente por el modelo.

Número de Parqueaderos: Un incremento en el número de parqueaderos aumenta el precio en 71.32 unidades (p < 2e-16), lo cual es altamente significativo.

Número de Baños: Cada baño adicional aumenta el precio en 43.86 unidades (p < 2e-16), también con alta significancia.

Bondad de ajuste: Residual Standard Error: El error estándar residual es de 104.6, lo que da una idea de la variabilidad de los residuales en torno a los valores ajustados.

Multiple R-squared y Adjusted R-squared: El valor de R-cuadrado ajustado es 0.7869, lo que indica que aproximadamente el 78.69% de la variabilidad en el precio de los apartamentos puede ser explicada por el modelo. El modelo tiene un F-statistic de 1511 (p < 2e-16), indicando que el modelo en su conjunto es altamente significativo.

4.2.2 Validación de los supuestos

4.2.2.1 Normalidad

Residuales: Los residuales muestran un rango amplio, con un valor mínimo de -1335.39 y un valor máximo de 829.89. Los cuartiles indican una distribución relativamente simétrica alrededor de la mediana, que es cercana a cero. Sin embargo, los valores extremos de los residuales sugieren la presencia de algunos valores atípicos en los datos.

coeficientes <- summary(modelo)$coefficients

# Comprobar la normalidad de los residuos
shapiro.test(modelo$residuals)
## 
##  Shapiro-Wilk normality test
## 
## data:  modelo$residuals
## W = 0.79823, p-value < 2.2e-16

4.2.2.2 Homocedasticidad

Según la prueba de Breusch-Pagan esta indica la presencia de heterocedasticidad .

# Comprobar homocedasticidad (prueba de Breusch-Pagan)
library(lmtest)
bptest(modelo)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo
## BP = 1064.5, df = 7, p-value < 2.2e-16

4.2.2.3 Indenpendencia de los residuos

El valor del estadístico Durbin-Watson cercano a 2 indica una autocorrelación positiva leve. Sin embargo, el p-valor bajo sugiere que la autocorrelación de los errores es significativa, lo cual puede indicar que los errores no son independientes.

# 4.3. Comprobar la independencia de los residuos (prueba de Durbin-Watson)
dwtest(modelo)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 1.6632, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

4.2.2.4 Multicolinealidad

Residuals vs Fitted: Este gráfico muestra una ligera tendencia no lineal en los residuales, lo que sugiere que podría haber una relación no capturada por el modelo. También se observan algunos puntos con valores de residuales altos, lo que indica posibles valores atípicos.

Q-Q Plot: Los residuales estandarizados no siguen completamente la línea de normalidad, especialmente en los extremos, lo que indica que los residuales no son perfectamente normales.

Scale-Location: Este gráfico muestra una dispersión creciente de los residuales a medida que aumenta el valor ajustado, lo que sugiere heterocedasticidad (varianza no constante de los errores).

Residuals vs Leverage: Muestra algunos puntos con alta influencia (alto leverage y residuales estandarizados altos), lo que indica que algunos valores tienen un impacto significativo en el modelo.

par(mfrow=c(2,2))
plot(modelo)

4.2.2.5 Análisis de VIF (Varianza Inflación Factor)

Los resultados de la prueba de Multicolinealidad no muestras valores alto que indiquen que haya multicolinealidad entre las variables analizadas.

# multicolinealidad (VIF)
library(car)
vif(modelo)
##                     GVIF Df GVIF^(1/(2*Df))
## Area_Construida 2.303460  1        1.517715
## Estrato         1.833015  3        1.106270
## No.Habitaciones 1.464154  1        1.210022
## No.Parqueaderos 2.048146  1        1.431134
## No.Banos        2.681307  1        1.637470

4.2.3 Predicciones

Las predicciones de precios basados en la variables del modelo para un apartamento de 300 m^2 con 3 parqueaderos, 3 baños y 5 habitaciones en el estrato 5 y en el estrato 6 está entre los 707 y los 843 millones de pesos cuyos costos están acorde con el presupuesto de los clientes.

4.2.3.1 Predicción para el estrato 5

predict(modelo,list(Area_Construida  = 300, No.Parqueaderos = 3, No.Banos = 3,
                      No.Habitaciones = 5, Estrato = "5")) # Estrato 5
##        1 
## 707.2627

4.2.3.2 Predicción para el estrato 6

predict(modelo,list(Area_Construida  = 300, No.Parqueaderos = 3, No.Banos = 3,
                      No.Habitaciones = 5, Estrato = "6")) # Estrato 5
##        1 
## 843.1509

4.2.4 Partición de los datos

Para explorar una mejora en el desempeño del modelo se proceso un análisis con una partición de un 70% de entrenamiento y un 30% de testeo junto a una validación cruzada de k-fold con un k=10.

El modelo de regresión lineal múltiple explica el 78.45% de la variabilidad de la precio de los apartamentos, con un buen ajuste y significancia estadística en la mayoría de las variables. El modelo tiene un buen ajuste global (\(R² = 0.7845\)) y la mejora frente al modelo anterior no es muy grande es solo del 0.0001.

library(caret)

apartamentos<-read.csv2("apartamentosEspacial_df.csv", header = TRUE, sep=",")
apartamentos$Precio<-as.numeric(apartamentos$Precio)
apartamentos$Area_Construida<-as.numeric(apartamentos$Area_Construida)
apartamentos$Estrato<-as.factor(apartamentos$Estrato)

apartamentos <- na.omit(apartamentos)

set.seed(123) # Para reproducibilidad
train_index <- sample(1:nrow(apartamentos), 0.7 * nrow(apartamentos))
apartamentos_train <- apartamentos[train_index, ]
apartamentos_test <- apartamentos[-train_index, ]


control <- trainControl(method = "cv", number = 10) # 10-fold cross-validation
formula_model <- Precio ~ Area_Construida + Estrato + No.Habitaciones + No.Parqueaderos + No.Banos
modelo_cv <- train(formula_model, data = apartamentos_train, method = "lm", trControl = control)

summary(modelo_cv)
## 
## Call:
## lm(formula = .outcome ~ ., data = dat)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1301.71   -42.79    -1.26    40.78   825.72 
## 
## Coefficients:
##                  Estimate Std. Error t value Pr(>|t|)    
## (Intercept)     -53.02034   14.70695  -3.605  0.00032 ***
## Area_Construida   1.57792    0.05948  26.528  < 2e-16 ***
## Estrato4         29.76709   10.26441   2.900  0.00377 ** 
## Estrato5         46.64443   10.45617   4.461 8.61e-06 ***
## Estrato6        187.52155   12.51798  14.980  < 2e-16 ***
## No.Habitaciones -26.04348    4.38380  -5.941 3.34e-09 ***
## No.Parqueaderos  74.51076    5.45484  13.660  < 2e-16 ***
## No.Banos         46.69610    4.02516  11.601  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 106.2 on 1996 degrees of freedom
## Multiple R-squared:  0.7845, Adjusted R-squared:  0.7837 
## F-statistic:  1038 on 7 and 1996 DF,  p-value: < 2.2e-16
print(varImp(modelo_cv))
## lm variable importance
## 
##                 Overall
## Area_Construida 100.000
## Estrato6         51.126
## No.Parqueaderos  45.537
## No.Banos         36.825
## No.Habitaciones  12.870
## Estrato5          6.606
## Estrato4          0.000
predicciones <- predict(modelo_cv, newdata = apartamentos_test)

# Mostrar las primeras predicciones
head(predicciones)
##         3         6        15        19        21        22 
## 108.53944  78.41933  92.62057  81.57516  81.57516  86.30891
# Comparar las predicciones con los valores reales
comparacion <- data.frame(Real = apartamentos_test$Precio, Predicho = predicciones)
head(comparacion)
##    Real  Predicho
## 3   152 108.53944
## 6    78  78.41933
## 15  125  92.62057
## 19  135  81.57516
## 21  130  81.57516
## 22  130  86.30891

4.2.5 Mapa de las ofertas de los apartamentos

Con la información obtenida se presentó la distribución espacial de los apartamentos del sur que pueden ser obtenidas por los clientes. Se presentan los apartamentos en el estrato 5 y 6 con precios inferiores a las 850 y las ofertas en estrato 5 y 6 con los requerimientos de los clientes (Area_Construida >= 300, No. Baños == 3, No. Parqueaderos==3, No.Habitaciones == 5).

Para los apartamentos solo se encontraron dos que pueden ser satisfactorios para los clientes. El primero con el id 7512 una área construida de 300 metros, con 5 baños 6 habitaciones y 3 parqueaderos en estrato 5 y el segundo con el id 7182 con un precio de 730 millones, un área construida de 573 \(m^2\), 3 parqueaderos, 8 baños y 5 habitaciones, también en estrato 5.

require(sf)
require(leaflet)
require(leaflet.extras)
require(dplyr)

#Carga de capas espaciales
Barrios<-sf::read_sf("Cali_WGS84.gpkg", layer='Barrios')
zonas<-sf::read_sf("Zonas.gpkg",   layer='Zonas_Precio_Total')
crs <- sf::st_crs("+proj=longlat +datum=WGS84 +no_defs")

datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
Apartamentos_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Apartamento")
apartamentosSur<-subset(Apartamentos_Imputadas, Zona == "Zona Sur")

datos_apartamentos_sin.na<- subset(Apartamentos_Imputadas, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial_apartamentos <- sf::st_as_sf(datos_apartamentos_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial_apartamentos <- sf::st_set_crs(Viviendas_Espacial_apartamentos, crs)
apartamentos_joined_sf <- sf::st_join(Viviendas_Espacial_apartamentos,Barrios , join = st_within)
zona_Sur<-dplyr::filter(zonas, Zonas_IDE == "Zona Sur")
Apartamentos_SurEspacial<-sf::st_intersection(zona_Sur, apartamentos_joined_sf)



E5 <- Apartamentos_SurEspacial %>%
  filter( Estrato == "5", Precio < 850, Area_Construida >= 300)


E6 <- Apartamentos_SurEspacial %>%
  filter( Estrato == "6", Precio < 850, Area_Construida >= 300)
  
ofertas5 <- Apartamentos_SurEspacial %>%
  filter(Area_Construida >= 300, Estrato == "5", No.Banos >= 3, No.Parqueaderos>=3, No.Habitaciones >= 5, Precio < 850)


ofertas6 <- Apartamentos_SurEspacial %>%
   filter(Area_Construida >= 300, Estrato == "6",No.Banos >= 3, No.Parqueaderos>=3, No.Habitaciones >= 5, Precio < 850)


ZonalabelsMediana <- sprintf(
  "<strong></strong><strong>%s</strong>",
  zonas$Zonas_IDE 
) %>% lapply(htmltools::HTML)

pal1 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(E5$Precio))
pal2 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(E6$Precio))
pal3 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(ofertas5$Precio))
pal4 <- colorNumeric(
  palette = "YlOrRd",
  domain = na.omit(ofertas6$Precio))



preciosLabels1 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  E5$Barrio, E5$Zona, E5$Precio
) %>% lapply(htmltools::HTML)
preciosLabels2 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  E6$Barrio, E6$Zona, E6$Precio
) %>% lapply(htmltools::HTML)
preciosLabels3 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  ofertas5$Barrio, ofertas5$Zona, ofertas5$Precio
) %>% lapply(htmltools::HTML)
preciosLabels4 <- sprintf(
  "<strong></strong><strong>%s</strong><br/><strong> %s</strong><br/>%g millones",
  ofertas6$Barrio, E5$Zona, ofertas6$Precio
) %>% lapply(htmltools::HTML)

map <-leaflet::leaflet(height=700, width = 800)%>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels)%>%
  setView(lng = -76.549478, lat = 3.3903, zoom = 13)%>%
  
  addPolygons(data = zonas, 
              opacity = 1,
              color = "white",
              label = ZonalabelsMediana,
              group = "Zona",
              stroke = 0.2)%>%
  
  addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE)%>%
  addLayersControl(overlayGroups = c( "Zona","Apartamentos E5", "Apartamentos E6", "Ofertas E5", "Ofertas E6"),    options = layersControlOptions(collapsed = FALSE))%>%
  addScaleBar(position = c("topright", "bottomright", "bottomleft", "topleft"),options = scaleBarOptions(metric = TRUE,
                                                                                                         imperial = FALSE, updateWhenIdle = TRUE))%>%
  addMapPane("ames_lines", zIndex = 430) %>%
   
   addCircles(data = E5, 
               color = ~pal1(Precio),  
           radius = ~Precio/10,
           group ="Apartamentos E5",
             label = preciosLabels1,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
  addCircles(data = E6, 
               color = ~pal2(Precio),  
           radius = ~Precio/10,
           group ="Apartamentos E6",
             label = preciosLabels2,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
    addCircles(data = ofertas5, 
               color = ~pal3(Precio),  
           radius = ~Precio/10,
           group ="Ofertas E5",
             label = preciosLabels3,
              labelOptions = labelOptions(
                style = list("font-weight" = "normal",
                             padding = "3px 8px"),
                textsize = "15px",
                direction = "auto"))%>%
     addLegend( position = "bottomleft", pal = pal1, values = na.omit(E5$Precio),
            title = 'Precios',
            opacity = 1)%>%
 
  addMiniMap(tiles = "CartoDB.Voyager")
map

Figura 12. Mapa de la distribución de las casas de la zona norte de Cali que están dentro del conjunto de datos de C&A.

5 Conclusiones

A pesar de la amplia oferta existente en el mercado inmobiliario de Cali, no se ha encontrado disponibilidad de una vivienda que cumpla exactamente con las características específicas que los clientes buscan. Esta situación representa un desafío, ya que los compradores tienen expectativas precisas en cuanto a espacios como parqueaderos, baños, y habitaciones. No obstante, la empresa C&A tiene la capacidad de ofrecer un portafolio más extenso en dichas zonas, brindando opciones que, si bien no cumplen exactamente con todos los requerimientos iniciales de los clientes, presentan ventajas notables.

Por ejemplo, se han identificado propiedades que, aunque no coinciden al 100% con las especificaciones deseadas, ofrecen precios competitivos, inferiores al presupuesto establecido, y características similares que podrían resultar más beneficiosas. Estas alternativas permitirán a los clientes reevaluar sus prioridades y considerar opciones que, en última instancia, podrían superar sus expectativas iniciales.

En cuanto a las casas disponibles, se identificaron varias oportunidades que ofrecen una excelente relación características-precio. Algunas de ellas, además, cuentan con características que incluso mejores que las deseadas. Para los apartamentos, la oferta es más limitada pero hay dos muy buenas opciones que los clientes podrían encontrar como un muy buen negocio ya que están por encima de las exigencias y con un precio inferior.

6 Anexos

6.1 Filtrado y limpieza de datos.

El conjunto inicial de los datos se compone 8322 registros de características de las viviendas encontradas en el mercado inmobiliario de Cali.

Para analizar los datos por los tipos de vivienda requeridos se separaron los conjuntos de datos entre Apartamentos y Casas. El análisis requirió filtrar las casas que solo pertenecen a la Zona Norte y los apartamentos que pertenecen a la Zona Sur.

6.1.1 Casas

Para abordar el problema inicial se aplicó un filtro de registros para las casas que pertenecer a la zona norte según el conjunto de datos (Tabla 1).

Tabla 1. Estadísticas descriptivas de los precios y el área construida por tipo de vivienda en el mercado inmobiliario en Cali.

require(reactable)

datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
Casas_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Casa")
casasNorte<-subset(Casas_Imputadas, Zona == "Zona Norte")

reactable(casasNorte)

Cuando se realizó la revisión de la distribución espacial con el filtro de registros, se observó que la etiqueta de “Zona Norte” no corresponde a las zonas de la Ciudad de Cali, designadas por la Infraestructura de Datos Espaciales del Municipio (Figura 12). Por esta razón se decidió realizar un filtro espacial basados en las capas espaciales de las zonas de Cali de la IDE- Municipio de Cali.

Para el análisis de las casas se filtraron los datos espacialmente y se exportaron para realizar los análisis posteriores.

require(sf)
require(leaflet)
require(leaflet.extras)

#Carga de capas espaciales
Barrios<-sf::read_sf("Cali_WGS84.gpkg", layer='Barrios')
zonas<-sf::read_sf("Zonas.gpkg",   layer='Zonas_Precio_Total')
crs <- sf::st_crs("+proj=longlat +datum=WGS84 +no_defs")

#Carga de datos iniciales
datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
#Filtración de lso registros de las casas del 
Casas_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Casa")
casasNorte<-subset(Casas_Imputadas, Zona == "Zona Norte")


datos_Vivienda_sin.na<- subset(casasNorte, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial <- sf::st_as_sf(datos_Vivienda_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial <- sf::st_set_crs(Viviendas_Espacial, crs)
joined_sf <- sf::st_join(Viviendas_Espacial,Barrios , join = st_within)

datos_casas_sin.na<- subset(Casas_Imputadas, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial_Casas <- sf::st_as_sf(datos_casas_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial_Casas <- sf::st_set_crs(Viviendas_Espacial_Casas, crs)
Casas_joined_sf <- sf::st_join(Viviendas_Espacial_Casas,Barrios , join = st_within)
zona_Norte<-dplyr::filter(zonas, Zonas_IDE == "Zona Norte")
casas_NorteEspacial<-sf::st_intersection(zona_Norte, Casas_joined_sf)





ZonalabelsMediana <- sprintf(
  "<strong></strong><strong>%s</strong>",
  zonas$Zonas_IDE 
) %>% lapply(htmltools::HTML)



map <-leaflet::leaflet(height=700, width = 800)%>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels)%>%
  setView(lng = -76.5225, lat = 3.42, zoom = 11.5)%>%
  
  addPolygons(data = zonas, 
              opacity = 1,
              color = "white",
              label = ZonalabelsMediana,
              group = "Zona",
              stroke = 0.2)%>%
  # addPolygons(data = Barrios, group = "Barrios", color = "grey",stroke = 0.2, opacity = 1)%>%
  addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE)%>%
  addLayersControl(overlayGroups = c( "Zona","Casas Norte Original",  "Filtro Espacial"),    options = layersControlOptions(collapsed = FALSE))%>%
  addScaleBar(position = c("topright", "bottomright", "bottomleft", "topleft"),options = scaleBarOptions(metric = TRUE,
                                                                                                         imperial = FALSE, updateWhenIdle = TRUE))%>%
  addMapPane("ames_lines", zIndex = 430) %>%
  addCircles(data = joined_sf, 
             color = "red",
             group ="Casas Norte Original"
            )%>%
   addCircles(data = casas_NorteEspacial, 
               color = "blue",
             group ="Filtro Espacial"
            )%>%
 
  addMiniMap(tiles = "CartoDB.Voyager")

map

Figura 12. Mapa de la distribución de las casas de la zona norte de Cali que están dentro del conjunto de datos de C&A.

6.1.2 Apartamentos

Para los apartamentos se aplicó un filtro de registros sobre aquellso que pertenecen a la zona sur (Tabla 2).

require(reactable)

datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
Apartamento_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Apartamento")
ApartamentoNorte<-subset(Apartamento_Imputadas, Zona == "Zona Norte")

reactable(ApartamentoNorte)

Similar a lo que ocurre con las casas, la distribución espacial de los apartamentos no corresponde con la etiqueta de los registros para la zona sur (Figura 13). Se decidió dejar el filtro espacial para hacer un análisis que correspondiera a lo que se observa en dicha distribución.

require(sf)
require(leaflet)
require(leaflet.extras)



Barrios<-sf::read_sf("Cali_WGS84.gpkg", layer='Barrios')
zonas<-sf::read_sf("Zonas.gpkg",   layer='Zonas_Precio_Total')
crs <- sf::st_crs("+proj=longlat +datum=WGS84 +no_defs")

datosImputadosTotales<-read.csv2("datosImputadosTotales.csv", header = TRUE, sep=",")
Apartamentos_Imputadas<-subset(datosImputadosTotales, Tipo.Vivienda == "Apartamento")
apartamentosSur<-subset(Apartamentos_Imputadas, Zona == "Zona Sur")

datos_Vivienda_sin.na<- subset(apartamentosSur, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial <- sf::st_as_sf(datos_Vivienda_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial <- sf::st_set_crs(Viviendas_Espacial, crs)
joined_sf <- sf::st_join(Viviendas_Espacial,Barrios , join = st_within)

datos_apartamentos_sin.na<- subset(Apartamentos_Imputadas, !is.na(Longitud) & !is.na(Latitud))
Viviendas_Espacial_apartamentos <- sf::st_as_sf(datos_apartamentos_sin.na, coords = c("Longitud", "Latitud"))
Viviendas_Espacial_apartamentos <- sf::st_set_crs(Viviendas_Espacial_apartamentos, crs)
apartamentos_joined_sf <- sf::st_join(Viviendas_Espacial_apartamentos,Barrios , join = st_within)
zona_Sur<-dplyr::filter(zonas, Zonas_IDE == "Zona Sur")
Apartamentos_SurEspacial<-sf::st_intersection(zona_Sur, apartamentos_joined_sf)





ZonalabelsMediana <- sprintf(
  "<strong></strong><strong>%s</strong>",
  zonas$Zonas_IDE 
) %>% lapply(htmltools::HTML)




map <-leaflet::leaflet(height=700, width = 800)%>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels)%>%
  setView(lng = -76.5225, lat = 3.42, zoom = 11.5)%>%
  addPolygons(data = zonas, 
              opacity = 1,
              color = "white",
              label = ZonalabelsMediana,
              group = "Zona",
              stroke = 0.2)%>%
  addFullscreenControl(position = "topleft", pseudoFullscreen = TRUE)%>%
  addLayersControl(overlayGroups = c( "Zona","Apartamentos Sur Original",  "Filtro Espacial"),    
                   options =   layersControlOptions(collapsed = FALSE))%>%
  addScaleBar(position = c("topright", "bottomright", "bottomleft", "topleft"),
              options = scaleBarOptions(metric = TRUE,
              imperial = FALSE, 
              updateWhenIdle = TRUE))%>%
  addMapPane("ames_lines", zIndex = 430) %>%
  addCircles(data = joined_sf, 
             color = "red",
             group ="Apartamentos Sur Original"
            )%>%
   addCircles(data = Apartamentos_SurEspacial, 
               color = "blue",
             group ="Filtro Espacial"
            )%>%
  addMiniMap(tiles = "CartoDB.Voyager")

map

Figura 7. Distribución de los apartamentos y sus precios en el mercado inmmobiliario en Cali.