##Cargue de datos y librerias

#Cargue de datos
data("vivienda")
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

Se identifica que la base de datos presenta valores faltantes, por tanto se procede realizar la identificación de la proporción de faltantes.

porcentaje_faltantes <- colSums(is.na(vivienda)) / nrow(vivienda) * 100

df_faltantes <- data.frame(
  Variable = names(porcentaje_faltantes),
  Porcentaje = porcentaje_faltantes
)

ggplot(df_faltantes, aes(x = reorder(Variable, -Porcentaje), y = Porcentaje)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  labs(
    title = "Porcentaje de Datos Faltantes por Variable",
    x = "Variable",
    y = "Porcentaje de Datos Faltantes"
  ) +
  theme_minimal()

Se logra identificar que existe un porcentaje alto de datos faltantes para las variables piso con más del 30% de datos faltantes, teniendo en cuenta que esta variable no es necesaria para nuestro analisis o construcción de nuestro modelo, procedemos a retirarla de la base de datos.

#Quitar variable piso
vivienda_sin_piso <- vivienda %>% dplyr::select(-piso)

# Mostrar las primeras 5 filas del nuevo data frame 'vivienda_sin_piso'
head(vivienda_sin_piso, 5)
## # A tibble: 5 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  1147 Zona O…       3     250        70            1      3            6 Casa 
## 2  1169 Zona O…       3     320       120            1      2            3 Casa 
## 3  1350 Zona O…       3     350       220            2      2            4 Casa 
## 4  5992 Zona S…       4     400       280            3      5            3 Casa 
## 5  1212 Zona N…       5     260        90            1      2            3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

La variable parqueadero cuenta con con cerca del 20% de datos faltantes, se asume que si la variable se encuentra en blanco es debido a que no tiene parqueadero,esto teniendo en cuenta que las propiedades inician con el valor 1, por tanto se procede a imputar en donde las variables N.A corresponden a 0.

# Crear un nuevo data frame 'viviendaimp' a partir de 'vivienda_sin_piso'
viviendaimp <- vivienda_sin_piso

# Imputar los NA de la variable 'parqueaderos' con 0 en el nuevo data frame 'viviendaimp'
viviendaimp$parqueaderos <- ifelse(is.na(viviendaimp$parqueaderos), 0, viviendaimp$parqueaderos)

# Mostrar las primeras 5 filas del data frame 'viviendaimp' para verificar la imputación
head(viviendaimp, 5)
## # A tibble: 5 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  1147 Zona O…       3     250        70            1      3            6 Casa 
## 2  1169 Zona O…       3     320       120            1      2            3 Casa 
## 3  1350 Zona O…       3     350       220            2      2            4 Casa 
## 4  5992 Zona S…       4     400       280            3      5            3 Casa 
## 5  1212 Zona N…       5     260        90            1      2            3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Por ultimo se retiran de la base las tres filas que cuentan con datos faltantes.

# Retirar las filas cuyo 'id' es NA del data frame 'viviendaimp'
vivienda2 <- viviendaimp[!is.na(viviendaimp$id), ]

# Mostrar las últimas 5 filas del nuevo data frame 'vivienda2'
tail(vivienda2, 5)
## # A tibble: 5 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  4328 Zona S…       6    1650       460            2      6            4 Casa 
## 2  4865 Zona S…       3     360       182            1      3            9 Casa 
## 3  6417 Zona S…       6    1800       400            3      6            5 Casa 
## 4  6998 Zona S…       6    1000       189            3      5            4 Apar…
## 5  8139 Zona S…       5     530       142            2      4            4 Casa 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Verificamos que no existan datos faltantes

# Verificar si hay algún valor NA en el data frame 'vivienda'
any(is.na(vivienda2))
## [1] FALSE

##FILTRO A LA BASE DE DATOS

  1. 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 todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).
# Filtrar las ofertas de casas de la zona norte y guardar en 'Viviendanorte'
Viviendanorte <- vivienda2 %>%
  filter(tipo == "Casa" & zona == "Zona Norte")

# Mostrar los primeros 3 registros de la base filtrada 'Viviendanorte'
head(Viviendanorte, 3)
## # A tibble: 3 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  1209 Zona N…       5     320       150            2      4            6 Casa 
## 2  1592 Zona N…       5     780       380            2      3            3 Casa 
## 3  4057 Zona N…       6     750       445            0      7            6 Casa 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
# Resumen estadístico de algunas variables relevantes
summary(Viviendanorte)
##        id             zona              estrato         preciom      
##  Min.   :  58.0   Length:722         Min.   :3.000   Min.   :  89.0  
##  1st Qu.: 766.2   Class :character   1st Qu.:3.000   1st Qu.: 261.2  
##  Median :2257.0   Mode  :character   Median :4.000   Median : 390.0  
##  Mean   :2574.6                      Mean   :4.202   Mean   : 445.9  
##  3rd Qu.:4225.0                      3rd Qu.:5.000   3rd Qu.: 550.0  
##  Max.   :8319.0                      Max.   :6.000   Max.   :1940.0  
##    areaconst       parqueaderos        banios        habitaciones   
##  Min.   :  30.0   Min.   : 0.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.: 140.0   1st Qu.: 0.000   1st Qu.: 2.000   1st Qu.: 3.000  
##  Median : 240.0   Median : 1.000   Median : 3.000   Median : 4.000  
##  Mean   : 264.9   Mean   : 1.314   Mean   : 3.555   Mean   : 4.507  
##  3rd Qu.: 336.8   3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 5.000  
##  Max.   :1440.0   Max.   :10.000   Max.   :10.000   Max.   :10.000  
##      tipo              barrio             longitud         latitud     
##  Length:722         Length:722         Min.   :-76.59   Min.   :3.333  
##  Class :character   Class :character   1st Qu.:-76.53   1st Qu.:3.452  
##  Mode  :character   Mode  :character   Median :-76.52   Median :3.468  
##                                        Mean   :-76.52   Mean   :3.460  
##                                        3rd Qu.:-76.50   3rd Qu.:3.482  
##                                        Max.   :-76.47   Max.   :3.496
# Crear el data frame para el mapa con las coordenadas de 'Viviendanorte'
Viviendanorte_mapa <- cbind(Viviendanorte[, c("longitud", "latitud")])

# Verificar que no haya NA en longitud y latitud antes de crear el mapa
Viviendanorte_mapa <- Viviendanorte_mapa[!is.na(Viviendanorte_mapa$longitud) & !is.na(Viviendanorte_mapa$latitud), ]

# Crear una paleta de colores específica para las propiedades
# Si se desea agrupar por una categoría, agregar una columna de factor para diferenciar (opcional)
pal <- colorFactor(palette = c("red", "blue", "green"), domain = NULL) # Usamos NULL para no especificar dominios, o puedes definir un factor si hay categorías

