Librerías a utilizar

library(readr)
library(tidyverse)
library(corrr)
library(ggplot2)
library(kableExtra)
library(knitr)
library(kableExtra)
library(GGally)
library(magrittr)

1 . Preparacion de los datos (I)

Estructura de los datos

dataset <- readr::read_csv("ar_properties.csv")

# Hay distintas formas para mostrar la estructura, elijo la función "str"
str(dataset)
## Classes 'spec_tbl_df', 'tbl_df', 'tbl' and 'data.frame': 388891 obs. of  24 variables:
##  $ id             : chr  "S0we3z3V2JpHUJreqQ2t/w==" "kMxcmAS8NvrynGBVbMOEaQ==" "Ce3ojF+ZTOkB8d+LI9dpxg==" "AUGpj3raGmOCiulSMGIBPA==" ...
##  $ ad_type        : chr  "Propiedad" "Propiedad" "Propiedad" "Propiedad" ...
##  $ start_date     : Date, format: "2019-04-14" "2019-04-14" ...
##  $ end_date       : Date, format: "2019-06-14" "2019-04-16" ...
##  $ created_on     : Date, format: "2019-04-14" "2019-04-14" ...
##  $ lat            : num  -34.9 -34.6 NA -34.7 -34.7 ...
##  $ lon            : num  -54.9 -58.4 NA -58.8 -58.8 ...
##  $ l1             : chr  "Uruguay" "Argentina" "Argentina" "Argentina" ...
##  $ l2             : chr  "Maldonado" "Capital Federal" "Bs.As. G.B.A. Zona Norte" "Bs.As. G.B.A. Zona Oeste" ...
##  $ l3             : chr  "Punta del Este" "Boedo" NA "Moreno" ...
##  $ l4             : chr  NA NA NA "Moreno" ...
##  $ l5             : chr  NA NA NA NA ...
##  $ l6             : logi  NA NA NA NA NA NA ...
##  $ rooms          : num  2 NA 2 2 2 4 NA 6 NA NA ...
##  $ bedrooms       : num  NA NA NA NA NA NA NA NA NA NA ...
##  $ bathrooms      : num  1 NA 1 2 3 1 3 3 NA NA ...
##  $ surface_total  : num  45 NA 200 460 660 NA 70 NA 1300 405 ...
##  $ surface_covered: num  40 NA NA 100 148 89 122 NA NA NA ...
##  $ price          : num  13000 0 NA NA NA NA NA NA 0 NA ...
##  $ currency       : chr  "UYU" NA NA NA ...
##  $ price_period   : chr  "Mensual" "Mensual" NA "Mensual" ...
##  $ title          : chr  "Departamento - Roosevelt" "PH - Boedo" "Ituzaingo  1100 - $ 1 - Casa Alquiler" "Dr. Vera   300 - Consulte precio - Casa en Venta" ...
##  $ property_type  : chr  "Departamento" "PH" "Casa" "Casa" ...
##  $ operation_type : chr  "Alquiler" "Venta" "Alquiler" "Venta" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   id = col_character(),
##   ..   ad_type = col_character(),
##   ..   start_date = col_date(format = ""),
##   ..   end_date = col_date(format = ""),
##   ..   created_on = col_date(format = ""),
##   ..   lat = col_double(),
##   ..   lon = col_double(),
##   ..   l1 = col_character(),
##   ..   l2 = col_character(),
##   ..   l3 = col_character(),
##   ..   l4 = col_character(),
##   ..   l5 = col_character(),
##   ..   l6 = col_logical(),
##   ..   rooms = col_double(),
##   ..   bedrooms = col_double(),
##   ..   bathrooms = col_double(),
##   ..   surface_total = col_double(),
##   ..   surface_covered = col_double(),
##   ..   price = col_double(),
##   ..   currency = col_character(),
##   ..   price_period = col_character(),
##   ..   title = col_character(),
##   ..   property_type = col_character(),
##   ..   operation_type = col_character()
##   .. )

Filtrado de registros

Para este problema el análisis lo realizaré para los registros que: + Pertenecen a Argentina y Capital Federal + Cuyo precio esta en dolares (USD) + El tipo de propiedad sea: Departamento, PH o Casa + El tipo de operacion sea Venta

