INTRODUCCIÓN

María, una experimentada agente de bienes raíces y fundadora de C&A en Cali, se enfrenta a un desafío único: asesorar a una compañía internacional en la compra de dos viviendas para sus empleados. A pesar de la disminución actual en las ventas de bienes raíces en Cali, María está equipada con una década de experiencia y un equipo de ocho agentes de bienes raíces. Este informe presenta un análisis exploratorio y la modelación de dos casos de viviendas, una casa en la zona norte y un apartamento en la zona sur, basándose en las condiciones proporcionadas por la compañía. Se incluyen recomendaciones, estimaciones y comparaciones de modelos para ayudar a María a tomar una decisión informada.

PAQUETES

Aquí se describen los paquetes usados

#devtools::install_github("dgonxalex80/paqueteMOD", force = TRUE)
library(devtools)
library(paqueteMOD)
library(tidyverse)
library(mice)
library(factoextra)
library(cluster)
library(fpc)
library(psych)
library(corrplot)
library(FactoMineR)
library(gridExtra)
library(pander)
library(ggdendro)
library(plotly)
library(lmtest)
library(car)

require(raster)
require(sf)

DATOS

Se inicia cargando los datos a analizar

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>

Se procede a realizar un resúmen de los datos

# Para todas las variables
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 observan algunos NA’s, se decide eliminar la variable piso ya que esta no estará presente en el análisis. La variable parqueadero es la que más NA’s presenta de todas las variables que contiene el dataset vivienda. Más adelante se ampliará este resúmen enfocado en cada caso de estudio.

# Eliminar la variable 'piso' de 'vivienda'
vivienda <- dplyr::select(vivienda, -piso)

FILTRADO DATASETS

Ahora se realiza un filtro para cada uno de los requerimientos que se tienen, llamando datos1 a las casas ubicadas en el norte de la ciudad, y datos2 a los apartamentos del sur de la ciudad.

Se inicia haciendo la revisión de las variables que se usarán para construir los filtros

names(vivienda)
##  [1] "id"           "zona"         "estrato"      "preciom"      "areaconst"   
##  [6] "parqueaderos" "banios"       "habitaciones" "tipo"         "barrio"      
## [11] "longitud"     "latitud"
table(vivienda$tipo)
## 
## Apartamento        Casa 
##        5100        3219
table(vivienda$zona)
## 
##  Zona Centro   Zona Norte   Zona Oeste Zona Oriente     Zona Sur 
##          124         1920         1198          351         4726

Se observa que la Zona Sur es la que más registros de vivienda presenta 4726, seguida de la Zona Norte 1920, algo bueno, ya que esto permitirá modelar ambas zonas con información suficiente.

Ahora se construyen los nuevos datasets que serán de utilidad más adelante para la construcción de los análisis y modelos.

# Datasets
datos1 <- vivienda %>% filter(tipo == "Casa", zona == "Zona Norte")
datos2 <- vivienda %>% filter(tipo == "Apartamento", zona == "Zona Sur")

Se revisan los datos.

Caso 1.

head(as.data.frame(datos1), 6)
##     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 4057 Zona Norte       6     750       445           NA      7            6
## 4 4460 Zona Norte       4     625       355            3      5            5
## 5 6081 Zona Norte       5     750       237            2      6            6
## 6 7824 Zona Norte       4     600       160            1      4            5
##   tipo barrio  longitud latitud
## 1 Casa  acopi -76.51341 3.47968
## 2 Casa  acopi -76.51674 3.48721
## 3 Casa  acopi -76.52950 3.38527
## 4 Casa  acopi -76.53179 3.40590
## 5 Casa  acopi -76.54044 3.36862
## 6 Casa  acopi -76.55210 3.42125

No se observa problema alguno a simple vista.

Caso 2.

head(as.data.frame(datos2), 6)
##     id     zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 5098 Zona Sur       4     290        96            1      2            3
## 2  698 Zona Sur       3      78        40            1      1            2
## 3 8199 Zona Sur       6     875       194            2      5            3
## 4 1241 Zona Sur       3     135       117           NA      2            3
## 5 5370 Zona Sur       3     135        78           NA      1            3
## 6 6975 Zona Sur       4     220        75            1      2            3
##          tipo        barrio  longitud latitud
## 1 Apartamento         acopi -76.53464 3.44987
## 2 Apartamento    aguablanca -76.50100 3.40000
## 3 Apartamento     aguacatal -76.55700 3.45900
## 4 Apartamento       alameda -76.51400 3.44100
## 5 Apartamento       alameda -76.53600 3.43600
## 6 Apartamento alférez real -76.54627 3.39109

Se observa un problema entre los barrios y las zonas a las que pertenecen, en datos2, ya que acopi es de la zona Norte.

MAPAS

Ahora se construyen un par de mapas para corroborar la ubicación de las viviendas. Para ello se carga el shape de la ciudad que se está analizando.

# Cargamos el shapefile con raster::shapefile()
shp_bcali <- shapefile('C:/Users/Andre/OneDrive/Documentos/COMPU_IEI/1_JAVERIANA_IEI/0_PROYECTOS AÑOS ANTERIORES JAVERIANA/PROPUESTA BARRIOS VEREDAS/PROPUESTA_BARRIOS_MANZANAS/_shp/BarriosCali84.shp')

# Convertimos a sf
shp_bcali_sf <- st_as_sf(shp_bcali)

Ahora se transforma el set de datos en un formato que sirva para este proposito.

# Convertir a sf
datos1_sf <- st_as_sf(na.omit(datos1), coords = c("longitud", "latitud"), crs = 4326)
datos2_sf <- st_as_sf(na.omit(datos2), coords = c("longitud", "latitud"), crs = 4326)

Ahora se construyen los mapas

# Definir colores pastel para 'Casa' y 'Apartamento'
colores <- c("Casa" = "salmon", "Apartamento" = "lightblue")

# Extraer las coordenadas
datos1_sf <- datos1_sf %>% mutate(x = map_dbl(geometry, ~st_coordinates(.x)[1, 1]),
                                  y = map_dbl(geometry, ~st_coordinates(.x)[1, 2]))
datos2_sf <- datos2_sf %>% mutate(x = map_dbl(geometry, ~st_coordinates(.x)[1, 1]),
                                  y = map_dbl(geometry, ~st_coordinates(.x)[1, 2]))

# Plot para datos1
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = datos1_sf, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = datos1_sf, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Barrios - Zona Norte, Tipo Casa", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

# Plot para datos2
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = datos2_sf, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = datos2_sf, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Barrios - Zona Sur, Tipo Apartamento", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Definitivamente hay un problema con los datos respeccto a sus ubicaciones que al parecer está relacionado a que algunas ubicaciones no concuerdan con sus barrios.

