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(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(GGally)
## Loading required package: ggplot2
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
library(tidyverse)
## ── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ tibble  3.0.3     ✓ purrr   0.3.4
## ✓ tidyr   1.1.2     ✓ stringr 1.4.0
## ✓ readr   1.4.0     ✓ forcats 0.5.0
## ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter()          masks stats::filter()
## x kableExtra::group_rows() masks dplyr::group_rows()
## x dplyr::lag()             masks stats::lag()
library(tidymodels)
## ── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidymodels 0.1.1 ──
## ✓ broom     0.7.1      ✓ recipes   0.1.14
## ✓ dials     0.0.9      ✓ rsample   0.0.8 
## ✓ infer     0.5.3      ✓ tune      0.1.1 
## ✓ modeldata 0.1.0      ✓ workflows 0.2.1 
## ✓ parsnip   0.1.4      ✓ yardstick 0.0.7
## ── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidymodels_conflicts() ──
## x scales::discard()        masks purrr::discard()
## x dplyr::filter()          masks stats::filter()
## x recipes::fixed()         masks stringr::fixed()
## x kableExtra::group_rows() masks dplyr::group_rows()
## x dplyr::lag()             masks stats::lag()
## x yardstick::spec()        masks readr::spec()
## x recipes::step()          masks stats::step()
library(plotly)
## 
## 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

1. Preparación de los datos

properties.train <- read.csv(file = './ar_properties_train.csv')
properties.test <- read.csv(file = './ar_properties_test.csv')
head(properties.train)  %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
id l3 rooms bathrooms surface_total surface_covered price property_type
r22OfzZ3kXooSPoE5HMuZQ== Almagro 1 1 40 37 92294 Departamento
atZQXVtyfG7+OiX6gYY3lA== Almagro 1 1 49 44 115000 Departamento
Tur/v/qE41UBT83NM/bI7w== Almagro 1 1 40 37 88900 Departamento
PNtNl6sWjHEyRPQAeRAIgw== Almagro 1 1 40 37 88798 Departamento
Lhg4dNG9VmPbHt0Swy2ATA== Almagro 1 1 49 44 110975 Departamento
oeLhuwomY2yj4A9TtDrgCA== Almagro 1 1 40 37 99000 Departamento
summary(properties.train)
##       id                 l3                rooms         bathrooms    
##  Length:32132       Length:32132       Min.   :1.000   Min.   :1.000  
##  Class :character   Class :character   1st Qu.:2.000   1st Qu.:1.000  
##  Mode  :character   Mode  :character   Median :3.000   Median :1.000  
##                                        Mean   :2.722   Mean   :1.427  
##                                        3rd Qu.:3.000   3rd Qu.:2.000  
##                                        Max.   :8.000   Max.   :5.000  
##  surface_total surface_covered      price        property_type     
##  Min.   : 28   Min.   : 26.00   Min.   : 69500   Length:32132      
##  1st Qu.: 46   1st Qu.: 42.00   1st Qu.:120000   Class :character  
##  Median : 65   Median : 58.00   Median :169000   Mode  :character  
##  Mean   : 80   Mean   : 70.61   Mean   :214991                     
##  3rd Qu.: 99   3rd Qu.: 85.00   3rd Qu.:260000                     
##  Max.   :320   Max.   :265.00   Max.   :950000

El data set cuenta con 8 variables por individuo:

l3_freq <- as.data.frame(table(properties.train$l3)) %>%
  arrange(desc(Freq))

l3_freq %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Var1 Freq
Palermo 4890
Belgrano 2736
Almagro 2486
Caballito 2393
Villa Crespo 2095
Recoleta 1824
Barrio Norte 1359
Villa Urquiza 1319
Flores 916
Nuñez 887
Balvanera 883
Villa Devoto 527
Colegiales 523
Villa del Parque 500
San Telmo 488
Saavedra 433
San Cristobal 417
Parque Centenario 404
Floresta 366
Puerto Madero 357
Boedo 336
Paternal 327
San Nicolás 325
Monserrat 310
Chacarita 292
Retiro 291
Villa Pueyrredón 285
Parque Chacabuco 282
Barracas 280
Villa Luro 264
Liniers 260
Mataderos 259
Once 255
Congreso 246
Coghlan 240
Las Cañitas 187
Abasto 179
Parque Patricios 171
Monte Castro 168
Constitución 139
Villa Santa Rita 128
Versalles 113
Villa Lugano 113
Villa Ortuzar 109
Centro / Microcentro 105
Boca 104
Villa General Mitre 99
Parque Avellaneda 78
Parque Chas 78
Pompeya 65
Velez Sarsfield 56
Villa Real 55
Tribunales 54
Agronomía 51
Villa Riachuelo 13
Villa Soldati 9
Catalinas 3
barplot(table(properties.train$l3))

l3_freq_percentage <- data.frame(orden = c(), barrio = c(), porcentaje = c(), sumatoria = c())

for (i in 1:nrow(l3_freq)) {
  percentage <- l3_freq$Freq[i] / nrow(properties.train) * 100
  sum <- percentage
  if (i > 1) {
    sum <- sum + l3_freq_percentage$sumatoria[i - 1]
  }
  
  l3_freq_percentage <- rbind(
    l3_freq_percentage,
    data.frame(
      orden = c(i),
      barrio = c(l3_freq$Var1[i]),
      porcentaje = c(percentage),
      sumatoria = c(sum)
    )
  )
}

l3_freq_percentage$barrio <- l3_freq$Var1

l3_freq_percentage %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
orden barrio porcentaje sumatoria
1 Palermo 15.2184738 15.21847
2 Belgrano 8.5148761 23.73335
3 Almagro 7.7368356 31.47019
4 Caballito 7.4474045 38.91759
5 Villa Crespo 6.5199801 45.43757
6 Recoleta 5.6765841 51.11415
7 Barrio Norte 4.2294286 55.34358
8 Villa Urquiza 4.1049421 59.44852
9 Flores 2.8507407 62.29927
10 Nuñez 2.7604880 65.05975
11 Balvanera 2.7480393 67.80779
12 Villa Devoto 1.6401095 69.44790
13 Colegiales 1.6276609 71.07556
14 Villa del Parque 1.5560812 72.63164
15 San Telmo 1.5187352 74.15038
16 Saavedra 1.3475663 75.49795
17 San Cristobal 1.2977717 76.79572
18 Parque Centenario 1.2573136 78.05303
19 Floresta 1.1390514 79.19208
20 Puerto Madero 1.1110420 80.30312
21 Boedo 1.0456865 81.34881
22 Paternal 1.0176771 82.36649
23 San Nicolás 1.0114528 83.37794
24 Monserrat 0.9647703 84.34271
25 Chacarita 0.9087514 85.25146
26 Retiro 0.9056392 86.15710
27 Villa Pueyrredón 0.8869663 87.04407
28 Parque Chacabuco 0.8776298 87.92170
29 Barracas 0.8714055 88.79310
30 Villa Luro 0.8216109 89.61471
31 Liniers 0.8091622 90.42388
32 Mataderos 0.8060500 91.22993
33 Once 0.7936014 92.02353
34 Congreso 0.7655919 92.78912
35 Coghlan 0.7469190 93.53604
36 Las Cañitas 0.5819744 94.11801
37 Abasto 0.5570771 94.67509
38 Parque Patricios 0.5321798 95.20727
39 Monte Castro 0.5228433 95.73011
40 Constitución 0.4325906 96.16270
41 Villa Santa Rita 0.3983568 96.56106
42 Versalles 0.3516743 96.91273
43 Villa Lugano 0.3516743 97.26441
44 Villa Ortuzar 0.3392257 97.60363
45 Centro / Microcentro 0.3267770 97.93041
46 Boca 0.3236649 98.25408
47 Villa General Mitre 0.3081041 98.56218
48 Parque Avellaneda 0.2427487 98.80493
49 Parque Chas 0.2427487 99.04768
50 Pompeya 0.2022906 99.24997
51 Velez Sarsfield 0.1742811 99.42425
52 Villa Real 0.1711689 99.59542
53 Tribunales 0.1680568 99.76348
54 Agronomía 0.1587203 99.92220
55 Villa Riachuelo 0.0404581 99.96265
56 Villa Soldati 0.0280095 99.99066
57 Catalinas 0.0093365 100.00000
boxplot(properties.train$rooms, properties.train$bathrooms, names = c('rooms', 'bathrooms'))

boxplot(properties.train$surface_total, properties.train$surface_covered, names = c('surface_total', 'surface_covered'))

boxplot(properties.train$price, names = c('price'))

table(properties.train$property_type) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Var1 Freq
Casa 809
Departamento 28203
PH 3120
barplot(table(properties.train$property_type))

ggpairs(
  data = properties.train %>%
    select(
      rooms,
      bathrooms,
      surface_total,
      surface_covered,
      price
    ),
  upper = list(continuous = ggally_cor_v1_5)
)

2. Modelo Regresión lineal múltiple

Utilizando el dataset de training:

a. Crear un modelo para predecir el precio con todas las covariables.

modelo_lm.todos <- lm(price ~ l3 + rooms + bathrooms + surface_total + surface_covered + property_type, data = properties.train)
tidy(modelo_lm.todos, conf.int = TRUE)
## # A tibble: 63 x 7
##    term           estimate std.error statistic  p.value conf.low conf.high
##    <chr>             <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
##  1 (Intercept)    -109144.     5784.   -18.9   5.28e-79 -120480.   -97808.
##  2 l3Agronomía       1241.    10652.     0.117 9.07e- 1  -19638.    22120.
##  3 l3Almagro        -3480.     5182.    -0.672 5.02e- 1  -13636.     6676.
##  4 l3Balvanera     -25835.     5491.    -4.70  2.55e- 6  -36598.   -15072.
##  5 l3Barracas       -7974.     6412.    -1.24  2.14e- 1  -20542.     4594.
##  6 l3Barrio Norte   51694.     5325.     9.71  3.03e-22   41255.    62132.
##  7 l3Belgrano       71926.     5170.    13.9   7.26e-44   61792.    82060.
##  8 l3Boca          -47781.     8258.    -5.79  7.28e- 9  -63968.   -31595.
##  9 l3Boedo         -19759.     6203.    -3.19  1.45e- 3  -31917.    -7600.
## 10 l3Caballito       6354.     5189.     1.22  2.21e- 1   -3817.    16525.
## # … with 53 more rows

b. Analizar los resultados del modelo:

Significado de los coeficientes estimados:

tidy(modelo_lm.todos, conf.int = TRUE) %>%
  select(term, statistic, p.value, conf.low, conf.high) %>% 
  arrange(p.value)
## # A tibble: 63 x 5
##    term                      statistic   p.value conf.low conf.high
##    <chr>                         <dbl>     <dbl>    <dbl>     <dbl>
##  1 l3Puerto Madero                41.6 0.         244026.   268144.
##  2 bathrooms                      44.7 0.          32862.    35873.
##  3 surface_covered                43.6 0.           1431.     1565.
##  4 property_typeDepartamento      34.4 1.35e-254   86272.    96700.
##  5 surface_total                  31.5 3.84e-215     835.      946.
##  6 (Intercept)                   -18.9 5.28e- 79 -120480.   -97808.
##  7 property_typePH                16.8 5.06e- 63   40826.    51615.
##  8 l3Belgrano                     13.9 7.26e- 44   61792.    82060.
##  9 l3Palermo                      13.2 1.30e- 39   57214.    77189.
## 10 l3Las Cañitas                  13.1 2.24e- 39   78440.   105927.
## # … with 53 more rows

En este modelo se observa que mientras las variables bathrooms, sufrace_covered, surface_total, property_type y rooms resultan estadísticamente significativas para explicar el precio de la propiedad (p-valores < 0.05), no pasa con todas las categorías de l3, hay algunas que resultan significativas y otras no.

ggplotly(
  ggplot(
    tidy(modelo_lm.todos, conf.int = TRUE),
    aes(estimate, term, xmin = conf.low, xmax = conf.high, height = 0)
  ) +
    geom_point(color = "forestgreen", size=2) +
    geom_vline(xintercept = 0, lty = 4, color = "black") +
    geom_errorbarh(color = "forestgreen", size=1) +
    theme_bw() +
    labs(y = "Coeficientes β", x = "Estimación"),
  height = 800
)

En el anterior gráfico se puede observar los coeficientes estimados para el modelo con sus respectivos intervalos de confianza. En él se puede apreciar que el intervalo de confianza (IC) del 95% de las variables surface_total, surface_covered, rooms, bathrooms y todas las dummies de property_type no contienen al cero, es decir, resultan estadisticamente significativas para explicar el precio de la propiedad. Los intervalos de confianza de algunas de las variables dummies de l3 sí contienen al cero, por lo que no se puede asegurar que todos los valores de dicha variable sean estadisticamente significativos para explicar el precio de la propiedad.

broom::glance(modelo_lm.todos)
## # A tibble: 1 x 12
##   r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
##       <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1     0.777         0.777 66933.     1803.       0    62 -4.03e5 8.05e5 8.06e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

c. Realizar un modelo sin la covariable l3 e interpretar sus resultados (todas las partes de la salida que consideren relevantes).

modelo_lm.noL3 <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + property_type, data = properties.train)
tidy(modelo_lm.noL3, conf.int = TRUE)
## # A tibble: 7 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)          -128676.    3333.      -38.6 1.07e-318 -135210.  -122143.
## 2 rooms                 -13657.     617.      -22.1 8.68e-108  -14867.   -12448.
## 3 bathrooms              42564.     899.       47.3 0.          40801.    44327.
## 4 surface_total            856.      33.0      25.9 1.40e-146     791.      920.
## 5 surface_covered         1819.      40.0      45.5 0.           1740.     1897.
## 6 property_typeDepart…  133033.    3049.       43.6 0.         127056.   139010.
## 7 property_typePH        66457.    3233.       20.6 2.59e- 93   60121.    72793.

