library(readr)
library(tidyverse)
library(corrr)
library(ggplot2)
library(kableExtra)
library(knitr)
library(kableExtra)
library(GGally)
library(magrittr)
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()
## .. )
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")
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)
# 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 |
# 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()
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(.))
# 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")
#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 |
# 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.
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)
Repetir los 4 análisis exploratorios realizados en el punto 4 y realizar unos breves comentarios sobre los cambios que encontraron
# 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")
#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 |
# 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.
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.