Introducción

María 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:

# Creacion un data frame con los datos de la tabla
tabla_viviendas <- data.frame(
  Características = c("Tipo", "Área Construida", "Parqueaderos", "Baños",
                      "Habitaciones", "Estrato", "Zona", "Crédito Preaprobado"),
  Vivienda1 = c("Casa", "200", "1", "2", "4", "4 o 5", "Norte", "350 millones"),
  Vivienda2 = c("Apartamento", "300", "3", "3", "5", "5 o 6", "Sur", "850 millones"),
  stringsAsFactors = FALSE
)

# Mostrar en consola
print(tabla_viviendas)
      Características    Vivienda1    Vivienda2
1                Tipo         Casa  Apartamento
2     Área Construida          200          300
3        Parqueaderos            1            3
4               Baños            2            3
5        Habitaciones            4            5
6             Estrato        4 o 5        5 o 6
7                Zona        Norte          Sur
8 Crédito Preaprobado 350 millones 850 millones

Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos).

Análisis

El alcance del presente informe fue el análisis de las condiciones de la Vivienda 1 en la cual se requiere localizar para la compra una vivienda del tipo Casa con un área de 200 m2, con 2 baños, 4 habitaciones, estrato 4 y en la zona Norte, para esto se tiene un préstamo preaprobado de 350 millones de pesos.

Se procede a realizar exploración del dataset en donde se evidenció errores en las etiqueas de los datos de geolocalización, como parte del preprosesamiento se procede a reclasificar geograficamente los puntos de acuerdo a las zonas de la ciudad de cali.

Se realiza el modelado mediante regresión lineal múltiple realizando las validaciones del modelo indentificando que la hipótesis de No autocorrelación no se cumple, razón por la cual se realizan modelos transformados comprobando los parámetros de P-valor y R2 ajustado con el fin de validar el mejor modelo.

Como resultado el mejor modelo fue el logaritmico sobre el cual se realizaron predicciones para estratos 4 y 5 según los requerimientos del cliente.

Conclusiones

Con base a los datos ya preprocesados y teniendo en cuenta el modelo final se realiza un filtrado de posibles alternativas para presentar al cliente.

Debido a la generación del mapa interactivo final es posible para el cliente y para el vendedor tomar una rápida decisión ya que visualiza la ubicación, el el estrato, el área construida, el numero de parqueaderos, el número de habitaciones y el barrio.

Es importante mencionar que el ejercicio es completamente escalable a otros tipos de variables que permitan aumentar la predicción del modelo.

Anexos

1. Filtrado por tipo de vivienda y Zona

Realice un filtro a la base de datos e incluya solo las ofertas de: base1: casas, de la zona norte de la ciudad.
Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. Adicional un mapa con los puntos de las bases, discutir si todoslos puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).

1.1 Carga y exploración del Dataset:

Realizamos las respectiva carga de datos para análisis y preprocesamiento.

library(paqueteMODELOS)
data("vivienda")
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>

1.2 Limpieza y preprosesamiento del dataset:

summary(vivienda)
       id           zona               piso              estrato     
 Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
 1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
 Median :4160   Mode  :character   Mode  :character   Median :5.000  
 Mean   :4160                                         Mean   :4.634  
 3rd Qu.:6240                                         3rd Qu.:5.000  
 Max.   :8319                                         Max.   :6.000  
 NA's   :3                                            NA's   :3      
    preciom         areaconst       parqueaderos        banios      
 Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
 1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
 Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
 Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
 3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
 Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
 NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
  habitaciones        tipo              barrio             longitud     
 Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
 1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
 Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
 Mean   : 3.605                                         Mean   :-76.53  
 3rd Qu.: 4.000                                         3rd Qu.:-76.52  
 Max.   :10.000                                         Max.   :-76.46  
 NA's   :3                                              NA's   :3       
    latitud     
 Min.   :3.333  
 1st Qu.:3.381  
 Median :3.416  
 Mean   :3.418  
 3rd Qu.:3.452  
 Max.   :3.498  
 NA's   :3      

Revisión tipos de variables:

Revisamos lo tipos de variables presentes en el dataset.

clase <- sapply(vivienda, class)
clase
          id         zona         piso      estrato      preciom    areaconst 
   "numeric"  "character"  "character"    "numeric"    "numeric"    "numeric" 
parqueaderos       banios habitaciones         tipo       barrio     longitud 
   "numeric"    "numeric"    "numeric"  "character"  "character"    "numeric" 
     latitud 
   "numeric" 

Imputación de variables:

Procedemos a realizar imputación de variables.

library(mice)


# Convertir el atributo "piso" a numérico y reemplazar valores faltantes con 1

vivienda$piso <- as.numeric(vivienda$piso)
vivienda$piso[is.na(vivienda$piso)] <- 1