En este modelo se observa que todas las variables resultan estadísticamente significativas para explicar el precio de la propiedad (p-valores < 0.05). Hay que resaltar que ningún intervalo de confianza contiene al 0.

Significado de los coeficientes estimados:

d. ¿Cuál es el modelo que mejor explica la variabilidad del precio?

broom::glance(modelo_lm.noL3)
## # A tibble: 1 x 12
##   r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
##       <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1     0.686         0.686 79419.    11674.       0     6 -4.08e5 8.16e5 8.16e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Como hay distinta cantidad de variables en los modelos se debe utilizar el \(R^2_a\). En este caso se podría decir que el mejor modelo es el que utiliza todas las covariables ya que su \(R^2_a\) es mayor (0.7766239) que el del modelo que no utiliza l3 (0.6855061).

3. Creación de variables

a. En el ejercicio anterior deberían haber encontrado que algunos barrios son significativos, aunque no todos. Crear una nueva variable barrios que permita agrupar a los barrios. Explicar el análisis exploratorio para definir la nueva variable y los criterios utilizados en la construcción de la misma.

Se creará una variable agrupando barrios por el metro cuadrado promedio.

barrios.sup <- properties.train %>%
  mutate(price_sup = price / surface_total) %>%
  group_by(l3) %>%
  summarize(price_sup = mean(price_sup)) %>%
  arrange(-price_sup)