Se analizan ahora algunos registros en ambos data sets que están fuera de sus respectivas zonas

# Crear un vector con los IDs que quieres
ids_fuerazona <- c(193, 221, 706, 6081, 6906, 7470)

# Filtrar los datos
revision_fuerazona <- vivienda %>% filter(id %in% ids_fuerazona)
revision_fuerazona
## # A tibble: 6 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  6081 Zona N…       5     750       237            2      6            6 Casa 
## 2   706 Zona S…       4     300       114            2      3            4 Apar…
## 3   221 Zona S…       5     360        99            1      3            2 Apar…
## 4   193 Zona S…       4     200       110            1      3            4 Apar…
## 5  6906 Zona N…       6    1080       650            2      0            7 Casa 
## 6  7470 Zona N…       4     340       264            2      5            7 Casa 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Al parecer hay un problema con la variable latitud en algunos registros, sin embargo el barrio coincide con la zona que afirma pertenecer.

Debido a lo anterior, se intentará entonces trabajar solo con los registros que se encuentren ubicados en la zona correspondiente a la que se desea evaluar, ya que no se quiere que el cliente termine viviendo por accidente en el Norte de la ciudad cuando lo que deseaba era vivir en el sur y viceversa.

Para ello se asume que las coordenadas registradas son correctas, ya que no se tienen direcciones exactas para revisar la verdadera ubicación de las viviendas, se usarán los datos completos por si algún predio está mal clasificado respecto a su zona, por eso se construirá un mapa nuevo que también involucre el mapa de comunas de la ciudad, pues hay indicios fuertes sobre que es posible definir tanto el sur de la ciudad como el norte mediante el uso de las comunas.

Para definir cuales son el norte y sur de la ciudad según sus comunas, se hace una revisión bibliográfica utilizando los siguientes enlaces:

https://idesc.cali.gov.co/download/mapas/zonas_geograficas.pdf y https://www.cali.gov.co/gobierno/publicaciones/148815/asi-estan-distribuidas-las-localidades-de-cali-distrito-especial/

Luego de la revisión se concluye que:

MAPA COMPLETO DE UBICACIONES

Se cargan las capas a usar

# Cargamos el shapefile con raster::shapefile()
shp_bcali <- shapefile('C:/Users/Andre/OneDrive/Documentos/COMPU_IEI/1_JAVERIANA_IEI/0_PROYECTOS AÑOS ANTERIORES JAVERIANA/PROPUESTA BARRIOS VEREDAS/PROPUESTA_BARRIOS_MANZANAS/_shp/BarriosCali84.shp')

# Convertimos a sf
shp_bcali_sf <- st_as_sf(shp_bcali)

# Cargar el shapefile con raster::shapefile()
shp_comunas <- shapefile('C:/Users/Andre/OneDrive/Documentos/COMPU_IEI/1_JAVERIANA_IEI/0_PROYECTOS AÑOS ANTERIORES JAVERIANA/PROPUESTA BARRIOS VEREDAS/PROPUESTA_BARRIOS_MANZANAS/_shp/mc_comunas.shp')

# Convertir a sf
shp_comunas_sf <- st_as_sf(shp_comunas)

Ahora se transforman los datos

# Convertir a sf
vivienda_sf <- st_as_sf(na.omit(vivienda), coords = c("longitud", "latitud"), crs = 4326)

Finalmente se construye el mapa

# Extraer las coordenadas
vivienda_sf <- vivienda_sf %>% mutate(x = map_dbl(geometry, ~st_coordinates(.x)[1, 1]),
                                  y = map_dbl(geometry, ~st_coordinates(.x)[1, 2]))