# Crear el mapa
leaflet(Viviendanorte_mapa) %>%
  addTiles() %>%  # Agregar el fondo del mapa
  addCircleMarkers(
    ~longitud, ~latitud,
    color = ~pal(1),  # Usamos un color fijo para todos, o puedes usar una variable categórica en lugar de 1
    popup = ~paste("Longitud:", longitud, "<br>", "Latitud:", latitud),  # Información emergente al hacer clic
    radius = 5,  # Tamaño de los puntos
    stroke = FALSE, fillOpacity = 0.8
  ) %>%
  addLegend("bottomright", pal = pal,
            values = ~1, title = "Viviendas Zona Norte", opacity = 1)

De aqui se logra observar se presentan valores en otras zonas, por tanto existe problemas con la variable zona, para realizar la correción correspondiente , se realizó un cruce de las viviendas con un archivo que contiene las comunas a partir de los datos de longitud y latitud, se creo una nueva columna “zonacomuna” asumiendo que las comunas de la zona norte son las comunas 6, 5, 4 y 2, de una vez se identificaron las comunas de la zona sur asumiendolas como las 10,16,17,18,19 y 22.

# Leer el shapefile de las comunas
comunas <- st_read("C:/Users/giova/Downloads/Comunas.shp")
## Reading layer `Comunas' from data source `C:\Users\giova\Downloads\Comunas.shp' using driver `ESRI Shapefile'
## Simple feature collection with 22 features and 4 fields
## Geometry type: POLYGON
## Dimension:     XY
## Bounding box:  xmin: -76.59076 ymin: 3.331819 xmax: -76.46125 ymax: 3.505871
## Geodetic CRS:  WGS 84
# Convertir el data frame 'vivienda' a un objeto sf (simple features)
vivienda_sf <- st_as_sf(vivienda2, coords = c("longitud", "latitud"), crs = st_crs(comunas))

# Realizar la unión espacial para obtener la comuna correspondiente para cada vivienda
vivienda_sf <- st_join(vivienda_sf, comunas, left = FALSE)

# Crear la columna 'zonacomuna' para verificar si la vivienda está en la zona norte o sur
vivienda_sf <- vivienda_sf %>%
  mutate(zonacomuna = case_when(
    comuna %in% c(6, 5, 4, 2) ~ "Zona Norte",
    comuna %in% c(16,17,18,22) ~ "Zona Sur",
    TRUE ~ "Otra Zona"
  ))

# Convertir el objeto sf de nuevo a un data frame, si es necesario
viviendacomunas <- as.data.frame(vivienda_sf)

# Mostrar los primeros 5 registros para verificar la nueva columna 'zonacomuna'
head(viviendacomunas, 5)
##     id         zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 1147 Zona Oriente       3     250        70            1      3            6
## 2 1169 Zona Oriente       3     320       120            1      2            3
## 3 1350 Zona Oriente       3     350       220            2      2            4
## 4 5992     Zona Sur       4     400       280            3      5            3
## 5 1212   Zona Norte       5     260        90            1      2            3
##          tipo      barrio                  geometry comuna    nombre shape_leng
## 1        Casa 20 de julio POINT (-76.51168 3.43382)     11 Comuna 11       9668
## 2        Casa 20 de julio POINT (-76.51237 3.43369)     11 Comuna 11       9668
## 3        Casa 20 de julio POINT (-76.51537 3.43566)     11 Comuna 11       9668
## 4        Casa  3 de julio      POINT (-76.54 3.435)     19 Comuna 19      24055
## 5 Apartamento       acopi  POINT (-76.5135 3.45891)      4  Comuna 4      11375
##   shape_area zonacomuna
## 1    3713816  Otra Zona
## 2    3713816  Otra Zona
## 3    3713816  Otra Zona
## 4   10820091  Otra Zona
## 5    4519035 Zona Norte

Se realiza nuevamente el filtrado de la base de datos para la zona norte

# Filtrar el data frame 'viviendacomunas' para incluir solo las viviendas en la "Zona Norte" y de tipo "Casa"
vivienda_nortecomunas <- viviendacomunas %>%
  filter(zonacomuna == "Zona Norte" & tipo == "Casa")

# Mostrar los primeros 3 registros del data frame filtrado
head(vivienda_nortecomunas, 3)
##     id       zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 1209 Zona Norte       5     320       150            2      4            6
## 2 1592 Zona Norte       5     780       380            2      3            3
## 3  504 Zona Norte       3     180       120            0      3            3
##   tipo barrio                  geometry comuna   nombre shape_leng shape_area
## 1 Casa  acopi POINT (-76.51341 3.47968)      2 Comuna 2      27825   10859503
## 2 Casa  acopi POINT (-76.51674 3.48721)      2 Comuna 2      27825   10859503
## 3 Casa  acopi  POINT (-76.49768 3.4706)      5 Comuna 5       8438    4199641
##   zonacomuna
## 1 Zona Norte
## 2 Zona Norte
## 3 Zona Norte

Se realiza nuevamente el filtrado de la base de datos para la zona sur

# Filtrar el data frame 'viviendacomunas' para incluir solo las viviendas en la "Zona Norte" y de tipo "Casa"
vivienda_surcomunas <- viviendacomunas %>%
  filter(zonacomuna == "Zona Sur" & tipo == "Apartamento")

# Mostrar los primeros 3 registros del data frame filtrado
head(vivienda_surcomunas, 3)
##     id       zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 1724 Zona Norte       5     240        87            1      3            3
## 2 4386 Zona Norte       5     310       137            2      3            4
## 3 5424 Zona Norte       4     320       108            2      3            3
##          tipo barrio                  geometry comuna    nombre shape_leng
## 1 Apartamento  acopi   POINT (-76.517 3.36971)     17 Comuna 17      17335
## 2 Apartamento  acopi POINT (-76.53105 3.38296)     17 Comuna 17      17335
## 3 Apartamento  acopi  POINT (-76.53638 3.4077)     17 Comuna 17      17335
##   shape_area zonacomuna
## 1   12677841   Zona Sur
## 2   12677841   Zona Sur
## 3   12677841   Zona Sur

Se procede a graficar el mapa de las viviendas por zona a partir de la columna creada zonacoumna, para validar que efectivamente se hayan filtrado de forma correcta

# Verificar si 'vivienda_nortecomunas' es un objeto sf
if (!inherits(vivienda_nortecomunas, "sf")) {
  # Convertir 'vivienda_nortecomunas' a un objeto sf usando la columna de geometría existente
  vivienda_nortecomunas <- st_as_sf(vivienda_nortecomunas)
}
# Extraer las coordenadas de longitud y latitud del objeto 'sf'
coords <- st_coordinates(vivienda_nortecomunas)

# Añadir las coordenadas extraídas como columnas al data frame 'vivienda_nortecomunas'
vivienda_nortecomunas$longitud <- coords[, 1]
vivienda_nortecomunas$latitud <- coords[, 2]
# Crear un data frame solo con las coordenadas para el mapa
vivienda_nortecomunas_mapa <- vivienda_nortecomunas[, c("longitud", "latitud")]