library(dplyr)

# Para realizar el filtro, utilizo las *pipes* del paquete *dplyr*
dataset_filtrado <- dataset %>%  
  # El filtro empieza con que la variable l1 corresponiente a país sea "Argentina", la variable l2 correspondiente a provinica sea "Capital Federal" y que la variable *currency* correspondiente al tipo de cambio sea en dólares
      dplyr::filter(l1=="Argentina" & l2=="Capital Federal" & currency=="USD" & 
  # en esta parte del filtro se busca que el tipo de propiedades sea "PH", "Casa" o "Departamento" 
             (property_type=="PH" | property_type=="Departamento" | property_type=="Casa") & 
  # por último se elige que el tipo de operación sea "Venta"
              operation_type=="Venta")

Selección de variables

Las variables a utilizar en el análisis son: id, l3, rooms, bedrooms, bathrooms, surface_total, surface_covered, price y property_type.

# Para realizar el filtro también utilizo las *pipes* y dentro de la función select se incluyen las varaibles requeridas
dataset_filtrado <- dataset_filtrado %>%
  dplyr::select(id,l3,rooms,bedrooms,bathrooms,surface_total,surface_covered,price,property_type) 

2. Analisis exploratorios (I)

Obtener la cantidad de valores unicos y de valores faltantes (NAs) para cada una de estas variables

# Creo una función para contar la cantidad de valores únicos
cant_unicos <- function(x)length(unique(x))

# Creo una lista con la cantidad de valores unicos para cada variable y para mejorar la visualización la traspongo
dt_unicos <- dataset_filtrado %>%
        dplyr::summarise_all(list(cant_unicos = cant_unicos)) %>%
             t()

# Para formatear la tabla utilizo lo función *kable_styling* 
knitr::kable(x =dt_unicos, col.names = "Cantidad de valores únicos",format = "html") %>%
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = F)
Cantidad de valores únicos
id_cant_unicos 61905
l3_cant_unicos 58
rooms_cant_unicos 24
bedrooms_cant_unicos 25
bathrooms_cant_unicos 15
surface_total_cant_unicos 671
surface_covered_cant_unicos 573
price_cant_unicos 4095
property_type_cant_unicos 3
# Creo otra función para contar la cantidad de faltantes 
cant_nas <- function(x)sum(is.na(x))

# Creo una lista con la cantidad de faltantes para cada variable y para mejorar la visualización la traspongo
dt_faltantes <- dataset_filtrado %>% 
                       dplyr::summarise_all(list(cant_nas = cant_nas)) %>% 
                        t()

# Para formatear la tabla utilizo lo función *kable_styling* 
knitr::kable(x =dt_faltantes, col.names = "Cantidad de valores faltantes", format = "html") %>%
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = F)
Cantidad de valores faltantes
id_cant_nas 0
l3_cant_nas 355
rooms_cant_nas 5314
bedrooms_cant_nas 25298
bathrooms_cant_nas 3196
surface_total_cant_nas 3671
surface_covered_cant_nas 2975
price_cant_nas 0
property_type_cant_nas 0

Obtener la matriz de correlacion para las variables numericas. Pista: usen ‘complete.obs’ para poder omitir los valores faltantes.

# Para poder realizar la matriz de correlación, es necesario primero filtrar la base de datos obteniendo así solamente las variables cuantitativas. En este caso serían todas las variables menos: *id*, *l3* y *property_type*
data_num <- dataset_filtrado %>%  
  dplyr::select(-id,-l3,-property_type)

# El método de correlación utilizado es el de *Pearson* y se omiten los valores faltantes con *complete.obs*
dt_corr <- data_num %>%  
            corrr::correlate(., use="complete.obs", method="pearson") %>% 
              corrr::shave() %>%
                corrr::fashion()

# Para formatear la tabla utilizo lo función *kable_styling* 
knitr::kable(x =dt_corr, format = "html") %>%
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = F)
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

El precio presenta una correlación de 0.498 con respecto a la cantidad de habitaciones, 0.616 con la variable cantidad de baños y una correlación mucho menor con las variables superficie total y superficie cubierta.