# Plot para vivienda
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = shp_comunas_sf, fill = NA, color = "black") +
  geom_sf(data = vivienda_sf, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_sf, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Viviendas en la ciudad", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Caso 1

Se inicia con el filtrado de datos para la correcta ubicación de las casas del norte de la ciudad

# Filtrar las comunas
comunas_filtradas <- shp_comunas_sf %>% filter(comuna %in% c(1, 2, 3))

# Asegurarse de que vivienda_sf y comunas_filtradas tienen el mismo CRS
vivienda_sf1 <- st_transform(vivienda_sf, st_crs(comunas_filtradas))

# Ahora realiza la intersección
vivienda_filtrada1 <- vivienda_sf1[comunas_filtradas, ]

# Crear un nuevo dataframe con los registros filtrados
new_datos1 <- data.frame(vivienda_filtrada1)

# Plot para vivienda filtrada
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = comunas_filtradas, fill = NA, color = "black") +
  geom_sf(data = vivienda_filtrada1, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_filtrada1, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Zona Norte", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Ahora se filtran los datos para que correspondan específicamente a las viviendas de tipo casa, ubicadas según los registros en la zona norte, esto con el fin de garantizar al máximo que la ubicación sea correcta.

# Filtrar por tipo de vivienda 'Casa'
vivienda_filtrada1 <- vivienda_filtrada1 %>% filter(tipo == 'Casa' & zona == 'Zona Norte')

# Crear un nuevo dataframe con los registros filtrados
new_datos1 <- data.frame(vivienda_filtrada1)
new_datos1 <- dplyr::select(new_datos1, -geometry)

# Plot para vivienda filtrada
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = comunas_filtradas, fill = NA, color = "black") +
  geom_sf(data = vivienda_filtrada1, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_filtrada1, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Zona Norte", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

El mapa muestra el resultado final del filtro realizado.

Caso 2

Ahora la idea es filtrar por comunas las viviendas que se encuentran al sur de la ciudad.

# Filtrar las comunas
comunas_filtradas <- shp_comunas_sf %>% filter(comuna %in% c(17, 18, 22))

# Asegurarse de que vivienda_sf y comunas_filtradas tienen el mismo CRS
vivienda_sf2 <- st_transform(vivienda_sf, st_crs(comunas_filtradas))

# Ahora realiza la intersección
vivienda_filtrada2 <- vivienda_sf2[comunas_filtradas, ]

# Crear un nuevo dataframe con los registros filtrados
new_datos2 <- data.frame(vivienda_filtrada2)

# Plot para vivienda filtrada
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = comunas_filtradas, fill = NA, color = "black") +
  geom_sf(data = vivienda_filtrada2, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_filtrada2, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Zona Sur", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Una vez se consigue esto, se procede a filtrar que las viviendas sean solo del tipo apartamento en la zona sur de la ciudad, esto con la finalidad de acertar con mayor precisión la verdadera ubicación de la vivienda.

# Filtrar por tipo de vivienda 'Casa'
vivienda_filtrada2 <- vivienda_filtrada2 %>% filter(tipo == 'Apartamento' & zona == 'Zona Sur')

# Crear un nuevo dataframe con los registros filtrados
new_datos2 <- data.frame(vivienda_filtrada2)
new_datos2 <- dplyr::select(new_datos2, -geometry)

# Plot para vivienda filtrada
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = comunas_filtradas, fill = NA, color = "black") +
  geom_sf(data = vivienda_filtrada2, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_filtrada2, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Zona Sur", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Aquí se obtiene el resultado final de la ubicación de los apartamentos de la zona sur. De aquí se debe proceder con el análisis exploratorio de los datos, para garantizar que los datos sean de buena calidad para realizar todo tipo de estimaciones y modelamientos sobre ellos.

ANÁLIS EXPLORATORIO DATASETS FILTRADOS POR UBICACIÓN Y COMUNAS

Caso 1

summary(new_datos1)
##        id           zona              estrato         preciom      
##  Min.   : 655   Length:297         Min.   :3.000   Min.   :  89.0  
##  1st Qu.:1823   Class :character   1st Qu.:4.000   1st Qu.: 360.0  
##  Median :3060   Mode  :character   Median :5.000   Median : 470.0  
##  Mean   :3151                      Mean   :4.754   Mean   : 528.1  
##  3rd Qu.:4435                      3rd Qu.:5.000   3rd Qu.: 600.0  
##  Max.   :8319                      Max.   :6.000   Max.   :1940.0  
##    areaconst       parqueaderos       banios       habitaciones   
##  Min.   :  60.0   Min.   :1.000   Min.   : 1.00   Min.   : 0.000  
##  1st Qu.: 207.0   1st Qu.:1.000   1st Qu.: 3.00   1st Qu.: 4.000  
##  Median : 294.0   Median :2.000   Median : 4.00   Median : 4.000  
##  Mean   : 314.5   Mean   :2.364   Mean   : 3.98   Mean   : 4.795  
##  3rd Qu.: 376.0   3rd Qu.:3.000   3rd Qu.: 5.00   3rd Qu.: 5.000  
##  Max.   :1188.0   Max.   :9.000   Max.   :10.00   Max.   :10.000  
##      tipo              barrio                x                y        
##  Length:297         Length:297         Min.   :-76.59   Min.   :3.448  
##  Class :character   Class :character   1st Qu.:-76.53   1st Qu.:3.467  
##  Mode  :character   Mode  :character   Median :-76.52   Median :3.480  
##                                        Mean   :-76.52   Mean   :3.475  
##                                        3rd Qu.:-76.52   3rd Qu.:3.485  
##                                        Max.   :-76.50   Max.   :3.496

Respecto a new_datos1 no presenta datos faltantes, la variable estrato solo incluye datos de viviendas clasificadas entre 3 y 6. Las viviendas presentan precios entre los 89 y los 1940 millones de COP. En cuanto al área construida varía entre los 60 y los 1188 metros cuadrados. Son casas entre 1 y 9 parqueaderos. Es importante notar que existen viviendas que no registran habitaciones, lo cual podría requerir una limpieza de datos, ya que estos registros podrían no tener sentido en el contexto de este análisis.

Ahora se revisan cuantas casas al norte de la ciudad presentan cero habitaciones

table(new_datos1$habitaciones) 
## 
##   0   1   2   3   4   5   6   7   8   9  10 
##   2   1   3  47 106  65  30  18  11   7   7

Al tratarse de solo dos casas se decide que no deberían afectar en nada los análisis realizados posteriormente. Finalmente se aclará que en la Zona Norte de la ciudad se cuenta con 297 casas con altas probabilidades de tener correcta su ubicación.

Ahora se procede a analizar a mayor detalle el precio y el área de las casas del norte de la ciudad:

describe(new_datos1$preciom)
##    vars   n   mean     sd median trimmed    mad min  max range skew kurtosis
## X1    1 297 528.12 251.23    470  490.87 177.91  89 1940  1851 2.07     6.16
##       se
## X1 14.58

El análisis de los precios de las viviendas en la Zona Norte de la ciudad revela que el precio medio es de 528.12 millones de COP, con una desviación estándar de 251.23 millones de COP, lo que indica una considerable variabilidad en los precios. La mediana del precio es de 470 millones de COP, lo que sugiere que la mitad de las viviendas tienen un precio inferior a este valor. El rango de precios, desde el mínimo de 89 millones de COP hasta el máximo de 1940 millones de COP, muestra la amplia gama de precios de las viviendas en esta zona. La asimetría positiva de 2.07 indica que la distribución de los precios está sesgada hacia la derecha, lo que significa que hay un número de viviendas con precios muy altos que están alejando la media de la mediana. La curtosis de 6.16 sugiere que la distribución tiene colas pesadas y un pico más agudo que una distribución normal, lo que indica la presencia de valores extremos en los precios de las viviendas.

describe(new_datos1$areaconst)
##    vars   n   mean     sd median trimmed   mad min  max range skew kurtosis
## X1    1 297 314.48 159.67    294  294.78 127.5  60 1188  1128 1.71     4.65
##      se
## X1 9.27

El análisis de los datos de las viviendas ubicadas en la Zona Norte de la ciudad muestra que el área construida promedio es de 314.48 metros cuadrados, con una desviación estándar de 159.67 metros cuadrados, lo que indica una variabilidad considerable en el tamaño de las viviendas. La mediana del área construida es de 294 metros cuadrados, lo que sugiere que la mitad de las viviendas tienen un tamaño inferior a este valor. El rango del área construida, desde el mínimo de 60 metros cuadrados hasta el máximo de 1188 metros cuadrados, muestra la amplia gama de tamaños de las viviendas en esta zona. La asimetría positiva de 1.71 indica que la distribución del área construida está sesgada hacia la derecha, lo que significa que hay un número de viviendas con áreas construidas muy grandes que están alejando la media de la mediana. La curtosis de 4.65 sugiere que la distribución tiene colas pesadas y un pico más agudo que una distribución normal, lo que indica la presencia de valores extremos en el área construida de las viviendas.

En resumen, las medianas muestran que tanto en el precio como en el área de las viviendas ubicadas en el norte de la ciudad, hay una oferta suficiente que se acerca mucho al caso requerido a evaluar.

Caso 2

summary(new_datos2)
##        id           zona              estrato         preciom      
##  Min.   :1251   Length:1701        Min.   :3.000   Min.   :  78.0  
##  1st Qu.:2481   Class :character   1st Qu.:4.000   1st Qu.: 220.0  
##  Median :3847   Mode  :character   Median :5.000   Median : 275.0  
##  Mean   :4032                      Mean   :4.853   Mean   : 333.4  
##  3rd Qu.:5527                      3rd Qu.:5.000   3rd Qu.: 360.0  
##  Max.   :8228                      Max.   :6.000   Max.   :1750.0  
##    areaconst      parqueaderos       banios      habitaciones  
##  Min.   : 45.0   Min.   : 1.00   Min.   :0.00   Min.   :0.000  
##  1st Qu.: 72.0   1st Qu.: 1.00   1st Qu.:2.00   1st Qu.:3.000  
##  Median : 90.0   Median : 1.00   Median :2.00   Median :3.000  
##  Mean   :102.8   Mean   : 1.44   Mean   :2.63   Mean   :2.994  
##  3rd Qu.:115.0   3rd Qu.: 2.00   3rd Qu.:3.00   3rd Qu.:3.000  
##  Max.   :932.0   Max.   :10.00   Max.   :7.00   Max.   :6.000  
##      tipo              barrio                x                y        
##  Length:1701        Length:1701        Min.   :-76.56   Min.   :3.334  
##  Class :character   Class :character   1st Qu.:-76.54   1st Qu.:3.366  
##  Mode  :character   Mode  :character   Median :-76.53   Median :3.372  
##                                        Mean   :-76.53   Mean   :3.374  
##                                        3rd Qu.:-76.52   3rd Qu.:3.384  
##                                        Max.   :-76.51   Max.   :3.409

Respecto a new_datos2, que corresponde a los registros de apartamentos en el sur de la ciudad, no presenta datos faltantes. La variable estrato solo incluye datos de apartamentos clasificados entre 3 y 6. Los apartamentos presentan precios que varían entre los 78 y los 1750 millones de COP. En cuanto al área construida, esta varía entre los 45 y los 932 metros cuadrados. Los apartamentos cuentan con al menos un parqueadero. Es importante notar que existen apartamentos que no registran baños ni habitaciones, lo cual podría requerir una limpieza de datos adicional, ya que estos registros podrían no tener sentido en el contexto de este análisis.

Al igual que en el caso 1, se decide revisar el número de apartamentos con cero habitaciones y en este caso también baños.

table(new_datos2$habitaciones)
## 
##    0    1    2    3    4    5    6 
##    4    6  242 1207  229   12    1
table(new_datos2$banios)
## 
##   0   1   2   3   4   5   6   7 
##   2  44 920 465 165  97   7   1

Se evidencia como son muy pocos los apartamentos con estas características por lo que se decide seguir trabajando con los datos como se encuentran. Finalmente, se aclara que en la Zona Sur de la ciudad se cuenta con 1701 apartamentos.

Ahora se procede a analizar a mayor detalle el precio y el área de estas variables:

describe(new_datos2$preciom)
##    vars    n   mean     sd median trimmed    mad min  max range skew kurtosis
## X1    1 1701 333.36 206.85    275  296.96 111.19  78 1750  1672 2.55     9.54
##      se
## X1 5.02

El análisis de los datos de los apartamentos ubicados en la Zona Sur de la ciudad muestra que el precio promedio es de 333.36 millones COP, con una desviación estándar de 206.85 millones COP, lo que indica una variabilidad considerable en los precios. La mediana del precio es de 275 millones COP, lo que sugiere que la mitad de los apartamentos tienen un precio inferior a este valor. El rango de precios, desde el mínimo de 78 millones COP hasta el máximo de 1750 millones COP, muestra la amplia gama de precios de los apartamentos en esta zona. La asimetría positiva de 2.55 indica que la distribución de los precios está sesgada hacia la derecha, lo que significa que hay un número de apartamentos con precios muy altos que están alejando la media de la mediana. La curtosis de 9.54 sugiere que la distribución tiene colas pesadas y un pico más agudo que una distribución normal, lo que indica la presencia de valores extremos en los precios de los apartamentos.

describe(new_datos2$areaconst)
##    vars    n   mean sd median trimmed   mad min max range skew kurtosis   se
## X1    1 1701 102.83 55     90   93.58 29.65  45 932   887 4.65    43.43 1.33

El análisis de los datos de los apartamentos ubicados en la Zona Sur de la ciudad muestra que el área construida promedio es de 102.83 metros cuadrados, con una desviación estándar de 55 metros cuadrados, lo que indica una variabilidad considerable en el tamaño de los apartamentos. La mediana del área construida es de 90 metros cuadrados, lo que sugiere que la mitad de los apartamentos tienen un tamaño inferior a este valor. El rango del área construida, desde el mínimo de 45 metros cuadrados hasta el máximo de 932 metros cuadrados, muestra la amplia gama de tamaños de los apartamentos en esta zona. La asimetría positiva de 4.65 indica que la distribución del área construida está sesgada hacia la derecha, lo que significa que hay un número de apartamentos con áreas construidas muy grandes que están alejando la media de la mediana. La curtosis de 43.43 sugiere que la distribución tiene colas pesadas y un pico más agudo que una distribución normal, lo que indica la presencia de valores extremos en el área construida de los apartamentos.

Ahora, se elimina la variable barrio pues presenta problemas de codificación que está afectando a los elementos gráficos.

new_datos1 <- dplyr::select(new_datos1, -barrio)
new_datos2 <- dplyr::select(new_datos2, -barrio)

GRAFICAS

Caso 1

Se inicia realizando un gráfico de dispersión para el primer caso para revisar si el valor y el área concuerda con el estrato socioeconómico de las casas.

# Gráfico para caso 1
plot_ly(new_datos1, x = ~areaconst, y = ~preciom, color = ~estrato, type = "scatter", mode = "markers") %>%
  layout(title = "Precio vs Área construida - Casas Zona Norte",
         xaxis = list(title = "Área construida"),
         yaxis = list(title = "Precio de la vivienda"))

Se observa que hay cierta relación pero las casas más caras no son necesariamente las ubicadas en el estrato más alto. también que hay gran variedad de precios y estratos entre los 200 y 400 metros cuadrados, luego de los 400 metros cuadrados, no hay viviendas por debajo de los 500 millones de COP, y además de ser una oferta más reducida los estratos se limitan a algunos pocos 4, 5 y 6, siendo 5 el más representativo con estas características.

Caso 2

# Gráfico para caso 2
plot_ly(new_datos2, x = ~areaconst, y = ~preciom, color = ~estrato, type = "scatter", mode = "markers") %>%
  layout(title = "Precio vs Área construida Apartamentos Zona Sur",
         xaxis = list(title = "Área construida"),
         yaxis = list(title = "Precio de la vivienda"))

Respecto a los apartamentos de la zona sur, contrario a las casas del norte, se ve una diferencia bien marcada, entre el estrato y el precio. También se observa como son escasos los apartamentos con más de 400 metros cuadrados, exactamente 6 que se pueden contar sin mayor esfuerzo, y algo particular de los apartamentos con más de 400 metros cuadrados es que hay 4 muy interesantes con un precio por debajo de los 800 millones de COP, y de estrato 5.

CORRELACIONES

Ahora se revisan los gráficos de correlación para cada caso.

# Seleccionar las variables de interés
variables <- c("preciom", "areaconst", "estrato", "banios", "habitaciones", "parqueaderos")
datos1_select <- new_datos1[variables]
datos2_select <- new_datos2[variables]

# Calcular la matriz de correlación de Spearman
correlacion_datos1 <- cor(datos1_select, method = "spearman")
correlacion_datos2 <- cor(datos2_select, method = "spearman")

Caso 1

# Crear los gráficos de calor
fig1 <- plot_ly(x = rownames(correlacion_datos1), y = colnames(correlacion_datos1), z = correlacion_datos1, type = "heatmap") 
fig1 <- fig1 %>% layout(title = "Correlación de Spearman (Caso 1)")
fig1

La matriz de correlación muestra que el precio de las casas (preciom) tiene una fuerte correlación positiva con el área construida (areaconst), lo que indica que las casas más grandes tienden a tener precios más altos. También hay correlaciones moderadas entre el precio y el número de baños (banios), habitaciones (habitaciones), y parqueaderos, lo que sugiere que estas características también pueden influir en el precio de las casas. Sin embargo, la correlación con el estrato es relativamente baja, lo que indica que el estrato puede no ser un predictor tan fuerte del precio en esta zona.

# Crear los gráficos de calor
fig2 <- plot_ly(x = rownames(correlacion_datos2), y = colnames(correlacion_datos2), z = correlacion_datos2, type = "heatmap") 
fig2 <- fig2 %>% layout(title = "Correlación de Spearman (Caso 2)")
fig2

El análisis de los datos de los apartamentos ubicados en la Zona Sur de la ciudad muestra que el precio (preciom) tiene una fuerte correlación positiva con el área construida (areaconst), lo que indica que los apartamentos más grandes tienden a tener precios más altos. También hay correlaciones fuertes entre el precio y el estrato, el número de baños (banios), y el número de parqueaderos, lo que sugiere que estas características también pueden influir en el precio de los apartamentos. Sin embargo, la correlación con el número de habitaciones es relativamente baja, lo que indica que el número de habitaciones puede no ser un predictor tan fuerte del precio en esta zona.

MODELO 1 - CASO 1

Ahora se estiman los respectivos modelos de regresión lineal multiple para cada caso:

# Estimar el modelo de regresión lineal múltiple para datos1
modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = new_datos1)