# Eliminar filas donde "preciom" tenga valores faltantes
vivienda<- vivienda %>% filter(!is.na(preciom))

# Imputar valores faltantes en "parqueaderos" con 1

vivienda$parqueaderos[is.na(vivienda$parqueaderos) | vivienda$parqueaderos == 0] <- 1

# Eliminar filas donde "habitaciones" tenga valores faltantes

vivienda <- vivienda %>% filter(!is.na(habitaciones) & habitaciones != 0)


# Aplicar la imputación con la librería mice
imputed_data <- mice(vivienda, m=1, method='pmm', maxit=5)

 iter imp variable
  1   1
  2   1
  3   1
  4   1
  5   1
vivienda <- complete(imputed_data)

Verificamos que no tengamos valores faltantes:

summary(vivienda)
       id           zona                piso           estrato     
 Min.   :   1   Length:8253        Min.   : 1.000   Min.   :3.000  
 1st Qu.:2081   Class :character   1st Qu.: 1.000   1st Qu.:4.000  
 Median :4165   Mode  :character   Median : 2.000   Median :5.000  
 Mean   :4162                      Mean   : 2.897   Mean   :4.638  
 3rd Qu.:6245                      3rd Qu.: 4.000   3rd Qu.:5.000  
 Max.   :8319                      Max.   :12.000   Max.   :6.000  
    preciom         areaconst       parqueaderos        banios      
 Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
 1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
 Median : 330.0   Median : 122.0   Median : 1.000   Median : 3.000  
 Mean   : 433.4   Mean   : 174.2   Mean   : 1.676   Mean   : 3.124  
 3rd Qu.: 540.0   3rd Qu.: 227.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
 Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
  habitaciones        tipo              barrio             longitud     
 Min.   : 1.000   Length:8253        Length:8253        Min.   :-76.59  
 1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
 Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
 Mean   : 3.634                                         Mean   :-76.53  
 3rd Qu.: 4.000                                         3rd Qu.:-76.52  
 Max.   :10.000                                         Max.   :-76.46  
    latitud     
 Min.   :3.333  
 1st Qu.:3.381  
 Median :3.416  
 Mean   :3.418  
 3rd Qu.:3.452  
 Max.   :3.498  

1.3 Filtrado por tipo de vivienda y Zona:

Filtramos del dataset por tipo de vivienda = Casa y Zona = Zona Norte.

# Filtrar base de datos

library(dplyr)
base1 <- vivienda %>% filter(tipo == "Casa", zona == "Zona Norte")
head(base1, 3)
    id       zona piso estrato preciom areaconst parqueaderos banios
1 1209 Zona Norte    2       5     320       150            2      4
2 1592 Zona Norte    2       5     780       380            2      3
3 4057 Zona Norte    2       6     750       445            1      7
  habitaciones tipo barrio  longitud latitud
1            6 Casa  acopi -76.51341 3.47968
2            3 Casa  acopi -76.51674 3.48721
3            6 Casa  acopi -76.52950 3.38527

1.4 Generación del mapa para Casas de la Zona Norte:

Con el fin de determinar las zonas de la ciudad de Cali, se buscó en la página de la Alcaldía de Cali la distribución geografica de las Zonas, se encontro en el sitio web: https://datos.cali.gov.co/es/dataset/servicio-wms-comunas-de-cali/resource/98aabf95-b972-4677-a215-474a33a78eb5 los zonas correspondientes a las comunas, se descarga el archivo tipo Sh (Shapefile) el cual tiene los poligonos correspondientes a las comunas, identificandose las comunas, en base a las comunas se determinan y definen las zonas.

library(sf)
library(leaflet)
library(dplyr)

# Leer el shapefile y transformar a WGS84
comunas <- st_read("C:/Users/ORAM/Desktop/mc_comunas/mc_comunas.shp") %>% 
  st_transform(crs = 4326)
Reading layer `mc_comunas' from data source 
  `C:\Users\ORAM\Desktop\mc_comunas\mc_comunas.shp' using driver `ESRI Shapefile'
Simple feature collection with 22 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1054753 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
Projected CRS: MAGNA-SIRGAS / Cali urban grid
# Asignar zonas según el valor de la columna 'comuna'
comunas <- comunas %>%
  mutate(Zona = case_when(
    comuna %in% c(2, 4, 5, 6) ~ "Zona Norte",
    comuna %in% c(7, 13, 14, 15, 16, 21) ~ "Zona Oriente",
    comuna %in% c(22, 17) ~ "Zona Sur",
    comuna %in% c(18, 19, 20) ~ "Zona Oeste",
    comuna %in% c(3, 9, 10, 11, 12, 8) ~ "Zona Centro",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(Zona))  # Conserva solo las comunas con zona definida

# Crear una paleta de colores para las zonas
pal <- colorFactor(
  palette = c("blue", "red", "green", "orange", "purple"),
  domain = comunas$Zona
)

# Crear el mapa base con los polígonos de comunas y leyenda
m <- leaflet(comunas) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~pal(Zona),
    fillOpacity = 0.5,
    color = "black",
    weight = 1,
    popup = ~paste("Comuna:", comuna, "<br>Zona:", Zona)
  ) %>%
  addLegend("bottomright", pal = pal, values = ~Zona, title = "Zonas")

