Se comienza el análisis propuesto cargando los paquetes necesarios, leyendo el conjunto de datos y mostrando la estructura del mismo a fin de conocer los atributos sobre los que se van a trabajar.
# Borrar variables del ambiente
rm(list = objects())
# Carga de paquetes necesarios para hacer los gráficos
require(Cairo)
require(GGally)
require(ggpubr)
require(knitr)
require(tidyverse)
# Uso de Cairo para renderizar los gráficos
options(bitmapType = "cairo")
# Cargar conjunto de datos
datos <- readr::read_csv(file = "ar_properties.csv")
# Mostrar estructura
head(datos)
## # A tibble: 6 x 24
## id ad_type start_date end_date created_on lat lon l1 l2
## <chr> <chr> <date> <date> <date> <dbl> <dbl> <chr> <chr>
## 1 S0we… Propie… 2019-04-14 2019-06-14 2019-04-14 -34.9 -54.9 Urug… Mald…
## 2 kMxc… Propie… 2019-04-14 2019-04-16 2019-04-14 -34.6 -58.4 Arge… Capi…
## 3 Ce3o… Propie… 2019-04-14 9999-12-31 2019-04-14 NA NA Arge… Bs.A…
## 4 AUGp… Propie… 2019-04-14 9999-12-31 2019-04-14 -34.7 -58.8 Arge… Bs.A…
## 5 m+Mw… Propie… 2019-04-14 2019-07-09 2019-04-14 -34.7 -58.8 Arge… Bs.A…
## 6 pJRP… Propie… 2019-04-14 2019-08-08 2019-04-14 -32.9 -60.7 Arge… Sant…
## # … with 15 more variables: l3 <chr>, l4 <chr>, l5 <chr>, l6 <lgl>,
## # rooms <dbl>, bedrooms <dbl>, bathrooms <dbl>, surface_total <dbl>,
## # surface_covered <dbl>, price <dbl>, currency <chr>,
## # price_period <chr>, title <chr>, property_type <chr>,
## # operation_type <chr>
A continuación, seleccionamos los datos indicados en las consignas del TP. Es decir, seleccionamos propiedades que cumplan con todas y cada una de las siguientes condiciones:
A partir de estos filtros nos quedamos con 61.905 propiedades.
datos.seleccionados <- datos %>%
dplyr::filter(
# propiedades de Capital Federal / Argentina
(l1 == "Argentina" & l2 == "Capital Federal") &
# cuyo precio está en dólares
(currency == "USD") &
# cuyo tipo sea Departamento, PH o Casa
(property_type %in% c("Departamento", "PH", "Casa")) &
# y que sean publicadas para Venta
(operation_type == "Venta")
) %>%
dplyr::select(id, l3, rooms, bedrooms, bathrooms, surface_total,
surface_covered, price, property_type)
Sobre la base de propiedades seleccionadas anteriormente, se realiza un primer análisis exploratorio buscando la cantidad de valores únicos y faltantes para cada variable.
resumen.cantidad <- purrr::map_dfr(
.x = colnames(datos.seleccionados),
.f = function(variable) {
# Seleccionamos los datos para una variable
valores.variable <- datos.seleccionados %>%
dplyr::pull(!! variable)
# Calculamos la cantidad de valores únicos y de valores faltantes
return (
data.frame(
variable = variable,
cantidad_unicos = length(unique(valores.variable)),
cantidad_faltantes = length(which(is.na(valores.variable))),
stringsAsFactors = FALSE
) %>%
dplyr::mutate(porcentaje_faltantes = 100 * cantidad_faltantes / length(valores.variable))
)
}
)
# Mostramos una tabla con los valores calculados
knitr::kable(
x = resumen.cantidad,
col.names = c("Variable", "Cantidad de valores únicos",
"Cantidad de valores faltantes", "Porcentaje de valores faltantes"),
align = c("l", "r", "r", "r"),
digits = 2
)
| Variable | Cantidad de valores únicos | Cantidad de valores faltantes | Porcentaje de valores faltantes |
|---|---|---|---|
| id | 61905 | 0 | 0.00 |
| l3 | 58 | 355 | 0.57 |
| rooms | 24 | 5314 | 8.58 |
| bedrooms | 25 | 25298 | 40.87 |
| bathrooms | 15 | 3196 | 5.16 |
| surface_total | 671 | 3671 | 5.93 |
| surface_covered | 573 | 2975 | 4.81 |
| price | 4095 | 0 | 0.00 |
| property_type | 3 | 0 | 0.00 |
Observamos que todas las propiedades tienen definidas un identificador, tipo de propiedad y precio (es decir, no presentan faltantes). Sin embargo, hay muchas propiedades que no cuentan con información de las demás variables seleccionadas (nivel administrativo 3, cantidad de habitaciones, cantidad de baños, superficie total y superficie cubierta). Particulamente, la variable bedrooms presenta un 40,87% de valores faltantes. El resto de las variables tiene un porcentaje de faltantes que varía entre 0,57% y 8,58%. Se deberán tratar estos faltantes a fin de poder realizar los modelos lineales solicitados.
A continuación seleccionamos las variables numéricas y calculamos la matriz de correlación a fin de detectar atributos muy correlacionados. Se busca eliminar atributos con muchos faltantes pero perdiendo la menor cantidad de información. Para ello buscamos qué atributos con faltantes están muy correlacionados a otros atributos con menor cantidad de faltantes.
# Primero buscamos las variables numéricas
variables.numericas <- purrr::map(
.x = colnames(datos.seleccionados),
.f = function(variable) {
# Si la variable tiene todos sus valores numéricos, devolvemos el nombre de la variable, sino NA.
return (ifelse(is.numeric(datos.seleccionados[[variable]]), variable, NA))
}
) %>% purrr::keep(
# Eliminamos todos los NA devueltos en el paso anterior.
.x = .,
.p = ~ ! is.na(.x)
) %>% unlist()
# Ahora seleccionamos esas variable y calculamos la matriz de correlación
datos.seleccionados.variables.numericas <- datos.seleccionados %>%
dplyr::select(dplyr::one_of(variables.numericas))
matriz.correlacion <- stats::cor(datos.seleccionados.variables.numericas,
use = "complete.obs")
# Mostramos una tabla considerando 3 dígitos decimales
knitr::kable(
x = matriz.correlacion,
digits = 3
)
| rooms | bedrooms | bathrooms | surface_total | surface_covered | price | |
|---|---|---|---|---|---|---|
| rooms | 1.000 | 0.921 | 0.613 | 0.068 | 0.075 | 0.487 |
| bedrooms | 0.921 | 1.000 | 0.616 | 0.067 | 0.072 | 0.432 |
| bathrooms | 0.613 | 0.616 | 1.000 | 0.062 | 0.068 | 0.599 |
| surface_total | 0.068 | 0.067 | 0.062 | 1.000 | 0.697 | 0.051 |
| surface_covered | 0.075 | 0.072 | 0.068 | 0.697 | 1.000 | 0.063 |
| price | 0.487 | 0.432 | 0.599 | 0.051 | 0.063 | 1.000 |
Observamos que hay una muy alta correlación entre rooms y bedrooms (0.921). También encontramos alta correlación entre rooms y bathrooms (0.613) por un lado y surface_total y surface_covered (0.697) por el otro. Esto es bastante lógico, ya que los pares de variables mencionados tienen, conceptualmente, relación entre si. Lo notable es que la variable precio tenga correlación tan baja con las variables de superficie. Sería de esperar que la superficie de una propiedad (sea cubierta o total) esté estrechamente relacionada con el precio. Esto es una señal temprana de la presencia de outliers (en el precio y/o en las variables de superficie).
Del análisis de la matriz de correlación, observamos que las variables rooms y bedrooms tienen una muy alta correlación. Dado que además la variable bedrooms contiene una alta proporción de valores faltantes, entonces la eliminamos sabiendo que prácticamente no estamos perdiendo información (por lo antedicho). Posteriormente, también eliminamos todas las observaciones que contengan algún dato faltante en alguna de sus variables. Luego de este segundo filtro, nos queda un conjunto de datos de 51.210 propiedades. Esto significa que del conjunto inicial de propiedades de Capital Federal (con 61.905 observaciones) se eliminó un 17,28% de los datos. Esta cantidad de datos eliminados no es una cantidad menor. La alternativa hubiera sido imputar los datos faltantes. Sin embargo, dada la abundancia de datos restantes, es posible continuar con la modelación del modo sugerido en las consignas del TP.
datos.sin.faltantes <- datos.seleccionados %>%
# Elimino el atributo bedrooms
dplyr::select(-bedrooms) %>%
# Calculo la cantidad de faltantes para cada observación
dplyr::mutate(
faltantes = purrr::pmap(
.l = .,
.f = function(...) {
# Devuelvo la cantidad de columnas que tienen atributo faltante
columnas <- list(...)
return (length(which(is.na(columnas))))
}
) %>% unlist()
) %>%
# Me quedo con las observaciones sin faltantes
dplyr::filter(faltantes == 0) %>%
# Elimino el atributo "faltantes"
dplyr::select(-faltantes)
# Eliminamos bedrooms de las variables numéricas
variables.numericas <- variables.numericas[-which(variables.numericas == "bedrooms")]
Con el conjunto de datos resultantes luego de la eliminación de datos faltantes, calculamos estadísticas descriptivas para la variable precio y realizamos un histograma de su distribución. Dado que hay algunos precios que son muy elevados, mostramos los precios en escala logarítmica para que la forma del histograma pueda apreciarse mejor. Esto también podría indicar la presencia de outliers en esta variable.
# Realizamos una tabla con las estadísticas descriptivas
cuartiles.precio <- quantile(datos.sin.faltantes$price,
probs = c(0.25, 0.5, 0.75))
estadisticas.descriptivas.precio <- datos.sin.faltantes %>%
dplyr::summarise(
Mínimo = min(price),
Q1 = cuartiles.precio[1],
Mediana = cuartiles.precio[2],
Media = mean(price),
Q3 = cuartiles.precio[3],
Máximo = max(price)
)
knitr::kable(
x = estadisticas.descriptivas.precio,
digits = 3
)
| Mínimo | Q1 | Mediana | Media | Q3 | Máximo |
|---|---|---|---|---|---|
| 6000 | 119000 | 170000 | 251577.2 | 270000 | 6e+06 |
# Y ahora el histograma
ggplot2::ggplot(data = datos.sin.faltantes) +
ggplot2::geom_histogram(mapping = ggplot2::aes(x = price/1000),
fill = "darkslategray4") +
ggplot2::scale_x_log10() +
ggplot2::labs(x = "Precio (en miles de USD)", y = "Frecuencia",
title = "Histograma para la variable precio",
subtitle = "Valores en escala logarítmica") +
ggplot2::theme_bw() +
ggplot2::theme(
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5),
)
Este histograma nos muestra que, en líneas generales, los valores de las propiedades están en su mayoría entre USD 100.000 y USD 1.000.000. A priori, estos valores son bastante lógicos y razonables para propiedades de Capital Federal. A continuación, calculamos las estadísticas descriptivas separando las propiedades por tipo (atributo property_type). Se observa que hay diferencias de precio notables para las casas en comparación con otros tipos de propiedades.
# Realizamos una tabla con las estadísticas descriptivas
estadisticas.descriptivas.precio.tipo.propiedad <- purrr::map_dfr(
.x = unique(datos.sin.faltantes$property_type),
.f = function(tipo_propiedad) {
datos.tipo.propiedad <- datos.sin.faltantes %>%
dplyr::filter(property_type == tipo_propiedad)
cuartiles.precio.tipo <- quantile(datos.tipo.propiedad$price,
probs = c(0.25, 0.5, 0.75))
estadisticas.descriptivas.precio.tipo <- datos.tipo.propiedad %>%
dplyr::summarise(
Tipo = tipo_propiedad,
Mínimo = min(price),
Q1 = cuartiles.precio.tipo[1],
Mediana = cuartiles.precio.tipo[2],
Media = mean(price),
Q3 = cuartiles.precio.tipo[3],
Máximo = max(price)
)
}
)
knitr::kable(
x = estadisticas.descriptivas.precio.tipo.propiedad,
digits = 3
)
| Tipo | Mínimo | Q1 | Mediana | Media | Q3 | Máximo |
|---|---|---|---|---|---|---|
| Casa | 20000 | 235000 | 335000 | 434188.8 | 490000 | 5000000 |
| Departamento | 6000 | 115000 | 164000 | 246855.7 | 260000 | 6000000 |
| PH | 32000 | 137000 | 190000 | 218747.4 | 270000 | 1500000 |
Para cada tipo de propiedad, se elabora un boxplot observándose una cantidad importante de propiedades con precios atípicos (outliers) para cada tipo de propiedad. Sin embargo, eso no significa que esos valores sean erróneos, pero lo deberemos tener en cuenta más adelante cuando se ajusten los modelos lineales.
ggplot2::ggplot(data = datos.sin.faltantes) +
ggplot2::geom_boxplot(mapping = ggplot2::aes(y = price/1000, fill = property_type)) +
ggplot2::facet_wrap(~ property_type, nrow = 1, scales = "free") +
ggplot2::labs(y = "Precio (en miles de USD)",
title = "Boxplots de precio por tipo de propiedad",
fill = "Tipo de propiedad") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5),
axis.title.x = ggplot2::element_blank(),
axis.text.x = ggplot2::element_blank(),
axis.ticks.x = ggplot2::element_blank()
)
Por último, para cerrar este segundo conjunto de análisis exploratorios, realizamos un correlograma de las variables numéricas. Lo primero que se observa es que también hay valores muy atípicos en los atributos surface_covered y surface_total (por la asimetría de la función de distribución). El análisis de las correlaciones ya fue expuesto anteriormente, por lo que no se profundizará más por el momento. A continuación se procederá a realizar un análisis y tratamiento de los valores atípicos o outliers.
datos.numericos <- datos.sin.faltantes %>%
dplyr::select(dplyr::one_of(variables.numericas))
GGally::ggpairs(data = datos.numericos,
lower = list(continuous = GGally::wrap("points", alpha = 0.3, size=0.1),
combo = GGally::wrap("dot", alpha = 0.4, size=0.2) )) +
ggplot2::labs(title = "Asociación de variables tomadas de a pares", x = "", y = "") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5)
)
El trabajo práctico propone tratar los outliers de la variable precio. Sin embargo, por sí sola, esa variable no nos indica si el outlier es un error de carga o no. Tampoco sería bueno dejar de lado lo observado anteriormente para los atributos relacionados con la superficie. Por tal motivo se propone un análisis de outliers con la siguiente lógica:
# Filtrado de observaciones según los criterios 1 a 3.
datos.filtrados <- datos.sin.faltantes %>%
dplyr::mutate(precio_superficie = price / surface_total,
razon_superficie_cubierta = surface_covered / surface_total) %>%
dplyr::filter(surface_total < 10000,
precio_superficie > 100 & precio_superficie < 15000,
razon_superficie_cubierta <= 1)
# Filtrado de observaciones según los criterio 4.
datos.sin.outliers <- purrr::map_dfr(
.x = unique(datos.filtrados$property_type),
.f = function(tipo) {
# Obtengo las propiedades de un cierto tipo y filtro por precio según lo expuesto.
datos.tipo.propiedad <- datos.filtrados %>%
dplyr::filter(property_type == tipo)
cuartiles <- quantile(datos.tipo.propiedad$price, probs = c(0.25, 0.75))
Q1 <- cuartiles[1]
Q3 <- cuartiles[2]
return (datos.tipo.propiedad %>%
dplyr::filter(
(price >= Q1 - 3 * (Q3 - Q1)) &
(price <= Q3 + 3 * (Q3 - Q1))
)
)
}
)
Luego de este procedimiento, queda un conjunto de datos con 48.597 observaciones. Es decir que se eliminaron alrededor de 5,1% de los datos. Sin embargo, como se verá en los próximos análisis, el nuevo conjunto de datos muestra mucha menos cantidad de datos atípicos, lo cual es beneficioso al momento de realizar los modelos lineales.
Luego de haber eliminado los outliers utilizando el mecanismo descripto anteriormente, volvemos a realizar los mismos análisis exploratorios. Calculamos estadísticas descriptivas para la variable precio y realizamos un histograma de su distribución. Lo primero que es importante destacar es que ahora ya no es necesaria la escala logarítmica para poder apreciar mejor el histograma. Sin embargo, igual persiste la asimetría a derecha.
# Realizamos una tabla con las estadísticas descriptivas
cuartiles.precio <- quantile(datos.sin.outliers$price,
probs = c(0.25, 0.5, 0.75))
estadisticas.descriptivas.precio <- datos.sin.outliers %>%
dplyr::summarise(
Mínimo = min(price),
Q1 = cuartiles.precio[1],
Mediana = cuartiles.precio[2],
Media = mean(price),
Q3 = cuartiles.precio[3],
Máximo = max(price)
)
knitr::kable(
x = estadisticas.descriptivas.precio,
digits = 3
)
| Mínimo | Q1 | Mediana | Media | Q3 | Máximo |
|---|---|---|---|---|---|
| 7500 | 115747 | 165000 | 205330.6 | 255000 | 1250000 |
# Y ahora el histograma
ggplot2::ggplot(data = datos.sin.outliers) +
ggplot2::geom_histogram(mapping = ggplot2::aes(x = price/1000),
fill = "darkslategray4") +
ggplot2::labs(x = "Precio (en miles de USD)", y = "Frecuencia",
title = "Histograma para la variable precio (sin outliers)") +
ggplot2::theme_bw() +
ggplot2::theme(
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5),
)
A continuación se calculan las estadísticas descriptivas agrupadas por tipo de propiedad. Siguen existiendo diferencias notables entre las casas y el resto de las propiedades. Pero esto seguramente se deba a que las casas, de por sí, tienen un precio de mercado mayor que el resto de las propiedades.
# Realizamos una tabla con las estadísticas descriptivas
estadisticas.descriptivas.precio.tipo.propiedad <- purrr::map_dfr(
.x = unique(datos.sin.outliers$property_type),
.f = function(tipo_propiedad) {
datos.tipo.propiedad <- datos.sin.outliers %>%
dplyr::filter(property_type == tipo_propiedad)
cuartiles.precio.tipo <- quantile(datos.tipo.propiedad$price,
probs = c(0.25, 0.5, 0.75))
estadisticas.descriptivas.precio.tipo <- datos.tipo.propiedad %>%
dplyr::summarise(
Tipo = tipo_propiedad,
Mínimo = min(price),
Q1 = cuartiles.precio.tipo[1],
Mediana = cuartiles.precio.tipo[2],
Media = mean(price),
Q3 = cuartiles.precio.tipo[3],
Máximo = max(price)
)
}
)
knitr::kable(
x = estadisticas.descriptivas.precio.tipo.propiedad,
digits = 3
)
| Tipo | Mínimo | Q1 | Mediana | Media | Q3 | Máximo |
|---|---|---|---|---|---|---|
| Casa | 20000 | 230000 | 328500 | 375874.4 | 470000 | 1250000 |
| Departamento | 7500 | 113000 | 159000 | 196670.6 | 240000 | 695000 |
| PH | 32000 | 135950 | 190000 | 215054.4 | 270000 | 650000 |
Luego se elabora un boxplot de precios para cada tipo de propiedad. Ahora se observa mejor la distribución de precios, aunque aún persistan valores atípicos. Sin embargo, esos valores de precio no pueden considerarse tan atípicos como para ser eliminados.
ggplot2::ggplot(data = datos.sin.outliers) +
ggplot2::geom_boxplot(mapping = ggplot2::aes(y = price/1000, fill = property_type)) +
ggplot2::facet_wrap(~ property_type, nrow = 1, scales = "free") +
ggplot2::labs(y = "Precio (en miles de USD)",
title = "Boxplots de precio por tipo de propiedad",
fill = "Tipo de propiedad") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5),
axis.title.x = ggplot2::element_blank(),
axis.text.x = ggplot2::element_blank(),
axis.ticks.x = ggplot2::element_blank()
)
Por último, realizamos un correlograma de las variables numéricas. Observamos que las distribuciones de las variables de superficie también han cambiado aunque aún persiste asimetría a derecha. Lo más importante es que ha aumentado considerablemente la correlación entre las variables de superficie y el precio (ahora son las variables más correlacionadas con esta última). Esto indicaría que las variables de superficie son aquellas que permitirían predecir mejor el precio.
datos.numericos <- datos.sin.outliers %>%
dplyr::select(dplyr::one_of(variables.numericas))
GGally::ggpairs(data = datos.numericos,
lower = list(continuous = GGally::wrap("points", alpha = 0.3, size=0.1),
combo = GGally::wrap("dot", alpha = 0.4, size=0.2) )) +
ggplot2::labs(title = "Asociación de variables tomadas de a pares", x = "", y = "") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5)
)
Efectuamos los modelos lineales simples solicitados para predecir la variable precio. El primero, basado en la cantidad de habitaciones (rooms) y el segundo, en función de la superficie total (surface_total). Adicionalmente, si bien no es pedido dentro de las consignas, se agrega otro modelo basado en la superficie cubierta (surface_covered), ya que se observa que por su correlación es el mejor atributo para predecir el precio.
modelo.habitaciones <- stats::lm(formula = price ~ rooms, data = datos.sin.outliers)
summary(modelo.habitaciones)
##
## Call:
## stats::lm(formula = price ~ rooms, data = datos.sin.outliers)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1424700 -58514 -14943 31486 902267
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37184.0 1046.3 35.54 <2e-16 ***
## rooms 62109.9 348.4 178.29 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 99920 on 48595 degrees of freedom
## Multiple R-squared: 0.3955, Adjusted R-squared: 0.3955
## F-statistic: 3.179e+04 on 1 and 48595 DF, p-value: < 2.2e-16
En este primer modelo encontramos que \(\beta_0 = 37184\). Este parámetro representa la ordenada al origen. Interpretar el valor del mismo no tiene demasiado sentido, dado que implicaría que una propiedad sin ninguna habitación tiene un valor de USD 37.184. De la salida, también se observa el parámetro \(\beta_1 = 62109,9\). Este parámetro representa la pendiente de la recta. Se interpreta que por cada habitación que sumamos a una propiedad, el precio de ésta se incrementa en aproximadamente USD 62.110. Ambos parámetros son muy significativos dado que tienen un p-valor extremadamente bajo. A continuación, se muestra un gráfico de la recta del ajuste con los valores de los parámetros.
ggplot2::ggplot(data = datos.sin.outliers, mapping = ggplot2::aes(x = rooms, y = price)) +
ggplot2::geom_point(mapping = ggplot2::aes(col = property_type)) +
ggplot2::geom_smooth(method = "lm", se = TRUE) +
ggpubr::stat_regline_equation(mapping = ggplot2::aes(label = paste(..eq.label.., ..adj.rr.label.., sep = "~~~~")),
label.y = 2e6) +
ggplot2::labs(x = "Cantidad de habitaciones", y = "Precio (USD)",
title = "Modelo lineal para precio de inmuebles",
subtitle = "Precio basado en cantidad de habitaciones",
col = "Tipo de propiedad") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5)
)
Del gráfico expuesto, se observa que el modelo en general subestima la mayoría de los precios de las casas. Para el caso de los departamentos y PH, no se observa ningún patrón aparente. En ambos casos hay precios sobrestimados y subestimados. Nótese que los valores que aparecen en la ecuación de la recta están redondeados para facilitar la visualización.
A continuación, se presenta el segundo modelo, basado en la superficie total. Encontramos que \(\beta_0 = 97815,93\). Por lo explicado para el primer modelo, no tiene mucho sentido interpretar el valor de este parámetro. El valor de la pendiente es \(\beta_1 = 1281,07\). Este parámetro se puede interpretar como el incremento en el precio de la propiedad por cada metro cuadrado adicional de superficie total. Ambos parámetros son muy significativos dado que tienen un p-valor extremadamente bajo. A continuación, se muestra un gráfico de la recta del ajuste con los valores de los parámetros.
modelo.superficie.total <- stats::lm(formula = price ~ surface_total, data = datos.sin.outliers)
summary(modelo.superficie.total)
##
## Call:
## stats::lm(formula = price ~ surface_total, data = datos.sin.outliers)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1911946 -49714 -20895 30132 780385
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 97815.928 661.758 147.8 <2e-16 ***
## surface_total 1282.065 6.084 210.7 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 92900 on 48595 degrees of freedom
## Multiple R-squared: 0.4774, Adjusted R-squared: 0.4774
## F-statistic: 4.44e+04 on 1 and 48595 DF, p-value: < 2.2e-16
ggplot2::ggplot(data = datos.sin.outliers, mapping = ggplot2::aes(x = surface_total, y = price)) +
ggplot2::geom_point(mapping = ggplot2::aes(col = property_type)) +
ggplot2::geom_smooth(method = "lm", se = TRUE) +
ggpubr::stat_regline_equation(mapping = ggplot2::aes(label = paste(..eq.label.., ..adj.rr.label.., sep = "~~~~")),
label.y = 2e+6) +
ggplot2::labs(x = "Superficie (metros cuadrados)", y = "Precio (USD)",
title = "Modelo lineal para precio de inmuebles",
subtitle = "Precio basado en superficie total",
col = "Tipo de propiedad") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5)
)
En este modelo vemos que desaparece la marcada subestimación de precios para las casas. Los valores que aparecen en la ecuación de la recta también están redondeados para facilitar la visualización.
Finalmente, se realiza un último modelo basado en la superficie cubierta. Esta es la variable que a priori aparecía como mejor candidata a modelar el precio debido a la alta correlación entre ambas variables. Analizando como en los casos anteriores, encontramos que \(\beta_0 = 75599,7\) y \(\beta_1 = 1810,25\). Este último parámetro se puede interpretar como el incremento en el precio de la propiedad por cada metro cuadrado adicional de superficie cubierta. Se observa que este valor es mayor al incremento en el precio por metro cuadrado de superficie total. Esto resulta totalmente razonable ya que por la lógica del negocio se sabe que el metro cuadrado cubierto tiene mayor valor que el no cubierto.
Ambos parámetros son muy significativos dado que tienen un p-valor extremadamente bajo. A continuación, se muestra un gráfico de la recta del ajuste con los valores de los parámetros (también redondeados).
modelo.superficie.cubierta <- stats::lm(formula = price ~ surface_covered, data = datos.sin.outliers)
summary(modelo.superficie.cubierta)
##
## Call:
## stats::lm(formula = price ~ surface_covered, data = datos.sin.outliers)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1775236 -45058 -17748 27719 730890
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 75599.693 670.850 112.7 <2e-16 ***
## surface_covered 1810.248 7.566 239.3 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 87080 on 48595 degrees of freedom
## Multiple R-squared: 0.5409, Adjusted R-squared: 0.5408
## F-statistic: 5.724e+04 on 1 and 48595 DF, p-value: < 2.2e-16
ggplot2::ggplot(data = datos.sin.outliers, mapping = ggplot2::aes(x = surface_covered, y = price)) +
ggplot2::geom_point(mapping = ggplot2::aes(col = property_type)) +
ggplot2::geom_smooth(method = "lm", se = TRUE) +
ggpubr::stat_regline_equation(mapping = ggplot2::aes(label = paste(..eq.label.., ..adj.rr.label.., sep = "~~~~")),
label.y = 2e+6) +
ggplot2::labs(x = "Superficie cubierta (metros cuadrados)", y = "Precio (USD)",
title = "Modelo lineal para precio de inmuebles",
subtitle = "Precio basado en superficie",
col = "Tipo de propiedad") +
ggplot2::theme_bw() +
ggplot2::theme(
legend.position = "bottom",
plot.title = ggplot2::element_text(hjust = 0.5),
plot.subtitle = ggplot2::element_text(hjust = 0.5)
)
Como conclusión, se puede decir que de los dos modelos pedidos, el que mejor ajusta el precio es el basado en superficie total. Sin embargo, el basado en superficie cubierta ajusta mejor aún todavía. La fundamentación de esta aseveración está basada en la inspección del valor del \(R^2\) resultante de cada uno de los modelos. Cabe destacar que esta interpretación está sujeta a que se cumplan los supuestos del modelo de regresión lineal. Sin embargo, este tema está fuera del abordaje de este trabajo práctico.