# Mostrar el resumen del modelo
summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = new_datos1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -722.82  -77.82  -22.69   44.70  927.48 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -204.58606   73.66256  -2.777 0.005836 ** 
## areaconst       0.85834    0.07392  11.611  < 2e-16 ***
## estrato        66.11978   15.76103   4.195 3.62e-05 ***
## habitaciones    6.24908    7.80276   0.801 0.423854    
## parqueaderos   25.20857    7.34077   3.434 0.000681 ***
## banios         14.79570   10.03779   1.474 0.141563    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 168.4 on 291 degrees of freedom
## Multiple R-squared:  0.5583, Adjusted R-squared:  0.5507 
## F-statistic: 73.57 on 5 and 291 DF,  p-value: < 2.2e-16

EVALUACIÓN DE SUPUESTOS - CASO 1

Outliers

# Residuales para modelo1
residuos1 <- residuals(modelo1)

# Crear un dataframe con los residuos
dfr1 <- data.frame(residuos = residuos1)

# Crear el gráfico de caja para modelo1
p1 <- plot_ly(dfr1, y = ~residuos, type = "box", name = "Modelo 1") %>%
  layout(title = "Gráfico de caja de los residuos - Modelo 1",
         yaxis = list(title = "Residuos"))

p1
# Crear el gráfico de dispersión para modelo1
plot_ly(dfr1, x = ~1:length(residuos1), y = ~residuos, mode = "markers", type = "scatter", 
        marker = list(size = 5, color = 'rgba(255, 182, 193, .9)', line = list(color = 'rgba(152, 0, 0, .8)', width = 1))) %>%
  layout(title = "Gráfico de dispersión de los residuos - Modelo 1", xaxis = list(title = "Observación"), yaxis = list(title = "Residuos"))