## `summarise()` ungrouping output (override with `.groups` argument)
barrios.sup %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
l3 price_sup
Puerto Madero 5438.285
Las Cañitas 3514.309
Palermo 3351.408
Belgrano 3316.252
Recoleta 3282.091
Nuñez 3225.282
Barrio Norte 3213.048
Retiro 2970.304
Colegiales 2858.154
Coghlan 2835.364
Villa Urquiza 2815.417
Saavedra 2620.620
Caballito 2609.084
Villa Ortuzar 2588.543
Chacarita 2587.910
Abasto 2553.281
Villa Crespo 2536.028
Almagro 2468.381
Parque Centenario 2463.233
San Telmo 2430.877
Villa Devoto 2422.252
Villa Pueyrredón 2385.928
Parque Chas 2363.225
Villa del Parque 2316.323
Centro / Microcentro 2307.064
Barracas 2283.309
San Nicolás 2245.270
Villa Luro 2233.816
Tribunales 2205.028
Paternal 2204.351
Monte Castro 2180.769
Flores 2163.566
Boedo 2150.451
Balvanera 2144.486
Villa General Mitre 2138.195
Once 2137.558
Villa Real 2134.709
Villa Santa Rita 2134.319
Monserrat 2121.919
Catalinas 2103.326
Congreso 2098.626
Liniers 2090.877
San Cristobal 2090.678
Agronomía 2009.210
Parque Chacabuco 1980.307
Versalles 1967.438
Parque Patricios 1946.075
Floresta 1880.990
Mataderos 1876.505
Velez Sarsfield 1842.675
Constitución 1786.940
Boca 1781.651
Parque Avellaneda 1733.703
Villa Riachuelo 1637.033
Villa Lugano 1425.710
Pompeya 1339.678
Villa Soldati 1239.104
summary(barrios.sup$price_sup)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1239    2091    2205    2365    2588    5438
quantile(barrios.sup$price_sup, c(0, 1/3, 2/3, 1)) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
x
0% 1239.104
33.33333% 2130.186
66.66667% 2441.663
100% 5438.285