# Verificar que no haya NA en longitud y latitud antes de crear el mapa
vivienda_nortecomunas_mapa <- vivienda_nortecomunas_mapa[!is.na(vivienda_nortecomunas_mapa$longitud) & !is.na(vivienda_nortecomunas_mapa$latitud), ]

# Crear una paleta de colores específica para las propiedades
pal <- colorFactor(palette = c("blue"), domain = NULL)  # Usamos un color fijo

# Crear el mapa
leaflet(vivienda_nortecomunas_mapa) %>%
  addTiles() %>%  # Agregar el fondo del mapa
  addCircleMarkers(
    ~longitud, ~latitud,
    color = ~pal(1),  # Usamos un color fijo para todos
    popup = ~paste("Longitud:", longitud, "<br>", "Latitud:", latitud),  # Información emergente al hacer clic
    radius = 5,  # Tamaño de los puntos
    stroke = FALSE, fillOpacity = 0.8
  ) %>%
  addLegend("bottomright", pal = pal,
            values = ~1, title = "Viviendas Zona Norte", opacity = 1)
# Verificar si 'vivienda_surcomunas' es un objeto sf
if (!inherits(vivienda_surcomunas, "sf")) {
  # Convertir 'vivienda_surcomunas' a un objeto sf usando la columna de geometría existente
  vivienda_surcomunas <- st_as_sf(vivienda_surcomunas)
}
# Extraer las coordenadas de longitud y latitud del objeto 'sf'
coords <- st_coordinates(vivienda_surcomunas)

# Añadir las coordenadas extraídas como columnas al data frame 'vivienda_surcomunas'
vivienda_surcomunas$longitud <- coords[, 1]
vivienda_surcomunas$latitud <- coords[, 2]
# Crear un data frame solo con las coordenadas para el mapa
vivienda_surcomunas_mapa <- vivienda_surcomunas[, c("longitud", "latitud")]

# Verificar que no haya NA en longitud y latitud antes de crear el mapa
vivienda_surcomunas_mapa <- vivienda_surcomunas_mapa[!is.na(vivienda_surcomunas_mapa$longitud) & !is.na(vivienda_surcomunas_mapa$latitud), ]
# Crear una paleta de colores específica para las propiedades
pal <- colorFactor(palette = c("red"), domain = NULL)  # Usamos color rojo para las viviendas de la Zona Sur

# Crear el mapa
leaflet(vivienda_surcomunas_mapa) %>%
  addTiles() %>%  # Agregar el fondo del mapa
  addCircleMarkers(
    ~longitud, ~latitud,
    color = ~pal(1),  # Usamos un color fijo (rojo) para todas las viviendas de la Zona Sur
    popup = ~paste("Longitud:", longitud, "<br>", "Latitud:", latitud),  # Información emergente al hacer clic
    radius = 5,  # Tamaño de los puntos
    stroke = FALSE, fillOpacity = 0.8
  ) %>%
  addLegend("bottomright", pal = pal,
            values = ~1, title = "Viviendas Zona Sur", opacity = 1)

De aqui se logra observar que las viviendas filtradas corresponden a las zonas norte y sur, el error correspondia a que la zona inicial de la base de datos vivienda cuenta con inconsistencias, posiblemente debido a que son datos cuyo origen es una pagina web en la cual el usuario selecciona manualmente la zona, por tanto el dato de georeferenciación, latitud y longitud es más confiable al cruzarlo con el archivo que identifica las coumnas de Cali.

  1. 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.

ANÁLISIS EXPLORATORIO

2.1 analisis exploratorio vivienda zona norte tipo casa

# Seleccionar solo las columnas necesarias
datos_interes <- vivienda_nortecomunas %>%
  st_drop_geometry() %>%  # Eliminar la columna de geometría
  dplyr::select(preciom, areaconst, banios, habitaciones,parqueaderos)

# Crear el gráfico de correlación usando GGally
pair_plot <- ggpairs(datos_interes, 
                     title = "Matriz de Correlación de Viviendas (Zona Norte - Tipo Casa)",
                     upper = list(continuous = wrap("cor", size = 5)), # Mostrar la correlación en la parte superior
                     diag = list(continuous = wrap("densityDiag")), # Mostrar la densidad en la diagonal
                     lower = list(continuous = wrap("points"))) # Mostrar puntos en la parte inferior

# Mostrar el gráfico
print(pair_plot)

Las relaciones más fuertes entre las variables se observan entre el precio de la vivienda y el área construida, el número de baños y el número de parqueaderos,esto es coherente teniendo en cuenta que comunmente factores como el tamaño de la casa y la cantidad de baños o parqueaderos deberian tienen un impacto significativo en el precio de la vivienda.

El grafico de dispersión refuerza lo observado previamente

# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_nortecomunas_df <- vivienda_nortecomunas %>%
  st_drop_geometry() %>%
  as.data.frame()

# Crear el gráfico de dispersión interactivo
fig <- plot_ly(
  data = vivienda_nortecomunas_df,
  x = ~areaconst,         # Eje x: Área construida
  y = ~preciom,           # Eje y: Precio en millones
  type = 'scatter',       # Tipo de gráfico: scatter plot (dispersión)
  mode = 'markers',       # Modo: solo puntos
  marker = list(size = 8, opacity = 0.7),  # Tamaño y opacidad de los puntos
  text = ~paste('Precio:', preciom, 'millones', '<br>Área:', areaconst, 'm²') # Texto emergente al hacer hover
) %>%
  layout(
    title = "Gráfico de Dispersión: Precio vs Área Construida (Zona Norte)",
    xaxis = list(title = "Área Construida (m²)"),
    yaxis = list(title = "Precio (Millones de Pesos)")
  )

# Mostrar el gráfico
fig

Se observa que en la medida en que incrementa el área tambien se incrementa el precio de forma mas o menos consistente.

2.2 analisis exploratorio vivienda zona sur tipo apartamento

# Instalar GGally si no está instalado
if (!require(GGally)) install.packages("GGally")

# Cargar las librerías necesarias
library(GGally)
library(ggplot2)
library(dplyr)

# Seleccionar solo las columnas necesarias
datos_interes2 <- vivienda_surcomunas %>%
  st_drop_geometry() %>%  # Eliminar la columna de geometría
  as.data.frame() %>%     # Convertir a data frame
  dplyr::select(preciom, areaconst, banios, habitaciones, parqueaderos)

# Crear el gráfico de correlación usando GGally
pair_plot2 <- ggpairs(datos_interes2, 
                     title = "Matriz de Correlación de Viviendas (Zona Sur - Tipo Apartamento)",
                     upper = list(continuous = wrap("cor", size = 5)), # Mostrar la correlación en la parte superior
                     diag = list(continuous = wrap("densityDiag")), # Mostrar la densidad en la diagonal
                     lower = list(continuous = wrap("points"))) # Mostrar puntos en la parte inferior