Tanto el scatterplot, como el boxplot, evidencian outliers, ya que el boxplot muestra como minimo y máximo, valores entre −255 y 220 por tanto, todo valor que sea superior o inferior a estos límites se van a entender como outliers.

El gráfico de residuales también es muy amplio y la diferencia entre los datos y los valores estimados por el modelo, no deberían tener diferencias tan grandes, por lo que se garantiza que si existen outliers.

Resulta interesante conocer cuantos outliers se tienen, para ello se revisa lo siguiente:

# Cálculo del IQR
Q1_1 <- quantile(residuos1, 0.25)
Q3_1 <- quantile(residuos1, 0.75)
IQR_1 <- Q3_1 - Q1_1

# Identificación de outliers
indices_outliers1 <- which((residuos1 < (Q1_1 - 1.5 * IQR_1)) | (residuos1 > (Q3_1 + 1.5 * IQR_1)))

# Contar el número de outliers
num_outliers1 <- length(indices_outliers1)

# Imprimir el número de outliers para modelo1
print(paste("Número de outliers para modelo1: ", num_outliers1))
## [1] "Número de outliers para modelo1:  29"

29 se considera un número bajo de registros por lo que sería interesante evaluar si quitando los outliers estos generarían una mejoría en el ajuste del modelo 1. Aclarar que esto no va a mejorar el modelo 1, a menos que estos puntos sean influyentes, de lo contrario solo se estaría perdiendo información.

Normalidad

# Test de Shapiro - Wilk para modelo1
shapiro1 <- shapiro.test(residuos1)
print(shapiro1)
## 
##  Shapiro-Wilk normality test
## 
## data:  residuos1
## W = 0.85543, p-value = 5.256e-16

Prueba de normalidad de Shapiro-Wilk: Esta prueba se utiliza para determinar si una muestra de datos se ajusta a una distribución normal. En este caso, el valor p es extremadamente pequeño (5.256e-16), lo que indica que podemos rechazar la hipótesis nula de que los residuos siguen una distribución normal. Por lo tanto, los residuos del modelo1 no parecen ser normalmente distribuidos.