Los grupos creando serán tres: - precio_bajo cuando promedio del precio sobre superficie total sea menor o igual a 2130.186 - precio_medio cuando el promedio del precio sobre superficie total sea mayor a 2130.186 y menor o igual a 2441.663 - precio_alto cuando el promedio del precio sobre superficie total sea mayor a 2441.663.

barrios.sup.map <- barrios.sup %>%
  mutate(map = case_when(
    price_sup <= 2130.186 ~ 'precio_bajo',
    price_sup <= 2441.663 ~ 'precio_medio',
    TRUE ~ 'precio_alto'
  ))

barrios.sup.map %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
l3 price_sup map
Puerto Madero 5438.285 precio_alto
Las Cañitas 3514.309 precio_alto
Palermo 3351.408 precio_alto
Belgrano 3316.252 precio_alto
Recoleta 3282.091 precio_alto
Nuñez 3225.282 precio_alto
Barrio Norte 3213.048 precio_alto
Retiro 2970.304 precio_alto
Colegiales 2858.154 precio_alto
Coghlan 2835.364 precio_alto
Villa Urquiza 2815.417 precio_alto
Saavedra 2620.620 precio_alto
Caballito 2609.084 precio_alto
Villa Ortuzar 2588.543 precio_alto
Chacarita 2587.910 precio_alto
Abasto 2553.281 precio_alto
Villa Crespo 2536.028 precio_alto
Almagro 2468.381 precio_alto
Parque Centenario 2463.233 precio_alto
San Telmo 2430.877 precio_medio
Villa Devoto 2422.252 precio_medio
Villa Pueyrredón 2385.928 precio_medio
Parque Chas 2363.225 precio_medio
Villa del Parque 2316.323 precio_medio
Centro / Microcentro 2307.064 precio_medio
Barracas 2283.309 precio_medio
San Nicolás 2245.270 precio_medio
Villa Luro 2233.816 precio_medio
Tribunales 2205.028 precio_medio
Paternal 2204.351 precio_medio
Monte Castro 2180.769 precio_medio
Flores 2163.566 precio_medio
Boedo 2150.451 precio_medio
Balvanera 2144.486 precio_medio
Villa General Mitre 2138.195 precio_medio
Once 2137.558 precio_medio
Villa Real 2134.709 precio_medio
Villa Santa Rita 2134.319 precio_medio
Monserrat 2121.919 precio_bajo
Catalinas 2103.326 precio_bajo
Congreso 2098.626 precio_bajo
Liniers 2090.877 precio_bajo
San Cristobal 2090.678 precio_bajo
Agronomía 2009.210 precio_bajo
Parque Chacabuco 1980.307 precio_bajo
Versalles 1967.438 precio_bajo
Parque Patricios 1946.075 precio_bajo
Floresta 1880.990 precio_bajo
Mataderos 1876.505 precio_bajo
Velez Sarsfield 1842.675 precio_bajo
Constitución 1786.940 precio_bajo
Boca 1781.651 precio_bajo
Parque Avellaneda 1733.703 precio_bajo
Villa Riachuelo 1637.033 precio_bajo
Villa Lugano 1425.710 precio_bajo
Pompeya 1339.678 precio_bajo
Villa Soldati 1239.104 precio_bajo
properties.train.barrios <- merge(properties.train, barrios.sup.map, by = 'l3') %>%
  rename(barrios = map)

