Enunciado

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

Preparación de los datos

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

Pasos requeridos para la obtención de los resultados

  1. Realice un filtro a la base de datos e incluya solo las ofertas de : base1: casas, de la zona norte de la ciudad. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).
# 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.

  1. Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.
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.

  1. Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente \(R^2\) y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).
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:

  1. Incluir más variables predictoras relevantes, si están disponibles como por ejemplo el barrio, los años de contruída, existencia de piscina, existencia de jacuzzi y otras comodidades.
  2. Realizar una exploración más detallada de los datos para identificar posibles valores atípicos o datos faltantes que puedan afectar el modelo.
  3. Evaluar la multicolinealidad entre las variables predictoras para asegurarte de que no estén altamente correlacionadas.
  4. Probar diferentes transformaciones de las variables.

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.

  1. Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).
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.

  1. Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.

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.

  1. Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.
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
  1. Realice los pasos del 1 al 6. Para la segunda solicitud que tiene un crédito pre-aprobado por valor de $850 millones.
# 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.

Conclusiones finales

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.