# Una forma más visual para poder observar la matriz de correlación es un gráfico, este gráfico utiliza la correlación de *Pearson*
data_num %>% 
  corrr::correlate() %>% 
    corrr::rplot()

3. Preparacion de los datos (II)

En el análisis anterior encontré que la variable bedrooms presenta una alta proporción de valores faltantes y que presenta una fuerte correlacion con la variable rooms. Por lo tanto, paso a eliminarla junto con dejar solamente los registros que no tienen valores faltantes.

dataset_filtrado <- dataset_filtrado %>%
                      dplyr::select(-bedrooms) %>% 
                        dplyr::filter(complete.cases(.))

4. Análisis exploratorios (II)

Estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) e histograma de la variable

# Las estadísticas descriptivas las obtengo con la función summary
summary(dataset_filtrado$price)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    6000  119000  170000  251577  270000 6000000
# Para realizar el histograma utilizo el paquete ggplot, además de hacer el histograma agrego la densidad acumulada en el gráfico.
ggplot2::ggplot(dataset_filtrado, ggplot2::aes(x=price)) +      ggplot2::geom_histogram(ggplot2::aes(y=..density..), colour="black", fill="pink")

Estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) por cada tipo de superficie.

#Primero hago el filtrado por tipo de propiedad para luego calcular las estadisticas descriptivas
precio <- dataset_filtrado %>% 
            dplyr::group_by(property_type) %>%
              dplyr::summarize(q1 = quantile(price,0.25),
                        prom = mean(price),
                        q3 = quantile(price,0.75),
                        min = min(price),
                        max = max (price))
# Para formatear la tabla utilizo lo función *kable_styling* 
knitr::kable(x =precio, format = "html") %>%
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = F)
property_type q1 prom q3 min max
Casa 235000 434188.8 490000 20000 5000000
Departamento 115000 246855.7 260000 6000 6000000
PH 137000 218747.4 270000 32000 1500000

Gráfico Boxplot de la variable precio por tipo de propiedad

# Para poder hacer un boxplot, la variable tipo de propiedad debe estar como factor
dataset_filtrado$property_type <- as.factor(dataset_filtrado$property_type)
ggplot2::ggplot(dataset_filtrado, ggplot2::aes(x=property_type, y=price)) +
                         ggplot2::geom_boxplot(alpha= 0.75, ggplot2::aes(color = property_type)) + 
                         ggplot2::labs(x = "Tipo de propiedad", 
                                       y = "Precio (USD)",
                         title = "Boxplots de precio por tipo de propiedad",
                         fill = "Tipo de propiedad")

A pesar que en los boxplots veo que hay muchos valores atípicos que podrían considerarse outliers con precios superiores al extremo del bigote, no voy a utilizar este gráfico para decidir que valores eliminar.

Para tener una visión mas global de los datos, realizo un correlograma:

# Primero elijo únicamente las numéricas para poder realizar el correlograma
dataset_filtrado %>% dplyr::select(-id,-l3) %>%
                        dplyr::mutate(property_type = factor(property_type)) %>%
                          GGally::ggpairs(., 
                              title = "Matriz de correlaciones",
                              mapping = ggplot2::aes(colour= property_type))

En este gráfico se repite la información de correlación obtenida anteriormente y el boxplot de precio dado el departamento. La mayoría de los datos corresponden a departamentos y en poca medida a casas y PHs. Es interesante notar que las variables correspondientes a la superficie tengan una correlación tan baja, siendo que por lo general el precio es función de la superficie.

5. Outliers

Dados los análisis anteriores, elijo las dos variables que creo más significativas para el análisis: superficie total y cantidad de habitaciones. No haré el análisis para las demás varaibles ya que la superficie cubierta y la cantidad de baños correlacionan en más de un 60% con las variables superficie total y cantidad de habitaciones respectivamente.

# Para la elección de los outliers primero realizo los scatterplots de la variable precio respecto de las variables: superficie total ("surface_total") y cantidad de habitaciones ("rooms")
ggplot2::ggplot(dataset_filtrado, ggplot2::aes(x=surface_total, y=price)) +
                         ggplot2::geom_point(ggplot2::aes(color = property_type))+
                         ggplot2::labs(x = "Superficie Total", 
                                       y = "Precio (USD)",
                                      title = "Precio por superficie")