Homocedasticidad

# Prueba Breusch-Pagan para la homocedasticidad para modelo1
bptest1 <- bptest(modelo1)
print(bptest1)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo1
## BP = 46.51, df = 5, p-value = 7.15e-09

Prueba de Breusch-Pagan: Esta prueba se utiliza para detectar la heterocedasticidad en un modelo de regresión lineal. En este caso, el valor p es muy pequeño (7.15e-09), lo que indica que podemos rechazar la hipótesis nula de homocedasticidad. Por lo tanto, parece haber heterocedasticidad en los residuos del modelo1, lo que significa que la varianza de los errores no es constante.

Autocorrelación

# Prueba Durbin-Watson para la no autocorrelación para modelo1
dwtest1 <- dwtest(modelo1)
print(dwtest1)
## 
##  Durbin-Watson test
## 
## data:  modelo1
## DW = 1.6621, p-value = 0.001517
## alternative hypothesis: true autocorrelation is greater than 0

Prueba de Durbin-Watson: Esta prueba se utiliza para detectar la presencia de autocorrelación en los residuos de un modelo de regresión. En este caso, el valor p es pequeño (0.001517), lo que indica que podemos rechazar la hipótesis nula de que no hay autocorrelación en los residuos. Por lo tanto, parece haber autocorrelación en los residuos del modelo1.

Sugerencia de reparación de supuestos

  1. Transformaciones de variables: Se puede probar transformaciones en las variables dependientes o independientes. Por ejemplo, una transformación logarítmica puede ayudar a normalizar los residuos y estabilizar la varianza.

  2. Revisar puntos influyentes: Los puntos influyentes son observaciones que tienen un gran efecto en la estimación de los parámetros del modelo. Se pueden identificar a través de medidas como la distancia de Cook y luego decidir si deben ser eliminados.

  3. Explorar otros modelos: Si los supuestos de la regresión lineal no se cumplen incluso después de intentar las correcciones anteriores, puedes considerar otros tipos de modelos que no requieran estos supuestos, como los modelos de regresión no lineal.

MODELO 2 - CASO 2

# Estimar el modelo de regresión lineal múltiple para datos1
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = new_datos2)

# Mostrar el resumen del modelo
summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = new_datos2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1061.78   -42.84    -2.00    40.41   910.20 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -274.16875   19.24598 -14.246  < 2e-16 ***
## areaconst       1.24854    0.06322  19.748  < 2e-16 ***
## estrato        55.13323    3.85112  14.316  < 2e-16 ***
## habitaciones  -20.00029    4.85729  -4.118 4.01e-05 ***
## parqueaderos   84.14232    4.89007  17.207  < 2e-16 ***
## banios         57.16639    4.08864  13.982  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 100.3 on 1695 degrees of freedom
## Multiple R-squared:  0.7657, Adjusted R-squared:  0.765 
## F-statistic:  1108 on 5 and 1695 DF,  p-value: < 2.2e-16

EVALUACIÓN DE SUPUESTOS - CASO 2

Outliers

# Residuales para modelo2
residuos2 <- residuals(modelo2)

# Crear un dataframe con los residuos
dfr2 <- data.frame(residuos = residuos2)

# Crear el gráfico de caja para modelo2
p2 <- plot_ly(dfr2, y = ~residuos, type = "box", name = "Modelo 2") %>%
  layout(title = "Gráfico de caja de los residuos - Modelo 2",
         yaxis = list(title = "Residuos"))

p2
# Crear el gráfico de dispersión para modelo2
plot_ly(dfr2, x = ~1:length(residuos2), y = ~residuos, mode = "markers", type = "scatter", 
        marker = list(size = 5, color = 'rgba(135, 206, 235, .9)', line = list(color = 'rgba(25, 25, 112, .8)', width = 1))) %>%
  layout(title = "Gráfico de dispersión de los residuos - Modelo 2", xaxis = list(title = "Observación"), yaxis = list(title = "Residuos"))

Se evidencian outliers, tanto en el scatterplot como en el boxplot. Respecto al número de outliers en este caso se revisa lo siguiente:

# Cálculo del IQR
Q1_2 <- quantile(residuos2, 0.25)
Q3_2 <- quantile(residuos2, 0.75)
IQR_2 <- Q3_2 - Q1_2

# Identificación de outliers
indices_outliers2 <- which((residuos2 < (Q1_2 - 1.5 * IQR_2)) | (residuos2 > (Q3_2 + 1.5 * IQR_2)))

# Contar el número de outliers
num_outliers2 <- length(indices_outliers2)

# Imprimir el número de outliers para modelo2
print(paste("Número de outliers para modelo2: ", num_outliers2))
## [1] "Número de outliers para modelo2:  80"

Por la cantidad de registros que se tienen de apartamentos, 80 parece ser una cantidad que podría retirarse de los registros para evaluar si es posible con esto mejorar el ajuste del modelo. Sin embargo aclarar que lo anterior no va a causar ningún efecto a menos que algunos de estos outliers sean puntos influyentes, por el momento se trabajará con los datos que se tienen disponibles.

Normalidad

# Test de Shapiro - Wilk para modelo2
shapiro2 <- shapiro.test(residuos2)
print(shapiro2)
## 
##  Shapiro-Wilk normality test
## 
## data:  residuos2
## W = 0.77252, p-value < 2.2e-16

Prueba de normalidad de Shapiro-Wilk: Esta prueba se utiliza para determinar si una muestra de datos se ajusta a una distribución normal. En este caso, el valor p es extremadamente pequeño (< 2.2e-16), lo que indica que podemos rechazar la hipótesis nula de que los residuos siguen una distribución normal. Por lo tanto, los residuos del modelo2 no parecen ser normalmente distribuidos.

Homocedasticidad

# Prueba Breusch-Pagan para la homocedasticidad para modelo2
bptest2 <- bptest(modelo2)
print(bptest2)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo2
## BP = 633.17, df = 5, p-value < 2.2e-16

Prueba de Breusch-Pagan: Esta prueba se utiliza para detectar la heterocedasticidad en un modelo de regresión lineal. En este caso, el valor p es muy pequeño (< 2.2e-16), lo que indica que podemos rechazar la hipótesis nula de homocedasticidad. Por lo tanto, parece haber heterocedasticidad en los residuos del modelo2, lo que significa que la varianza de los errores no es constante.

Autocorrelación

# Prueba Durbin-Watson para la no autocorrelación para modelo2
dwtest2 <- dwtest(modelo2)
print(dwtest2)
## 
##  Durbin-Watson test
## 
## data:  modelo2
## DW = 1.5854, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

