Informe Ejecutivo Caso C&A

La situación actual del mercado inmobiliario en Cali, afectada por la disminución en las ventas debido a las tensiones políticas y sociales del país, así como el impacto de la industria de la construcción, también será tomada en cuenta. A partir de esta información, se desarrollará una estimación utilizando técnicas de modelación que permitan una mejor toma de decisiones.

El análisis incluirá la identificación de la zona correcta de las propiedades a partir de las coordenadas geográficas, comparando su ubicación con la zona declarada, para verificar la coherencia entre ambas variables. De este modo, se busca ofrecer una respuesta integral y detallada que permita a la compañía realizar una inversión informada y eficiente en el contexto actual del sector inmobiliario de Cali.

Ofertas potenciales vivienda 1:

El modelo ha estimado que el precio de la Vivienda 1 es 312.101 millones de pesos. Este valor está dentro del crédito preaprobado de 350 millones de pesos, lo que indica que esta propiedad es posible y viable financieramente encontrar una vivienda con estas características para la empresa. Entre las principales ofertas se encuentran las siguientes opciones:

Entre las cuales resalta la vivienda con ID 4210 que cuenta con una de las mayores áreas construidas, 4 habitaciones, 3 baños, 3 parqueaderos y se encuentra dentro del rango de precio pre-establecido.

Ofertas potenciales vivienda 2:

El modelo ha estimado que el precio de la Vivienda 1 es 312.101 millones de pesos. Este valor está dentro del crédito preaprobado de 350 millones de pesos, lo que indica que esta propiedad es posible y viable financieramente encontrar una vivienda con estas características para la empresa. Entre las principales ofertas se encuentran las siguientes opciones:

Entre las cuales resalta la vivienda con ID 7182 que cuenta con una de las mayores áreas construidas, 5 habitaciones, 8 baños, 3 parqueaderos y se encuentra dentro del rango de precio pre-establecido. Por otro lado, si se quiere conseguir una vivienda con todas las características, pero por un menor precio sería preferible la vievienda con ID 6868, cuenta con un área construida de 370 métros cuadrados a un precio de 370 millones de pesos.

Conclusiones:

Vivienda 1:

Vivienda 2:

Anexos

Cargamos las librerías necesarias:

Visualizamos los primeros registros y su estructura:

head(vivienda)
## # A tibble: 6 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1147 Zona O… <NA>        3     250        70            1      3            6
## 2  1169 Zona O… <NA>        3     320       120            1      2            3
## 3  1350 Zona O… <NA>        3     350       220            2      2            4
## 4  5992 Zona S… 02          4     400       280            3      5            3
## 5  1212 Zona N… 01          5     260        90            1      2            3
## 6  1724 Zona N… 01          5     240        87            1      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Vivienda 1

Se aplica filtro a las ofertas de casas en la “Zona Norte”:

filtro_norte_casas <- subset(vivienda, zona == "Zona Norte" & tipo == "Casa")

head(filtro_norte_casas, 3)
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1209 Zona N… 02          5     320       150            2      4            6
## 2  1592 Zona N… 02          5     780       380            2      3            3
## 3  4057 Zona N… 02          6     750       445           NA      7            6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Utilizando la librería leaflet construimos un mapa para visualizar la ubicación de las viviendas filtradas mendiante su respectiva longitud y latitud.

leaflet(filtro_norte_casas) %>%
  addTiles() %>%
  addCircleMarkers(~longitud, ~latitud, label = ~as.character(barrio), radius = 3, color = "blue")

Se visualiza una disconformidad significativa entre la ubicación geográfica proporcionada por la longitud y latitud de cada una de las vivientas y la especificada por la columna “Zona” de un gran número de viviendas. Aunque todas las viviendas cuentan con la etiqueta “Zona Norte”, es evidente la presencia de viviendas incluso al otro extremo de la cidad, por lo tanto, esta problema pudo deberse a un mal etiquetado de las propiedades en el que no se tuvo en cuenta las coordenadas geográficas.