Asumo que todas las variables de superficies están medidas en m2. En el scatterplot se puede ver que hay un PH con una superficie mucho mas grande que las demás y su precio es bajo. Esta propiedad en particular dice tener una superficie total de 126062 y una superficie cubierta de 126. Con la información provista por el dataset pude encontrar el internet la propiedad, de acuerdo a la descripción este PH es de 126m2 y no 126062. Por lo tanto, decido excluir del análisis a esta propiedad y su duplicado. El siguiente dato con valor de superficie total más elevado es una casa con 16725m2 en Villa Urquiza, este valor es un poco extraño para la zona. Tras buscar el anuncio en internet, descubrí que en realidad la superficie total es de 160m2. Este dato también se desecha del análisis. Por último, analizo el Departamento cuya superficie total es 11250m2, esta propiedad al igual que las anteriores, tiene realmente una superficie de 112m2 de acuerdo al anuncio. Comienzo a sospechar de todos los anuncios y las unidades de medición utilizadasn al publicarlos.

superficie <- dataset_filtrado %>%
                dplyr::filter(dataset_filtrado$surface_total>1000 & dataset_filtrado$surface_total<11000)
  
ggplot2::ggplot(superficie, ggplot2::aes(x=surface_total, y=price)) +
                         ggplot2::geom_point(ggplot2::aes(colour = property_type)) +
                         ggplot2::labs(x = "Superficie Total (m2)", 
                                       y = "Precio (USD)",
                                       title = "Precio por superficie")

En el scatterplot encuentro que hay muchos departamentos de bajo precio con alta superficie total. Como criterio de corte utilizo que todos los anuncios cuya superficie es mayor a 2000mt2 son outliers. Este valor corresponde a la superficie total de la última casa antes de los 2500mt2.

Ahora paso a hacer el análisis del precio respecto de la variable cantidad de habitaciones. Como primer paso realizo el scatterplot correspondiente.

ggplot2::ggplot(dataset_filtrado, ggplot2::aes(x=rooms, y=price)) +
                         ggplot2::geom_point(ggplot2::aes(color = property_type))+
                         ggplot2::labs(x = "Cantidad de habitaciones", 
                                       y = "Precio (USD)",
                                       title = "Precio por cantidad de habitaciones")

En el scatterplot referente a la cantidad de habitaciones, encuentro que hay un PH con 32. Este valor me parece alto para una propiedad, luego de buscarlo en internet encuentro que se trata de una propiedad compuesta por 2 PHs que se venden juntos. Debido a que no es un error mantengo el dato en el análisis.

# Una vez concluido el análisis de outliers paso a eliminarlos de la base de datos filtrada.
dataset_clean <- dataset_filtrado %>%
                  dplyr::filter(dataset_filtrado$surface_total<2501)

6. Analisis exploratorios (III)

Repetir los 4 análisis exploratorios realizados en el punto 4 y realizar unos breves comentarios sobre los cambios que encontraron

Estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) e un histograma de la variable

# Las estadísticas descriptivas las obtengo con la función summary
summary(dataset_clean$price)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    6000  119000  170000  251671  271500 6000000
# Para realizar el histograma utilizo el paquete ggplot, además de hacer el histograma agrego la densidad acumulada en el gráfico.
ggplot2::ggplot(dataset_clean, ggplot2::aes(x=price)) +         ggplot2::geom_histogram(ggplot2::aes(y=..density..), bins = 8 ,colour="black", fill="pink")

Estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) por cada tipo de superficie.

#Primero hago el filtrado por tipo de propiedad para luego calcular las estadisticas descriptivas
precio <- dataset_clean %>% 
            dplyr::group_by(property_type) %>%
              dplyr::summarize(q1 = quantile(price,0.25),
                        prom = mean(price),
                        q3 = quantile(price,0.75),
                        min = min(price),
                        max = max (price))

# Para formatear la tabla utilizo lo función *kable_styling* 
knitr::kable(x =precio, format = "html") %>%
  kableExtra::kable_styling(bootstrap_options = "striped", full_width = F)
property_type q1 prom q3 min max
Casa 235000 434410.1 490000 20000 5000000
Departamento 115000 246945.7 260000 6000 6000000
PH 137000 218822.9 272000 32000 1500000