head(properties.train.barrios) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
l3 id rooms bathrooms surface_total surface_covered price property_type price_sup barrios
Abasto 8c0orLjaWrwN5NYYZ9oCYQ== 3 1 81 75 184000 Departamento 2553.281 precio_alto
Abasto Ka1KIE3+G5Ca36dVZuat4w== 4 2 77 73 235000 Departamento 2553.281 precio_alto
Abasto JdQovtBqRtnAGa9UbuzXbw== 5 2 145 104 330000 Departamento 2553.281 precio_alto
Abasto i5Ll4GEXKs40b36b3honww== 1 1 28 28 99800 Departamento 2553.281 precio_alto
Abasto opYtEK0kxXcMU4WvYy/jfg== 1 1 54 42 128000 Departamento 2553.281 precio_alto
Abasto W+B7/p1CZdoyXJKreB3WHw== 4 2 77 73 235000 Departamento 2553.281 precio_alto

b. Calcular el modelo que predice el precio en función de las nuevas covariables e interpretar sus resultados (todas las partes de la salida que consideren relevantes).

modelo_lm.barrios <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + property_type + barrios, data = properties.train.barrios)
tidy(modelo_lm.barrios, conf.int = TRUE)
## # A tibble: 9 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)           -81384.    3242.      -25.1 1.03e-137  -87738.   -75029.
## 2 rooms                 -11511.     584.      -19.7 6.48e- 86  -12656.   -10365.
## 3 bathrooms              37024.     855.       43.3 0.          35349.    38700.
## 4 surface_total            911.      31.2      29.2 1.29e-184     849.      972.
## 5 surface_covered         1719.      37.8      45.5 0.           1645.     1793.
## 6 property_typeDepart…  104944.    2918.       36.0 1.14e-277   99224.   110664.
## 7 property_typePH        53361.    3063.       17.4 1.17e- 67   47357.    59365.
## 8 barriosprecio_bajo    -71262.    1484.      -48.0 0.         -74171.   -68353.
## 9 barriosprecio_medio   -52439.    1103.      -47.5 0.         -54601.   -50278.

En este modelo se observa que todas las variables resultan estadísticamente significativas para explicar el precio de la propiedad (p-valores < 0.05). Hay que resaltar que ningún intervalo de confianza contiene al 0.

Significado de los coeficientes estimados:

c. ¿Qué modelo explica mejor la variabilidad de los datos, el que utiliza la variable l3 o el que utiliza barrio? En su opinión, ¿Qué modelo es más útil? ¿Porqué?

broom::glance(modelo_lm.barrios)
## # A tibble: 1 x 12
##   r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
##       <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1     0.719         0.719 75071.    10278.       0     8 -4.06e5 8.13e5 8.13e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Como hay distinta cantidad de variables en los modelos se debe utilizar el \(R^2_a\). En este caso se podría decir que el mejor modelo es el que utiliza todas las covariables ya que su \(R^2_a\) es mayor (0.7766239) que el resto de los modelos (0.6855061 para el modelo sin l3 y 0.7190024 para el modelo de barrios).

d. La interpretación de los coeficientes de las variables surface_covered y surface_total puede ser un poco problemática ya que se encuentran altamente correlacionadas. Entonces, podemos construir una nueva variable sup_descubierta para la diferencia entre ambas superficies. Calcular nuevamente el modelo lineal del punto 3.b) (modelo con variable barrio) para todas las covariables previas (excepto surface_total), incluyendo surface_covered y sup_descubierta e interpretar los coeficientes de estas dos últimas variables.

properties.train.sup_descubierta <- properties.train.barrios %>%
  mutate(sup_descubierta = surface_total - surface_covered)

