1. Preparación de los datos

Leer archivo

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(knitr)
library(kableExtra)
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
properties <- read.csv(file = './ar_properties.csv')

properties %>%
  head() %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
id ad_type start_date end_date created_on lat lon l1 l2 l3 l4 l5 l6 rooms bedrooms bathrooms surface_total surface_covered price currency price_period title property_type operation_type
S0we3z3V2JpHUJreqQ2t/w== Propiedad 2019-04-14 2019-06-14 2019-04-14 -34.94331 -54.92966 Uruguay Maldonado Punta del Este NA NA NA 2 NA 1 45 40 13000 UYU Mensual Departamento - Roosevelt Departamento Alquiler
kMxcmAS8NvrynGBVbMOEaQ== Propiedad 2019-04-14 2019-04-16 2019-04-14 -34.63181 -58.42060 Argentina Capital Federal Boedo NA NA NA NA NA NA NA NA 0 NA Mensual PH - Boedo PH Venta
Ce3ojF+ZTOkB8d+LI9dpxg== Propiedad 2019-04-14 9999-12-31 2019-04-14 NA NA Argentina Bs.As. G.B.A. Zona Norte NA NA NA NA 2 NA 1 200 NA NA NA NA Ituzaingo 1100 - $ 1 - Casa Alquiler Casa Alquiler
AUGpj3raGmOCiulSMGIBPA== Propiedad 2019-04-14 9999-12-31 2019-04-14 -34.65470 -58.79089 Argentina Bs.As. G.B.A. Zona Oeste Moreno Moreno NA NA 2 NA 2 460 100 NA NA Mensual Dr. Vera 300 - Consulte precio - Casa en Venta Casa Venta
m+MwZmJl3OoxmfWcB//sBA== Propiedad 2019-04-14 2019-07-09 2019-04-14 -34.65495 -58.78712 Argentina Bs.As. G.B.A. Zona Oeste Moreno Moreno NA NA 2 NA 3 660 148 NA NA Mensual L. N. Alem 2400 - Consulte precio - Casa en Venta Casa Venta
pJRPrB5G8JlK9J3Yg5F+Hg== Propiedad 2019-04-14 2019-08-08 2019-04-14 -32.93547 -60.68398 Argentina Santa Fe Rosario NA NA NA 4 NA 1 NA 89 NA NA Mensual Casa - Ludueña Casa Venta

Filtrar registros y seleccionar columnas

properties.filtered <-
  properties %>%
    filter(
      l1 == 'Argentina'
      & l2 == 'Capital Federal'
      & currency == 'USD'
      & (
        property_type == 'Departamento'
        | property_type == 'PH'
        | property_type == 'Casa'
      )
      & operation_type == 'Venta'
    ) %>%
    select(
      id,
      l3,
      rooms,
      bedrooms,
      bathrooms,
      surface_total,
      surface_covered,
      price,
      property_type
    )

print(dim(properties.filtered))
## [1] 61905     9
properties.filtered %>%
  head() %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
id l3 rooms bedrooms bathrooms surface_total surface_covered price property_type
oyj+f764ALCYodIqBvWAww== Barracas NA NA NA 300 180 320000 PH
HdjpKrqdwYfH9YU1DKjltg== Boedo 6 NA 2 178 240 500000 Casa
YwWE3rTb2+gmsBwjUHmAPQ== Palermo NA NA 2 240 157 350000 Casa
6AxnSWOhbIU8TUCqb+paBg== Belgrano 3 NA 4 157 NA 470000 Casa
U4fk+co3Rd8JDMot0pQI6Q== Versalles NA NA 1 140 110 155000 Casa
AfdcsqUSelai1ofCAq2B0Q== Velez Sarsfield 3 NA 2 95 69 199900 Casa

2. Análisis exploratorios (I)

Valores faltantes:

colSums(is.na(properties.filtered)) %>%
  sort(decreasing = TRUE) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
x
bedrooms 25298
rooms 5314
surface_total 3671
bathrooms 3196
surface_covered 2975
l3 355
id 0
price 0
property_type 0

Valores únicos:

sapply(properties.filtered, function (x) length(unique(x))) %>%
  sort(decreasing = TRUE) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
x
id 61905
price 4095
surface_total 671
surface_covered 573
l3 58
bedrooms 25
rooms 24
bathrooms 15
property_type 3

Matriz de correlación para variables númericas

library(corrr)