base1 <- filtro_norte_casas[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")]

correlaciones <- cor(base1, use = "complete.obs")

plot_ly(
  x = colnames(correlaciones),
  y = rownames(correlaciones),
  z = correlaciones,
  type = "heatmap",
  colorscale = "Viridis"
) %>%
  layout(title = "Mapa de calor de correlaciones",
         xaxis = list(title = ""),
         yaxis = list(title = ""))

Interpretación:

# Crear el modelo de regresión lineal múltiple
modelo_regresion <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = filtro_norte_casas)

summary(modelo_regresion)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones + 
##     parqueaderos, data = filtro_norte_casas)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -784.29  -77.56  -16.03   47.67  978.61 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -238.17090   44.40551  -5.364 1.34e-07 ***
## areaconst       0.67673    0.05281  12.814  < 2e-16 ***
## estrato        80.63495    9.82632   8.206 2.70e-15 ***
## banios         18.89938    7.48800   2.524    0.012 *  
## habitaciones    7.64511    5.65873   1.351    0.177    
## parqueaderos   24.00598    5.86889   4.090 5.14e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 155.1 on 429 degrees of freedom
##   (287 observations deleted due to missingness)
## Multiple R-squared:  0.6041, Adjusted R-squared:  0.5995 
## F-statistic: 130.9 on 5 and 429 DF,  p-value: < 2.2e-16

Interpretación de coeficientes:

A continuación se realiza una validación de los supuestos:

plot(modelo_regresion, which = 1)

Los residuos se encuentran distribuidos de manera bastante uniforme alrededor de la línea horizontal (cero), lo cual es un buen indicio. Esto sugiere que el supuesto de linealidad se cumple razonablemente bien, ya que no se observa un patrón claro en los residuos.

Sin embargo, parece haber algunos puntos alejados, especialmente en los valores ajustados mayores a 800. La razón de esto puede ser por la presencia de algunos outliers o casos donde el modelo no predice bien.

plot(modelo_regresion, which = 2)

Los puntos en el gráfico siguen en su mayoría una línea recta, esto indica que los residuos se aproximan a una distribución normal en el rango intermedio. Es indicio de que el supuesto de normalidad de los residuos se cumple razonablemente bien en la mayoría de los casos.

plot(modelo_regresion, which = 3)

A medida que los valores ajustados aumentan, especialmente a partir de 600, hay un aumento en la dispersión de los residuos, lo que indica un patrón de heteroscedasticidad. Los residuos tienden a dispersarse más a medida que los valores ajustados aumentan.

plot(modelo_regresion, which = 4)

La observación 632 tiene un valor de distancia de Cook mucho más alto que el resto, esto indica que tiene una gran influencia en el ajuste del modelo. Por lo tanto, esta observación podría estar afectando los coeficientes de manera desproporcionada.

Si se confirma que estas observaciones son outliers, se podría eliminar o ajustar el modelo para manejar mejor estos puntos, lo que podría mejorar el ajuste general del modelo.

A continuación se realiza la predicción del modelo según las características de la vivienda 1:

# Definir las características de la Vivienda 1 para realizar la predicción
nueva_vivienda <- data.frame(
  areaconst = 200, 
  estrato = 4,  
  banios = 2,     
  habitaciones = 4,
  parqueaderos = 1 
)

# Realizar la predicción usando el modelo de regresión
prediccion_precio <- predict(modelo_regresion, nueva_vivienda)

# Mostrar el precio estimado para la nueva vivienda
prediccion_precio
##       1 
## 312.101

Con este resultado se pretende ahora encontrar potenciales ofertas que contesten a la solicitud de la vivienda 1. Iniciando con un filtro de las ofertas según los criterios definidos:

# Filtrar ofertas que cumplan con el presupuesto y sean similares a la Vivienda 1
ofertas_potenciales <- filtro_norte_casas %>%
  filter(tipo == "Casa", 
         preciom <= 350,
         areaconst >= 180 & areaconst <= 220,
         habitaciones >= 3 & habitaciones <= 5,
         id != 5412,
         )

# Mostrar las primeras 5 ofertas potenciales
head(ofertas_potenciales, 5)
## # A tibble: 5 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  4511 Zona N… <NA>        4     275       190           NA      2            3
## 2   767 Zona N… 03          3     270       180            4      2            3
## 3   612 Zona N… 01          3     270       196            1      2            4
## 4  4210 Zona N… 01          5     350       200            3      3            4
## 5  4267 Zona N… 01          5     335       202            1      4            5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