Prueba de Durbin-Watson: Esta prueba se utiliza para detectar la presencia de autocorrelación en los residuos de un modelo de regresión. En este caso, el valor p es pequeño (< 2.2e-16), lo que indica que podemos rechazar la hipótesis nula de que no hay autocorrelación en los residuos. Por lo tanto, parece haber autocorrelación en los residuos del modelo2.

USO DE LOS MODELOS - PREDICCIONES

Caso 1

# Crear un dataframe con las características del caso 1
caso1 <- data.frame(areaconst = 200, estrato = 4, habitaciones = 4, parqueaderos = 1, banios = 2)

# Predecir el precio para el caso 1 usando modelo1
prediccion1 <- predict(modelo1, newdata = caso1)

# Imprimir la predicción para el caso 1
print(paste("Predicción del precio para el caso 1: ", prediccion1))
## [1] "Predicción del precio para el caso 1:  311.357069200076"

El modelo muestra que con las características solicitadas de la casa en el norte de la ciudad, es factible conseguir una propiedad con 311 millones de COP aproximandamente por lo que los 350 pre aprobados por el banco son más que sufiente para responder a la propuesta en su forma más básica.

# Crear un dataframe con las características del caso 1
caso1_2 <- data.frame(areaconst = 200, estrato = 5, habitaciones = 4, parqueaderos = 1, banios = 2)

# Predecir el precio para el caso 1 usando modelo1
prediccion1_2 <- predict(modelo1, newdata = caso1_2)

# Imprimir la predicción para el caso 1
print(paste("Predicción del precio para el caso 1: ", prediccion1_2))
## [1] "Predicción del precio para el caso 1:  377.476854183966"

Sin embargo el modelo muestra que si se incrementa el estrato de 4 a 5, este pre aprobado deja de ser suficiente ya que una vivienda de las mismas características en dicho estrato, se exedería por casi 27 millones de COP.

Caso 2

# Crear un dataframe con las características del caso 2
caso2 <- data.frame(areaconst = 300, estrato = 5, habitaciones = 5, parqueaderos = 3, banios = 3)

# Predecir el precio para el caso 2 usando modelo2
prediccion2 <- predict(modelo2, newdata = caso2)

# Imprimir la predicción para el caso 2
print(paste("Predicción del precio para el caso 2: ", prediccion2))
## [1] "Predicción del precio para el caso 2:  699.985401565066"

El modelo muestra que en las condiciones básicas de la solicitud, es más que suficiente un pre aprobado por 850 millones de COP.

# Crear un dataframe con las características del caso 2
caso2_2 <- data.frame(areaconst = 300, estrato = 6, habitaciones = 5, parqueaderos = 3, banios = 3)

# Predecir el precio para el caso 2 usando modelo2
prediccion2_2 <- predict(modelo2, newdata = caso2_2)

# Imprimir la predicción para el caso 2
print(paste("Predicción del precio para el caso 2: ", prediccion2_2))
## [1] "Predicción del precio para el caso 2:  755.118629299215"

Si la vivienda fuera del estrato 6, esta con las mismas condiciones de la anterior, tendría un precio aproximado de 755 millones de COP. Por lo que para el segundo caso, con el pre aprobado es suficiente para conseguir un apartamento en el sur de la ciudad que cumpla con los requisitos deseados.

REVISIÓN DE LA OFERTA

Caso 1

# Para new_data1
new_data1_filtered <- new_datos1 %>%
  filter(
    areaconst >= 200,
    parqueaderos >= 1,
    banios >= 2,
    habitaciones >= 4,
    estrato %in% c(4, 5),
    preciom <= 350 )
# Para new_data1_filtered
set.seed(123)  # Establece una semilla para reproducibilidad
sample_new_data1_filtered <- new_data1_filtered %>%
  sample_n(5)

# Imprime la muestra
print(sample_new_data1_filtered)
##     id       zona estrato preciom areaconst parqueaderos banios habitaciones
## 1 3043 Zona Norte       5     330       275            2      3            5
## 2 1887 Zona Norte       5     340       203            2      3            4
## 3 1163 Zona Norte       5     350       216            2      2            4
## 4 4800 Zona Norte       5     340       250            2      4            4
## 5 1343 Zona Norte       5     320       200            2      4            4
##   tipo         x       y
## 1 Casa -76.52350 3.48329
## 2 Casa -76.51803 3.48257
## 3 Casa -76.51218 3.48181
## 4 Casa -76.53300 3.46500
## 5 Casa -76.51524 3.48893

Finalmente, se construyen los mapas de las ofertas que concuerdan con los requerimientos de la solicitud.

# ID específicos que quieres mostrar
ids_especificos <- c(3043, 1887, 1163, 4800, 1343)

# Filtrar por ID específicos
vivienda_filtrada1_ids <- vivienda_filtrada1 %>% filter(id %in% ids_especificos)

# Plot para vivienda filtrada
ggplot() +
  geom_sf(data = shp_bcali_sf, fill = "white") +
  geom_sf(data = comunas_filtradas, fill = NA, color = "black") +
  geom_sf(data = vivienda_filtrada1_ids, aes(color = tipo), size = 1) +
  scale_color_manual(values = colores) +
  geom_text(data = vivienda_filtrada1_ids, aes(x = x, y = y, label = id), check_overlap = TRUE, size = 3) +
  theme_minimal() +
  labs(title = "Zona Norte", x = "Longitud", y = "Latitud", color = "Tipo de vivienda")

Caso 2

# Para new_data2
new_data2_filtered <- new_datos2 %>%
  filter(
    areaconst >= 300,
    parqueaderos >= 3,
    banios >= 3,
    habitaciones >= 5,
    estrato %in% c(5, 6),
    preciom <= 850 )

new_data2_filtered
##  [1] id           zona         estrato      preciom      areaconst   
##  [6] parqueaderos banios       habitaciones tipo         x           
## [11] y           
## <0 rows> (or 0-length row.names)

Aunque el modelo logra predecir el precio del apartamento, el catalogo no evidencia tener oferta que satisfaga al cliente en este aspecto.

vivienda %>%
     filter(
         areaconst >= 300,
         parqueaderos >= 3,
         banios >= 3,
         habitaciones >= 5,
         estrato %in% c(5, 6),
         preciom <= 850,
         tipo == "Apartamento")
## # A tibble: 3 × 12
##      id zona    estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <dbl> <chr>     <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1  7182 Zona S…       5     730       573            3      8            5 Apar…
## 2  7512 Zona S…       5     670       300            3      5            6 Apar…
## 3  3572 Zona N…       5     450       307            4      4            5 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Haciendo una revisión más profunda, se encuentra que en el catalogo hay dos apartamentos que afirman estar en la zona sur, sin embargo, el nombre de su barrio, y el valor de su latitud, no concuerdan con lo zona en la que se cree, se encuentran ubicados.