properties.filtered %>%
  select(
    rooms,
    bedrooms,
    bathrooms,
    surface_total,
    surface_covered,
    price
  ) %>% 
  correlate(use = 'complete.obs') %>%
  shave() %>%
  fashion() %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
## 
## Correlation method: 'pearson'
## Missing treated using: 'complete.obs'
rowname rooms bedrooms bathrooms surface_total surface_covered price
rooms
bedrooms .92
bathrooms .61 .62
surface_total .07 .07 .06
surface_covered .07 .07 .07 .70
price .49 .43 .60 .05 .06

¿Cómo es la correlación entre las variables surface_total y surface_covered?

La correlación entre las variables surface_total y surface_covered es .70. Lo que implica que hay una gran correlación positiva entre ambas variables. Para observar mejor la relación, se procede a hacer un gráfico de dispersión entre ambas variables.

library(ggplot2)

ggplot(
  na.omit(properties.filtered),
  aes(x = surface_total, y = surface_covered)
) + 
  geom_point()

Aunque el gráfico resulte un poco confuso por unos valores que parecieran outliers, se puede observar como hay un gran grupo de puntos en los cuales su valor de surface_covered es mayor al aumentar surface_total.

¿Y la correlación entre rooms y bathrooms?

El valor de correlación entre las variables rooms y bathrooms es de .61. Lo que implica que hay una notable correlación positiva entre ambas variables. Como el caso anterior, se procede a hacer un gráfico de dispersión.

ggplot(
  na.omit(properties.filtered),
  aes(x = rooms, y = bathrooms)
) + 
  geom_point()

En este último gráfico se puede observar dicha relación entre variables, aunque es menos notoría que en el caso anterior.

¿Cómo es la correlación de la variable a explicar, price, con el resto de las variables?

La variable que presenta una mayor correlación con nuestra variable a explicar, price, es la variable bathrooms con un valor de correlación de .60. Seguido por la variable rooms (.49) y bedrooms (.43). Debido al análisis hecho anteriormente, es lógico esperar que si la variable bathrooms presenta una significativa correlación con price, rooms también este bastante correlacionada. Por último, las variables surface_covered (0.06) y surface_total (0.05), se podría decir que ni presenten correlación alguna.

Se continua haciendo graficos de dispersión para price en función de dichas variables.

library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
plot_var <- function (x) {
  ggplot(
    na.omit(properties.filtered),
    aes_string(x = x, y = 'price')
  ) + 
    geom_point()
}

grid.arrange(
  plot_var('rooms'),
  plot_var('bedrooms'),
  plot_var('bathrooms'),
  plot_var('surface_total'),
  plot_var('surface_covered'),
  ncol = 2
)

En estos gráficos, se observa lo comentado anteriormente. Las variables surface_total y surface_covered parecen no tener relación con price. Mientras que en los otros gráficos se ve una tendecía de un aumento de price a medida que incrementa la variable en el eje x.

3. Limpieza de Datos

library(tidyr)

properties.clean <- properties.filtered %>%
  select(-bedrooms) %>%
  drop_na() %>%
  filter(surface_total >= surface_covered)

properties.clean %>%
  head() %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
id l3 rooms bathrooms surface_total surface_covered price property_type
AfdcsqUSelai1ofCAq2B0Q== Velez Sarsfield 3 2 95 69 199900 Casa
ESzybdH7YU2uIU1/kHtRGw== Nuñez 1 1 44 38 147000 Departamento
r22OfzZ3kXooSPoE5HMuZQ== Almagro 1 1 40 37 92294 Departamento
atZQXVtyfG7+OiX6gYY3lA== Almagro 1 1 49 44 115000 Departamento
R7IsTtVsxPCiRbIHMaWGxA== Almagro 1 1 40 37 77000 Departamento
Tur/v/qE41UBT83NM/bI7w== Almagro 1 1 40 37 88900 Departamento
dim(properties.clean)
## [1] 50828     8

4. Análisis exploratorios (II)

Crear una nueva variable precio_en_miles que sea la variable price divida por 1000. Obtener estadísticas descriptivas para esta nueva variable (cuartiles, promedio, mínimo y máximo) y realizar un histograma de la misma.

properties.new <- data.frame(properties.clean)
properties.new$precio_en_miles <- properties.new$price / 1000