# Mostrar el gráfico
print(pair_plot2)

Las características que parecen tener un mayor impacto en el precio incluyen el área construida, el número de baños y el número de parqueaderos en comparación con la “Zona Norte” para casas muestra que algunas características son más relevantes en una zona que en otra como los baños, probablemente debido a las diferencias en el tipo de las propiedades.

el grafico de dispersión refuerza lo observado previamente

# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_surcomunas_df <- vivienda_surcomunas %>%
  st_drop_geometry() %>%
  as.data.frame()

# Crear el gráfico de dispersión interactivo
fig2 <- plot_ly(
  data = vivienda_surcomunas_df,
  x = ~areaconst,         # Eje x: Área construida
  y = ~preciom,           # Eje y: Precio en millones
  type = 'scatter',       # Tipo de gráfico: scatter plot (dispersión)
  mode = 'markers',       # Modo: solo puntos
  marker = list(size = 8, opacity = 0.7),  # Tamaño y opacidad de los puntos
  text = ~paste('Precio:', preciom, 'millones', '<br>Área:', areaconst, 'm²') # Texto emergente al hacer hover
) %>%
  layout(
    title = "Gráfico de Dispersión: Precio vs Área Construida (Zona Sur)",
    xaxis = list(title = "Área Construida (m²)"),
    yaxis = list(title = "Precio (Millones de Pesos)")
  )

# Mostrar el gráfico
fig2

Tambien Se observa que en la medida en que incrementa el área se incrementa el precio de forma más marcada.

  1. 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).
# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_modelo <- vivienda_nortecomunas %>%
  st_drop_geometry() %>%  # Eliminar la columna de geometría para trabajar con un data frame regular
  as.data.frame()  # Asegurarnos de que el objeto sea un data frame regular

# Verificar que el objeto sea un data frame
class(vivienda_modelo)  # Debe devolver "data.frame"
## [1] "data.frame"
# Seleccionar solo las columnas necesarias utilizando dplyr::select()
vivienda_modelo <- dplyr::select(vivienda_modelo, preciom, areaconst, parqueaderos, estrato, banios, habitaciones)

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

# Resumen del modelo
summary(modelo_precio)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = vivienda_modelo)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -930.94  -86.00  -15.49   41.94 1152.11 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -195.08755   37.57761  -5.192 2.86e-07 ***
## areaconst       0.83537    0.05146  16.232  < 2e-16 ***
## parqueaderos   20.92241    5.04807   4.145 3.90e-05 ***
## estrato        82.31305    9.09597   9.049  < 2e-16 ***
## banios         19.39137    7.01045   2.766  0.00585 ** 
## habitaciones   -5.23238    5.32398  -0.983  0.32611    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 171.2 on 596 degrees of freedom
## Multiple R-squared:  0.6486, Adjusted R-squared:  0.6456 
## F-statistic:   220 on 5 and 596 DF,  p-value: < 2.2e-16

Análisis del Modelo de Regresión Lineal Múltiple zona norte

Intercepto: Coeficiente: -195.088, el coeficiente del intercepto indica el valor esperado del precio de una vivienda cuando todas las variables explicativas (área construida, parqueaderos, estrato, baños y habitaciones) son iguales a cero, este valor no tiene una interpretación práctica, ya que una vivienda con un área construida de 0 metros cuadrados, sin parqueaderos, de estrato 0, sin baños y sin habitaciones no existe en la realidad. Por lo tanto, el intercepto solo sirve como un punto de referencia en el modelo.

Área Construida (areaconst): Coeficiente: 0.835, manteniendo todas las demás variables constantes, un aumento de una unidad (1 metro cuadrado) en el área construida de la vivienda se asocia, en promedio, con un incremento de 0.835 millones de pesos en el precio de la casa. Este coeficiente es altamente significativo (valor p < 2e-16), lo que indica que el área construida es un predictor importante del precio de la vivienda en la zona norte de Cali.

Número de Parqueaderos (parqueaderos): Coeficiente: 20.922, manteniendo todas las demás variables constantes, un incremento de 1 en la cantidad de parqueaderos se asocia, en promedio, con un aumento de 20.922 millones de pesos en el precio de la vivienda. Este coeficiente es estadísticamente significativo (valor p = 3.90e-05), lo que indica que el número de parqueaderos es un factor importante para determinar el precio de las casas en esta área.

Estrato (estrato): Coeficiente: 82.313, manteniendo todas las demás variables constantes, un incremento de 1 unidad en el estrato se asocia, en promedio, con un aumento de 82.313 millones de pesos en el precio de la vivienda. Este resultado es estadísticamente significativo (valor p < 2e-16), lo que sugiere que el nivel socioeconómico del área (representado por el estrato) tiene un impacto considerable en el precio de las casas en la zona norte.

Número de Baños (baños): Coeficiente: 19.391,manteniendo todas las demás variables constantes, un incremento de 1 en el número de baños se asocia, en promedio, con un aumento de 19.391 millones de pesos en el precio de la vivienda. Este coeficiente es significativo (valor p = 0.00585), indicando que el número de baños es una variable relevante en el modelo.

Número de Habitaciones (habitaciones): Coeficiente: -5.232,manteniendo todas las demás variables constantes, un incremento de 1 en el número de habitaciones se asocia con una disminución de 5.232 millones de pesos en el precio de la vivienda. Sin embargo, este coeficiente no es estadísticamente significativo (valor p = 0.32611), lo que indica que no hay suficiente evidencia para afirmar que el número de habitaciones afecta el precio de la vivienda de manera significativa. Este resultado podría ser contraintuitivo y sugiere que esta variable no aporta mucha información al modelo. Evaluación del Modelo

R-cuadrado (R²): El valor del R-cuadrado es 0.6486, lo que indica que aproximadamente el 64.86% de la variabilidad en el precio de las casas en la región norte se explica por las variables independientes incluidas en el modelo (área construida, parqueaderos, estrato, baños y habitaciones). Esto sugiere que el modelo tiene un ajuste moderado a bueno.

Error estándar residual: El error estándar de los residuos es 171.2, lo cual nos indica la dispersión de los datos alrededor de la línea de regresión ajustada. Cuanto menor sea este valor, mejor será el ajuste del modelo.

##Recomendaciones para Mejorar el Modelo zona norte

Incluir Variables Adicionales:

Para mejorar la capacidad predictiva del modelo, se podrían considerar variables adicionales que puedan afectar el precio de las viviendas, como la Ubicación específica, la Antigüedad de la propiedad: Casas más nuevas tienden a ser más caras, tambien se puede eliminar variables no significativas, dado que el número de habitaciones no es un predictor significativo en este modelo (valor p = 0.32611), podríamos considerar eliminar esta variable para simplificar el modelo y mejorar su precisión. Transformar Variables:

Validación de supuestos zona norte

  1. 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).
# Graficar residuos vs valores ajustados para verificar la linealidad
plot(modelo_precio$fitted.values, resid(modelo_precio),
     xlab = "Valores Ajustados", ylab = "Residuos",
     main = "Gráfico de Residuos vs Valores Ajustados")
abline(h = 0, col = "red", lwd = 2)

El gráfico de residuos vs valores ajustados muestra que los residuos no están distribuidos de manera completamente aleatoria alrededor de la línea horizontal en cero. Hay un ligero patrón curvilíneo, especialmente en los extremos, indica que la relación entre las variables independientes y la variable dependiente no es completamente lineal.

# Prueba de Durbin-Watson
dwtest(modelo_precio)
## 
##  Durbin-Watson test
## 
## data:  modelo_precio
## DW = 1.8124, p-value = 0.009455
## alternative hypothesis: true autocorrelation is greater than 0

Un valor de Durbin-Watson cercano a 2 indica poca o ninguna autocorrelación en los residuos, sin embargo, el valor p bajo sugiere que hay una evidencia de autocorrelación positiva

# Graficar residuos estandarizados vs valores ajustados para verificar la homoscedasticidad
plot(modelo_precio$fitted.values, rstandard(modelo_precio),
     xlab = "Valores Ajustados", ylab = "Residuos Estandarizados",
     main = "Gráfico de Residuos Estandarizados vs Valores Ajustados")
abline(h = 0, col = "red", lwd = 2)

El gráfico de residuos estandarizados vs valores ajustados muestra una cierta dispersión en forma de embudo, donde los residuos tienden a dispersarse más a medida que aumentan los valores ajustados, indicando heteroscedasticidad.

# Gráfico Q-Q para verificar la normalidad de los errores
qqnorm(resid(modelo_precio), main = "Gráfico Q-Q de los Residuos")
qqline(resid(modelo_precio), col = "red", lwd = 2)

# Prueba de Shapiro-Wilk para la normalidad de los residuos
shapiro.test(resid(modelo_precio))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelo_precio)
## W = 0.81553, p-value < 2.2e-16

el gráfico Q-Q muestra que los residuos no se ajustan perfectamente a la línea de normalidad. Hay desviaciones notables en ambos extremos del gráfico.

# Calcular VIF para evaluar la colinealidad
vif(modelo_precio)
##    areaconst parqueaderos      estrato       banios habitaciones 
##     1.692205     1.337130     1.690403     2.157940     1.825203

Los valores de VIF están por debajo de 5, lo que indica que no hay una multicolinealidad significativa entre las variables independientes en el modelo.

El modelo de regresión lineal múltiple presenta algunos problemas con los supuestos de linealidad, homoscedasticidad y normalidad de los errores para mejorarlo se podrian considerar las siguientes acciones:

  1. Transformación de Variables: Transformar las variables independientes o la variable dependiente para mejorar la linealidad, la homoscedasticidad y la normalidad de los errores.

  2. Manejo de Outliers: Revisar posibles outliers que afecten los resultados y considerar su eliminación o ajuste

Partición de datos en entrenamiento y prueba

  1. Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime el modelo con la muestra del 70%. Muestre los resultados.
# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_modelo <- vivienda_nortecomunas %>%
  st_drop_geometry() %>%
  as.data.frame()

# Fijar una semilla para la reproducibilidad
set.seed(123)

# Crear una columna de índice de muestra aleatoria (70% entrenamiento, 30% prueba)
sample_split <- sample.split(vivienda_modelo$preciom, SplitRatio = 0.7)

# Crear conjuntos de entrenamiento y prueba
train_set <- subset(vivienda_modelo, sample_split == TRUE)
test_set <- subset(vivienda_modelo, sample_split == FALSE)

# Comprobar el tamaño de los conjuntos
nrow(train_set)  
## [1] 436
nrow(test_set)  
## [1] 166

Se ajusta el modelo usando el conjunto de entrenamiento

# Ajustar el modelo de regresión lineal 
modelo_precio_train <- lm(preciom ~ areaconst + parqueaderos + estrato + banios + habitaciones, data = train_set)

# Mostrar el resumen del modelo ajustado
summary(modelo_precio_train)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = train_set)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -861.14  -91.76  -16.35   47.82 1151.90 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -209.0744    45.3487  -4.610 5.31e-06 ***
## areaconst       0.8131     0.0611  13.309  < 2e-16 ***
## parqueaderos   21.5411     6.0565   3.557 0.000417 ***
## estrato        94.8037    11.1477   8.504 3.05e-16 ***
## banios         17.1181     8.4591   2.024 0.043626 *  
## habitaciones  -10.5088     6.6485  -1.581 0.114696    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 177.8 on 430 degrees of freedom
## Multiple R-squared:  0.6307, Adjusted R-squared:  0.6264 
## F-statistic: 146.9 on 5 and 430 DF,  p-value: < 2.2e-16

Predicciones con el modelo usando datos de prueba

  1. Realice predicciones con el modelo anterior usando los datos de prueba (30%)
# Realizar predicciones con el modelo ajustado usando el conjunto de prueba
predicciones <- predict(modelo_precio_train, newdata = test_set)

# Ver los primeros valores predichos
head(predicciones)
##       11       17       27       29       33       34 
## 666.8731 284.2246 195.5772 190.3598 189.0295 137.3418
# Calcular el Error Cuadrático Medio (MSE)
mse <- mean((test_set$preciom - predicciones)^2)

# Calcular el Error Absoluto Medio (MAE)
mae <- mean(abs(test_set$preciom - predicciones))

# Calcular el RMSE (Raíz del Error Cuadrático Medio)
rmse <- sqrt(mse)

# Calcular el MAPE (Error Porcentual Absoluto Medio)
mape <- mean(abs((test_set$preciom - predicciones) / test_set$preciom)) * 100

# Mostrar las métricas de rendimiento
cat("MSE:", mse, "\n")
## MSE: 24238.4
cat("MAE:", mae, "\n")
## MAE: 105.6264
cat("RMSE:", rmse, "\n")
## RMSE: 155.6869
cat("MAPE:", mape, "%\n")
## MAPE: 23.21534 %

MSE (Error Cuadrático Medio): 24238.4 indica que el modelo está haciendo buenas predicciones, pero el valor que tienes es relativamente alto, lo que sugiere que hay una cantidad considerable de error en las predicciones del modelo.

MAE (Error Absoluto Medio): 105.6264 el MAE sugiere que, en promedio, el modelo se desvía en alrededor de 105.6 millones de pesos del valor real de las viviendas. Esto también es una desviación significativa, lo que implica que las predicciones pueden no ser muy precisas.

RMSE (Raíz del Error Cuadrático Medio): 155.6869 significa que, en promedio, las predicciones del modelo tienen un error de aproximadamente 155.7 millones de pesos. Al igual que el MSE y el MAE, este valor también indica que hay errores significativos en las predicciones del modelo.