# Agregar los puntos de base1

m <- m %>%
  addCircleMarkers(
    data = base1,
    lng = ~longitud, 
    lat = ~latitud,
    color = "black",
    fillColor = ~pal(zona),
    fillOpacity = 1,
    radius = 5,
    popup = ~paste("Zona:", zona)
  )

# Visualizar el mapa
m

1.5 Analisis datos geograficos para Casas de la Zona Norte:

Se observa que algunos los puntos en color rojo (Zona Norte) en el dataset se encuentran mal clasificados ya que se observan puntos etiquetados y no corresponden a la Zona Norte, por lo tanto se procede a realizar una reclasificación geográfica en base a las Zonas reales definidas por los archivos shapefile de la Alcaldía de Cali.

# Leer el shapefile y transformarlo a WGS84
comunas <- st_read("C:/Users/ORAM/Desktop/mc_comunas/mc_comunas.shp") %>% 
  st_transform(crs = 4326)
Reading layer `mc_comunas' from data source 
  `C:\Users\ORAM\Desktop\mc_comunas\mc_comunas.shp' using driver `ESRI Shapefile'
Simple feature collection with 22 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1054753 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
Projected CRS: MAGNA-SIRGAS / Cali urban grid
# Asignar la zona a cada comuna según su valor
comunas <- comunas %>%
  mutate(Zona = case_when(
    comuna %in% c(2, 4, 5, 6) ~ "Zona Norte",
    comuna %in% c(7, 13, 14, 15, 16, 21) ~ "Zona Oriente",
    comuna %in% c(22, 17) ~ "Zona Sur",
    comuna %in% c(18, 19, 20) ~ "Zona Oeste",
    comuna %in% c(3, 9, 10, 11, 12, 8) ~ "Zona Centro",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(Zona))  # Se conservan solo las comunas con zona definida

# Crear una paleta de colores para las zonas
pal <- colorFactor(
  palette = c("blue", "red", "green", "orange", "purple"),
  domain = comunas$Zona
)

# Convertir base1 en objeto espacial (sf)
base1_sf <- st_as_sf(base1, coords = c("longitud", "latitud"), crs = 4326)

# Realizar join espacial con comunas para asignar la zona correcta a cada punto
base2 <- st_join(base1_sf, comunas[, "Zona"], left = TRUE)

# Actualizar (reclasificar) el atributo 'zona' con la zona determinada geográficamente
base2 <- base2 %>%
  mutate(zona = Zona) %>%  # asigna la zona correcta
  select(-Zona)           # elimina la columna redundante

# Extraer coordenadas en columnas para facilitar la visualización en leaflet
base2 <- base2 %>%
  mutate(longitud = st_coordinates(geometry)[,1],
         latitud = st_coordinates(geometry)[,2])

# Crear el mapa base con los polígonos de comunas y leyenda
m <- leaflet(comunas) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~pal(Zona),
    fillOpacity = 0.5,
    color = "black",
    weight = 1,
    popup = ~paste("Comuna:", comuna, "<br>Zona:", Zona)
  ) %>%
  addLegend("bottomright", pal = pal, values = ~Zona, title = "Zonas")

# Agregar los puntos de base2 ya reclasificados, utilizando el color correspondiente a cada zona
m <- m %>%
  addCircleMarkers(
    data = base2,
    lng = ~longitud,
    lat = ~latitud,
    color = "black",
    fillColor = ~pal(zona),
    fillOpacity = 1,
    radius = 5,
    popup = ~paste("Zona:", zona)
  )

# Visualizar el mapa
m

1.6 Filtramos nuevamente por Casa y Zona Norte:

base3 <- base2 %>% filter(tipo == "Casa", zona == "Zona Norte")
library(sf)
library(leaflet)
library(dplyr)

# Leer el shapefile y transformar a WGS84
comunas <- st_read("C:/Users/ORAM/Desktop/mc_comunas/mc_comunas.shp") %>% 
  st_transform(crs = 4326)
Reading layer `mc_comunas' from data source 
  `C:\Users\ORAM\Desktop\mc_comunas\mc_comunas.shp' using driver `ESRI Shapefile'