Se debe revisar si el cliente tiene algún tipo de flexibilidad en algún aspecto distinto al estrato, ya que de lo contrario no habría una oferta posible para este caso de venta. El factor decisivo encontrado fue el número de habitaciones, algo que el modelo no está reflejando apropiadamente en el segundo modelo.

CONCLUSIONES

El modelo sin corrección de supuestos permite generar predicciones, pero estas no resultan del todo precisas, ya que apenas modela los datos necesarios para determinar el precio de una vivienda.

Es evidente que existe una relación entre las variables precio y área, tanto para casas como para apartamentos, como muestra el gráfico de correlaciones. La correlación en ambos casos es la más alta.

Resulta extraño que en el segundo modelo, el valor del coeficiente relacionado al número de habitaciones se muestre con signo negativo. Al revisar los datos, se puede observar que las viviendas con más habitaciones suelen ser más costosas. Quizás el problema se centra en un posible desequilibrio de las categorías de esa variable, de lo contrario, no se entiende cómo esto es posible.

El primer modelo no logra tener un buen nivel explicativo sobre la variable precio, mientras que el segundo modelo apenas alcanza un nivel decente para esta tarea.

Aunque no se realizaron transformaciones, estas no garantizan que el modelo mejore. Por el contrario, suelen complicar la interpretación del mismo. Lo mejor sería cambiar el modelo a utilizar por uno que se adapte mejor a los datos, como algún modelo espacial, o que realice ponderaciones y que no esté basado en supuestos de normalidad y linealidad.

La espacialidad juega un papel importante en este caso, ya que los datos no garantizan que las viviendas estén ubicadas realmente donde se supone que deberían estar. Sin embargo, con los datos disponibles, aunque se pueden desarrollar estrategias para minimizar el error de esto, no se garantiza que las viviendas realmente estén bien ubicadas.

Finalmente, es importante destacar que aunque el modelo sea capaz de predecir la variable dependiente, esto no garantiza que haya una vivienda dentro del catálogo que satisfaga las necesidades específicas del cliente, lo que puede llevar a un terreno que obliga a negociar.

ANEXOS

Algunas métricas que valdría la pena revisar son:

library(Metrics)  # para MAE

# Asume que 'datos' es tu conjunto de datos y 'y' es tu variable dependiente
predicciones_modelo1 <- predict(modelo1, newdata = new_datos1)
predicciones_modelo2 <- predict(modelo2, newdata = new_datos2)

# MAE
mae_modelo1 <- mae(new_datos1$preciom, predicciones_modelo1)
mae_modelo2 <- mae(new_datos2$preciom, predicciones_modelo2)

# AIC y BIC
aic_modelo1 <- AIC(modelo1)
bic_modelo1 <- BIC(modelo1)

aic_modelo2 <- AIC(modelo2)
bic_modelo2 <- BIC(modelo2)

# R cuadrado
r2_modelo1 <- summary(modelo1)$r.squared
r2_modelo2 <- summary(modelo2)$r.squared

# Imprimir las métricas
cat(paste("Modelo 1: MAE =", mae_modelo1, ", AIC =", aic_modelo1, ", BIC =", bic_modelo1, ", R2 =", r2_modelo1), "\n")
## Modelo 1: MAE = 107.623391731262 , AIC = 3895.8023083472 , BIC = 3921.65843331881 , R2 = 0.55832639127086
cat(paste("Modelo 2: MAE =", mae_modelo2, ", AIC =", aic_modelo2, ", BIC =", bic_modelo2, ", R2 =", r2_modelo2), "\n")
## Modelo 2: MAE = 60.0423687765542 , AIC = 20511.3789751918 , BIC = 20549.4517763386 , R2 = 0.765683245174864

Algo intersante también podría ser revisar el modelo sin intercepto, ya que este carece de sentido al ser negativo y no iniciar directamente en cero.

# Estimar los modelos de regresión lineal múltiple sin interceptos
modelo1_sin_intercepto <- lm(preciom ~ 0 + areaconst + estrato + habitaciones + parqueaderos + banios, data = new_datos1)
modelo2_sin_intercepto <- lm(preciom ~ 0 + areaconst + estrato + habitaciones + parqueaderos + banios, data = new_datos2)

# Predecir con los nuevos modelos
predicciones_modelo1_sin_intercepto <- predict(modelo1_sin_intercepto, newdata = new_datos1)
predicciones_modelo2_sin_intercepto <- predict(modelo2_sin_intercepto, newdata = new_datos2)

# Calcular MAE
mae_modelo1_sin_intercepto <- mae(new_datos1$preciom, predicciones_modelo1_sin_intercepto)
mae_modelo2_sin_intercepto <- mae(new_datos2$preciom, predicciones_modelo2_sin_intercepto)

# Calcular AIC y BIC
aic_modelo1_sin_intercepto <- AIC(modelo1_sin_intercepto)
bic_modelo1_sin_intercepto <- BIC(modelo1_sin_intercepto)

aic_modelo2_sin_intercepto <- AIC(modelo2_sin_intercepto)
bic_modelo2_sin_intercepto <- BIC(modelo2_sin_intercepto)

# Calcular R cuadrado
r2_modelo1_sin_intercepto <- summary(modelo1_sin_intercepto)$r.squared
r2_modelo2_sin_intercepto <- summary(modelo2_sin_intercepto)$r.squared

# Imprimir las métricas
cat(paste("Modelo 1 sin intercepto: MAE =", mae_modelo1_sin_intercepto, ", AIC =", aic_modelo1_sin_intercepto, ", BIC =", bic_modelo1_sin_intercepto, ", R2 =", r2_modelo1_sin_intercepto), "\n")
## Modelo 1 sin intercepto: MAE = 108.478943976458 , AIC = 3901.57244259177 , BIC = 3923.73483542459 , R2 = 0.916565051377911
cat(paste("Modelo 2 sin intercepto: MAE =", mae_modelo2_sin_intercepto, ", AIC =", aic_modelo2_sin_intercepto, ", BIC =", bic_modelo2_sin_intercepto, ", R2 =", r2_modelo2_sin_intercepto), "\n")
## Modelo 2 sin intercepto: MAE = 64.4612322869546 , AIC = 20701.7339323885 , BIC = 20734.3677619429 , R2 = 0.927095990885046

Se observa que el modelo sin intercepto si se compara con los modelos anteriores, se mantiene muy similar respecto a métricas como el MAE, AIC, y BIC, pero mejora mucho respecto a su nivel explicativo según el R cuadrado.