El valor de R-cuadrado (R²) de 0.6307 significa que el modelo de regresión puede explicar el 63.07% de la variabilidad en el precio de las viviendas a partir de las variables predictoras incluidas en el modelo.

Predicción sobre el precio de la vivienda zona norte

tambien se hizo una predicción sobre el precio de la vivienda con las características de la primera solicitud.

# Predicción

data2 <- data.frame(
  areaconst = 200,
  parqueaderos = 1,
  estrato = c(4, 5),
  banios = 2,
  habitaciones = 4
)

# Realizar la predicción
predict(modelo_precio, newdata = data2)
##        1        2 
## 340.0138 422.3268

la predicción indica que el precio para una vivienda estrato 4 tipo casa con las caracteristicas requeridas tendria un valor de 340 millones, mientras que la misma vivienda en estrato 5 costaria al rededor de los 422 millones.

Ofertas potenciales zona norte

  1. 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.
vivienda_nortecomunas <- st_drop_geometry(vivienda_nortecomunas)

# Filtrar las propiedades que cumplen con los criterios
ofertas_potenciales <- vivienda_nortecomunas %>%
  filter(
    estrato %in% c(4, 5),  # Incluir tanto estrato 4 como 5
    areaconst >= 190 & areaconst <= 210,  # Rango cercano a 200 m²
    parqueaderos == 1,
    banios == 2,
    habitaciones == 4,
    preciom <= 350  # Precio dentro del límite de crédito
  ) %>%
  arrange(preciom)  # Ordenar por precio para priorizar las ofertas más económicas

# Verificar las 5 primeras ofertas
head(ofertas_potenciales, 5)
##  [1] id           zona         estrato      preciom      areaconst   
##  [6] parqueaderos banios       habitaciones tipo         barrio      
## [11] comuna       nombre       shape_leng   shape_area   zonacomuna  
## [16] longitud     latitud     
## <0 rows> (or 0-length row.names)

No se cuenta con viviendas con estas caracteristicas exactas, por tanto se presentan algunos ajustes en los parametros de busqueda, en donde se busca un rango superior a 190 metros, con más de un parqueadero, considerando que más de estas atributos pueden ser condiciones deseables potencialmente.

Con estos nuevos criterios se logran identificar tres prospectos potenciales.

vivienda_nortecomunas <- st_drop_geometry(vivienda_nortecomunas)

# Filtrar las propiedades que cumplen con los criterios
ofertas_potenciales <- vivienda_nortecomunas %>%
  filter(
    estrato %in% c(4, 5), 
    areaconst >= 190 ,  # Rango superior a 200 m²
    parqueaderos >= 1,
    banios == 2,
    habitaciones == 4,
    preciom <= 350  # Precio dentro del límite de crédito
  ) %>%
  arrange(preciom)  # Ordenar por precio para priorizar las ofertas más económicas

# Verificar las 5 primeras ofertas
head(ofertas_potenciales, 5)
##     id       zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 1822 Zona Norte       4     340       295            2      2            4
## 2 1163 Zona Norte       5     350       216            2      2            4
## 3 1943 Zona Norte       5     350       346            1      2            4
##   tipo    barrio comuna   nombre shape_leng shape_area zonacomuna  longitud
## 1 Casa    vipasa      2 Comuna 2      27825   10859503 Zona Norte -76.51777
## 2 Casa la merced      2 Comuna 2      27825   10859503 Zona Norte -76.51218
## 3 Casa    vipasa      2 Comuna 2      27825   10859503 Zona Norte -76.51847
##   latitud
## 1 3.48060
## 2 3.48181
## 3 3.47503

Se encontraron tres propiedades que cumplen en gran medida con las caracteristicas requeridas, una de estrato 4 y dos de estrato 5.

# Asegurarse de que el dataframe esté en un formato adecuado
vivienda_nortecomunas_df <- vivienda_nortecomunas %>%
  st_drop_geometry() %>%
  as.data.frame()

# Filtrar el dataset para incluir solo los IDs específicos
oferta_potencial <- vivienda_nortecomunas_df %>%
  filter(id %in% c(1822, 1163, 1943))

# Verificar que los datos tengan las columnas de longitud y latitud
if (!all(c("longitud", "latitud") %in% colnames(oferta_potencial))) {
  stop("Las columnas 'longitud' y 'latitud' son necesarias para el mapa.")
}

# Crear el mapa interactivo
mapa_oferta <- leaflet(oferta_potencial) %>%
  addTiles() %>%  # Agregar fondo de mapa
  addCircleMarkers(
    ~longitud, ~latitud,
    radius = 8,  # Tamaño de los puntos
    color = "red",  # Color de los puntos
    stroke = FALSE, fillOpacity = 0.8,
    popup = ~paste(
      "ID:", id, "<br>",
      "Precio:", preciom, "millones", "<br>",
      "Área construida:", areaconst, "m²", "<br>",
      "Estrato:", estrato, "<br>",
      "Parqueaderos:", parqueaderos, "<br>",
      "Baños:", banios, "<br>",
      "Habitaciones:", habitaciones
    )  # Información emergente al hacer clic
  )

# Mostrar el mapa
mapa_oferta

Modelo de regresión lineal multiple para zona sur

Aplicar los mismos pasos para la vivienda tipo apartamento zona sur

# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_modelo_sur <- vivienda_surcomunas %>%
  st_drop_geometry() %>%  # Eliminar la columna de geometría
  as.data.frame()  # Convertir a data frame

# Verificar que el objeto sea un data frame
class(vivienda_modelo_sur)  # Debe devolver "data.frame"
## [1] "data.frame"
# Seleccionar solo las columnas necesarias utilizando dplyr::select()
vivienda_modelo_sur <- dplyr::select(vivienda_modelo_sur, preciom, areaconst, parqueaderos, estrato, banios, habitaciones)

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

# Resumen del modelo
summary(modelo_precio_sur)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = vivienda_modelo_sur)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1295.13   -42.28    -2.17    34.47   906.70 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -232.33868   16.66591 -13.941  < 2e-16 ***
## areaconst       1.50079    0.05715  26.261  < 2e-16 ***
## parqueaderos   50.35258    3.60535  13.966  < 2e-16 ***
## estrato        53.68143    3.49194  15.373  < 2e-16 ***
## banios         58.28287    3.73059  15.623  < 2e-16 ***
## habitaciones  -21.95747    4.18467  -5.247  1.7e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 101.4 on 2098 degrees of freedom
## Multiple R-squared:  0.7634, Adjusted R-squared:  0.7629 
## F-statistic:  1354 on 5 and 2098 DF,  p-value: < 2.2e-16

Coeficientes del Modelo

Intercepto (Intercept): -232.34, este es el valor estimado del precio de la vivienda cuando todas las variables independientes son 0. Aunque no tiene una interpretación práctica directa