Gráfico Boxplot de la variable precio por tipo de propiedad

# Para poder hacer un boxplot, la variable tipo de propiedad debe estar como factor
dataset_clean$property_type <- as.factor(dataset_clean$property_type)
ggplot2::ggplot(dataset_clean, ggplot2::aes(x=property_type, y=price)) +
                         ggplot2::geom_boxplot(alpha= 0.75, ggplot2::aes(color = property_type)) + 
                         ggplot2::labs(x = "Tipo de propiedad", 
                                       y = "Precio (USD)",
                         title = "Boxplots de precio por tipo de propiedad",
                         fill = "Tipo de propiedad")

Al volver a realizar los análisis exploratorios, encuentro que el histograma continúa con la tendencia de tener más valores cuando los precios son más bajos manteniendo la asimétria derecha. La media al ser sensible a outliers cambió significativamente, de USD 251577 pasó a USD 251671 y la mediana se mantuvo sin variación. En cuanto a los boxplots todavía se observan valores atípicos.

7. Modelo lineal

En esta parte realicé un modelo lineal simple para explicar el precio en función de las habitaciones (rooms) y posteriormente otro modelo explicando el precio en función de la superficie total (surface_total).

# Modelo lineal simple para precio en función de cantidad de habitaciones
lm_rooms <- lm(data = dataset_clean, formula = price ~ rooms)
summary(lm_rooms)
## 
## Call:
## lm(formula = price ~ rooms, data = dataset_clean)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -2712151   -97614   -32699    37290  5413041 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -41931.4     2522.1  -16.63   <2e-16 ***
## rooms       104815.1      806.2  130.01   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 254000 on 51162 degrees of freedom
## Multiple R-squared:  0.2483, Adjusted R-squared:  0.2483 
## F-statistic: 1.69e+04 on 1 and 51162 DF,  p-value: < 2.2e-16

De acuerdo a los resultados obtenidos en este primer modelo, el B0 es -41931,4 este valor carece de sentido práctico, es el valor correspondiente a la ordenada al origen. El B1 del modelo es 104815,5, esto significa que por cada habitación que se agrega el precio del inmueble en dólares aumenta ese valor.

ggplot2::ggplot(dataset_clean, ggplot2::aes(x=rooms, y=price)) +
                         ggplot2::geom_point(ggplot2::aes(color = property_type)) +
                         ggplot2::geom_smooth(method = "lm") +
                         ggplot2::labs(x = "Cantidad de habitaciones", 
                                       y = "Precio (USD)",
                                       title = "Relación precio y cantidad de habitaciones") +
                         ggplot2::labs(fill = "Tipo de propiedad")

A continuación se pueden observar los resultados del segundo modelo:

# Modelo lineal simple para precio en función de cantidad de habitaciones
lm_surface <- lm(data = dataset_clean, price ~ surface_total)
summary(lm_surface)
## 
## Call:
## lm(formula = price ~ surface_total, data = dataset_clean)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -4976642   -49874   -21933    15694  4976773 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   41169.73    1463.62   28.13   <2e-16 ***
## surface_total  2294.09      11.82  194.09   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 222300 on 51162 degrees of freedom
## Multiple R-squared:  0.4241, Adjusted R-squared:  0.4241 
## F-statistic: 3.767e+04 on 1 and 51162 DF,  p-value: < 2.2e-16

En este caso el B0 es 41169,73 y el B1 es 2294,09, este último valor corresponde a lo que aumenta el precio del inmueble en dólares al aumentar en 1m2 la superficie total.

ggplot2::ggplot(dataset_clean, ggplot2::aes(x=surface_total,y=price)) +
                         ggplot2::geom_point(ggplot2::aes(color = property_type)) +
                         ggplot2::geom_smooth(method = "lm") +
                         ggplot2::labs(x = "Superficie total (m2)",
                                       y = "Precio (USD)", 
                                       title = "Relación precio y superficie total")

Ambos modelos tienen una porporción de variabilidad explicada baja, en el primer modelo es un 24,83% y en el segundo un 42,41%. Dados estos resultados, elegiría el segundo modelo para predecir, es decir, el que utiliza la variable superficie total.