El listado nos indica 5 ofertas que se ajustan muy bien a los criterios de búsqueda, resaltando entre ellas la vivienda con ID 4210 que cuenta con una de las mayores áreas construidas, 4 habitaciones, 3 baños, 3 parqueaderos y se encuentra dentro del rango de precio pre-establecido.

# Filtrar las opciones seleccionadas
opciones_seleccionadas <- filtro_norte_casas[filtro_norte_casas$id %in% c(4511, 4267, 767, 612, 4210), ]

# Crear un mapa interactivo
mapa <- leaflet(opciones_seleccionadas) %>%
  addTiles() %>% 
  addMarkers(~longitud, ~latitud, 
             popup = ~paste("ID:", id, "<br>",
                            "Precio:", preciom, "millones<br>",
                            "Área:", areaconst, "m²<br>",
                            "Habitaciones:", habitaciones, "<br>",
                            "Baños:", banios, "<br>",
                            "Parqueaderos:", parqueaderos))

# Mostrar el mapa
mapa

Vivienda 2

Se aplica filtro a las ofertas de casas en la “Zona Norte”:

filtro_sur_apartamentos <- subset(vivienda, zona == "Zona Sur" & tipo == "Apartamento")
head(filtro_sur_apartamentos, 3)
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5098 Zona S… 05          4     290        96            1      2            3
## 2   698 Zona S… 02          3      78        40            1      1            2
## 3  8199 Zona S… <NA>        6     875       194            2      5            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Utilizando la librería leaflet construimos un mapa para visualizar la ubicación de las viviendas filtradas mendiante su respectiva longitud y latitud.

leaflet(
  filtro_sur_apartamentos) %>% 
  addTiles() %>% 
  addCircleMarkers(~longitud, ~latitud, label = ~as.character(barrio), radius = 3, color = "blue")

Se visualiza una disconformidad significativa entre la ubicación geográfica proporcionada por la longitud y latitud de cada una de las vivientas y la especificada por la columna “Zona” de un gran número de viviendas. Aunque todas las viviendas cuentan con la etiqueta “Zona Sur”, es evidente la presencia de viviendas incluso al otro extremo de la ciudad, por lo tanto, esta problema pudo deberse a un mal etiquetado de las propiedades en el que no se tuvo en cuenta las coordenadas geográficas.

