María comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se trasladó a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad.
Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo (no más de dos páginas) donde analice los dos casos y sus recomendaciones. Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos.
#install.packages("devtools") # solo la primera vez
devtools::install_github("dgonxalex80/paqueteMOD", force =TRUE)
## fs (1.6.0 -> 1.6.1) [CRAN]
## cli (3.6.0 -> 3.6.1) [CRAN]
## cachem (1.0.6 -> 1.0.7) [CRAN]
## fastmap (1.1.0 -> 1.1.1) [CRAN]
## htmltools (0.5.4 -> 0.5.5) [CRAN]
## httpuv (1.6.8 -> 1.6.9) [CRAN]
## xfun (0.36 -> 0.38 ) [CRAN]
##
## There are binary versions available but the source versions are later:
## binary source needs_compilation
## cli 3.6.0 3.6.1 TRUE
## htmltools 0.5.4 0.5.5 TRUE
## xfun 0.37 0.38 TRUE
##
## Binaries will be installed
## package 'fs' successfully unpacked and MD5 sums checked
## package 'cli' successfully unpacked and MD5 sums checked
## package 'cachem' successfully unpacked and MD5 sums checked
## package 'fastmap' successfully unpacked and MD5 sums checked
## package 'htmltools' successfully unpacked and MD5 sums checked
## package 'httpuv' successfully unpacked and MD5 sums checked
## package 'xfun' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\oscar\AppData\Local\Temp\RtmpacCrJV\downloaded_packages
## ── R CMD build ─────────────────────────────────────────────────────────────────
##
checking for file 'C:\Users\oscar\AppData\Local\Temp\RtmpacCrJV\remotesd5741d35e7d\dgonxalex80-paqueteMOD-dc10c86/DESCRIPTION' ...
checking for file 'C:\Users\oscar\AppData\Local\Temp\RtmpacCrJV\remotesd5741d35e7d\dgonxalex80-paqueteMOD-dc10c86/DESCRIPTION' ...
✔ checking for file 'C:\Users\oscar\AppData\Local\Temp\RtmpacCrJV\remotesd5741d35e7d\dgonxalex80-paqueteMOD-dc10c86/DESCRIPTION'
##
─ preparing 'paqueteMOD':
##
checking DESCRIPTION meta-information ...
checking DESCRIPTION meta-information ...
✔ checking DESCRIPTION meta-information
##
─ checking for LF line-endings in source and make files and shell scripts
##
─ checking for empty or unneeded directories
##
─ building 'paqueteMOD_0.0.0.1.tar.gz'
##
##
library(paqueteMOD)
data("vivienda")
#install.packages("leaflet")
#install.packages("fontawesome")
#install.packages("caret")
#install.packages("car")
#install.packages("gtable")
library(dplyr)
library(plotly)
library(paqueteMOD)
library(caret)
library(GGally)
Realice un filtro a la base de datos e incluya solo las ofertas de: base1: casas, de la zona norte de la ciudad. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. ¿Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por qué?).
Creamos una nueva variable llamada base la cual ubica en Base 1 a Base 5 las casas dependiendo de su zona.
attach(vivienda)
vivienda$base <- ifelse(vivienda$zona =="Zona Norte" & vivienda$tipo =="Casa", "Base 1",
ifelse(vivienda$zona =="Zona Centro" & vivienda$tipo =="Casa", "Base 2",
ifelse(vivienda$zona =="Zona Oeste" & vivienda$tipo =="Casa", "Base 3",
ifelse(vivienda$zona =="Zona Oriente" & vivienda$tipo =="Casa", "Base 4",
ifelse(vivienda$zona == "Zona Sur" & vivienda$tipo == "Casa", "Base 5", "NA")))))
head(vivienda,3)
## # A tibble: 3 × 14
## 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
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
library(dplyr)
# Filtro para la base 1: casas de la zona norte de la ciudad
base1 <- filter(vivienda, zona == "Zona Norte" & tipo == "Casa")
# Mostrar los primeros 3 registros y una tabla
head(base1, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4057 Zona N… 02 6 750 445 NA 7 6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 2: casas de la zona centro de la ciudad
base2 <- filter(vivienda, zona == "Zona Centro" & tipo == "Casa")
# Mostrar los primeros 3 registros y una tabla
head(base2, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5298 Zona C… 01 3 650 240 2 4 4
## 2 5107 Zona C… 02 4 400 460 NA 5 7
## 3 5117 Zona C… 02 3 380 290 NA 4 8
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 3: casas de la zona oeste de la ciudad
base3 <- filter(vivienda, zona == "Zona Oeste" & tipo == "Casa")
# Mostrar los primeros 3 registros y una tabla
head(base3, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6928 Zona O… 03 6 1850 302 4 4 3
## 2 7510 Zona O… 03 6 1950 400 4 5 3
## 3 7586 Zona O… 03 6 870 275 3 5 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 4: casas de la zona oriente de la ciudad
base4 <- filter(vivienda, zona == "Zona Oriente" & tipo == "Casa")
# Mostrar los primeros 3 registros y una tabla
head(base4, 3)
## # A tibble: 3 × 14
## 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
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 5: casas de la zona sur de la ciudad
base5 <- filter(vivienda, zona == "Zona Sur" & tipo == "Casa")
# Mostrar los primeros 3 registros y una tabla
head(base5, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5992 Zona S… 02 4 400 280 3 5 3
## 2 5157 Zona S… 02 3 500 354 1 2 4
## 3 5501 Zona S… 02 3 175 102 NA 2 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
Los colores correspondientes para cada zona son:
En la visualización del mapa se evidencian puntos de colores en zonas que no les corresponde, esto se puede dar porque desde el set las zonas de algunos registros no corresponden con la ubicación cartográfica dada por la longitud y latitud. Además, como solo tenemos 5 puntos cardinales y no se tienen en cuenta los puntos intermedios las ubicaciones que estén entre 2 de ellos van a tender a alguno de los dos generando esta variación en algunas zonas específicas.
library(leaflet)
# Filtrar solo las viviendas de tipo Casa
vivienda_casas <- subset(vivienda, tipo == "Casa" & zona=="Zona Norte")
# Convertir la variable "base" en una variable de tipo carácter
vivienda_casas$base <- as.character(vivienda_casas$base)
# Usar colorFactor() para asignar automáticamente un color único a cada nivel de la variable categórica
colores <- colorFactor(palette = "Set1", domain = vivienda_casas$base)
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa <- leaflet(vivienda_casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(base), radius = 4)
# Mostrar el mapa
mapa
Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio del apartamento) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.
La variable parqueaderos es la que tiene más valores vacíos con 269 por lo que estos valores NA serán reemplazados con 0 indicando que la casa no tiene parqueadero, de resto las demás variables tienen entre 3 y 2 datos vacíos. Tiene 8 variables numéricas, 5 categóricas y una de tipo character.
Revisión grafica de las variables a utilizar en el modelo de regresión lineal múltiple:
Encontramos que la mayoría de las casas de la Zona Norte tienen un precio menor a 1000 millones, lo que genera que el grafico se desplace hacia la izquierda.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_casas$preciom)
##
## Shapiro-Wilk normality test
##
## data: vivienda_casas$preciom
## W = 0.86297, p-value < 2.2e-16
plot_ly(vivienda_casas, x = ~preciom, type = "histogram")%>%
layout(title = "Distribución de precios")
Encontramos observaciones de habitaciones con valores de 0 y esto no es real puesto que cualquier casa tendría por lo menos una habitación. Por lo que procedemos a eliminar los registros con cero habitaciones.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_casas$habitaciones)
##
## Shapiro-Wilk normality test
##
## data: vivienda_casas$habitaciones
## W = 0.90995, p-value < 2.2e-16
plot_ly(vivienda_casas, x = ~habitaciones, type = "histogram") %>%
layout(title = "Distribución de habitaciones")
vivienda_casas=vivienda_casas[vivienda_casas$habitaciones!=0,]
summary(vivienda_casas$habitaciones)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 3.000 4.000 4.635 5.000 10.000
Encontramos registros de casas que tienen cero baños, esto no puede ser un dato real puesto que no sería conveniente utilizar una casa que no tenga baño. Por lo que procedemos a eliminar los registros con cero baños.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_casas$banios)
##
## Shapiro-Wilk normality test
##
## data: vivienda_casas$banios
## W = 0.92163, p-value < 2.2e-16
plot_ly(vivienda_casas, x = ~banios, type = "histogram") %>%
layout(title = "Distribución de baños")
vivienda_casas=vivienda_casas[vivienda_casas$banios!=0,]
summary(vivienda_casas$banios)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.0 2.0 3.0 3.6 4.0 10.0
Se evidencia que la mayoría de las casas y apartamentos se encuentran situados en estratos 5.
vivienda_casas$estrato <- ifelse(vivienda_casas$estrato == 3, "Estrato3", vivienda_casas$estrato)
vivienda_casas$estrato <- ifelse(vivienda_casas$estrato == 4, "Estrato4", vivienda_casas$estrato)
vivienda_casas$estrato <- ifelse(vivienda_casas$estrato == 5, "Estrato5", vivienda_casas$estrato)
vivienda_casas$estrato <- ifelse(vivienda_casas$estrato == 6, "Estrato6", vivienda_casas$estrato)
plot_ly(vivienda_casas, x = ~estrato,y=~estrato, type = "bar") %>%
layout(title = "Distribución de estrato")
Encontramos que el valor mínimo de área es de 30 y el máximo de 1440 metros cuadrados y la mayoría de los registros se encuentran desplazados hacia la izquierda con valores menores a los 600 metros cuadrados.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_casas$areaconst)
##
## Shapiro-Wilk normality test
##
## data: vivienda_casas$areaconst
## W = 0.85991, p-value < 2.2e-16
plot_ly(vivienda_casas, x = ~areaconst, type = "histogram") %>%
layout(title = "Distribución de areaconst")
summary(vivienda_casas$areaconst)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 30.0 140.0 240.0 262.1 336.0 1440.0
Encontramos que para parqueaderos teníamos 269 datos faltantes, pero en este caso el valor faltante de parqueadero significa que la casa no tiene parqueadero, por lo que lo reemplazamos por 0
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_casas$parqueaderos)
##
## Shapiro-Wilk normality test
##
## data: vivienda_casas$parqueaderos
## W = 0.76961, p-value < 2.2e-16
summary(vivienda_casas$parqueaderos)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.000 1.000 2.000 2.181 3.000 10.000 269
vivienda_casas$parqueaderos <- ifelse(is.na(vivienda_casas$parqueaderos), 0, vivienda_casas$parqueaderos)
summary(vivienda_casas$parqueaderos)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 0.000 1.000 1.343 2.000 10.000
plot_ly(vivienda_casas, x = ~parqueaderos,y=~parqueaderos, type = "bar") %>%
layout(title = "Distribución de Parqueaderos")
summary(vivienda_casas)
## id zona piso estrato
## Min. : 58.0 Length:700 Length:700 Length:700
## 1st Qu.: 750.8 Class :character Class :character Class :character
## Median :2183.5 Mode :character Mode :character Mode :character
## Mean :2549.3
## 3rd Qu.:4209.2
## Max. :8319.0
## preciom areaconst parqueaderos banios
## Min. : 89.0 Min. : 30.0 Min. : 0.000 Min. : 1.0
## 1st Qu.: 256.0 1st Qu.: 140.0 1st Qu.: 0.000 1st Qu.: 2.0
## Median : 390.0 Median : 240.0 Median : 1.000 Median : 3.0
## Mean : 443.2 Mean : 262.1 Mean : 1.343 Mean : 3.6
## 3rd Qu.: 550.0 3rd Qu.: 336.0 3rd Qu.: 2.000 3rd Qu.: 4.0
## Max. :1940.0 Max. :1440.0 Max. :10.000 Max. :10.0
## habitaciones tipo barrio longitud
## Min. : 1.000 Length:700 Length:700 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.53
## Median : 4.000 Mode :character Mode :character Median :-76.52
## Mean : 4.633 Mean :-76.52
## 3rd Qu.: 5.000 3rd Qu.:-76.50
## Max. :10.000 Max. :-76.47
## latitud base
## Min. :3.337 Length:700
## 1st Qu.:3.455 Class :character
## Median :3.469 Mode :character
## Mean :3.461
## 3rd Qu.:3.482
## Max. :3.496
str(vivienda_casas)
## spc_tbl_ [700 × 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:700] 1209 1592 4057 4460 6081 ...
## $ zona : chr [1:700] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
## $ piso : chr [1:700] "02" "02" "02" "02" ...
## $ estrato : chr [1:700] "Estrato5" "Estrato5" "Estrato6" "Estrato4" ...
## $ preciom : num [1:700] 320 780 750 625 750 600 420 490 230 180 ...
## $ areaconst : num [1:700] 150 380 445 355 237 160 200 118 160 120 ...
## $ parqueaderos: num [1:700] 2 2 0 3 2 1 4 2 0 0 ...
## $ banios : num [1:700] 4 3 7 5 6 4 4 4 2 3 ...
## $ habitaciones: num [1:700] 6 3 6 5 6 5 5 4 3 3 ...
## $ tipo : chr [1:700] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:700] "acopi" "acopi" "acopi" "acopi" ...
## $ longitud : num [1:700] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:700] 3.48 3.49 3.39 3.41 3.37 ...
## $ base : chr [1:700] "Base 1" "Base 1" "Base 1" "Base 1" ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Como los valores faltantes de las variables preciom, areaconst, estrato, banio, habitaciones son pocos entre 2 y 3 se procede a eliminar estos valores para la futura creación de un modelo.
vivienda1 <- na.omit(vivienda_casas[c("preciom","areaconst","banios","habitaciones")])
Una vez todas las variables son del tipo correcto realizamos el análisis de correlación, para el caso de preciom y las variables predictoras habitaciones, banios, estrato, areaconst obtuvimos los siguientes resultados:
preciom y habitaciones: Esto indica una correlación positiva débil, lo que sugiere que a medida que el número de habitaciones en una propiedad aumenta, el precio también tiende a aumentar, pero la relación no es muy fuerte.
preciom y banios: Esto indica una correlación positiva moderada, lo que sugiere que a medida que el número de baños en una propiedad aumenta, el precio también tiende a aumentar, y la relación es un poco más fuerte que la correlación entre precio y habitaciones.
preciom y areacost: Esto indica una correlación positiva fuerte, lo que sugiere que a medida que el tamaño de la propiedad (área construida) aumenta, el precio también tiende a aumentar, y la relación es significativamente más fuerte que la correlación entre precio y número de habitaciones o baños.
Luego procedemos a revisar la correlación que tienen las variables predictoras entre sí:
areaconst y habiaciones: Esto indica una correlación positiva débil, lo que sugiere que a medida que el número de habitaciones en una propiedad aumenta, el tamaño de la propiedad (área construida) también tiende a aumentar, pero la relación no es muy fuerte.
areaconst y banios: Esto indica una correlación positiva moderada, lo que sugiere que a medida que el número de baños en una propiedad aumenta, el tamaño de la propiedad (área construida) también tiende a aumentar, y la relación es un poco más fuerte que la correlación entre área construida y número de habitaciones.
banios y habitaciones: Esto indica una correlación positiva moderada, lo que sugiere que a medida que el número de baños en una propiedad aumenta, el número de habitaciones también tiende a aumentar, y la relación es un poco más fuerte que la correlación entre precio y número de habitaciones.
#install.packages("GGally", dependencies = TRUE)
library(GGally)
cor_1=vivienda1[,c("preciom","areaconst","banios","habitaciones")]
gcor<-ggplotly(ggpairs(cor_1))
gcor
Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f (área construida, estrato, numero de cuartos, numero de parqueaderos, numero de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).
Conclusión:
Encontramos que el modelo paso a paso presenta un R cuadrado de 0.6605. Esto indica el modelo explica un 66% de la variable dependiente a partir de las variables independientes, este valor es bastante bueno, pero solo aplica para el caso en que el modelo conoce los resultados de la variable dependiente, también para mejorar este valor podría ser necesario incluir más variables independientes o explorar las posibles relaciones entre las variables.
Para escoger el modelo con el que nos quedaremos, vamos a realizar una validación cruzada y ver quien obtiene los mejores resultados.
La fórmula del modelo es la siguiente:
preciom = 25.29855 + 0.79867(areaconst) + 72.97539(estratoEstrato4) + 135.66243(estratoEstrato5) + 318.74219(estratoEstrato6) + 32.47268(banios)
El coeficiente de determinación ajustado (Adjusted R-squared) en este modelo es 0.6605. Este valor indica que el modelo explica el 66.05% de la variación en los precios de las viviendas, una vez que se ha tenido en cuenta el número de variables incluidas en el modelo.
# se define el modelo ingenuo y= b0
modelo_b0<- lm(preciom ~ 1, data=vivienda_casas)
#define model with all predictors
modelo_all <- lm(preciom ~ habitaciones + banios + areaconst + estrato + parqueaderos, data = vivienda_casas)
# Se aplica el proceso forward stepwise regression
forward <- step(modelo_b0, direction='forward', scope=formula(modelo_all), trace=0)
# Visualización de los resultados
forward$anova
## Step Df Deviance Resid. Df Resid. Dev AIC
## 1 NA NA 699 50116309 7827.144
## 2 + areaconst -1 26740934 698 23375376 7295.279
## 3 + estrato -3 5415885 695 17959491 7116.784
## 4 + banios -1 1066850 694 16892641 7075.916
# resultado final del modelo
summary(forward)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios, data = vivienda_casas)
##
## Residuals:
## Min 1Q Median 3Q Max
## -935.28 -73.16 -16.53 47.44 1087.04
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 25.29855 16.06822 1.574 0.116
## areaconst 0.79867 0.04354 18.345 < 2e-16 ***
## estratoEstrato4 72.97539 17.23530 4.234 2.60e-05 ***
## estratoEstrato5 135.66243 15.89866 8.533 < 2e-16 ***
## estratoEstrato6 318.74219 26.25005 12.143 < 2e-16 ***
## banios 32.47268 4.90496 6.620 7.18e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156 on 694 degrees of freedom
## Multiple R-squared: 0.6629, Adjusted R-squared: 0.6605
## F-statistic: 273 on 5 and 694 DF, p-value: < 2.2e-16
Tradicionalmente la separación del set de entrenamiento y prueba se realiza en proporción de 80% / 20% y esta será la proporción que utilizaremos para la validación cruzada, realizamos 1000 iteraciones lo que nos dio como resultado un MAE de 98.10 millones, un R cuadrado de 66.05% y un MAPE de 22%. Teniendo en cuenta las unidades que estamos manejando este no sería un modelo robusto para la predicción de las variables, pero si nos presenta un acercamiento y una tendencia determinados por un MAE de 98.10 millones o un MAPE de 22%.
set.seed(1)
control=trainControl(method = "LGOCV",number=1000,p=0.8)
modelo1_vc=train(preciom ~ areaconst + estrato + banios,data=vivienda_casas,method="lm",trControl=control,metric="MAE")
modelo1_vc
## Linear Regression
##
## 700 samples
## 3 predictor
##
## No pre-processing
## Resampling: Repeated Train/Test Splits Estimated (1000 reps, 80%)
## Summary of sample sizes: 561, 561, 561, 561, 561, 561, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 157.1353 0.6579119 98.10216
##
## Tuning parameter 'intercept' was held constant at a value of TRUE
summary(modelo1_vc)
##
## Call:
## lm(formula = .outcome ~ ., data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -935.28 -73.16 -16.53 47.44 1087.04
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 25.29855 16.06822 1.574 0.116
## areaconst 0.79867 0.04354 18.345 < 2e-16 ***
## estratoEstrato4 72.97539 17.23530 4.234 2.60e-05 ***
## estratoEstrato5 135.66243 15.89866 8.533 < 2e-16 ***
## estratoEstrato6 318.74219 26.25005 12.143 < 2e-16 ***
## banios 32.47268 4.90496 6.620 7.18e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156 on 694 degrees of freedom
## Multiple R-squared: 0.6629, Adjusted R-squared: 0.6605
## F-statistic: 273 on 5 and 694 DF, p-value: < 2.2e-16
MAPE = 98.10216/mean(vivienda_casas$preciom)*100
MAPE
## [1] 22.13639
Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas solo realizar sugerencias de que se podría hacer).
par(mfrow = c(2, 2))
plot(forward)
###Normalidad
El valor p es menor que 0.05 por lo que no podemos aceptar Ho y nos sugiere que los residuos no se comportan de forma normal.
En este caso sería necesario realizar transformación de las variables que pueden estar causando el problema de normalidad en los residuos, también se podría optar por usar otras variables que no generen este problema.
e= forward$residuals
shapiro.test(e)
##
## Shapiro-Wilk normality test
##
## data: e
## W = 0.83008, p-value < 2.2e-16
El valor p es menor que 0.05 por lo que no podemos aceptar Ho y nos sugiere que los residuos no se comportan de forma independiente.
se puede incluir variables adicionales que puedan explicar la autocorrelación en los residuos, como variables de tiempo o variables de serie temporal. También se pueden utilizar técnicas de remuestreo o de bootstrap para tener en cuenta la posible dependencia entre las observaciones y obtener estimaciones de los errores estándar más precisas.
library(lmtest)
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
dwtest(forward)
##
## Durbin-Watson test
##
## data: forward
## DW = 1.6882, p-value = 1.384e-05
## alternative hypothesis: true autocorrelation is greater than 0
El valor p es menor que 0.05 por lo que no podemos aceptar Ho y nos sugiere que los residuos no se comportan con varianza constante.
En estos casos es necesario realizar transformaciones de las variables o utilizar métodos más robustos.
gqtest(forward)
##
## Goldfeld-Quandt test
##
## data: forward
## GQ = 1.2796, df1 = 344, df2 = 344, p-value = 0.01126
## alternative hypothesis: variance increases from segment 1 to 2
los valores de FIV son relativamente bajos, lo que sugiere que no hay una multicolinealidad preocupante en el modelo. Además, el valor de FIV para “estrato” es ligeramente superior al de las otras variables, pero sigue estando por debajo del umbral de preocupación. Por lo tanto, podemos decir que el modelo no tiene problemas significativos de multicolinealidad.
library(car)
## Warning: package 'car' was built under R version 4.2.3
## Loading required package: carData
##
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
##
## recode
vif(forward)
## GVIF Df GVIF^(1/(2*Df))
## areaconst 1.510639 1 1.229080
## estrato 1.398107 3 1.057442
## banios 1.488530 1 1.220053
##Paso 5: Predecir el precio de la Vivienda
Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.
Con el modelo encontramos que una casa en la zona norte con un área construida de 200 metros cuadrados, estrato 4, dos baños y 4 habitaciones puede estar alrededor de 322.9539 millones de pesos.
Con el modelo encontramos que una casa en la zona norte con un área construida de 200 metros cuadrados, estrato 5, dos baños y 4 habitaciones puede estar alrededor de 385.641 millones de pesos.
casa_1<-data.frame(areaconst=200,estrato="Estrato4",banios=2,habitaciones=4)
casa_2<-data.frame(areaconst=200,estrato="Estrato5",banios=2,habitaciones=4)
Precio_1<-predict(forward,data.frame(casa_1),type="response")
Precio_2<-predict(forward,data.frame(casa_2),type="response")
print(paste("Precio de la casa de estrato 4: ", as.character(Precio_1), "; Precio de la casa de estrato 5: ", as.character(Precio_2)))
## [1] "Precio de la casa de estrato 4: 322.953946834107 ; Precio de la casa de estrato 5: 385.640980759379"
Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito preaprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.
La predicción del modelo nos indicaba que una casa con estas condiciones de estrato 4, podría costar alrededor de 322 millones, en este caso la oferta está por 360 millones, pero tenemos que considerar que ofrece 16 metros cuadrados más que los utilizados para realizar la predicción, esto puede suponer el aumento en el precio de la casa. Esta podría ser una buena opción si la persona interesada puede ampliar su presupuesto 10 millones más.
La predicción del modelo indica que una casa estrato 5 con estas condiciones, puede costar 385 millones, pero nos encontramos que hay una casa ofertada por 350 millones estando dentro del presupuesto y siendo la mejor opción de compra. Las demás sobrepasan el presupuesto y pueden suponer un costo extra demasiado elevado al presupuesto original.
oferta1<-filter(vivienda_casas, areaconst>="200" & areaconst<="280" & estrato=="Estrato4" & habitaciones=="4" & banios=="2")
oferta2<-filter(vivienda_casas, areaconst>="200" & areaconst<="280" & estrato=="Estrato5" & habitaciones=="4" & banios=="2")
ofertas<-rbind(oferta1,oferta2)
ofertas
## # A tibble: 7 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1222 Zona N… 02 Estrat… 360 216 2 2 4
## 2 1821 Zona N… <NA> Estrat… 550 265 0 2 4
## 3 1191 Zona N… <NA> Estrat… 405 280 0 2 4
## 4 3121 Zona N… 02 Estrat… 580 247 3 2 4
## 5 1163 Zona N… <NA> Estrat… 350 216 2 2 4
## 6 58 Zona N… <NA> Estrat… 400 212 0 2 4
## 7 4555 Zona N… <NA> Estrat… 550 200 3 2 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
En este caso en el mapa podemos visualizar de color rojo la ubicación de la casa que mejor se adapta a las condiciones requeridas por el cliente.
library(leaflet)
# Convertir la variable "base" en una variable de tipo carácter
ofertas$base <- as.character(ofertas$base)
# Asignar colores a cada nivel de la variable categórica, incluyendo un color para cuando "preciom" = 350
colores <- ifelse(ofertas$preciom == 350, "red",
colorFactor(palette = c("purple", "blue", "green"), domain = ofertas$base)(ofertas$base))
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa <- leaflet(ofertas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores, radius = 4)
# Mostrar el mapa
mapa
Realice un filtro a la base de datos e incluya solo las ofertas de: base1: casas, de la zona norte de la ciudad. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. ¿Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por qué?).
Creamos una nueva variable llamada base la cual ubica en Base 1 a Base 5 las casas dependiendo de su zona.
attach(vivienda)
## The following objects are masked from vivienda (pos = 8):
##
## areaconst, banios, barrio, estrato, habitaciones, id, latitud,
## longitud, parqueaderos, piso, preciom, tipo, zona
vivienda$base <- ifelse(vivienda$zona =="Zona Norte" & vivienda$tipo =="Apartamento", "Base 1",
ifelse(vivienda$zona =="Zona Centro" & vivienda$tipo =="Apartamento", "Base 2",
ifelse(vivienda$zona =="Zona Oeste" & vivienda$tipo =="Apartamento", "Base 3",
ifelse(vivienda$zona =="Zona Oriente" & vivienda$tipo =="Apartamento", "Base 4",
ifelse(vivienda$zona == "Zona Sur" & vivienda$tipo == "Apartamento", "Base 5", "NA")))))
head(subset(vivienda, tipo == "Apartamento"), 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1212 Zona N… 01 5 260 90 1 2 3
## 2 1724 Zona N… 01 5 240 87 1 3 3
## 3 2326 Zona N… 01 4 220 52 2 2 3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
library(dplyr)
# Filtro para la base 1: Apartamentos de la zona norte de la ciudad
base1 <- filter(vivienda, zona == "Zona Norte" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base1, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1212 Zona N… 01 5 260 90 1 2 3
## 2 1724 Zona N… 01 5 240 87 1 3 3
## 3 2326 Zona N… 01 4 220 52 2 2 3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 2: Apartamentos de la zona centro de la ciudad
base2 <- filter(vivienda, zona == "Zona Centro" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base2, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4654 Zona C… 03 3 100 70 NA 2 3
## 2 4408 Zona C… 05 3 120 84 1 2 3
## 3 4395 Zona C… 04 3 125 66.8 NA 2 3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 3: Apartamentos de la zona oeste de la ciudad
base3 <- filter(vivienda, zona == "Zona Oeste" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base3, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6999 Zona O… 01 6 870 200 2 5 3
## 2 8037 Zona O… 01 4 130 50 NA 1 3
## 3 8055 Zona O… 01 4 165 61 1 2 3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 4: Apartamentos de la zona oriente de la ciudad
base4 <- filter(vivienda, zona == "Zona Oriente" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base4, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 82 Zona O… 01 3 115 111 1 2 4
## 2 78 Zona O… 02 3 58 50 1 1 2
## 3 999 Zona O… 02 3 135 120 NA 2 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
# Filtro para la base 5: Apartamentos de la zona sur de la ciudad
base5 <- filter(vivienda, zona == "Zona Sur" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base5, 3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5098 Zona S… 05 4 290 96 1 2 3
## 2 698 Zona S… 02 3 78 40 1 1 2
## 3 8199 Zona S… <NA> 6 875 194 2 5 3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
Los colores correspondientes para cada zona son:
En la visualización del mapa se evidencian puntos de colores en zonas que no les corresponde, esto se puede dar porque desde el set las zonas de algunos registros no corresponden con la ubicación cartográfica dada por la longitud y latitud. Además, como solo tenemos 5 puntos cardinales y no se tienen en cuenta los puntos intermedios las ubicaciones que estén entre 2 de ellos van a tender a alguno de los dos generando esta variación en algunas zonas específicas.
library(leaflet)
# Filtrar solo las viviendas de tipo Apartamento
vivienda_apartamento <- subset(vivienda, tipo == "Apartamento" & zona=="Zona Sur")
# Convertir la variable "base" en una variable de tipo carácter
vivienda_apartamento$base <- as.character(vivienda_apartamento$base)
# Usar colorFactor() para asignar automáticamente un color único a cada nivel de la variable categórica
colores <- colorFactor(palette = "Set1", domain = vivienda_apartamento$base)
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa <- leaflet(vivienda_apartamento) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(base), radius = 4)
# Mostrar el mapa
mapa
Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio del apartamento) en función del área construida, estrato, numero de baños, número de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.
La variable parqueaderos es la que tiene más valores vacíos por lo que estos valores NA serán reemplazados con 0 indicando que la casa no tiene parqueadero, de resto las demás variables tienen entre 3 y 2 datos vacíos.
Revisión grafica de las variables a utilizar en el modelo de regresión lineal múltiple:
Encontramos que la mayoría de los apartamentos de la zona sur tienen un precio menor a 1000 millones, lo que genera que el grafico se desplace hacia la izquierda.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_apartamento$preciom)
##
## Shapiro-Wilk normality test
##
## data: vivienda_apartamento$preciom
## W = 0.75683, p-value < 2.2e-16
plot_ly(vivienda_apartamento, x = ~preciom, type = "histogram")%>%
layout(title = "Distribución de preciosm")
Encontramos observaciones de habitaciones con valores de 0 y esto no es real puesto que cualquier apartamento tendría por lo menos una habitación. Por lo que procedemos a eliminar los registros con cero habitaciones.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_apartamento$habitaciones)
##
## Shapiro-Wilk normality test
##
## data: vivienda_apartamento$habitaciones
## W = 0.7592, p-value < 2.2e-16
plot_ly(vivienda_apartamento, x = ~habitaciones, type = "histogram") %>%
layout(title = "Distribución de habitaciones")
vivienda_apartamento=vivienda_apartamento[vivienda_apartamento$habitaciones!=0,]
summary(vivienda_apartamento$habitaciones)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 3.000 3.000 2.974 3.000 6.000
Encontramos registros de apartamentos que tienen cero baños, esto no puede ser un dato real puesto que no sería conveniente utilizar una casa que no tenga baño. Por lo que procedemos a eliminar los registros con cero baños.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_apartamento$banios)
##
## Shapiro-Wilk normality test
##
## data: vivienda_apartamento$banios
## W = 0.79549, p-value < 2.2e-16
plot_ly(vivienda_apartamento, x = ~banios, type = "histogram") %>%
layout(title = "Distribución de baños")
vivienda_apartamento=vivienda_apartamento[vivienda_apartamento$banios!=0,]
summary(vivienda_apartamento$banios)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 2.000 2.000 2.494 3.000 8.000
Se evidencia que la mayoria de apartamentos se encuentran situados en estratos 5.
vivienda_apartamento$estrato <- ifelse(vivienda_apartamento$estrato == 3, "Estrato3", vivienda_apartamento$estrato)
vivienda_apartamento$estrato <- ifelse(vivienda_apartamento$estrato == 4, "Estrato4", vivienda_apartamento$estrato)
vivienda_apartamento$estrato <- ifelse(vivienda_apartamento$estrato == 5, "Estrato5", vivienda_apartamento$estrato)
vivienda_apartamento$estrato <- ifelse(vivienda_apartamento$estrato == 6, "Estrato6", vivienda_apartamento$estrato)
plot_ly(vivienda_apartamento, x = ~estrato,y=~estrato, type = "bar") %>%
layout(title = "Distribución de estrato")
Encontramos que el valor mínimo de área es de 30 y el máximo de 932 metros cuadrados y la mayoría de los registros se encuentran desplazados hacia la izquierda con valores menores a los 300 metros cuadrados.
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_apartamento$areaconst)
##
## Shapiro-Wilk normality test
##
## data: vivienda_apartamento$areaconst
## W = 0.68696, p-value < 2.2e-16
plot_ly(vivienda_apartamento, x = ~areaconst, type = "histogram") %>%
layout(title = "Distribución de areaconst")
summary(vivienda_apartamento$areaconst)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 40.00 65.00 85.00 97.28 110.00 932.00
Encontramos que para parqueaderos tenía datos faltantes, pero en este caso el valor faltante de parqueadero significa que la casa no tiene parqueadero, por lo que lo reemplazamos por 0
Encontramos que el valor p es menor que 0.05 por lo que se rechaza Ho lo que indica que la variable no se distribuye d forma normal.
shapiro.test(vivienda_apartamento$parqueaderos)
##
## Shapiro-Wilk normality test
##
## data: vivienda_apartamento$parqueaderos
## W = 0.62644, p-value < 2.2e-16
summary(vivienda_apartamento$parqueaderos)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 1.000 1.000 1.000 1.411 2.000 10.000 402
vivienda_apartamento$parqueaderos <- ifelse(is.na(vivienda_apartamento$parqueaderos), 0, vivienda_apartamento$parqueaderos)
summary(vivienda_casas$parqueaderos)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 0.000 1.000 1.343 2.000 10.000
plot_ly(vivienda_apartamento, x = ~parqueaderos,y=~parqueaderos, type = "bar") %>%
layout(title = "Distribución de Parqueaderos")
summary(vivienda_apartamento)
## id zona piso estrato
## Min. : 3 Length:2777 Length:2777 Length:2777
## 1st Qu.:2290 Class :character Class :character Class :character
## Median :4004 Mode :character Mode :character Mode :character
## Mean :4129
## 3rd Qu.:5875
## Max. :8302
## preciom areaconst parqueaderos banios
## Min. : 75.0 Min. : 40.00 Min. : 0.000 Min. :1.000
## 1st Qu.: 175.0 1st Qu.: 65.00 1st Qu.: 1.000 1st Qu.:2.000
## Median : 245.0 Median : 85.00 Median : 1.000 Median :2.000
## Mean : 296.9 Mean : 97.28 Mean : 1.207 Mean :2.494
## 3rd Qu.: 335.0 3rd Qu.:110.00 3rd Qu.: 2.000 3rd Qu.:3.000
## Max. :1750.0 Max. :932.00 Max. :10.000 Max. :8.000
## habitaciones tipo barrio longitud
## Min. :1.000 Length:2777 Length:2777 Min. :-76.57
## 1st Qu.:3.000 Class :character Class :character 1st Qu.:-76.54
## Median :3.000 Mode :character Mode :character Median :-76.53
## Mean :2.973 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :6.000 Max. :-76.46
## latitud base
## Min. :3.334 Length:2777
## 1st Qu.:3.370 Class :character
## Median :3.383 Mode :character
## Mean :3.390
## 3rd Qu.:3.406
## Max. :3.497
str(vivienda_apartamento)
## spc_tbl_ [2,777 × 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:2777] 5098 698 8199 1241 5370 ...
## $ zona : chr [1:2777] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
## $ piso : chr [1:2777] "05" "02" NA NA ...
## $ estrato : chr [1:2777] "Estrato4" "Estrato3" "Estrato6" "Estrato3" ...
## $ preciom : num [1:2777] 290 78 875 135 135 220 210 105 115 220 ...
## $ areaconst : num [1:2777] 96 40 194 117 78 75 72 68 58 84 ...
## $ parqueaderos: num [1:2777] 1 1 2 0 0 1 2 0 1 0 ...
## $ banios : num [1:2777] 2 1 5 2 1 2 2 2 2 2 ...
## $ habitaciones: num [1:2777] 3 2 3 3 3 3 3 3 2 3 ...
## $ tipo : chr [1:2777] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
## $ barrio : chr [1:2777] "acopi" "aguablanca" "aguacatal" "alameda" ...
## $ longitud : num [1:2777] -76.5 -76.5 -76.6 -76.5 -76.5 ...
## $ latitud : num [1:2777] 3.45 3.4 3.46 3.44 3.44 ...
## $ base : chr [1:2777] "Base 5" "Base 5" "Base 5" "Base 5" ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Como los valores faltantes de las variables preciom, areaconst, estrato, banio, habitaciones son pocos entre 2 y 3 se procede a eliminar estos valores para la futura creación de un modelo.
vivienda1 <- na.omit(vivienda_apartamento[c("preciom","areaconst","banios","habitaciones")])
Una vez todas las variables son del tipo correcto realizamos el análisis de correlación, para el caso de preciom y las variables predictoras habitaciones, banios, estrato, areaconst obtuvimos los siguientes resultados:
preciom y habitaciones: Indica una correlación positiva moderada entre el precio de la vivienda y el número de habitaciones. Es decir, a medida que aumenta el número de habitaciones en una vivienda, su precio tiende a aumentar, pero no tanto como en el caso de la correlación con el área construida o el número de baños.
preciom y banios: Indica una correlación positiva y fuerte entre el precio de la vivienda y el número de baños. Es decir, a medida que aumenta el número de baños en una vivienda, su precio también tiende a aumentar.
preciom y areacost: Indica que hay una correlación positiva y fuerte entre el precio de la vivienda y el área construida. Es decir, a medida que el tamaño de la vivienda aumenta, su precio también tiende a aumentar.
Luego procedemos a revisar la correlación que tienen las variables predictoras entre si:
areaconst y habiaciones: Indica una correlación positiva moderada entre el área construida y el número de habitaciones. Es decir, a medida que aumenta el tamaño de la vivienda, es más probable que tenga más habitaciones, pero no tanto como en el caso de la correlación con el número de baños.
areaconst y banios: Esto indica una correlación positiva y fuerte entre el área construida y el número de baños. Es decir, a medida que aumenta el tamaño de la vivienda, es más probable que tenga más baños.
banios y habitaciones: Esto indica una correlación positiva moderada entre el número de baños y el número de habitaciones en una vivienda. Es decir, a medida que aumenta el número de baños en una vivienda, también es más probable que tenga más habitaciones, pero no tanto como en el caso de la correlación con el área construida o el precio de la vivienda.
#install.packages("GGally", dependencies = TRUE)
library(GGally)
cor_1=vivienda1[,c("preciom","areaconst","banios","habitaciones")]
gcor<-ggplotly(ggpairs(cor_1))
gcor
Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, numero de cuartos, numero de parqueaderos, numero de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).
Conclusión:
Encontramos que el modelo paso a paso presenta un R cuadrado de 0.6605. Esto indica el modelo explica un 66% de la variable dependiente a partir de las variables independientes, este valor es bastante bueno, pero solo aplica para el caso en que el modelo conoce los resultados de la variable dependiente, también para mejorar este valor podría ser necesario incluir más variables independientes o explorar las posibles relaciones entre las variables.
Para escoger el modelo con el que nos quedaremos, vamos a realizar una validación cruzada y ver quien obtiene los mejores resultados.
La fórmula del modelo es la siguiente:
preciom = -1.03597 + 1.35976(areaconst) + 17.49104(estratoEstrato4) + 36.93566(estratoEstrato5) + 198.17289(estratoEstrato6) + 45.27180(parqueaderos) + 42.05035(banios) - 15.86854(habitaciones)
El coeficiente de determinación ajustado (Adjusted R-squared) en este modelo es 0.6605. Este valor indica que el modelo explica el 66.05% de la variación en los precios de las viviendas, una vez que se ha tenido en cuenta el número de variables incluidas en el modelo.
# se define el modelo ingenuo y= b0
modelo_b0<- lm(preciom ~ 1, data=vivienda_apartamento)
#define model with all predictors
modelo_all <- lm(preciom ~ habitaciones + banios + areaconst + estrato + parqueaderos, data = vivienda_apartamento)
# Se aplica el proceso forward stepwise regression
forward <- step(modelo_b0, direction='forward', scope=formula(modelo_all), trace=0)
# Visualización de los resultados
forward$anova
## Step Df Deviance Resid. Df Resid. Dev AIC
## 1 NA NA 2776 101137355 29168.45
## 2 + areaconst -1 57951797.1 2775 43185558 26807.30
## 3 + estrato -3 17406214.8 2772 25779343 25380.55
## 4 + parqueaderos -1 2632091.1 2771 23147252 25083.48
## 5 + banios -1 1382646.8 2770 21764605 24914.44
## 6 + habitaciones -1 178127.2 2769 21586478 24893.62
# resultado final del modelo
summary(forward)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios +
## habitaciones, data = vivienda_apartamento)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1128.01 -36.37 0.19 32.96 902.12
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.03597 10.20047 -0.102 0.919
## areaconst 1.35976 0.04723 28.787 < 2e-16 ***
## estratoEstrato4 17.49104 6.95729 2.514 0.012 *
## estratoEstrato5 36.93566 7.27806 5.075 4.13e-07 ***
## estratoEstrato6 198.17289 9.18097 21.585 < 2e-16 ***
## parqueaderos 45.27180 2.93647 15.417 < 2e-16 ***
## banios 42.05035 2.97293 14.144 < 2e-16 ***
## habitaciones -15.86854 3.31972 -4.780 1.84e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 88.29 on 2769 degrees of freedom
## Multiple R-squared: 0.7866, Adjusted R-squared: 0.786
## F-statistic: 1458 on 7 and 2769 DF, p-value: < 2.2e-16
Tradicionalmente la separación del set de entrenamiento y prueba se realiza en proporción de 80% / 20% y esta será la proporción que utilizaremos para la validación cruzada, realizamos 1000 iteraciones lo que nos dio como resultado un MAE de 53.13 millones, un R cuadrado de 77.93% y un MAPE de 17.89%. Teniendo en cuenta las unidades que estamos manejando este no sería un modelo robusto para la predicción de las variables, pero si nos presenta un acercamiento y una tendencia determinados por un MAE de 53.13 millones o un MAPE de 17.89%.
set.seed(1)
control=trainControl(method = "LGOCV",number=1000,p=0.8)
modelo1_vc=train(preciom ~ areaconst + estrato + banios + parqueaderos,data=vivienda_apartamento,method="lm",trControl=control,metric="MAE")
modelo1_vc
## Linear Regression
##
## 2777 samples
## 4 predictor
##
## No pre-processing
## Resampling: Repeated Train/Test Splits Estimated (1000 reps, 80%)
## Summary of sample sizes: 2222, 2222, 2222, 2222, 2222, 2222, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 90.24671 0.7793434 53.13275
##
## Tuning parameter 'intercept' was held constant at a value of TRUE
summary(modelo1_vc)
##
## Call:
## lm(formula = .outcome ~ ., data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1095.99 -37.07 1.72 32.51 912.65
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -35.29691 7.28649 -4.844 1.34e-06 ***
## areaconst 1.32473 0.04685 28.278 < 2e-16 ***
## estratoEstrato4 20.90230 6.94783 3.008 0.00265 **
## estratoEstrato5 40.14467 7.27556 5.518 3.75e-08 ***
## estratoEstrato6 206.45320 9.05156 22.809 < 2e-16 ***
## banios 36.89361 2.78119 13.265 < 2e-16 ***
## parqueaderos 44.81477 2.94647 15.210 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 88.64 on 2770 degrees of freedom
## Multiple R-squared: 0.7848, Adjusted R-squared: 0.7843
## F-statistic: 1684 on 6 and 2770 DF, p-value: < 2.2e-16
MAPE = 53.13275/mean(vivienda_apartamento$preciom)*100
MAPE
## [1] 17.8942
Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas solo realizar sugerencias de que se podría hacer).
par(mfrow = c(2, 2))
plot(forward)
El valor p es menor que 0.05 por lo que no podemos aceptar Ho y nos sugiere que los residuos no se comportan de forma normal.
En este caso sería necesario realizar transformación de las variables que pueden estar causando el problema de normalidad en los residuos, también se podría optar por usar otras variables que no generen este problema.
e= forward$residuals
shapiro.test(e)
##
## Shapiro-Wilk normality test
##
## data: e
## W = 0.76965, p-value < 2.2e-16
El valor p es menor que 0.05 por lo que no podemos aceptar Ho y nos sugiere que los residuos no se comportan de forma independiente.
se puede incluir variables adicionales que puedan explicar la autocorrelación en los residuos, como variables de tiempo o variables de serie temporal. También se pueden utilizar técnicas de remuestreo o de bootstrap para tener en cuenta la posible dependencia entre las observaciones y obtener estimaciones de los errores estándar más precisas.
library(lmtest)
dwtest(forward)
##
## Durbin-Watson test
##
## data: forward
## DW = 1.7105, p-value = 8.573e-15
## alternative hypothesis: true autocorrelation is greater than 0
El valor p es mayor que 0.05 por lo que no podemos rechazar Ho y nos sugiere que los residuos se comportan con varianza constante.
gqtest(forward)
##
## Goldfeld-Quandt test
##
## data: forward
## GQ = 0.89335, df1 = 1381, df2 = 1380, p-value = 0.9819
## alternative hypothesis: variance increases from segment 1 to 2
En este caso, los valores de GVIF son menores que 5, lo que indica que no hay una multicolinealidad importante en el modelo. En general, se considera que un GVIF mayor que 5 indica una multicolinealidad problemática.
library(car)
vif(forward)
## GVIF Df GVIF^(1/(2*Df))
## areaconst 2.141332 1 1.463329
## estrato 1.872772 3 1.110233
## parqueaderos 1.853102 1 1.361287
## banios 2.712041 1 1.646828
## habitaciones 1.470241 1 1.212535
Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.
Con el modelo encontramos que un apartamento en la zona sur con un área construida de 300 metros cuadrados, estrato 5, 3 baños y 5 habitaciones puede estar alrededor de 626.45 millones de pesos.
Con el modelo encontramos que un apartamento en la zona sur con un área construida de 300 metros cuadrados, estrato 6, 3 baños y 5 habitaciones puede estar alrededor de 787.68 millones de pesos.
apart_1<-data.frame(areaconst=300,estrato="Estrato5",banios=3,habitaciones=5,parqueaderos=3)
apart_2<-data.frame(areaconst=300,estrato="Estrato6",banios=3,habitaciones=5,parqueaderos=3)
Precio_1<-predict(forward,data.frame(apart_1),type="response")
Precio_2<-predict(forward,data.frame(apart_2),type="response")
print(paste("Precio del apartamento estrato 5: ", as.character(Precio_1), "; Precio del apartamento estrato 6: ", as.character(Precio_2)))
## [1] "Precio del apartamento estrato 5: 626.451390128223 ; Precio del apartamento estrato 6: 787.688625648374"
Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito preaprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.
En este caso la oferta que mejor se ajusta al presupuesto es la de estrato 5 y precio 650 millones, aunque ofrece 25 metros cuadrados menos de área construida es la única que está dentro del presupuesto del preaprobado del crédito puesto que las que tienen más de 300 metros cuadrados superan por mucho el valor del crédito.
oferta1<-filter(vivienda_apartamento, areaconst>="250" & areaconst<="380" & estrato=="Estrato5" & habitaciones=="5" )
oferta2<-filter(vivienda_apartamento, areaconst>="250" & areaconst<="380" & estrato=="Estrato6" & habitaciones=="5" )
ofertas<-rbind(oferta1,oferta2)
ofertas
## # A tibble: 8 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5306 Zona S… 12 Estrat… 650 275 2 5 5
## 2 3388 Zona S… <NA> Estrat… 480 260 1 5 5
## 3 2308 Zona S… 04 Estrat… 350 258 2 4 5
## 4 8036 Zona S… <NA> Estrat… 530 256 3 5 5
## 5 5248 Zona S… 08 Estrat… 1150 344 4 5 5
## 6 5460 Zona S… 08 Estrat… 1150 346 2 6 5
## 7 4 Zona S… <NA> Estrat… 1280 346 4 6 5
## 8 4266 Zona S… 05 Estrat… 700 250 2 4 5
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # base <chr>
En este caso en el mapa podemos visualizar de color rojo la ubicación del apartamento que mejor se adapta a las condiciones requeridas por el cliente.
library(leaflet)
# Convertir la variable "base" en una variable de tipo carácter
ofertas$base <- as.character(ofertas$base)
# Asignar colores a cada nivel de la variable categórica, incluyendo un color para cuando "preciom" = 350
colores <- ifelse(ofertas$preciom == 650, "red",
colorFactor(palette = c("purple", "blue", "green"), domain = ofertas$base)(ofertas$base))
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa <- leaflet(ofertas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores, radius = 4)
# Mostrar el mapa
mapa
La regresión lineal múltiple permite establecer la relación de dos o mas variables independientes de un modelo lineal que son utilizadas para predecir el valor de una variable dependiente con mayor precisión por que tiene en cuenta mas valores para determinarse.
La regresión lineal múltiple presenta beneficios en los que destacan: Mayor precisión en las predicciones, facilidad de modelar relaciones complejas entre las variables, determinación de las variables más importantes y mejoras en la precisión, sin embargo el proceso es mas extenso en comparación con la regresión lineal simple.
La regresión lineal múltiple puede ser limitada por los supuestos de la regresión, la multicolinealidad, el riesgo de sobreajuste del modelo, la selección de variables y el tamaño de la muestra. Es importante abordar estas limitaciones al utilizar la regresión lineal múltiple y evaluar la efectividad del modelo en función de su capacidad para predecir nuevos datos. Y determinar si los resultados con esta técnica cumplen los objetivos del proyecto o si se requiere modelos mas complejos.