head(properties.train.sup_descubierta) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
l3 id rooms bathrooms surface_total surface_covered price property_type price_sup barrios sup_descubierta
Abasto 8c0orLjaWrwN5NYYZ9oCYQ== 3 1 81 75 184000 Departamento 2553.281 precio_alto 6
Abasto Ka1KIE3+G5Ca36dVZuat4w== 4 2 77 73 235000 Departamento 2553.281 precio_alto 4
Abasto JdQovtBqRtnAGa9UbuzXbw== 5 2 145 104 330000 Departamento 2553.281 precio_alto 41
Abasto i5Ll4GEXKs40b36b3honww== 1 1 28 28 99800 Departamento 2553.281 precio_alto 0
Abasto opYtEK0kxXcMU4WvYy/jfg== 1 1 54 42 128000 Departamento 2553.281 precio_alto 12
Abasto W+B7/p1CZdoyXJKreB3WHw== 4 2 77 73 235000 Departamento 2553.281 precio_alto 4
modelo_lm.sup_descubierta <- lm(price ~ rooms + bathrooms + surface_covered + sup_descubierta + property_type + barrios, data = properties.train.sup_descubierta)
tidy(modelo_lm.sup_descubierta, conf.int = TRUE)
## # A tibble: 9 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)           -81384.    3242.      -25.1 1.03e-137  -87738.   -75029.
## 2 rooms                 -11511.     584.      -19.7 6.48e- 86  -12656.   -10365.
## 3 bathrooms              37024.     855.       43.3 0.          35349.    38700.
## 4 surface_covered         2630.      19.8     133.  0.           2591.     2669.
## 5 sup_descubierta          911.      31.2      29.2 1.29e-184     849.      972.
## 6 property_typeDepart…  104944.    2918.       36.0 1.14e-277   99224.   110664.
## 7 property_typePH        53361.    3063.       17.4 1.17e- 67   47357.    59365.
## 8 barriosprecio_bajo    -71262.    1484.      -48.0 0.         -74171.   -68353.
## 9 barriosprecio_medio   -52439.    1103.      -47.5 0.         -54601.   -50278.

En este modelo se observa que todas las variables resultan estadísticamente significativas para explicar el precio de la propiedad (p-valores < 0.05). Hay que resaltar que ningún intervalo de confianza contiene al 0.

Significado de los coeficientes estimados: - El coeficiente estimado surface_covered se interpreta como el incremento en el precio de una propiedad por aumentar en una unidad la superficie cubierta (manteniendo el resto de las variables constantes). Particularmente, cada incremento en una unidad en la superficie cubierta resultará en un aumento de 2629.8403 en el precio de la propiedad. - El coeficiente estimado sup_descubierta se interpreta como el incremento en el precio de una propiedad por aumentar en una unidad la superficie descubierta (manteniendo el resto de las variables constantes). Particularmente, cada incremento en una unidad en la superficie descubierta resultará en un aumento de 910.6953 en el precio de la propiedad.

broom::glance(modelo_lm.sup_descubierta)
## # A tibble: 1 x 12
##   r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
##       <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1     0.719         0.719 75071.    10278.       0     8 -4.06e5 8.13e5 8.13e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Este modelo tiene el mismo \(R^2\) que el modelo de barrios anterior, así que, se podría decir que explican una variabilidad muy similar.

4. Diagnóstico del modelo

Analizar los residuos del modelo elaborado en el punto 3.d) y evaluar el cumplimiento de los supuestos del modelo lineal.

plot(modelo_lm.sup_descubierta)

broom::augment(modelo_lm.sup_descubierta) %>%
  arrange(-.hat) %>%
  head()
## # A tibble: 6 x 13
##    price rooms bathrooms surface_covered sup_descubierta property_type barrios
##    <int> <int>     <int>           <int>           <int> <chr>         <chr>  
## 1 649000     8         5             153               0 Casa          precio…
## 2 185000     4         1             260               0 Casa          precio…
## 3 169000     4         1             250               0 Casa          precio…
## 4 390000     4         5             250              70 Casa          precio…
## 5 475000     3         4             251               0 Casa          precio…
## 6 335000     2         2             220              15 Casa          precio…
## # … with 6 more variables: .fitted <dbl>, .resid <dbl>, .std.resid <dbl>,
## #   .hat <dbl>, .sigma <dbl>, .cooksd <dbl>

Diagnóstico del modelo: El modelo creado no cumple con los supuestos del modelo lineal. Parecen existir problemas de heterocedasticidad (varianza no constante) y falta de normalidad.

5. Modelog Log(price)

Crear un modelo para log(price) e interpretar los parámetros estimados de este nuevo modelo. Comparar la performance del modelo de 3.d) con éste, tanto en términos de la variabilidad explicada como del cumplimiento de los supuestos del modelo lineal:

\(log(price) = \beta_0 + \beta_1 log(rooms) + \beta_2 log(bathrooms) + \beta_3 log(surface_covered) + \beta_4 property\_type + \beta_5 barrio + \beta_6 surface\_patio\)

modelo_lm.log <- lm(log(price) ~ log(rooms) + log(bathrooms) + log(surface_covered) + property_type + barrios + sup_descubierta , data = properties.train.sup_descubierta)
tidy(modelo_lm.log, conf.int = TRUE)
## # A tibble: 9 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)           8.42     0.0239      352.   0.         8.38      8.47   
## 2 log(rooms)           -0.0782   0.00485     -16.1  2.81e- 58 -0.0877   -0.0687 
## 3 log(bathrooms)        0.183    0.00493      37.2  2.52e-297  0.174     0.193  
## 4 log(surface_covered)  0.859    0.00570     151.   0.         0.848     0.871  
## 5 property_typeDepart…  0.238    0.00947      25.1  6.51e-138  0.219     0.256  
## 6 property_typePH       0.0635   0.00998       6.37 1.95e- 10  0.0440    0.0831 
## 7 barriosprecio_bajo   -0.367    0.00489     -75.1  0.        -0.377    -0.358  
## 8 barriosprecio_medio  -0.245    0.00363     -67.6  0.        -0.253    -0.238  
## 9 sup_descubierta       0.00405  0.000103     39.3  0.         0.00385   0.00425