base2 <- filtro_sur_apartamentos[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")]
correlaciones <- cor(base1, use = "complete.obs") 
plot_ly(x = colnames(correlaciones), y = rownames(correlaciones), z = correlaciones, type = "heatmap",   colorscale = "Viridis" ) %>% 
  layout(title = "Mapa de calor de correlaciones", xaxis = list(title = ""), yaxis = list(title = ""))

Interpretación:

# Crear el modelo de regresión lineal múltiple
modelo_regresion2 <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = filtro_sur_apartamentos) 
summary(modelo_regresion2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones + 
##     parqueaderos, data = filtro_sur_apartamentos)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1092.02   -42.28    -1.33    40.58   926.56 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -261.62501   15.63220 -16.736  < 2e-16 ***
## areaconst       1.28505    0.05403  23.785  < 2e-16 ***
## estrato        60.89709    3.08408  19.746  < 2e-16 ***
## banios         50.69675    3.39637  14.927  < 2e-16 ***
## habitaciones  -24.83693    3.89229  -6.381 2.11e-10 ***
## parqueaderos   72.91468    3.95797  18.422  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 98.02 on 2375 degrees of freedom
##   (406 observations deleted due to missingness)
## Multiple R-squared:  0.7485, Adjusted R-squared:  0.748 
## F-statistic:  1414 on 5 and 2375 DF,  p-value: < 2.2e-16

Interpretación de coeficientes:

A continuación se realiza una validación de los supuestos:

plot(modelo_regresion2, which = 1)

Los residuos se encuentran distribuidos de manera bastante uniforme alrededor de la línea horizontal (cero), lo cual es un buen indicio. Esto sugiere que el supuesto de linealidad se cumple razonablemente bien, ya que no se observa un patrón claro en los residuos.

parece haber algunos puntos alejados, especialmente en los valores ajustados mayores a 800. La razón de esto puede ser por la presencia de algunos outliers o casos donde el modelo no predice bien. Los puntos etiquetados, como 1531, 2383, y otros, son casos que requieren una revisión más detallada, ya que podrían estar influyendo en el modelo de manera desproporcionada.

plot(modelo_regresion2, which = 2)

Los puntos en el gráfico siguen en su mayoría una línea recta, esto indica que los residuos se aproximan a una distribución normal en el rango intermedio. Es indicio de que el supuesto de normalidad de los residuos se cumple razonablemente bien en la mayoría de los casos.

plot(modelo_regresion2, which = 3)

Los puntos en el gráfico siguen en su mayoría una línea recta, esto indica que los residuos se aproximan a una distribución normal en el rango intermedio. Es indicio de que el supuesto de normalidad de los residuos se cumple razonablemente bien en la mayoría de los casos.

También se observan algunas desviaciones en los extremos, con algunos puntos alejados de la línea teórica, como los etiquetados 2383, 444, y 533. Esto sugiere que en los valores más extremos, los residuos no siguen completamente una distribución normal, lo cual podría estar relacionado con outliers o casos donde el modelo tiene menos precisión.

plot(modelo_regresion2, which = 4)

La observación 2383 tiene un valor de distancia de Cook mucho más alto que el resto, esto indica que tiene una gran influencia en el ajuste del modelo. Además, otras observaciones, como 977 y 2569, también muestran cierta influencia en el modelo, aunque en menor medida que la observación 2383.

Si se confirma que estas observaciones son outliers, se podría eliminar o ajustar el modelo para manejar mejor estos puntos, lo que podría mejorar el ajuste general del modelo.

A continuación se realiza la predicción del modelo según las características de la vivienda 2:

# Definir las características de la Vivienda 2 para realizar la predicción
nueva_vivienda2 <- data.frame(areaconst = 300,    estrato = 6,     banios = 3,        habitaciones = 5,   parqueaderos = 3 )

# Realizar la predicción usando el modelo de regresión
prediccion_precio2 <- predict(modelo_regresion2, nueva_vivienda2)

# Mostrar el precio estimado para la nueva vivienda
prediccion_precio2
##        1 
## 735.9218

Con este resultado se pretende ahora encontrar potenciales ofertas que contesten a la solicitud de la vivienda 2. Iniciando con un filtro de las ofertas según los criterios definidos:

# Filtrar ofertas que cumplan con el presupuesto y sean similares a la Vivienda 2 
ofertas_potenciales2 <- filtro_sur_apartamentos %>%   filter(
tipo == "Apartamento", preciom <= 850, areaconst >= 300, habitaciones >= 5, id != 5412, parqueaderos >= 3)
# Mostrar las primeras 5 ofertas potenciales
head(ofertas_potenciales2, 5)
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  7182 Zona S… <NA>        5     730       573            3      8            5
## 2  6868 Zona S… 03          3     370       300            3      6            5
## 3  7512 Zona S… <NA>        5     670       300            3      5            6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

El listado nos indica 5 ofertas que se ajustan muy bien a los criterios de búsqueda, resaltando entre ellas la vivienda con ID 7182 que cuenta con una de las mayores áreas construidas, 5 habitaciones, 8 baños, 3 parqueaderos y se encuentra dentro del rango de precio pre-establecido. Por otro lado, si se quiere conseguir una vivienda con todas las características, pero por un menor precio sería preferible la vievienda con ID 6868, cuenta con un área construida de 370 métros cuadrados a un precio de 370 millones de pesos.

# Filtrar las opciones seleccionadas
opciones_seleccionadas <- filtro_sur_apartamentos[filtro_sur_apartamentos$id %in% c(7182, 6868, 7512), ]

# Crear un mapa interactivo
mapa <- leaflet(opciones_seleccionadas) %>%
  addTiles() %>% 
  addMarkers(~longitud, ~latitud, 
             popup = ~paste("ID:", id, "<br>",
                            "Precio:", preciom, "millones<br>",
                            "Área:", areaconst, "m²<br>",
                            "Habitaciones:", habitaciones, "<br>",
                            "Baños:", banios, "<br>",
                            "Parqueaderos:", parqueaderos))

mapa