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.
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)
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)
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.
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.
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.
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:
La zona sur de la ciudad está compuesta por las comunas 17,y 22, pero se agrega la 18 al contrastar la información de ambos enlaces.
La zona norte de la ciudad está compuesta por las comunas 1, 2 y 3, además de algunos barrios, pero se decide solo trabajar con las viviendas ubicadas en estas comunas, y no tener en cuenta estos barrios, ya que se desconoce sus nombre exactos.
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")
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.
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.
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.
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)
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.
# 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.
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")
# 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.
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
El modelo de regresión lineal para las casas en el norte de la
ciudad muestra que el área construida (areaconst), el
estrato, y el número de parqueaderos tienen una relación
estadísticamente significativa con el precio (preciom), con
valores p menores a 0.05.
Los coeficientes para areaconst,
estrato y parqueaderos son positivos. Esto
indica que un aumento en cualquiera de estas variables, manteniendo
constantes las demás, se espera que resulte en un aumento en el precio
de la casa. El tamaño del aumento se corresponderá con el valor del
coeficiente de la variable en cuestión. Por ejemplo, por cada aumento de
una unidad en el área construida (areaconst), se espera un
aumento en el precio de la casa igual al coeficiente de
areaconst.
El número de habitaciones y baños no son estadísticamente significativos en este modelo, lo que sugiere que no tienen un efecto significativo en el precio de las casas en el norte de la ciudad, al menos en este modelo.
El R cuadrado ajustado del modelo es 0.5507, lo que indica que aproximadamente el 55.07% de la variabilidad en el precio de las casas se puede explicar por las variables incluidas en el modelo.
# 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.
# 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.
# 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.
# 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.
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.
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.
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.
# 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
El modelo de regresión lineal para los apartamentos en el sur de
la ciudad muestra que todas las variables, incluyendo el área construida
(areaconst), el estrato, el número de habitaciones,
parqueaderos y baños, tienen una relación estadísticamente significativa
con el precio (preciom), con valores p menores a
0.05.
El coeficiente para areaconst, estrato,
parqueaderos, banios es positivo, lo que
indica que un aumento en cualquiera de estas variables, manteniendo las
demás constantes, resulta en un aumento en el precio del apartamento. El
aumento en el precio se corresponderá con el valor del coeficiente de la
variable en cuestión.
El coeficiente para habitaciones es -20.00029, lo
que indica que un aumento en el número de habitaciones está asociado con
una disminución en el precio del apartamento. Lo cúal es algo extraño ya
que la realidad es que un apartamento entre más habitaciones posee más
costoso suele ser.
El R cuadrado ajustado del modelo es 0.765, lo que indica que aproximadamente el 76.5% de la variabilidad en el precio de los apartamentos se puede explicar por las variables incluidas en el modelo.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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")
# 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.
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.
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.