Simple feature collection with 22 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1054753 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
Projected CRS: MAGNA-SIRGAS / Cali urban grid
# Asignar zonas según el valor de la columna 'comuna'
comunas <- comunas %>%
  mutate(Zona = case_when(
    comuna %in% c(2, 4, 5, 6) ~ "Zona Norte",
    comuna %in% c(7, 13, 14, 15, 16, 21) ~ "Zona Oriente",
    comuna %in% c(22, 17) ~ "Zona Sur",
    comuna %in% c(18, 19, 20) ~ "Zona Oeste",
    comuna %in% c(3, 9, 10, 11, 12, 8) ~ "Zona Centro",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(Zona))  # Conserva solo las comunas con zona definida

# Crear una paleta de colores para las zonas
pal <- colorFactor(
  palette = c("blue", "red", "green", "orange", "purple"),
  domain = comunas$Zona
)

# Crear el mapa base con los polígonos de comunas y leyenda
m <- leaflet(comunas) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~pal(Zona),
    fillOpacity = 0.5,
    color = "black",
    weight = 1,
    popup = ~paste("Comuna:", comuna, "<br>Zona:", Zona)
  ) %>%
  addLegend("bottomright", pal = pal, values = ~Zona, title = "Zonas")

# Agregar los puntos de base3

m <- m %>%
  addCircleMarkers(
    data = base3,
    lng = ~longitud, 
    lat = ~latitud,
    color = "black",
    fillColor = ~pal(zona),
    fillOpacity = 1,
    radius = 5,
    popup = ~paste("Zona:", zona)
  )

# Visualizar el mapa
m

2. Análisis exploratorio correlación variable respuesta

Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

2.1 Matriz de correlaciòn de las variables

Realizamos exploración de las variables

library(ggplot2)
library(plotly)
library(dplyr)
library(corrplot)
head(base3)
Simple feature collection with 6 features and 13 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -76.51797 ymin: 3.46284 xmax: -76.49768 ymax: 3.48721
Geodetic CRS:  WGS 84
    id       zona piso estrato preciom areaconst parqueaderos banios
1 1209 Zona Norte    2       5     320       150            2      4
2 1592 Zona Norte    2       5     780       380            2      3
3  504 Zona Norte    1       3     180       120            1      3
4  604 Zona Norte    1       5     520       455            1      5
5 1003 Zona Norte    1       3     380       300            1      5
6 1840 Zona Norte    1       5     395       165            1      4
  habitaciones tipo barrio                  geometry  longitud latitud
1            6 Casa  acopi POINT (-76.51341 3.47968) -76.51341 3.47968
2            3 Casa  acopi POINT (-76.51674 3.48721) -76.51674 3.48721
3            3 Casa  acopi  POINT (-76.49768 3.4706) -76.49768 3.47060
4            4 Casa  acopi POINT (-76.49966 3.46284) -76.49966 3.46284
5            8 Casa  acopi POINT (-76.50743 3.46566) -76.50743 3.46566
6            4 Casa  acopi POINT (-76.51797 3.47651) -76.51797 3.47651
summary(base3)
       id           zona                piso         estrato     
 Min.   :  88   Length:522         Min.   :1.00   Min.   :3.000  
 1st Qu.: 620   Class :character   1st Qu.:1.00   1st Qu.:3.000  
 Median :1412   Mode  :character   Median :1.00   Median :4.000  
 Mean   :2033                      Mean   :1.55   Mean   :4.192  
 3rd Qu.:3222                      3rd Qu.:2.00   3rd Qu.:5.000  
 Max.   :7226                      Max.   :4.00   Max.   :6.000  
    preciom         areaconst       parqueaderos        banios      
 Min.   : 110.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
 1st Qu.: 240.8   1st Qu.: 135.0   1st Qu.: 1.000   1st Qu.: 2.000  
 Median : 375.0   Median : 233.0   Median : 1.000   Median : 3.000  
 Mean   : 422.2   Mean   : 254.6   Mean   : 1.828   Mean   : 3.525  
 3rd Qu.: 530.0   3rd Qu.: 333.8   3rd Qu.: 2.000   3rd Qu.: 4.000  
 Max.   :1800.0   Max.   :1440.0   Max.   :10.000   Max.   :10.000  
  habitaciones       tipo              barrio                   geometry  
 Min.   : 2.00   Length:522         Length:522         POINT        :522  
 1st Qu.: 3.00   Class :character   Class :character   epsg:4326    :  0  
 Median : 4.00   Mode  :character   Mode  :character   +proj=long...:  0  
 Mean   : 4.63                                                            
 3rd Qu.: 5.00                                                            
 Max.   :10.00                                                            
    longitud         latitud     
 Min.   :-76.55   Min.   :3.453  
 1st Qu.:-76.52   1st Qu.:3.468  
 Median :-76.52   Median :3.476  
 Mean   :-76.51   Mean   :3.476  
 3rd Qu.:-76.50   3rd Qu.:3.484  
 Max.   :-76.48   Max.   :3.496  