Área Construida (areaconst): 1.50,manteniendo todas las demás variables constantes, un aumento de 1 m² en el área construida se asocia, en promedio, con un aumento de 1.50 millones de pesos en el precio de la vivienda. Este coeficiente es altamente significativo (valor p < 2e-16), lo que indica que el área construida es un predictor importante del precio de la vivienda en la zona sur de Cali.

Número de Parqueaderos (parqueaderos): 50.35, manteniendo todas las demás variables constantes, un incremento de 1 parqueadero se asocia, en promedio, con un aumento de 50.35 millones de pesos en el precio de la vivienda. Este coeficiente es estadísticamente significativo (valor p < 2e-16), indicando que el número de parqueaderos es un factor relevante en la determinación del precio.

Estrato (estrato): 53.68, manteniendo todas las demás variables constantes, un aumento de 1 unidad en el estrato se asocia, en promedio, con un incremento de 53.68 millones de pesos en el precio de la vivienda. Este resultado es estadísticamente significativo (valor p < 2e-16), sugiriendo que el nivel socioeconómico del área tiene un impacto considerable en el precio de las casas en la zona sur.

Número de Baños (banios): 58.28 manteniendo todas las demás variables constantes, un aumento de 1 baño se asocia, en promedio, con un incremento de 58.28 millones de pesos en el precio de la vivienda. Este coeficiente también es significativo (valor p < 2e-16), lo que indica que el número de baños es una variable importante en el modelo.

Número de Habitaciones (habitaciones): -21.96,Manteniendo todas las demás variables constantes, un aumento de 1 en el número de habitaciones se asocia con una disminución de 21.96 millones de pesos en el precio de la vivienda. Este coeficiente es estadísticamente significativo (valor p = 1.7e-07). Este resultado puede parecer contraintuitivo y sugiere que el número de habitaciones podría estar relacionado negativamente con el precio en esta muestra, quizás debido a otros factores no capturados por el modelo. Evaluación del Modelo

El valor de R² de 0.7634 sugiere que aproximadamente el 76.34% de la variabilidad en el precio de las viviendas en la zona sur de Cali se explica por las variables independientes incluidas en el modelo. Esto indica que el modelo tiene un buen ajuste.

Validación de supuestos zona sur

# Graficar residuos vs valores ajustados para verificar la linealidad
plot(modelo_precio_sur$fitted.values, resid(modelo_precio_sur),
     xlab = "Valores Ajustados", ylab = "Residuos",
     main = "Gráfico de Residuos vs Valores Ajustados (Zona Sur)")
abline(h = 0, col = "red", lwd = 2)

# Prueba de Durbin-Watson
library(lmtest)
dwtest(modelo_precio_sur)
## 
##  Durbin-Watson test
## 
## data:  modelo_precio_sur
## DW = 1.5934, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
# Graficar residuos estandarizados vs valores ajustados para verificar la homoscedasticidad
plot(modelo_precio_sur$fitted.values, rstandard(modelo_precio_sur),
     xlab = "Valores Ajustados", ylab = "Residuos Estandarizados",
     main = "Gráfico de Residuos Estandarizados vs Valores Ajustados (Zona Sur)")
abline(h = 0, col = "red", lwd = 2)

# Gráfico Q-Q para verificar la normalidad de los errores
qqnorm(resid(modelo_precio_sur), main = "Gráfico Q-Q de los Residuos (Zona Sur)")
qqline(resid(modelo_precio_sur), col = "red", lwd = 2)

# Prueba de Shapiro-Wilk para la normalidad de los residuos
shapiro.test(resid(modelo_precio_sur))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelo_precio_sur)
## W = 0.75949, p-value < 2.2e-16
# Calcular VIF para evaluar la colinealidad
library(car)
vif(modelo_precio_sur)
##    areaconst parqueaderos      estrato       banios habitaciones 
##     2.019007     1.774856     1.807051     2.529172     1.328700
  1. Gráfico de Residuos vs. Valores Ajustados Observaciones:

En el gráfico de residuos vs. valores ajustados, los residuos no están distribuidos completamente de manera aleatoria alrededor de la línea horizontal en cero. Hay una concentración de puntos cerca de la línea en los valores ajustados bajos, pero se observa mayor dispersión a medida que los valores ajustados aumentan, este patrón sugiere que puede haber una relación no lineal entre las variables predictoras y la variable de respuesta o que el modelo no captura completamente la estructura de los datos.

El gráfico Q-Q muestra que los residuos no siguen una distribución normal perfectamente. Hay desviaciones significativas en ambos extremos del gráfico (colas más gruesas), esto indica que los residuos tienen una distribución más sesgada o con colas más pesadas que la normal.

El gráfico de residuos estandarizados vs. valores ajustados también muestra una dispersión mayor a medida que los valores ajustados aumentan. Esto sugiere heteroscedasticidad

El valor de Durbin-Watson (1.5934) está por debajo de 2, lo que indica la presencia de autocorrelación positiva en los residuos. El p-valor extremadamente bajo (< 2.2e-16) confirma la significancia de esta autocorrelación.

Prueba de Normalidad de Shapiro-Wilk, un valor W de 0.75949 y un p-valor extremadamente bajo indican que los residuos no siguen una distribución normal.

# Calcular VIF para evaluar la colinealidad
vif(modelo_precio_sur)
##    areaconst parqueaderos      estrato       banios habitaciones 
##     2.019007     1.774856     1.807051     2.529172     1.328700

Los valores de VIF están por debajo de 5, lo que indica que no hay una multicolinealidad significativa entre las variables independientes en el modelo.

Recomendaciones: El modelo de regresión lineal múltiple para vivienda_surcomunas muestra algunos problemas con los supuestos de normalidad, homoscedasticidad, independencia de los errores y linealidad, se pueden realizar la transformación de Variables , tambien se recomienda revisar posibles outliers que afecten los resultados y considera su eliminación o ajuste.

Partición de datos para Zona Sur

Dividir los datos en 70% entrenamiento y 30% prueba para el dataset de apartamentos sur

# Convertir el objeto 'sf' a un data frame regular eliminando la geometría
vivienda_modelo_sur <- vivienda_surcomunas %>%
  st_drop_geometry() %>%
  as.data.frame()

# Fijar una semilla para la reproducibilidad
set.seed(123)

# Crear una columna de índice de muestra aleatoria (70% entrenamiento, 30% prueba)
library(caTools)
sample_split <- sample.split(vivienda_modelo_sur$preciom, SplitRatio = 0.7)

# Crear conjuntos de entrenamiento y prueba
train_set_sur <- subset(vivienda_modelo_sur, sample_split == TRUE)
test_set_sur <- subset(vivienda_modelo_sur, sample_split == FALSE)

