Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:
| Características | Vivienda 1 | Vivienda 2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| Área construida (m^2) | 200 | 300 |
| Parqueaderos | 1 | 3 |
| Baños | 2 | 3 |
| Habitaciones | 4 | 5 |
| Estrato | 4 o 5 | 5 o 6 |
| Zona | Norte | Sur |
| Crédito preaprobado | 350 millones | 850 millones |
Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos) .
Para empezar se realiza la instalación e importación de las librerias y los datos y se muestra el dataframe para observar sus características generales.
#install.packages("devtools") # solo la primera vez
#devtools::install_github("dgonxalex80/paqueteMODELOS", force =TRUE)
#install.packages("GGally")
library(paqueteMODELOS)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(leaflet)
## Warning: package 'leaflet' was built under R version 4.2.3
library(GGally)
## Warning: package 'GGally' was built under R version 4.2.3
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
data("vivienda")
head(vivienda,5)
## # A tibble: 5 × 13
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1147 Zona … <NA> 3 250 70 1 3 6 Casa 20 de…
## 2 1169 Zona … <NA> 3 320 120 1 2 3 Casa 20 de…
## 3 1350 Zona … <NA> 3 350 220 2 2 4 Casa 20 de…
## 4 5992 Zona … 02 4 400 280 3 5 3 Casa 3 de …
## 5 1212 Zona … 01 5 260 90 1 2 3 Apar… acopi
## # … with 2 more variables: longitud <dbl>, latitud <dbl>, and abbreviated
## # variable names ¹areaconst, ²parqueaderos, ³habitaciones
A continuación se utiliza la función str() para observar sus dimeciones, tipos de variables y demás.
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:8322] 1147 1169 1350 5992 1212 ...
## $ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
## $ piso : chr [1:8322] NA NA NA "02" ...
## $ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
## $ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
## $ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
## $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
## $ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
## $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
## $ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
## $ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
## - 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>
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
Seguidamente para validar la existencia de valores faltantes se realiza el siguiente código.
valores_faltantes <- colSums(is.na(vivienda))
data.frame(column = names(valores_faltantes), missing_count = valores_faltantes)
## column missing_count
## id id 3
## zona zona 3
## piso piso 2638
## estrato estrato 3
## preciom preciom 2
## areaconst areaconst 3
## parqueaderos parqueaderos 1605
## banios banios 3
## habitaciones habitaciones 3
## tipo tipo 3
## barrio barrio 3
## longitud longitud 3
## latitud latitud 3
# 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 × 13
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1209 Zona … 02 5 320 150 2 4 6 Casa acopi
## 2 1592 Zona … 02 5 780 380 2 3 3 Casa acopi
## 3 4057 Zona … 02 6 750 445 NA 7 6 Casa acopi
## # … with 2 more variables: longitud <dbl>, latitud <dbl>, and abbreviated
## # variable names ¹areaconst, ²parqueaderos, ³habitaciones
summary(base1)
## id zona piso estrato
## Min. : 58.0 Length:722 Length:722 Min. :3.000
## 1st Qu.: 766.2 Class :character Class :character 1st Qu.:3.000
## Median :2257.0 Mode :character Mode :character Median :4.000
## Mean :2574.6 Mean :4.202
## 3rd Qu.:4225.0 3rd Qu.:5.000
## Max. :8319.0 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 89.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 261.2 1st Qu.: 140.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 390.0 Median : 240.0 Median : 2.000 Median : 3.000
## Mean : 445.9 Mean : 264.9 Mean : 2.182 Mean : 3.555
## 3rd Qu.: 550.0 3rd Qu.: 336.8 3rd Qu.: 3.000 3rd Qu.: 4.000
## Max. :1940.0 Max. :1440.0 Max. :10.000 Max. :10.000
## NA's :287
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:722 Length:722 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.507 Mean :-76.52
## 3rd Qu.: 5.000 3rd Qu.:-76.50
## Max. :10.000 Max. :-76.47
##
## latitud
## Min. :3.333
## 1st Qu.:3.452
## Median :3.468
## Mean :3.460
## 3rd Qu.:3.482
## Max. :3.496
##
str(base1)
## spc_tbl_ [722 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:722] 1209 1592 4057 4460 6081 ...
## $ zona : chr [1:722] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
## $ piso : chr [1:722] "02" "02" "02" "02" ...
## $ estrato : num [1:722] 5 5 6 4 5 4 5 5 3 3 ...
## $ preciom : num [1:722] 320 780 750 625 750 600 420 490 230 190 ...
## $ areaconst : num [1:722] 150 380 445 355 237 160 200 118 160 435 ...
## $ parqueaderos: num [1:722] 2 2 NA 3 2 1 4 2 NA NA ...
## $ banios : num [1:722] 4 3 7 5 6 4 4 4 2 0 ...
## $ habitaciones: num [1:722] 6 3 6 5 6 5 5 4 3 0 ...
## $ tipo : chr [1:722] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:722] "acopi" "acopi" "acopi" "acopi" ...
## $ longitud : num [1:722] -76.5 -76.5 -76.5 -76.5 -76.5 ...
## $ latitud : num [1:722] 3.48 3.49 3.39 3.41 3.37 ...
## - 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>
colores <- colorFactor(palette = c( "blue"), domain = base1$zona)
# se crea el mapa con las Casas según su latitud y longitud según el color marcado en el paso anterior.
mapa <- leaflet(base1) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(zona), radius = 4)
# Ver Mapa
mapa
Al generar el mapa y observar las ubicaciones de las viviendas, es claro que hay varias que aparecen ubicadas en el sur, esto puede ser atribuído a una mala recolección de los datos, que llevó a que las coordenadas estuviesen mal asignadas para algunas viviendas.
vivienda_casas <- vivienda
vivienda_casas$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_casas,3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1147 Zona … <NA> 3 250 70 1 3 6 Casa 20 de…
## 2 1169 Zona … <NA> 3 320 120 1 2 3 Casa 20 de…
## 3 1350 Zona … <NA> 3 350 220 2 2 4 Casa 20 de…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
# Filtrar solo las viviendas de tipo Casa
vivienda_casas <- subset(vivienda_casas, tipo == "Casa")
# Usar colorFactor() para asignar automáticamente un color único a cada nivel de la variable categórica
colores <- colorFactor(palette = "Set2", 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"
mapa2 <- leaflet(vivienda_casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(base), radius = 4)
# Mostrar el mapa
mapa2
Por otro lado al comparar las demás zonas, encontramos el mismo comportamiento errático en algunas viviendas hacia zonas opuestas mal asignada.
plot_ly(vivienda_casas, x = ~preciom, type = "histogram")%>%
layout(title = "Histograma de precios")
summary(vivienda_casas$preciom)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 77 300 430 540 670 1999
Al analizar los precios encontramos que estos tienen un sesgo positivo o hacia la derecha indicando que la mayoría de precios tienden hacia el límite inferios sin embargo hay algunas viviendas que tienen precios extremadamente altos en comparación, lo cual es un comportamiento que uno esperaria en el sector de las viviendas, pues las viviendas mas costosas no suelen ser las mas abundantes debido precisamente a que la mayoría de la población no puede acceder a ellas.
plot_ly(vivienda_casas, x = ~banios, type = "histogram")%>%
layout(title = "Histograma de numero de baños")
summary(vivienda_casas$banios)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 3.000 4.000 3.894 5.000 10.000
Al analizar el número de baños es interesante encontrar que hayan viviendas con 0 baños, esto puede quizas ser atribuido a que estas sean viviendas en lugares margináles de la ciudad de Cali donde sea posible encontrar viviendas que no cuenten con condiciones mínimas como por ejemplo un baño. En cuanto a la distrinución de los datos de esta varibale tienden a un comportamiento positivo gráficamente sin embargo hay un leve sesgo positivo.
plot_ly(vivienda_casas, x = ~habitaciones, type = "histogram") %>%
layout(title = "Histograma de habitaciones")
summary(vivienda_casas$habitaciones)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 3.00 4.00 4.61 5.00 10.00
En cuanto al número de habitaciones hay un sorprendente alto número de habitaciones con 0 habitaciones, lo cual pueda quizas ser atribuído a viviendas de un solo ambiente, sin embargo lo extraño es que este tipo de viviendas usualmente suelen ser apartamentos no casas, por otro lado estos valores tambien podrían estar mal asignados.
plot_ly(vivienda_casas, x = ~estrato,y=~estrato, type = "bar") %>%
layout(title = "Distribución de estrato")
Es importatante notar que no existen datos de viviendas inferiores a estrato 3, esto podría indicar que hipótesis anteriores en torno al numero de baños igual a 0, relacionadas a que esto podría hacer referencia a viviendas en sectores marginales podría ser incorrecto, pues de haber sido así habrían viviendas de estrato 1.
plot_ly(vivienda_casas, x = ~areaconst, type = "histogram") %>%
layout(title = "Histograma de area contruida")
summary(vivienda_casas$areaconst)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 30.0 154.0 240.0 273.4 350.0 1745.0
Como se puede obervar el área construída tiene un fuerte sesgo a la derecha, lo cual tiene sentido pues al igual que los precios, las casas con la mayor extención de terreno tienden a ser las menos comunes.
# Create a summary table of the "zona" variable
zona_countsa_cas <- vivienda_casas %>%
count(zona)
# Create the bar chart
plot_ly(zona_countsa_cas, x = ~zona, y = ~n, type = "bar") %>%
layout(title = "Distribución de Zona")
En el gráfico anterior se puede observar que la mayoria de casas se encuentran en la zona sur en el dataset analizado.
summary(vivienda_casas)
## id zona piso estrato
## Min. : 1 Length:3219 Length:3219 Min. :3.000
## 1st Qu.:1856 Class :character Class :character 1st Qu.:3.000
## Median :4190 Mode :character Mode :character Median :5.000
## Mean :3963 Mean :4.485
## 3rd Qu.:5862 3rd Qu.:5.000
## Max. :8319 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 77 Min. : 30.0 Min. : 1.00 Min. : 0.000
## 1st Qu.: 300 1st Qu.: 154.0 1st Qu.: 1.00 1st Qu.: 3.000
## Median : 430 Median : 240.0 Median : 2.00 Median : 4.000
## Mean : 540 Mean : 273.4 Mean : 2.29 Mean : 3.894
## 3rd Qu.: 670 3rd Qu.: 350.0 3rd Qu.: 3.00 3rd Qu.: 5.000
## Max. :1999 Max. :1745.0 Max. :10.00 Max. :10.000
## NA's :733
## habitaciones tipo barrio longitud
## Min. : 0.00 Length:3219 Length:3219 Min. :-76.59
## 1st Qu.: 3.00 Class :character Class :character 1st Qu.:-76.54
## Median : 4.00 Mode :character Mode :character Median :-76.53
## Mean : 4.61 Mean :-76.53
## 3rd Qu.: 5.00 3rd Qu.:-76.52
## Max. :10.00 Max. :-76.46
##
## latitud base
## Min. :3.333 Length:3219
## 1st Qu.:3.383 Class :character
## Median :3.413 Mode :character
## Mean :3.415
## 3rd Qu.:3.449
## Max. :3.496
##
vivienda_casas_corr_1 <- vivienda_casas[,c("preciom","areaconst","estrato","banios","habitaciones")]
ggpairs(vivienda_casas_corr_1, title=" ", progress = FALSE)
Al nalizar las correlaciones de las variables se encuentra que las variables con los mas altos índices de correlación son precio - area construida y precio - estrato, esto se evidencia tanto gráficamente como numéricamente.
lmm = lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = vivienda_casas)
summary(lmm)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios,
## data = vivienda_casas)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1108.22 -115.81 -21.89 75.94 994.43
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -486.50564 20.31625 -23.947 <2e-16 ***
## areaconst 0.71729 0.02904 24.704 <2e-16 ***
## estrato 126.41582 4.78981 26.393 <2e-16 ***
## parqueaderos 65.00246 3.48820 18.635 <2e-16 ***
## banios 29.60128 3.51717 8.416 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 206.1 on 2481 degrees of freedom
## (733 observations deleted due to missingness)
## Multiple R-squared: 0.6807, Adjusted R-squared: 0.6801
## F-statistic: 1322 on 4 and 2481 DF, p-value: < 2.2e-16
Al analizar el resultado obtenido de la regresión tenemos coeficientes:
Intercepto: El intercepto es -486.51. Esto significa que cuando todas las variables predictoras (área construida, estrato, parqueaderos y baños) son iguales a cero, el precio estimado de la vivienda sería de -486.51 unidades. Sin embargo, esta interpretación es poco práctica ya que no tiene sentido que todas las variables sean cero en la realidad.
Área construida: El coeficiente de área construida es 0.71729. Cada aumento unitario en el área construida se asocia, en promedio, con un aumento de 0.71729 unidades en el precio de la vivienda. Esto sugiere que el precio de la vivienda tiende a aumentar a medida que aumenta el área construida, lo cual es lógico.
Estrato: El coeficiente de estrato es 126.42. Esto indica que, en promedio, las viviendas en estratos más altos tienden a tener un precio 126.42 unidades más alto que las viviendas en estratos más bajos. Esto también tiene sentido, ya que los estratos más altos suelen estar asociados con viviendas más costosas.
Parqueaderos: El coeficiente de parqueaderos es 65.00. Cada parqueadero adicional se asocia, en promedio, con un aumento de 65.00 unidades en el precio de la vivienda. Esto sugiere que la disponibilidad de parqueaderos puede influir significativamente en el precio de la vivienda.
Baños: El coeficiente de baños es 29.60. Cada baño adicional se asocia, en promedio, con un aumento de 29.60 unidades en el precio de la vivienda. Esto indica que el número de baños también influye en el precio de la vivienda.
Por otro lado el valor de \(R^2\) es 0.6807, lo que significa que aproximadamente el 68.07% de la variabilidad en los precios de las viviendas se explica por las variables predictoras incluidas en el modelo. Esto sugiere que el modelo tiene un buen poder explicativo en términos generales.
Ajuste del Modelo y Posibles Mejoras:
El valor de \(R^2\) es relativamente alto, lo que indica que el modelo tiene una capacidad razonable para explicar la variabilidad en los precios de las viviendas. Sin embargo, aun tiene una considerable oportunidad de mejor, para lograrla se podría considerar:
Por otro lado es importante recordar que la significancia estadística de los coeficientes indica la confiabilidad de las relaciones identificadas. Todos los coeficientes parecen ser estadísticamente significativos en este modelo, lo que es un buen signo debido a que todos tienen un valor P inferior a 0.05.
En resumen, el modelo parece tener un buen ajuste en términos de \(R^2\), y los coeficientes tienen interpretaciones lógicas y son estadísticamente significativos. Sin embargo, siempre es posible realizar mejoras adicionales en el modelo para aumentar su capacidad predictiva.
par(mfrow = c(2, 2))
plot(lmm)
Gráficamente se puede observar que se violan los supuestos de linealidad y no autocorrelación debido al patron observado en la gráfica de residuals vs fitted, tambien se viola es supuesto de normalidad como se ve en la gráfica de normalidad Q-Q, así mismo se viola el supuesto de homocedasticidad pues no se evidencia una varianza constante en la gráfica de Scale-Location.
residu=lmm$residuals
shapiro.test(residu)
##
## Shapiro-Wilk normality test
##
## data: residu
## W = 0.8966, p-value < 2.2e-16
Al realizar la prueba de normalidad de Shapiro-Wilk con las siguientes hipótesis:
\(H_{0}\): los residuos se distribuyen normalmente.
\(H_{1}\): La hipótesis alternativa sugiere que los datos no siguen una distribución normal.
Y al evaluar los resultados de los estadisticos tenemos que por un lado siendo el valor de W 0.8966 cercano a 1 indica que los residuos se asemejan a una distribución normal. En este caso, W es menor que 1 (0.8966), lo que sugiere que la distribución de los residuos se desvía en cierta medida de una distribución normal.Final mente el valor p asociado a la prueba es muy pequeño (< 2.2e-16). Esto indica una evidencia sólida en contra de la hipótesis nula. En resumen, la prueba de Shapiro-Wilk sugiere que los residuos de tu modelo de regresión no siguen una distribución normal, ya que el valor p es extremadamente pequeño. Esto significa que hay evidencia estadística para rechazar la hipótesis nula que asume normalidad en los residuos. Por lo tanto, se concluye que los residuos no son normalmente distribuidos en este modelo.
#install.packages("lmtest")
library(lmtest)
## Warning: package 'lmtest' was built under R version 4.2.3
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
bptest(lmm)
##
## studentized Breusch-Pagan test
##
## data: lmm
## BP = 313.09, df = 4, p-value < 2.2e-16
Al realizar la prueba studentized Breusch-Pagan para evaluar la homocedasticidad de los residuos y teniendo las siguientes hipótesis:
\(H_{0}\): La varianza de los residuos es constante (homocedasticidad).
\(H_{1}\): La varianza de los residuos no es constante (heterocedasticidad).
Encontramos que el valor p asociado a la prueba es muy pequeño, siendo menor a 2.2e-16”. Esto indica una evidencia sólida en contra de la hipótesis nula (H0) de homocedasticidad. En resumen, la prueba de Breusch-Pagan sugiere que hay evidencia estadística significativa para rechazar la hipótesis nula de homocedasticidad en los residuos del modelo lo cual implica que la varianza de los residuos no es constante y que hay heterocedasticidad presente en el modelo.
dwtest(lmm)
##
## Durbin-Watson test
##
## data: lmm
## DW = 1.5644, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
Al realizar la prueba Durbin-Watson para evaluar la autocorrelación de los residuos y teniendo las siguientes hipótesis:
\(H_{0}\): no hay autocorrelación en los residuos, es decir, no hay patrones sistemáticos de correlación en los errores.
\(H_{1}\): existe autocorrelación en los residuos y hay patrones sistemáticos de correlación en los errores.
El valor del estadístico DW es 1.5644, dado que este estadístico tiene un rango de valores entre 0 y 4 y que un valor cercano a 2 sugiere que no hay autocorrelación en los residuos. En este caso indica que podría haber autocorrelación positiva en los residuos, es decir, que los residuos podrían estar correlacionados positivamente entre sí.Por otro lado el valor p asociado a la prueba es muy pequeño, representado como “< 2.2e-16”. Esto indica una evidencia sólida en contra de la hipótesis nula de ausencia de autocorrelación en los residuos. Esto tiene implicaciones para la independencia de los residuos y podría afectar la validez de las inferencias realizadas en el modelo.
Algunas soluciones para la violación de los supuestos:
Para el caso del supuesto de linealidad una de las soluciones que podría ayudar es realizar algunas transformaciones a las variables que no evidencien una relación lineal con el precio. Podrían aplicarce transformaciones logaritmicas, exponenciales o Box-Cox.
Para la violación del supuesto de homocedasticidad tambien puede ser de utilidad una transformación logarítmica ya sea a algunas variables predictoras, a la variable de precio a ambas. Así mismo puede ser de utilidad eliminar algunos datos atípicos que esten teniendo un impacto muy elevado en la regresión.
En cuanto al supuesto de Autocorrelación dado que este es virtualmente imposible de relajar, lo ideal sería aplicar las medidas anteriores, y volver a evaluar los supuestos, si sigue habiendo autocorrelación podría haber errores en los datos tomados o sería un indicador de que se requiere otro tipo de regresión.
Modelo con variables dummy de zona
En adición al modelo anterior, a modo de ejercicio tambien se realizó el mismo modelo adicionando la variable de zona como dummies.
vivienda_casas_2 <- vivienda_casas[,c("preciom","areaconst","estrato","banios","habitaciones","zona")]
encoded_zona <- as.data.frame(model.matrix(~ zona - 1, data = vivienda_casas_2))
colnames(encoded_zona) <- sub("^zona", "", colnames(encoded_zona))
vivienda_casas_2 <- vivienda_casas_2[, !colnames(vivienda_casas_2) %in% "zona"]
vivienda_casas_2_encoded <- cbind(vivienda_casas_2, encoded_zona)
lmm2 <- lm(preciom ~ .- `Zona Sur`, data = vivienda_casas_2_encoded)
summary(lmm2)
##
## Call:
## lm(formula = preciom ~ . - `Zona Sur`, data = vivienda_casas_2_encoded)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1391.60 -119.65 -24.31 75.74 1134.86
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -413.60292 23.42737 -17.655 < 2e-16 ***
## areaconst 0.88247 0.02565 34.410 < 2e-16 ***
## estrato 135.20953 4.76116 28.398 < 2e-16 ***
## banios 46.28911 3.37726 13.706 < 2e-16 ***
## habitaciones -15.76148 2.65486 -5.937 3.22e-09 ***
## `Zona Centro` 80.00180 22.58212 3.543 0.000402 ***
## `Zona Norte` -35.93501 9.52324 -3.773 0.000164 ***
## `Zona Oeste` 60.62610 16.81454 3.606 0.000316 ***
## `Zona Oriente` 7.31379 15.20491 0.481 0.630539
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 208.9 on 3210 degrees of freedom
## Multiple R-squared: 0.6607, Adjusted R-squared: 0.6599
## F-statistic: 781.4 on 8 and 3210 DF, p-value: < 2.2e-16
Encontramos que el \(R^2\) disminuyó, es muy importante notar, que en las variables dummy, solo la de “Zona Oriente” no es significante, esto podría indicar un problema en el comportamiento de esta zona específico, se recomendaría en este caso validar que ocurre esoecíficamente con esta variable, para ajustarlo y correr de nuevo el modelo.
Para solucionar este punto se realiza la predicción del precio utilizando los dos modelos creados previamente.
viv1 = data.frame(
areaconst = 200,
parqueaderos = 1,
estrato = c(4, 5),
banios = 2,
habitaciones = 4
)
viv1
## areaconst parqueaderos estrato banios habitaciones
## 1 200 1 4 2 4
## 2 200 1 5 2 4
predict(lmm,viv1)
## 1 2
## 286.8203 413.2361
Con este primer modelo, encontramos que los precios predecidos para los estratos 3 y 4 son respectivamente 286 millones y 413 millones, para el caso de la primera vivienda solo aplicaría según el modelo la vivienda en estrato 3.
viv1_2 = data.frame(
areaconst = 200,
parqueaderos = 1,
estrato = c(4, 5),
banios = 2,
habitaciones = 4,
'Zona Centro' = 0,
'Zona Norte' = 1,
'Zona Oeste' = 0,
'Zona Oriente' = 0,
'Zona Sur' = 0
)
colnames(viv1_2) <- gsub("\\.", " ", colnames(viv1_2))
viv1_2
## areaconst parqueaderos estrato banios habitaciones Zona Centro Zona Norte
## 1 200 1 4 2 4 0 1
## 2 200 1 5 2 4 0 1
## Zona Oeste Zona Oriente Zona Sur
## 1 0 0 0
## 2 0 0 0
predict(lmm2,viv1_2)
## 1 2
## 297.3272 432.5367
Con respecto a la predicción anterior, al utilizar el modelo creado con las variables dummy de zona, se aumenta un poco el precio pero sigue presentando un comportamiento similar al anterior, de la misma manera sigue siendo una casa en estrato 3 la apropiada.
casas <- filter(vivienda_casas, areaconst >= 200, parqueaderos >= 1,
banios >= 2, habitaciones >= 4, zona == "Zona Norte" ,estrato >= 4, estrato <= 5, preciom <= 350) %>%
arrange(preciom)
min(casas$preciom)
## [1] 230
max(casas$preciom)
## [1] 350
head(casas,10)
## # A tibble: 10 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1020 Zona… 02 4 230 250 2 3 5 Casa la me…
## 2 1009 Zona… <NA> 5 250 243 1 4 5 Casa el bo…
## 3 7432 Zona… 01 4 260 280 2 4 6 Casa los a…
## 4 1914 Zona… 02 5 300 205 2 5 6 Casa vipasa
## 5 4458 Zona… 02 4 315 270 2 4 4 Casa el bo…
## 6 1343 Zona… 02 5 320 200 2 4 4 Casa la fl…
## 7 3053 Zona… 02 5 320 230 2 4 4 Casa la fl…
## 8 1144 Zona… <NA> 4 320 200 2 4 4 Casa la me…
## 9 1151 Zona… <NA> 5 320 210 2 3 5 Casa urban…
## 10 766 Zona… <NA> 5 321 249 1 5 5 Casa la me…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
Al filtrar los datos se encuentra que hay viviendas en estrato 4 y 5 que cumplen las restricciones de presupuesto y locativas.
colores2 <- colorFactor(palette = c( "blue"), domain = casas$zona)
# se crea el mapa con las Casas según su latitud y longitud según el color marcado en el paso anterior.
mapa3 <- leaflet(casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores2(zona), radius = 4)
# Ver Mapa
mapa3
# Filtro para la base 1: casas de la zona norte de la ciudad
base1_apt <- filter(vivienda, zona == "Zona Sur" & tipo == "Apartamento")
# Mostrar los primeros 3 registros y una tabla
head(base1_apt, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 5098 Zona … 05 4 290 96 1 2 3 Apar… acopi
## 2 698 Zona … 02 3 78 40 1 1 2 Apar… aguab…
## 3 8199 Zona … <NA> 6 875 194 2 5 3 Apar… aguac…
## # … with 2 more variables: longitud <dbl>, latitud <dbl>, and abbreviated
## # variable names ¹areaconst, ²parqueaderos, ³habitaciones
str(base1_apt)
## spc_tbl_ [2,787 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:2787] 5098 698 8199 1241 5370 ...
## $ zona : chr [1:2787] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
## $ piso : chr [1:2787] "05" "02" NA NA ...
## $ estrato : num [1:2787] 4 3 6 3 3 4 3 3 3 4 ...
## $ preciom : num [1:2787] 290 78 875 135 135 220 210 105 115 220 ...
## $ areaconst : num [1:2787] 96 40 194 117 78 75 72 68 58 84 ...
## $ parqueaderos: num [1:2787] 1 1 2 NA NA 1 2 NA 1 NA ...
## $ banios : num [1:2787] 2 1 5 2 1 2 2 2 2 2 ...
## $ habitaciones: num [1:2787] 3 2 3 3 3 3 3 3 2 3 ...
## $ tipo : chr [1:2787] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
## $ barrio : chr [1:2787] "acopi" "aguablanca" "aguacatal" "alameda" ...
## $ longitud : num [1:2787] -76.5 -76.5 -76.6 -76.5 -76.5 ...
## $ latitud : num [1:2787] 3.45 3.4 3.46 3.44 3.44 ...
## - 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>
summary(base1_apt)
## id zona piso estrato
## Min. : 3 Length:2787 Length:2787 Min. :3.00
## 1st Qu.:2292 Class :character Class :character 1st Qu.:4.00
## Median :4004 Mode :character Mode :character Median :5.00
## Mean :4131 Mean :4.63
## 3rd Qu.:5876 3rd Qu.:5.00
## Max. :8302 Max. :6.00
##
## preciom areaconst parqueaderos banios
## Min. : 75.0 Min. : 40.00 Min. : 1.000 Min. :0.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 : 297.3 Mean : 97.47 Mean : 1.415 Mean :2.488
## 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
## NA's :406
## habitaciones tipo barrio longitud
## Min. :0.000 Length:2787 Length:2787 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.966 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :6.000 Max. :-76.46
##
## latitud
## Min. :3.334
## 1st Qu.:3.370
## Median :3.383
## Mean :3.390
## 3rd Qu.:3.406
## Max. :3.497
##
colores <- colorFactor(palette = c( "blue"), domain = base1_apt$zona)
# se crea el mapa con las Casas según su latitud y longitud según el color marcado en el paso anterior.
mapa <- leaflet(base1) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(zona), radius = 4)
## Warning in colores(zona): Some values were outside the color scale and will be
## treated as NA
## Warning in colores(zona): Some values were outside the color scale and will be
## treated as NA
# Ver Mapa
mapa
vivienda_apartamentos <- vivienda
vivienda_apartamentos$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(vivienda_apartamentos,3)
## # A tibble: 3 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 1147 Zona … <NA> 3 250 70 1 3 6 Casa 20 de…
## 2 1169 Zona … <NA> 3 320 120 1 2 3 Casa 20 de…
## 3 1350 Zona … <NA> 3 350 220 2 2 4 Casa 20 de…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
# Filtrar solo las viviendas de tipo Casa
vivienda_apartamentos <- subset(vivienda_apartamentos, tipo == "Apartamento")
# Usar colorFactor() para asignar automáticamente un color único a cada nivel de la variable categórica
colores <- colorFactor(palette = "Set2", domain = vivienda_apartamentos$base)
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa2 <- leaflet(vivienda_apartamentos) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(base), radius = 4)
# Mostrar el mapa
mapa2
Como ya se mencionó previamente se encuentra que las categorizaciones de las zonas de varias viviendas son erradas pues las zonas pues estas no coinciden con la ubicaion geografica en gran medida.
plot_ly(vivienda_apartamentos, x = ~preciom, type = "histogram")%>%
layout(title = "Histograma de precios")
summary(vivienda_apartamentos$preciom)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 58.0 175.0 279.0 366.9 430.0 1950.0
Al analizar los precios de los apartamentos se encuenta un comportamiento relativamente parecido a las casas en el sentido que hay un claro sesgo positivo, lo cual tiene sentido ya que es sabido que existen unos pocos apartamentos y casas con lujos y en zonas muy exclusivas que cuentan con precios desproporcionadamente altos que causan este sesgo.
plot_ly(vivienda_apartamentos, x = ~banios, type = "histogram")%>%
layout(title = "Histograma de numero de baños")
summary(vivienda_apartamentos$banios)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 2.000 2.000 2.617 3.000 8.000
A diferencia de las casas que tenian una mayor concentración de numero de baños en 3 y 4, en el caso de los apartamentos hay una abrumadora mayoría con 2 baños y hay un sesgo positivo ocacionado por algunos apartamentos con mas baños, esto tiene sentido ya que los apartamentos son con frecuencia mas pequeños y compactos.
plot_ly(vivienda_apartamentos, x = ~habitaciones, type = "histogram") %>%
layout(title = "Histograma de habitaciones")
summary(vivienda_apartamentos$areaconst)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 35.0 68.0 90.0 112.8 130.0 932.0
Igual que en el caso anterior los aprtamentos tienden a tener menor número de habitaciones que las casas con una extrema mayoría de 3 habitaciones y a diferencia de las casas no hay un sesgo evidente a simple vista. Sin embargo al comparar la media y mediana si se evidencia un pequeño sesgo positivo.
plot_ly(vivienda_apartamentos, x = ~estrato,y=~estrato, type = "bar") %>%
layout(title = "Distribución de estrato")
plot_ly(vivienda_apartamentos, x = ~areaconst, type = "histogram") %>%
layout(title = "Histograma de area contruida")
summary(vivienda_apartamentos$areaconst)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 35.0 68.0 90.0 112.8 130.0 932.0
# Create a summary table of the "zona" variable
zona_countsa_apt <- vivienda_apartamentos %>%
count(zona)
# Create the bar chart
plot_ly(zona_countsa_apt, x = ~zona, y = ~n, type = "bar") %>%
layout(title = "Distribución de Zona")
Al igual que en el caso de las casas se encuentra que la mayoría de casas están en la zona sur, esto es un poco extraño y puede indicar que la base de datos tiene una muestra sumamente desbalanceada.
summary(vivienda_apartamentos)
## id zona piso estrato
## Min. : 3 Length:5100 Length:5100 Min. :3.000
## 1st Qu.:2180 Class :character Class :character 1st Qu.:4.000
## Median :4158 Mode :character Mode :character Median :5.000
## Mean :4284 Mean :4.727
## 3rd Qu.:6556 3rd Qu.:6.000
## Max. :8317 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 35.0 Min. : 1.000 Min. :0.000
## 1st Qu.: 175.0 1st Qu.: 68.0 1st Qu.: 1.000 1st Qu.:2.000
## Median : 279.0 Median : 90.0 Median : 1.000 Median :2.000
## Mean : 366.9 Mean :112.8 Mean : 1.568 Mean :2.617
## 3rd Qu.: 430.0 3rd Qu.:130.0 3rd Qu.: 2.000 3rd Qu.:3.000
## Max. :1950.0 Max. :932.0 Max. :10.000 Max. :8.000
## NA's :869
## habitaciones tipo barrio longitud
## Min. :0.000 Length:5100 Length:5100 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 :2.971 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :9.000 Max. :-76.46
##
## latitud base
## Min. :3.334 Length:5100
## 1st Qu.:3.380 Class :character
## Median :3.419 Mode :character
## Mean :3.419
## 3rd Qu.:3.453
## Max. :3.498
##
vivienda_apartamentos_corr_1 <- vivienda_apartamentos[,c("preciom","areaconst","estrato","banios","habitaciones")]
ggpairs(vivienda_apartamentos_corr_1, title=" ", progress = FALSE)
A diferencia de las casas en el caso de los apartamentos tienen una correlación mas estrecha en torno a el precio las variables de area, estrato y baños, especialmente precio y area construída, lo cual se ve tanto en los indices de correlación y gráficamente, sin embargo tambien hay una alta correlación entre baños y area construida, lo que puede conducir a futuro a la multicolinealidad en el modelo de regresion lineal multiple con estas dos variables.
lmm3 = lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = vivienda_apartamentos)
summary(lmm3)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios,
## data = vivienda_apartamentos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1650.27 -59.58 2.30 49.99 997.22
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -393.25164 12.29814 -31.98 <2e-16 ***
## areaconst 1.95143 0.04886 39.94 <2e-16 ***
## estrato 62.52578 3.05126 20.49 <2e-16 ***
## parqueaderos 93.25437 4.19559 22.23 <2e-16 ***
## banios 39.30175 3.16986 12.40 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 139.7 on 4226 degrees of freedom
## (869 observations deleted due to missingness)
## Multiple R-squared: 0.7781, Adjusted R-squared: 0.7779
## F-statistic: 3705 on 4 and 4226 DF, p-value: < 2.2e-16
A diferencia del caso de la regresión para solo casas, en la regresión de apartamentos tiene un superior a 0.7 con un 0.7781 es decir representa casi el 78% de la variación, lo cual es considerablemente alto y es algo que tiene sentido al evaluar las correlaciones de las variables mas altas para este caso. Así mismo con valores p menores a 0.05 para cada coeficiente así como el modelo general se puede decir que estadisticamente significante .
par(mfrow = c(2, 2))
plot(lmm3)
residu=lmm3$residuals
shapiro.test(residu)
##
## Shapiro-Wilk normality test
##
## data: residu
## W = 0.8417, p-value < 2.2e-16
bptest(lmm3)
##
## studentized Breusch-Pagan test
##
## data: lmm3
## BP = 1242.3, df = 4, p-value < 2.2e-16
dwtest(lmm3)
##
## Durbin-Watson test
##
## data: lmm3
## DW = 1.6378, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
Al igual que el caso del modelo de las casas se encuentra que el modelo viola los mismos supuestos, las soluciones planteadas para el caso de las viviendas también aplican para este caso y podrían solucionar varios de los inconvenientes con estos supuestos.
Al igual que en el caso del modelo para las casas se realizó a modo de ejercicio un modelo que involucrara la variable de zona como dummies.
vivienda_apartamentos_2 <- vivienda_apartamentos[,c("preciom","areaconst","estrato","banios","habitaciones","zona")]
encoded_zona_2 <- as.data.frame(model.matrix(~ zona - 1, data = vivienda_apartamentos_2))
colnames(encoded_zona_2) <- sub("^zona", "", colnames(encoded_zona_2))
vivienda_apartamentos_2 <- vivienda_apartamentos_2[, !colnames(vivienda_apartamentos_2) %in% "zona"]
vivienda_apartamentos_2_encoded <- cbind(vivienda_apartamentos_2, encoded_zona_2)
lmm4 <- lm(preciom ~ .- `Zona Sur`, data = vivienda_apartamentos_2_encoded)
summary(lmm4)
##
## Call:
## lm(formula = preciom ~ . - `Zona Sur`, data = vivienda_apartamentos_2_encoded)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1904.61 -51.00 1.05 45.20 971.19
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -237.33109 13.76705 -17.239 <2e-16 ***
## areaconst 2.22296 0.04204 52.873 <2e-16 ***
## estrato 56.91854 2.68697 21.183 <2e-16 ***
## banios 61.11292 2.99576 20.400 <2e-16 ***
## habitaciones -32.93022 3.32925 -9.891 <2e-16 ***
## `Zona Centro` -20.09085 27.71277 -0.725 0.4685
## `Zona Norte` 10.99780 4.72094 2.330 0.0199 *
## `Zona Oeste` 98.77094 5.57407 17.720 <2e-16 ***
## `Zona Oriente` -0.77096 17.69355 -0.044 0.9652
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 134.6 on 5091 degrees of freedom
## Multiple R-squared: 0.7839, Adjusted R-squared: 0.7836
## F-statistic: 2308 on 8 and 5091 DF, p-value: < 2.2e-16
Como se puede ver tanto el valor del \(R^2\) con el \(R^2 ajustado\) aumentaron a 0.78, lo cual es muy postivo, a diferencia del modelo realizado para las casas anteriormente al incluir las variables dummy en este modelo para apartamento si hay un impacto de mejora. Sin embargo al igual que en el modelo para las casas el valor p de la zona oriente y centro son muy elevados.
viv2 = data.frame(
areaconst = 300,
parqueaderos = 3,
estrato = c(5, 6),
banios = 3,
habitaciones = 5
)
viv2
## areaconst parqueaderos estrato banios habitaciones
## 1 300 3 5 3 5
## 2 300 3 6 3 5
predict(lmm3,viv2)
## 1 2
## 902.4732 964.9990
Al realizar la predicción del precio con las caracteristicas del apartamente en cuestion, para estratos 5 y 6 se obtiene respectivamente precios de 902 millones y 964 millones, lo cual está fuera del presupuesto sin embargo al realizar las predicciones con el modelo que incluye las variables dummy de zona este precio cambia como se ve a continuación.
viv2_2 = data.frame(
areaconst = 300,
parqueaderos = 3,
estrato = c(5, 6),
banios = 3,
habitaciones = 5,
'Zona Centro' = 0,
'Zona Norte' = 0,
'Zona Oeste' = 0,
'Zona Oriente' = 0,
'Zona Sur' = 1
)
colnames(viv2_2) <- gsub("\\.", " ", colnames(viv2_2))
viv2_2
## areaconst parqueaderos estrato banios habitaciones Zona Centro Zona Norte
## 1 300 3 5 3 5 0 0
## 2 300 3 6 3 5 0 0
## Zona Oeste Zona Oriente Zona Sur
## 1 0 0 1
## 2 0 0 1
predict(lmm4,viv2_2)
## 1 2
## 732.8381 789.7567
Como se puede ver el precio en este caso está en ambos casos dentro del presupuesto.
apartamentos <- filter(vivienda_apartamentos, areaconst >= 300, parqueaderos >= 3,
banios >= 3, habitaciones >= 5, zona == "Zona Sur" ,estrato >= 5, estrato <= 6, preciom <= 850) %>%
arrange(preciom)
min(apartamentos$preciom)
## [1] 670
max(apartamentos$preciom)
## [1] 730
head(apartamentos,10)
## # A tibble: 2 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 7512 Zona … <NA> 5 670 300 3 5 6 Apar… semin…
## 2 7182 Zona … <NA> 5 730 573 3 8 5 Apar… guada…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
colores3 <- colorFactor(palette = c( "blue"), domain = apartamentos$zona)
# se crea el mapa con las Casas según su latitud y longitud según el color marcado en el paso anterior.
mapa3 <- leaflet(apartamentos) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores3(zona), radius = 4)
# Ver Mapa
mapa3
apartamentos2 <- filter(vivienda_apartamentos, areaconst >= 300, parqueaderos >= 3,
banios >= 3, habitaciones >= 5, zona != "Zona Norte" ,estrato >= 5, estrato <= 6, preciom <= 850) %>%
arrange(preciom)
min(apartamentos2$preciom)
## [1] 670
max(apartamentos2$preciom)
## [1] 730
head(apartamentos2,10)
## # A tibble: 2 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 7512 Zona … <NA> 5 670 300 3 5 6 Apar… semin…
## 2 7182 Zona … <NA> 5 730 573 3 8 5 Apar… guada…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
Al realizar la busqueda de viviendas que cumplan con las condiciones solo se logran encontrar con dos apartamentos, tanto al buscar solo en la zona sur como al buscar en las demas zonas diferentes a la zona norte. Sin embargo al expandir la busqueda a la zona norte este numero aumenta a 3 como se ve a continuación.
apartamentos3 <- filter(vivienda_apartamentos, areaconst >= 300, parqueaderos >= 3,
banios >= 3, habitaciones >= 5 ,estrato >= 5, estrato <= 6, preciom <= 850) %>%
arrange(preciom)
min(apartamentos3$preciom)
## [1] 450
max(apartamentos3$preciom)
## [1] 730
head(apartamentos3,10)
## # A tibble: 3 × 14
## id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 3572 Zona … 05 5 450 307 4 4 5 Apar… versa…
## 2 7512 Zona … <NA> 5 670 300 3 5 6 Apar… semin…
## 3 7182 Zona … <NA> 5 730 573 3 8 5 Apar… guada…
## # … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
## # abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
Finalmente se realiza un modelo final a manera de ejercicio que incluya adicionalmente la variable de tipo de vivienda y se entrena con todo el dataset de vivienda para compararlo con los demás modelos creados.
vivienda_general <- vivienda
vivienda_general <- vivienda_general[,c("preciom","areaconst","estrato","banios","habitaciones","zona","tipo")]
vivienda_general <- vivienda_general[complete.cases(vivienda_general$tipo), ]
encoded_zona_3 <- as.data.frame(model.matrix(~ zona - 1, data = vivienda_general))
encoded_tipo <- as.data.frame(model.matrix(~ tipo - 1, data = vivienda_general))
colnames(encoded_zona_3) <- sub("^zona", "", colnames(encoded_zona_3))
colnames(encoded_tipo) <- sub("^tipo", "", colnames(encoded_tipo))
vivienda_general <- vivienda_general[, !colnames(vivienda_general) %in% "zona"]
vivienda_general <- vivienda_general[, !colnames(vivienda_general) %in% "tipo"]
vivienda_general_encoded <- cbind(vivienda_general, encoded_zona_3,encoded_tipo)
lmm5 <- lm(preciom ~ .- `Zona Sur`- `Casa`, data = vivienda_general_encoded)
summary(lmm5)
##
## Call:
## lm(formula = preciom ~ . - `Zona Sur` - Casa, data = vivienda_general_encoded)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1725.85 -87.24 -14.98 58.65 1182.81
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -353.39554 13.27228 -26.627 < 2e-16 ***
## areaconst 1.07083 0.01986 53.912 < 2e-16 ***
## estrato 100.56238 2.60117 38.660 < 2e-16 ***
## banios 67.24679 2.24124 30.004 < 2e-16 ***
## habitaciones -26.46109 1.94144 -13.630 < 2e-16 ***
## `Zona Centro` 66.38501 16.51627 4.019 5.89e-05 ***
## `Zona Norte` 0.17274 4.88679 0.035 0.972
## `Zona Oeste` 118.20835 6.09687 19.388 < 2e-16 ***
## `Zona Oriente` 15.13776 10.67028 1.419 0.156
## Apartamento 2.43424 5.47100 0.445 0.656
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 176.5 on 8309 degrees of freedom
## Multiple R-squared: 0.7118, Adjusted R-squared: 0.7115
## F-statistic: 2280 on 9 and 8309 DF, p-value: < 2.2e-16
Como se puede ver no mejoró el desempeño al incluir mas variable al modelo como la variable de tipo de vivienda. Contrario a ello pareciera haber empeorado, sin emnbargo habría que someterlo a testeo mas exaustivo para afirmar esto.
Los modelos obtenidos tienen varias oportunidades de mejora, en cuanto al poder predictivo así como a los supuestos. Sin embargo es muy importante realizar una depuración del dataset, pues hay varios datos que generan dudas como se evidenció en el análisis exploratorio, así mismo es importante validar la ubicación de las casas y apartamentos del dataset pues como se vió varios están mal etiqeutados.