col_var <- c("preciom", "areaconst", "estrato", "parqueaderos","banios","habitaciones")
num_vars1 <- base3[, intersect(names(base3), col_var)]

library(sf)

# Elimina la columna de geometría y convierte a data.frame
num_vars <- st_drop_geometry(num_vars1)
# Selecciona solo las columnas deseadas
num_vars <- num_vars[, c( "preciom","areaconst","estrato", "parqueaderos", "banios", "habitaciones")]

cor_matrix <- cor(num_vars, use = "complete.obs")
# Visualizar la matriz de correlación
corrplot(cor_matrix, method = "color", addCoef.col = "black", tl.cex = 0.8)

2.2 Relaciòn entre el Precio y el Area construida

p1 <- ggplot(base3, aes(x = areaconst, y = preciom)) +
  geom_point(alpha = 0.5, color = "blue") +
  labs(title = "Relación Precio y Área Construida",
       x = "Área Construida (m²)", y = "Precio (millones)") +
  theme_minimal()

ggplotly(p1)

Existe una relación positiva debil de 0,74, puede evidenciarse en el gráfico que a mayor área construida mayor es el precio.

2.3 Relaciòn Precio y el Estrato

p2 <- ggplot(base3, aes(x = factor(estrato), y = preciom)) +
  geom_boxplot(fill = "purple") +
  labs(title = "Distribución del Precio por Estrato",
       x = "Estrato", y = "Precio (millones)") +
  theme_minimal()

ggplotly(p2)

Existe una relación positiva debil de 0,61, puede evidenciarse en el gráfico que a mayor es el estrato mayor es el precio.

2.4 Realaciòn entre el Precio y el nùmero de parqueaderos

p3 <- ggplot(base3, aes(x = factor(parqueaderos), y = preciom)) +
  geom_boxplot(fill = "lightgreen") +
  labs(title = "Relación Precio por Número de Parqueaderos",
       x = "Número de Parqueaderos", y = "Precio (millones)") +
  theme_minimal()

ggplotly(p3)

Existe una relación positiva debil de 0,43, puede evidenciarse en el gráfico que a mayor es el número de parqueaderos mayor es el precio.

2.5 Relaciòn Precio y el Número de baños

p4 <- ggplot(base3, aes(x = factor(banios), y = preciom)) +
  geom_boxplot(fill = "lightcoral") +
  labs(title = "Relación Precio por Número de Baños",
       x = "Número de Baños", y = "Precio (millones)") +
  theme_minimal()

ggplotly(p4)

Existe una relación positiva debil de 0,55, puede evidenciarse en el gráfico que a mayor es el número de baños mayor es el precio.

2.6 Relaciòn Precio y el Número de habitaciones

p5 <- ggplot(base3, aes(x = factor(habitaciones), y = preciom)) +
  geom_boxplot(fill = "orange") +
  labs(title = "Relación Precio por Número de Habitaciones",
       x = "Número de Habitaciones", y = "Precio (millones)") +
  theme_minimal()

ggplotly(p5)

Existe una relación positiva debil de 0,42, puede evidenciarse en el gráfico que a mayor es el número de habitaciones mayor es el precio.

3. Análisis exploratorio correlación variable respuesta

Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

Con el fin de evaluar el impacto de las variables en el modelo se aplicará la técnica de eliminación hacia atrás en donde evaluaremos el P-valor y el R2 de los modelos.

Modelo 1: Implementación con todas las variables

modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = base3)

# Resumen del modelo
summary(modelo1)

Call:
lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
    banios, data = base3)

Residuals:
    Min      1Q  Median      3Q     Max 
-888.17  -63.76  -16.13   34.36 1107.19 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -204.71275   34.86450  -5.872 7.73e-09 ***
areaconst       0.74557    0.05323  14.008  < 2e-16 ***
estrato        73.67848    8.48534   8.683  < 2e-16 ***
habitaciones    8.99057    5.18120   1.735  0.08330 .  
parqueaderos   18.60321    5.46768   3.402  0.00072 ***
banios         14.92826    6.49415   2.299  0.02192 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 146.8 on 516 degrees of freedom
Multiple R-squared:  0.6528,    Adjusted R-squared:  0.6495 
F-statistic: 194.1 on 5 and 516 DF,  p-value: < 2.2e-16

Observamos que el P-Valor de la variable habitaciones supera el 5%, luego procedemos a eliminarla ya que no es estadisticamente significativa, calculamos el siguiente modelo:

Modelo 2: Implementación eliminando la variable habitaciones

modelo2 <- lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = base3)

# Resumen del modelo
summary(modelo2)

Call:
lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios, 
    data = base3)

Residuals:
    Min      1Q  Median      3Q     Max 