En este modelo se observa que todas las variables resultan estadísticamente significativas para explicar el precio de la propiedad (p-valores < 0.05). Hay que resaltar que ningún intervalo de confianza contiene al 0.

Significado de los coeficientes estimados:

broom::glance(modelo_lm.log)
## # A tibble: 1 x 12
##   r.squared adj.r.squared sigma statistic p.value    df logLik   AIC   BIC
##       <dbl>         <dbl> <dbl>     <dbl>   <dbl> <dbl>  <dbl> <dbl> <dbl>
## 1     0.791         0.791 0.247    15218.       0     8  -668. 1356. 1440.
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Como este modelo tiene la misma cantidad de variables que el del punto 3.d, se puede usar el valor de \(R^2\) para comparar la variabilidad explicada entre ellos. El \(R^2\) del modelo log(price) es mayor que el del otro modelo (0.7912287 contra 0.7190723), así que, se puede decir que el modelo log(price) explica una mayor variabilidad.

plot(modelo_lm.log)

broom::augment(modelo_lm.log) %>%
  arrange(-.hat) %>%
  head()
## # A tibble: 6 x 12
##   `log(price)` `log(rooms)` `log(bathrooms)` `log(surface_co… property_type
##          <dbl>        <dbl>            <dbl>            <dbl> <chr>        
## 1         12.9         0                0                5.19 Casa         
## 2         12.9         0                0                5.19 Casa         
## 3         12.3         0                1.10             5.01 Casa         
## 4         11.7         0                0                5.16 PH           
## 5         12.5         1.39             0                4.79 Casa         
## 6         12.3         1.39             0                4.79 Casa         
## # … with 7 more variables: barrios <chr>, sup_descubierta <int>, .fitted <dbl>,
## #   .std.resid <dbl>, .hat <dbl>, .sigma <dbl>, .cooksd <dbl>

Diagnóstico del modelo: El modelo creado no cumple con los supuestos del modelo lineal. Parecen existir problemas de heterocedasticidad (varianza no constante) y falta de normalidad.

6. Selección de modelo

Se generan dos modelos. El primero definiendo la variable dependiente, price, en forma logaritmica, mientras el resto de las covariables en forma lineal (se utilizarán las variables rooms, bathrooms, surface_covered, property_type, barrios y sup_descubierta).

modelo_lm.1 <- lm(log(price) ~ rooms + bathrooms + surface_covered + property_type + barrios + sup_descubierta , data = properties.train.sup_descubierta)
tidy(modelo_lm.1, conf.int = TRUE)
## # A tibble: 9 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)          11.0     0.0120        922.  0.        11.0      11.1    
## 2 rooms                 0.0429  0.00216        19.9 1.84e- 87  0.0387    0.0471 
## 3 bathrooms             0.123   0.00316        39.0 0.         0.117     0.129  
## 4 surface_covered       0.00797 0.0000730     109.  0.         0.00782   0.00811
## 5 property_typeDepart…  0.283   0.0108         26.3 1.32e-150  0.262     0.304  
## 6 property_typePH       0.130   0.0113         11.5 2.36e- 30  0.107     0.152  
## 7 barriosprecio_bajo   -0.372   0.00548       -67.9 0.        -0.383    -0.361  
## 8 barriosprecio_medio  -0.250   0.00407       -61.4 0.        -0.258    -0.242  
## 9 sup_descubierta       0.00473 0.000115       41.0 0.         0.00450   0.00496
broom::glance(modelo_lm.1)
## # A tibble: 1 x 12
##   r.squared adj.r.squared sigma statistic p.value    df logLik   AIC   BIC
##       <dbl>         <dbl> <dbl>     <dbl>   <dbl> <dbl>  <dbl> <dbl> <dbl>
## 1     0.737         0.737 0.277    11261.       0     8 -4369. 8758. 8841.
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

El segundo definiendo a la variable dependiente en forma lineal mientras que algunas de las independientes (rooms, sufrace_covered) en forma logaritmica.

modelo_lm.2 <- lm(price ~ log(rooms) + log(surface_covered) + property_type + barrios, data = properties.train.sup_descubierta)
tidy(modelo_lm.2, conf.int = TRUE)
## # A tibble: 7 x 7
##   term                 estimate std.error statistic   p.value conf.low conf.high
##   <chr>                   <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
## 1 (Intercept)          -920195.     6871.   -134.   0.        -933663.  -906727.
## 2 log(rooms)            -58581.     1576.    -37.2  4.64e-296  -61670.   -55491.
## 3 log(surface_covered)  276807.     1578.    175.   0.         273713.   279900.
## 4 property_typeDepart…   70709.     3020.     23.4  2.92e-120   64790.    76627.
## 5 property_typePH        15676.     3234.      4.85 1.26e-  6    9337.    22015.
## 6 barriosprecio_bajo    -76155.     1581.    -48.2  0.         -79253.   -73056.
## 7 barriosprecio_medio   -56193.     1176.    -47.8  0.         -58499.   -53888.
broom::glance(modelo_lm.2)
## # A tibble: 1 x 12
##   r.squared adj.r.squared  sigma statistic p.value    df  logLik    AIC    BIC
##       <dbl>         <dbl>  <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1     0.678         0.678 80316.    11296.       0     6 -4.08e5 8.17e5 8.17e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