quantile(properties.new$precio_en_miles) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
x
0% 6
25% 119
50% 170
75% 270
100% 6000
summary(properties.new$precio_en_miles)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     6.0   119.0   170.0   251.6   270.0  6000.0
boxplot(properties.new$precio_en_miles)

hist(properties.new$precio_en_miles, main = 'Histograma de precio_en_miles')

Obtener las mismas estadísticas descriptivas de la nueva variable precio_en_miles para cada tipo de propiedad y realizar boxplots paralelos de la variable según tipo de propiedad. ¿Qué diferencias encuentran entre los tipos de propiedad?

data.frame(
  Departamento = quantile(
    properties.new$precio_en_miles[properties.new$property_type == 'Departamento']
  ),
  PH = quantile(
    properties.new$precio_en_miles[properties.new$property_type == 'PH']
  ),
  Casa = quantile(
    properties.new$precio_en_miles[properties.new$property_type == 'Casa']
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Departamento PH Casa
0% 6.0 32 20
25% 115.0 137 235
50% 164.9 190 330
75% 260.0 270 490
100% 6000.0 1500 5000
cbind(
  as.data.frame(
    apply(
      data.frame(
        Departamento = properties.new$precio_en_miles[properties.new$property_type == 'Departamento']
      ),
      2,
      summary
    )
  ),
  as.data.frame(
    apply(
      data.frame(
        PH = properties.new$precio_en_miles[properties.new$property_type == 'PH']
      ),
      2,
      summary
    )
  ),
  as.data.frame(
    apply(
      data.frame(
        Casa = properties.new$precio_en_miles[properties.new$property_type == 'Casa']
      ),
      2,
      summary
    )
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Departamento PH Casa
Min. 6.0000 32.0000 20.0000
1st Qu. 115.0000 137.0000 235.0000
Median 164.9000 190.0000 330.0000
Mean 247.3604 218.5077 433.4101
3rd Qu. 260.0000 270.0000 490.0000
Max. 6000.0000 1500.0000 5000.0000
ggplot(
  properties.new,
  aes(x=property_type, y=precio_en_miles)
) + 
  geom_boxplot(fill="slateblue", alpha=0.2) + 
  xlab("cyl")

Los tres grupos presentan máximos bastante altos en comparación con la mayoría de las observaciones. Esto sucede principalmente en los grupos Departamento y Casa, los cuales tienen máximos de 6000 y 5000 aunque su tercer cuartil este en 260 y 490 respectivamente. En el grupo PH se puede observar un comportamiento similar, pero con un máximo más cercano (1500 respecto a 270 del tercer cuartil).

Se puede resaltar el grupo Casa tendería a tener valores más altos que los otros dos grupos, el valor de su media (433) es casi el doble de la media de PH (218) y también se podría decir que es significativamente más alto al comparandolo con Departamento (247).

Los grupos Departamento y PH pareciera tener observaciones distribuidas de manera similar (sus primeros cuartiles, medias y 3eros cuartiles son similares). Pero, el grupo Departamento tiene extremos (es decir, máximo y mínimo) más distanciados.

Realizar un gráfico con la función ggpairs de las variables numéricas (sin abrir por tipo de propiedad). Comenten los aspectos principales que observan en el gráfico

library(GGally)
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
ggpairs(
  properties.new %>%
    select(
      rooms,
      bathrooms,
      surface_total,
      surface_covered,
      price,
      precio_en_miles
    )
)

Se graficó tanto price como precio_en_miles, lo que hace que las dos últimas filas del gráfico aporten exactamente la misma información ya que hay una es una función lineal de la otra.

Se vuelven a destacar las posible relaciónes entre, por un lado, las variables rooms y bathrooms, y por otro surface_covered y surface_total. También, hay que resaltar una posible relación entre la variable price (o precio_en_miles) y bathrooms.

5. Outliers

Graficar un scatterplot de la variable precio_en_miles y superficie_total. ¿Detectan alguna anomalía?

ggplot(
  na.omit(properties.new),
  aes(x = surface_total, y = precio_en_miles)
) + 
  geom_point()

Parece existir un pequeño de grupo de observaciones con surface_total alto y precio_en_miles bajo que podrían tener un comportamiento anómalo.

Eliminar los outliers univariados de las variables precio_en_miles, rooms y surface_total. Utilizar y fundamentar el o los criterio/s y métodos que consideren adecuados.

boxplot(
  properties.new %>%
    select(precio_en_miles, surface_total)
)

Los boxplots nos muestran que hay outliers superiores. Se procederá a removerlos con dicho criterio.

box_plot_criteria <- function (x) {
  quantiles <- quantile(x)
  iqr <- IQR(x)
  inf <- quantiles[[2]] - 1.5 * iqr
  sup <- quantiles[[4]] + 1.5 * iqr
  
  list(
    inf = inf,
    sup = sup
  )
}

box_plot.limits.surface_total <- box_plot_criteria(properties.new$surface_total)
box_plot.limits.precio_en_miles <- box_plot_criteria(properties.new$precio_en_miles)

data.frame(
  var = c(
    'surface_total',
    'precio_en_miles'
  ),
  inf = c(
    box_plot.limits.surface_total$inf,
    box_plot.limits.precio_en_miles$inf
  ),
  sup = c(
    box_plot.limits.surface_total$sup,
    box_plot.limits.precio_en_miles$sup
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
var inf sup
surface_total -46.5 197.5
precio_en_miles -107.5 496.5
properties.no_outliers_sup <- properties.new %>%
  filter(
    surface_total < box_plot.limits.surface_total$sup
    & precio_en_miles < box_plot.limits.precio_en_miles$sup
  )

Ya que según este criterio la cota inferior para los outliers es negativa, se utilizará el criterio de los percentiles.

q <- c(seq(0, .2, 0.01))

data.frame(
  surface_total = quantile(
    properties.no_outliers_sup$surface_total,
    q
  ),
  precio_en_miles = quantile(
    properties.no_outliers_sup$precio_en_miles,
    q
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
surface_total precio_en_miles
0% 12 7.5
1% 23 59.5
2% 26 66.0
3% 28 70.0
4% 30 74.0
5% 31 77.0
6% 32 79.9
7% 33 82.0
8% 34 85.0
9% 35 85.9
10% 35 88.5
11% 36 90.0
12% 36 92.0
13% 37 94.0
14% 37 95.0
15% 38 97.0
16% 38 99.0
17% 39 99.8
18% 39 100.0
19% 40 104.0
20% 40 105.0

Se observa que del primer al segundo percentil hay un aumento anómalo en comparación con el aumento entre los siguientes percentiles. Es decir, para surface_total el aumento en dicho percentil es aproximadamente el doble y en precio_en_miles de casi ocho veces. Como no se observa estos comportamientos en el resto de los percentiles, se supone que estos datos son anómalos y se procede a utilizar dicho percentil como una cota inferior.

properties.last <- properties.no_outliers_sup %>%
  filter(
    surface_total >= quantile(properties.no_outliers_sup$surface_total, q)[[2]] &
    precio_en_miles >= quantile(properties.no_outliers_sup$precio_en_miles, q)[[2]]
  )

6. Análisis exploratorios (III)

Repetir los análisis exploratorios realizados en el punto 4 al dataset sin outliers. ¿Detectan algún cambio? Explicar.

quantile(properties.last$precio_en_miles) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
x
0% 59.5
25% 115.0
50% 159.0
75% 232.9
100% 496.0
summary(properties.last$precio_en_miles)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    59.5   115.0   159.0   184.2   232.9   496.0
boxplot(properties.last$precio_en_miles)

hist(properties.last$precio_en_miles, main = 'Histograma de precio_en_miles')

Aunque el boxplot sigue marcando la existencia de outliers, podemos ver que ya no hay tanta separación entre el tercer cuartil y el máximo. Además, si observamos el gráfico de la distribución de dicha variable, se puede apreciar que ya no se encuentran observaciones únicas con valores muy altos “ensuciando” dicho gráfico.

data.frame(
  Departamento = quantile(
    properties.last$precio_en_miles[properties.last$property_type == 'Departamento']
  ),
  PH = quantile(
    properties.last$precio_en_miles[properties.last$property_type == 'PH']
  ),
  Casa = quantile(
    properties.last$precio_en_miles[properties.last$property_type == 'Casa']
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Departamento PH Casa
0% 59.5 59.9 62.900
25% 113.0 130.0 175.000
50% 155.0 175.0 239.000
75% 230.0 250.0 299.999
100% 496.0 490.0 492.000
cbind(
  as.data.frame(
    apply(
      data.frame(
        Departamento = properties.last$precio_en_miles[properties.last$property_type == 'Departamento']
      ),
      2,
      summary
    )
  ),
  as.data.frame(
    apply(
      data.frame(
        PH = properties.last$precio_en_miles[properties.last$property_type == 'PH']
      ),
      2,
      summary
    )
  ),
  as.data.frame(
    apply(
      data.frame(
        Casa = properties.last$precio_en_miles[properties.last$property_type == 'Casa']
      ),
      2,
      summary
    )
  )
) %>%
  kable() %>%
  kable_styling(bootstrap_options = c("striped"))
Departamento PH Casa
Min. 59.5000 59.9000 62.9000
1st Qu. 113.0000 130.0000 175.0000
Median 155.0000 175.0000 239.0000
Mean 181.9512 194.7291 241.5403
3rd Qu. 230.0000 250.0000 299.9990
Max. 496.0000 490.0000 492.0000
ggplot(
  properties.last,
  aes(x=property_type, y=precio_en_miles)
) + 
  geom_boxplot(fill="slateblue", alpha=0.2) + 
  xlab("cyl")

En este caso vuelve a suceder que los gráficos de caja sugieren la existencia de outliers. Aunque se sigue viendo que el valor máximo estaría alejado del tercer cuartil, al compararlo con las observaciones antes de realizar el tratamineto de outliers, vemos que esta separación disminuyó significativamente.

ggpairs(
  properties.last %>%
    select(
      rooms,
      bathrooms,
      surface_total,
      surface_covered,
      price,
      precio_en_miles
    )
)

Se debe resaltar que tras el tratamiento de outliers la correlación entre surface_total y precio_en_miles aumentó significativamente. Previo al tratamiento de outliers dicho valor era de 0.064, mientras que ahora es de 0.753.

7. Modelo lineal

Realizar un modelo lineal simple para explicar el precio_en_miles en función de las habitaciones (rooms). Explicar el significado de los valores de los coeficientes.

model.rooms <- lm(formula = precio_en_miles ~ rooms, data = properties.last)
summary(model.rooms)
## 
## Call:
## lm(formula = precio_en_miles ~ rooms, data = properties.last)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -504.2  -46.2  -11.1   31.9  339.2 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  55.3027     0.8661   63.85   <2e-16 ***
## rooms        50.2674     0.3096  162.38   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 72.95 on 44247 degrees of freedom
## Multiple R-squared:  0.3734, Adjusted R-squared:  0.3734 
## F-statistic: 2.637e+04 on 1 and 44247 DF,  p-value: < 2.2e-16
plot_model <- function (model, var, label) {
  y_intercept <- model$coefficients[1]
  slope = model$coefficients[2]
  
  properties.last %>%
    ggplot(., aes_string(x = var, y = 'precio_en_miles')) + 
    geom_abline(intercept = y_intercept, slope = slope, color="forestgreen", size=1.5) +
    geom_point() +
    theme_bw() +
    labs(title=label, x=var, y="Precio") 
}

plot_model(model.rooms, 'rooms', 'precio_en_miles ~ rooms')

Realizar un modelo lineal simple para explicar el precio_en_miles en función de la superficie total (surface_total). Explicar el significado de los valores de los coeficientes.

model.surface_total <- lm(formula = precio_en_miles ~ surface_total, data = properties.last)
summary(model.surface_total)
## 
## Call:
## lm(formula = precio_en_miles ~ surface_total, data = properties.last)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -323.25  -32.01   -8.45   25.40  322.36 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   46.811342   0.640068   73.14   <2e-16 ***
## surface_total  1.935798   0.008052  240.42   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 60.69 on 44247 degrees of freedom
## Multiple R-squared:  0.5664, Adjusted R-squared:  0.5664 
## F-statistic: 5.78e+04 on 1 and 44247 DF,  p-value: < 2.2e-16
plot_model(model.surface_total, 'surface_total', 'precio_en_miles ~ surface_total')

¿Cuál modelo usarían para predecir el precio? ¿Por qué?

library(broom)
glance(model.rooms)
## # 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.373         0.373  73.0    26367.       0     1 -2.53e5 5.05e5 5.05e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>
glance(model.surface_total)
## # 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.566         0.566  60.7    57802.       0     1 -2.44e5 4.89e5 4.89e5
## # … with 3 more variables: deviance <dbl>, df.residual <int>, nobs <int>

Observamos que el modelo que utiliza la superficie total como variable explicativa tiene el mayor R-cuadrado: \(R^2=0.5664\) y por lo tanto es el mejor modelo para explicar el precio en miles del grupo de propiedades que se esta analizando.