-883.88  -64.45  -16.97   36.74 1123.30 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -173.61668   29.96450  -5.794 1.19e-08 ***
areaconst       0.77615    0.05032  15.423  < 2e-16 ***
estrato        69.35294    8.12668   8.534  < 2e-16 ***
parqueaderos   18.81243    5.47697   3.435 0.000641 ***
banios         20.74336    5.57350   3.722 0.000220 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 147.1 on 517 degrees of freedom
Multiple R-squared:  0.6508,    Adjusted R-squared:  0.6481 
F-statistic: 240.9 on 4 and 517 DF,  p-value: < 2.2e-16

Observamos que el P-valor para areaconst, estrato, parqueaderos, banios son significativas, el R2 ajustado paso de 64,95% a 64,81% lo cual indica que eliminar la variable habitaciones redujo una cantidad mínima la capacidad explicativa del modelo.

a continuación evaluaremos el R2 ajustado para modeos transformados:

Modelo 3: Transformación logaritmica

modelo_log <- lm(log(preciom) ~ log(areaconst) + estrato + parqueaderos + banios, data = base3)
summary(modelo_log)

Call:
lm(formula = log(preciom) ~ log(areaconst) + estrato + parqueaderos + 
    banios, data = base3)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.72796 -0.16071 -0.01485  0.14577  1.11496 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    2.274141   0.105591  21.537  < 2e-16 ***
log(areaconst) 0.496238   0.025709  19.302  < 2e-16 ***
estrato        0.167807   0.015038  11.159  < 2e-16 ***
parqueaderos   0.033123   0.009553   3.467 0.000569 ***
banios         0.055483   0.009920   5.593 3.62e-08 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.2562 on 517 degrees of freedom
Multiple R-squared:  0.7918,    Adjusted R-squared:  0.7902 
F-statistic: 491.7 on 4 and 517 DF,  p-value: < 2.2e-16

Modelo 4: Transformación cuadratica

modelo_quad <- lm(preciom ~ areaconst + I(areaconst^2) + estrato + parqueaderos + banios, data = base3)
summary(modelo_quad)

Call:
lm(formula = preciom ~ areaconst + I(areaconst^2) + estrato + 
    parqueaderos + banios, data = base3)

Residuals:
    Min      1Q  Median      3Q     Max 
-388.49  -69.40  -13.09   41.41 1076.67 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -1.715e+02  2.903e+01  -5.907 6.34e-09 ***
areaconst       1.402e+00  1.164e-01  12.040  < 2e-16 ***
I(areaconst^2) -6.340e-04  1.071e-04  -5.918 5.95e-09 ***
estrato         5.151e+01  8.429e+00   6.111 1.95e-09 ***
parqueaderos    1.656e+01  5.319e+00   3.113  0.00195 ** 
banios          1.371e+01  5.528e+00   2.480  0.01346 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 142.5 on 516 degrees of freedom
Multiple R-squared:  0.673, Adjusted R-squared:  0.6698 
F-statistic: 212.4 on 5 and 516 DF,  p-value: < 2.2e-16

Modelo 5: Transformación raiz-cuadrada

modelo_sqrt <- lm(sqrt(preciom) ~ sqrt(areaconst) + estrato + parqueaderos + banios, data = base3)
summary(modelo_sqrt)

Call:
lm(formula = sqrt(preciom) ~ sqrt(areaconst) + estrato + parqueaderos + 
    banios, data = base3)

Residuals:
     Min       1Q   Median       3Q      Max 
-13.2407  -1.4939  -0.3028   1.1703  16.9603 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)      0.69646    0.56780   1.227  0.22054    
sqrt(areaconst)  0.64789    0.03602  17.986  < 2e-16 ***
estrato          1.61346    0.16112  10.014  < 2e-16 ***
parqueaderos     0.38108    0.10494   3.632  0.00031 ***
banios           0.49308    0.10847   4.546 6.82e-06 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.815 on 517 degrees of freedom
Multiple R-squared:  0.7472,    Adjusted R-squared:  0.7453 
F-statistic: 382.1 on 4 and 517 DF,  p-value: < 2.2e-16

Conclusión modelos transformados:

En base al R2 ajustado se determinó que el modelo logaritmico (modelo 5) es el que presenta un R2 ajustado del 79.02% siendo superior al modelo 1 en el cual se empleaban todos los atributos, por lo tanto el modelo 2 es posible mejorarlo haciendo esta transformación.

4. Validación de los supuestos del modelo

Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

4.1 Normalidad de los residuos

A continuación evaluaremos los supuestos para el modelo 2:

modelo2 <- lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = base3)

# Histograma de residuos
hist(modelo2$residuals, main = "Histograma de residuos", xlab = "Residuos", col = "lightblue")

# Gráfico Q-Q
qqnorm(modelo2$residuals, main = "Gráfico Q-Q de residuos")
qqline(modelo2$residuals, col = "red")