De los 5 modelos ya realizados, se eligirá los que expliquen la mayor variabilidad, es decir, el modelo de log(price) y el de todas las covariables.

modelos <- list(
  modelo_lm.todos = modelo_lm.todos,
  modelo_lm.noL3 = modelo_lm.noL3,
  modelo_lm.barrios = modelo_lm.barrios,
  modelo_lm.sup_descubierta = modelo_lm.sup_descubierta,
  modelo_lm.log = modelo_lm.log
)
map_df(modelos, broom::glance, .id = "model") %>%
  arrange(desc(adj.r.squared))
## # A tibble: 5 x 13
##   model r.squared adj.r.squared   sigma statistic p.value    df  logLik    AIC
##   <chr>     <dbl>         <dbl>   <dbl>     <dbl>   <dbl> <dbl>   <dbl>  <dbl>
## 1 mode…     0.791         0.791 2.47e-1    15218.       0     8 -6.68e2 1.36e3
## 2 mode…     0.777         0.777 6.69e+4     1803.       0    62 -4.03e5 8.05e5
## 3 mode…     0.719         0.719 7.51e+4    10278.       0     8 -4.06e5 8.13e5
## 4 mode…     0.719         0.719 7.51e+4    10278.       0     8 -4.06e5 8.13e5
## 5 mode…     0.686         0.686 7.94e+4    11674.       0     6 -4.08e5 8.16e5
## # … with 4 more variables: BIC <dbl>, deviance <dbl>, df.residual <int>,
## #   nobs <int>

Evalue estos 4 (cuatro) modelos en términos de su capacidad predictiva en el dataset de training, fundamentando claramente su elección con la/s métrica/s que considere adecuada/s.

Como se va a utilizar un modelo para predecir nuevos datos nos interesa observar el error de los modelos en ambos datasets: training y testing. Una métrica para medir el error de predicción es RMSE (error cuadrático medio o root mean square error en inglés). Hay que resaltar que el valor de esta métrica está en las mismas unidades que los datos originales.

\(RMSE = \sqrt{\frac{\sum(\hat{y_i}-y_i)^2}{n}}\)

Hay que tener en cuenta que dos de los modelos tienen como variable dependiente log(price) y no price. Así que, se deberá transformar el .fitted para que refleje el price.

modelos_evaluar <- list(
  modelo_lm.todos = modelo_lm.todos,
  modelo_lm.log = modelo_lm.log,
  modelo_lm.1 = modelo_lm.1,
  modelo_lm.2 = modelo_lm.2
)

predicciones_training <- map(.x = modelos_evaluar, .f = augment)
predicciones_training$modelo_lm.log$.fitted = exp(predicciones_training$modelo_lm.log$.fitted)
predicciones_training$modelo_lm.1$.fitted = exp(predicciones_training$modelo_lm.1$.fitted)

map_dfr(
  .x = predicciones_training,
  .f = rmse,
  truth = properties.train$price,
  estimate = .fitted,
  .id = "modelo"
) %>%
  arrange(.estimate)
## # A tibble: 4 x 4
##   modelo          .metric .estimator .estimate
##   <chr>           <chr>   <chr>          <dbl>
## 1 modelo_lm.todos rmse    standard      66867.
## 2 modelo_lm.2     rmse    standard     183567.
## 3 modelo_lm.log   rmse    standard     184529.
## 4 modelo_lm.1     rmse    standard     198955.

Predecir los valores de precios de las propiedades en el dataset de testing (recuerden que deben realizar las mismas transformaciones que hicieron en el set de training) y comparar la performance de los modelos seleccionados con la/s métrica/s elegidas previamente. Determinar con cuál modelo se queda y por qué.

properties.test.sup_descubierta <- merge(properties.test, barrios.sup.map, by = 'l3') %>%
  rename(barrios = map) %>%
  mutate(sup_descubierta = surface_total - surface_covered)

predicciones_testing = map(.x = modelos_evaluar, .f = augment, newdata = properties.test.sup_descubierta) 

predicciones_testing$modelo_lm.log$.fitted = exp(predicciones_testing$modelo_lm.log$.fitted)
predicciones_testing$modelo_lm.1$.fitted = exp(predicciones_testing$modelo_lm.1$.fitted)

map_dfr(
  .x = predicciones_testing,
  .f = rmse,
  truth = price,
  estimate = .fitted,
  .id="modelo"
) %>%
  arrange(.estimate)
## # A tibble: 4 x 4
##   modelo          .metric .estimator .estimate
##   <chr>           <chr>   <chr>          <dbl>
## 1 modelo_lm.todos rmse    standard      65805.
## 2 modelo_lm.log   rmse    standard      71810.
## 3 modelo_lm.2     rmse    standard      79718.
## 4 modelo_lm.1     rmse    standard      87227.