# Comprobar el tamaño de los conjuntos
nrow(train_set_sur)  
## [1] 1500
nrow(test_set_sur)
## [1] 604
# Ajustar el modelo de regresión lineal múltiple usando el conjunto de entrenamiento
modelo_precio_train_sur <- lm(preciom ~ areaconst + parqueaderos + estrato + banios + habitaciones, data = train_set_sur)

# Mostrar el resumen del modelo ajustado
summary(modelo_precio_train_sur)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = train_set_sur)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1162.83   -45.09    -2.20    35.31   920.51 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -234.06440   20.30971  -11.53  < 2e-16 ***
## areaconst       1.34183    0.06512   20.61  < 2e-16 ***
## parqueaderos   57.12732    4.29696   13.29  < 2e-16 ***
## estrato        54.56354    4.28318   12.74  < 2e-16 ***
## banios         60.28988    4.55577   13.23  < 2e-16 ***
## habitaciones  -21.83439    5.04280   -4.33 1.59e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 105.4 on 1494 degrees of freedom
## Multiple R-squared:  0.7532, Adjusted R-squared:  0.7523 
## F-statistic: 911.7 on 5 and 1494 DF,  p-value: < 2.2e-16

Realice predicciones con el modelo anterior usando los datos de prueba (30%)

# Realizar predicciones con el modelo ajustado usando el conjunto de prueba
predicciones_sur <- predict(modelo_precio_train_sur, newdata = test_set_sur)

# Ver los primeros valores predichos
head(predicciones_sur)
##        7        8       11       15       24       25 
## 139.9035 119.7761 237.4056 139.9035 138.5617 219.9618
# Calcular el Error Cuadrático Medio (MSE)
mse_sur <- mean((test_set_sur$preciom - predicciones_sur)^2)

# Calcular el Error Absoluto Medio (MAE)
mae_sur <- mean(abs(test_set_sur$preciom - predicciones_sur))

# Calcular el RMSE (Raíz del Error Cuadrático Medio)
rmse_sur <- sqrt(mse_sur)

# Calcular el MAPE (Error Porcentual Absoluto Medio)
mape_sur <- mean(abs((test_set_sur$preciom - predicciones_sur) / test_set_sur$preciom)) * 100

# Mostrar las métricas de rendimiento
cat("MSE:", mse_sur, "\n")
## MSE: 8374.708
cat("MAE:", mae_sur, "\n")
## MAE: 58.02957
cat("RMSE:", rmse_sur, "\n")
## RMSE: 91.51343
cat("MAPE:", mape_sur, "%\n")
## MAPE: 20.74411 %

Análisis de las Métricas de Rendimiento

MSE (Error Cuadrático Medio): 8374.708, Hay una cantidad significativa de error en las predicciones, pero comparado con el modelo anterior (vivienda_nortecomunas), este es mucho menor, lo que indica un mejor rendimiento.

MAE (Error Absoluto Medio): 58.02957, En promedio, el modelo se desvía por 58.03 millones de pesos del valor real de las viviendas. Este valor es bastante razonable para modelos de predicción de precios de viviendas, y muestra una mejora significativa en comparación con el modelo para vivienda_nortecomunas. RMSE (Raíz del Error Cuadrático Medio): 91.51343

Un RMSE de 91.51 millones de pesos significa que, en promedio, las predicciones del modelo tienen un error de aproximadamente 91.51 millones de pesos. Este es un error más bajo en comparación con el modelo para vivienda_nortecomunas, lo que indica un mejor rendimiento en la zona sur. MAPE (Error Porcentual Absoluto Medio): 20.74411%

El valor de R-cuadrado (R²) de 0.75 : Este valor sugiere que el modelo tiene un ajuste alto. Un R² de 0.75 significa que el modelo de regresión puede explicar el 75% de la variabilidad en el precio de las viviendas a partir de las variables predictoras incluidas en el modelo.

Predicción del precio Zona sur

Predecir el precio con el modelo inicial para una vivienda tipo apartamento en el sur

# Crear el data frame con las características de la vivienda para la predicción
nueva_vivienda <- data.frame(
  areaconst = 300,        # Área construida de 300 m²
  parqueaderos = 3,       # 3 parqueaderos
  estrato = c(5, 6),      # Estrato 5 y 6 para hacer dos predicciones
  banios = 3,             # 3 baños
  habitaciones = 5        # 5 habitaciones
)

# Realizar la predicción con el modelo inicial
predicciones_nueva_vivienda <- predict(modelo_precio_sur, newdata = nueva_vivienda)

# Mostrar los resultados de las predicciones
predicciones_nueva_vivienda
##        1        2 
## 702.4245 756.1060

Ofertas potenciales Zona Sur

6.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 850 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

vivienda_surcomunas <- st_drop_geometry(vivienda_surcomunas)

# Filtrar las propiedades que cumplen con los criterios
ofertas_potenciales2 <- vivienda_surcomunas %>%
  filter(
    estrato %in% c(5, 6),  # Incluir tanto estrato 5 como 6
    areaconst >= 300 & areaconst <= 310,  # Rango cercano a 200 m²
    parqueaderos == 3,
    banios == 3,
    habitaciones == 5,
    preciom <= 850  # Precio dentro del límite de crédito
  ) %>%
  arrange(preciom)  # Ordenar por precio para priorizar las ofertas más económicas

# Verificar las 5 primeras ofertas
head(ofertas_potenciales2, 5)
##  [1] id           zona         estrato      preciom      areaconst   
##  [6] parqueaderos banios       habitaciones tipo         barrio      
## [11] comuna       nombre       shape_leng   shape_area   zonacomuna  
## [16] longitud     latitud     
## <0 rows> (or 0-length row.names)

Teniendo en cuenta que no se identifican viviendas con estas caracteristicas exactas se procede a filtrar con algunos ajustes en los parametros de busqueda, en donde se busca un rango superior a 300 metros, con más de tres parqueaderos, baños y habitaciones, considerando que más de estas atributos pueden ser condiciones deseables potencialmente.

Con estos nuevos criterios tampoco se logran identificar prospectos potenciales.

vivienda_surcomunas <- st_drop_geometry(vivienda_surcomunas)

# Filtrar las propiedades que cumplen con los criterios
ofertas_potenciales3 <- vivienda_surcomunas %>%
  filter(
    estrato %in% c(5, 6),  # Incluir tanto estrato 5 como 6
    areaconst >= 300 ,  # Rango superior a 300 m²
    parqueaderos >= 3,
    banios >= 3,
    habitaciones >=5,
    preciom <= 850  # Precio dentro del límite de crédito
  ) %>%
  arrange(preciom)  # Ordenar por precio para priorizar las ofertas más económicas
head(ofertas_potenciales3, 5)
##  [1] id           zona         estrato      preciom      areaconst   
##  [6] parqueaderos banios       habitaciones tipo         barrio      
## [11] comuna       nombre       shape_leng   shape_area   zonacomuna  
## [16] longitud     latitud     
## <0 rows> (or 0-length row.names)
# Verificar las 5 primeras ofertas