Este supuesto indica que los residuos (errores) deben seguir una distribución normal,Esto es fundamental para la validez de los intervalos de confianza y pruebas de hipótesis sobre los coeficientes, los puntos e alinean aproximadamente a una linea recta por lo tanto la hipótesis se cumple.

4.2 Homocedasticidad

# Gráfico de residuos vs valores ajustados
plot(modelo2$fitted.values, modelo2$residuals,
     main = "Residuos vs Valores ajustados",
     xlab = "Valores ajustados",
     ylab = "Residuos",
     pch = 20, col = "blue")
abline(h = 0, col = "red", lwd = 2)


La homocedasticidad significa que la varianza de los errores es constante a lo largo de todos los niveles de las variables independientes, si la dispersión varía (heterocedasticidad), los estimadores pueden ser ineficientes y los intervalos de confianza pueden estar sesgados, los residuos se distribuyen aleatoriamente al rededor de cero, por lo tanto la hipótesis se cumple.

4.3 No autocorrelación

# Gráfico de residuos a lo largo del orden de las observaciones
plot(modelo2$residuals, type = "l",
     main = "Residuos en orden de observaciones",
     xlab = "Índice de observación",
     ylab = "Residuos")

# Test de Durbin-Watson
library(lmtest)
dwtest_result <- dwtest(modelo2)
print(dwtest_result)

    Durbin-Watson test

data:  modelo2
DW = 1.8281, p-value = 0.02201
alternative hypothesis: true autocorrelation is greater than 0


Los errores deben ser independientes entre sí. La autocorrelación ocurre, por ejemplo, en datos de series temporales, cuando el error de una observación está correlacionado con el error de otra. La presencia de autocorrelación puede invalidar las inferencias del modelo.

El test de Durbin-Watson tiene como hipótesis nula que no existe autocorrelación de primer orden en los residuos. Un valor de DW igual a 2 indicaría ausencia de autocorrelación, mientras que valores menores a 2 sugieren autocorrelación positiva.

En este caso, DW = 1.8281 se acerca a 2, pero lo importante es el p-value. Al ser p-value (0.02201) menor que el nivel de significancia típico de 0.05, se rechaza la hipótesis nula. Esto indica evidencia estadísticamente significativa de que existe autocorrelación positiva en los residuos, por lo tanto la hipótesis no se cumple.

4.4 Multicolinealidad

# Matriz de dispersión para las variables predictoras
pairs(num_vars[, c("areaconst", "estrato", "parqueaderos", "banios")],
      main = "Scatterplot Matrix de variables predictoras")

# Matriz de correlación
cor_matrix <- cor(num_vars[, c("areaconst", "estrato", "parqueaderos", "banios")])
print(cor_matrix)
             areaconst   estrato parqueaderos    banios
areaconst    1.0000000 0.5037465    0.3540357 0.5269484
estrato      0.5037465 1.0000000    0.3675210 0.4376173
parqueaderos 0.3540357 0.3675210    1.0000000 0.3979139
banios       0.5269484 0.4376173    0.3979139 1.0000000
# Calcular VIF
library(car)
vif_values <- vif(modelo2)
print(vif_values)
   areaconst      estrato parqueaderos       banios 
    1.607215     1.467350     1.273080     1.541828 


La multicolinealidad se presenta cuando dos o más variables independientes están altamente correlacionadas entre sí, lo que puede dificultar la estimación precisa de los coeficientes y aumentar la varianza de los estimadores.

La multicolinealidad se evalúa tanto mediante la matriz de correlación como con el Factor de Inflación de la Varianza (VIF).

Matriz de correlación: Los valores entre las variables predictoras son moderados (alrededor de 0.35 a 0.53), lo cual sugiere que si bien existe cierta relación, no es tan alta como para generar problemas graves en la estimación.

VIF: Los valores de VIF son todos inferiores a 2, y generalmente se considera que un VIF mayor a 5 (o en algunos contextos mayor a 10) indicaría un problema serio de multicolinealidad. En este caso, los VIF bajos indican que la varianza de los coeficientes no se ve excesivamente inflada por correlaciones entre las variables, por lo tanto la hipotesis se cumple.

5. Predicción del modelo

Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.

Debido a que el modelo 2 no cumplió con el supuesto de la no autocorrelación, empleamos el modelo 5 (modelo logaritmico) el cual obtuvo un mayor R2 ajustado.

Para una casa con 200 m2, estrato = 4, parqueaderos =1 y baños = 2:

nuevo <- data.frame(areaconst = 200, 
                    estrato = 4, 
                    parqueaderos = 1, 
                    banios = 2)

# Realiza la predicción en escala logarítmica
pred_log <- predict(modelo_log, newdata = nuevo)

# Transforma la predicción a la escala original
prediccion <- exp(pred_log)
prediccion
       1 
304.5045 

Para una casa con 200 m2, estrato = 5, parqueaderos = 1 y baños = 2:

nuevo <- data.frame(areaconst = 200, 
                    estrato = 5, 
                    parqueaderos = 1, 
                    banios = 2)

# Realiza la predicción en escala logarítmica
pred_log <- predict(modelo_log, newdata = nuevo)

# Transforma la predicción a la escala original
prediccion <- exp(pred_log)
prediccion
       1 
360.1401 

Conclusiones prediccion del modelo

Con la información entregada se tiene como primera opción una casa con un costo de 304.5 millones de pesos lo cual está dentro del credito aprobado, como segundaopción se tiene 360.14 millones de pesos en estrato 5 sin embrago supera el valor del crédito establecido.

6. Potenciales ofertas

Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

# Aplicar los filtros solicitados

base4= base3

base4$geometry <- NULL  # Opción 1

base4 <- subset(base4,
                preciom <= 350 &
                estrato >= 4 &
                areaconst >= 200 &
                parqueaderos >= 1 &
                banios >= 2 &
                habitaciones >= 4)

6.1 Visualizacion alternativas de compra

Se presentan 31 alternativas de compra, se procede a ordenar de mayor a menor precio.

library(DT)
datatable(base4)
library(sf)
library(leaflet)
library(dplyr)

# Leer el shapefile y transformar a WGS84
comunas <- st_read("C:/Users/ORAM/Desktop/mc_comunas/mc_comunas.shp") %>% 
  st_transform(crs = 4326)
Reading layer `mc_comunas' from data source 
  `C:\Users\ORAM\Desktop\mc_comunas\mc_comunas.shp' using driver `ESRI Shapefile'
Simple feature collection with 22 features and 4 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 1054753 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
Projected CRS: MAGNA-SIRGAS / Cali urban grid
# Asignar zonas según el valor de la columna 'comuna'
comunas <- comunas %>%
  mutate(Zona = case_when(
    comuna %in% c(2, 4, 5, 6) ~ "Zona Norte",
    comuna %in% c(7, 13, 14, 15, 16, 21) ~ "Zona Oriente",
    comuna %in% c(22, 17) ~ "Zona Sur",
    comuna %in% c(18, 19, 20) ~ "Zona Oeste",
    comuna %in% c(3, 9, 10, 11, 12, 8) ~ "Zona Centro",
    TRUE ~ NA_character_
  )) %>%
  filter(!is.na(Zona))  # Conserva solo las comunas con zona definida

# Crear una paleta de colores para las zonas
pal <- colorFactor(
  palette = c("blue", "red", "green", "orange", "purple"),
  domain = comunas$Zona
)

# Crear el mapa base con los polígonos de comunas y leyenda
m <- leaflet(comunas) %>%
  addTiles() %>%
  addPolygons(
    fillColor = ~pal(Zona),
    fillOpacity = 0.5,
    color = "black",
    weight = 1,
    popup = ~paste("Comuna:", comuna, "<br>Zona:", Zona)
  ) %>%
  addLegend("bottomright", pal = pal, values = ~Zona, title = "Zonas")

# Agregar los puntos de base4

# Crear popup con detalles de cada propiedad
popup_info <- ~paste0(
  "<b>ID:</b> ", id, "<br>",
  "<b>Barrio:</b> ", barrio, "<br>",
  "<b>Estrato:</b> ", estrato, "<br>",
  "<b>Precio:</b> ", format(preciom, big.mark = ","), " Millones COP<br>",
  "<b>Área Construida:</b> ", areaconst, " m²<br>",
  "<b>Piso:</b> ", piso, "<br>",
  "<b>Parqueaderos:</b> ", parqueaderos, "<br>",
  "<b>Baños:</b> ", banios, "<br>",
  "<b>Habitaciones:</b> ", habitaciones, "<br>",
  "<b>Zona:</b> ", zona
)

# Agregar los puntos con ID y detalles en el popup
m <- m %>%
  addCircleMarkers(
    data = base4,
    lng = ~longitud, 
    lat = ~latitud,
    color = "black",
    fillColor = ~pal(zona),
    fillOpacity = 1,
    radius = 5,
    popup = popup_info,  # Popup con toda la info
    label = ~as.character(id),  # Muestra el ID como etiqueta visible
    labelOptions = labelOptions(
      noHide = TRUE,  # Hace que la etiqueta siempre sea visible
      direction = "top",
      textOnly = TRUE,
      style = list(
        "color" = "black",
        "font-weight" = "bold",
        "font-size" = "12px"
      )
    )
  )

# Visualizar el mapa
m

En el mapa interactivo se presentan las 31 alternativas presentes, al dar click en el punto se puede mostrar información complemetaria como es el id, el precio, el barrio, el estrato, el número de parqueaderos, el número de habitaciones, lo cual facilita la negociación a los asesores.