En este trabajo se usa la base de datos de ofertas de vivienda en Cali para estimar modelos de regresión lineal simple y múltiple. Escogí el barrio Ciudad Jardín porque tiene bastantes ofertas y porque hay casas y apartamentos en cantidades parecidas, lo que sirve para usar el tipo de vivienda como variable dummy.
La variable que se quiere explicar es el precio de la vivienda en millones de pesos.
library(readxl)
library(dplyr)
library(ggplot2)
datos <- read_excel("Datos_Vivienda.xlsx",
col_types = c("text", "text", "text",
"numeric", "numeric", "numeric",
"numeric", "numeric", "text", "text",
"numeric", "numeric"))
id = 1:dim(datos)[1]
datos = data.frame(id, datos)
# Filtro por el barrio Ciudad Jardín
datos_sub <- datos %>% filter(tolower(trimws(Barrio)) == "ciudad jardin")
nrow(datos_sub)
## [1] 540
table(datos_sub$Tipo)
##
## Apartamento Casa
## 230 310
Quedan 540 ofertas: 310 casas y 230 apartamentos.
Antes de modelar hice una limpieza básica:
piso porque tiene muchos datos
vacíos y además no aplica para casas.datos_sub <- datos_sub %>%
filter(!is.na(parqueaderos), Banos > 0, Habitaciones > 0) %>%
mutate(Estrato = as.numeric(Estrato),
Tipo = factor(Tipo))
nrow(datos_sub)
## [1] 515
summary(datos_sub[, c("precio_millon", "Area_contruida", "parqueaderos", "Banos", "Habitaciones")])
## precio_millon Area_contruida parqueaderos Banos
## Min. : 180.0 Min. : 50.0 Min. : 1.000 Min. :1.000
## 1st Qu.: 466.5 1st Qu.: 133.0 1st Qu.: 2.000 1st Qu.:3.000
## Median : 680.0 Median : 200.0 Median : 2.000 Median :4.000
## Mean : 755.4 Mean : 255.4 Mean : 2.755 Mean :4.192
## 3rd Qu.: 900.0 3rd Qu.: 330.0 3rd Qu.: 4.000 3rd Qu.:5.000
## Max. :1900.0 Max. :1586.0 Max. :10.000 Max. :9.000
## Habitaciones
## Min. :1.000
## 1st Qu.:3.000
## Median :4.000
## Mean :3.763
## 3rd Qu.:4.000
## Max. :9.000
Después de la limpieza quedan 515 viviendas. El precio va desde 180 hasta 1900 millones y el área desde 50 hasta 1586 m². La mitad de las viviendas tiene menos de 200 m², pero hay algunas casas muy grandes.
cor(datos_sub[, c("precio_millon", "Area_contruida", "parqueaderos", "Banos", "Habitaciones", "Estrato")])
## precio_millon Area_contruida parqueaderos Banos Habitaciones
## precio_millon 1.0000000 0.7315459 0.7071535 0.6428504 0.51054250
## Area_contruida 0.7315459 1.0000000 0.6526906 0.6239281 0.54967284
## parqueaderos 0.7071535 0.6526906 1.0000000 0.5395097 0.45223583
## Banos 0.6428504 0.6239281 0.5395097 1.0000000 0.72214899
## Habitaciones 0.5105425 0.5496728 0.4522358 0.7221490 1.00000000
## Estrato 0.1797282 0.1445882 0.1362386 0.1488046 0.06455348
## Estrato
## precio_millon 0.17972824
## Area_contruida 0.14458822
## parqueaderos 0.13623861
## Banos 0.14880457
## Habitaciones 0.06455348
## Estrato 1.00000000
Las variables que más se relacionan con el precio son el área construida (0.73), los parqueaderos (0.71) y los baños (0.64). Por eso escogí esas tres para los modelos simples. El estrato casi no se relaciona con el precio porque en Ciudad Jardín casi todo es estrato 6.
ggplot(datos_sub, aes(x = Area_contruida, y = precio_millon, color = Tipo)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Precio vs área construida en Ciudad Jardín",
x = "Área construida (m2)", y = "Precio (millones)") +
theme_minimal()
En el gráfico se ve que a mayor área mayor precio, pero la línea de los apartamentos es más inclinada que la de las casas. También se ve que los puntos se van abriendo a medida que aumenta el área, es decir, hay más variación en las viviendas grandes. Esto puede generar problemas de heterocedasticidad en los modelos.
Para validar los supuestos se usan los cuatro gráficos de residuos:
Además, para la normalidad se usa la prueba de Shapiro-Wilk (H0: los residuos son normales). Si el p-valor es mayor a 0.05 se cumple el supuesto.
mod1 <- lm(precio_millon ~ Area_contruida, data = datos_sub)
summary(mod1)
##
## Call:
## lm(formula = precio_millon ~ Area_contruida, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1006.90 -158.20 -47.47 87.21 1045.01
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 361.61807 19.79987 18.26 <2e-16 ***
## Area_contruida 1.54179 0.06344 24.30 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 258.2 on 513 degrees of freedom
## Multiple R-squared: 0.5352, Adjusted R-squared: 0.5343
## F-statistic: 590.6 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
precio = 361.62 + 1.54 * Area_contruida
Por cada metro cuadrado adicional de área construida, el precio aumenta en promedio 1.54 millones. El intercepto no tiene una interpretación real porque no existen viviendas de 0 m². Los dos coeficientes son significativos y el R² es 0.535, o sea que el área explica más o menos el 53% de la variación del precio.
Validación de supuestos
par(mfrow = c(2, 2))
plot(mod1)
shapiro.test(residuals(mod1))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod1)
## W = 0.89102, p-value < 2.2e-16
El modelo no cumple normalidad ni homocedasticidad, así que se prueba una transformación logarítmica.
Como el área y el precio son positivos y tienen valores muy dispersos, se aplica logaritmo a las dos variables (modelo log-log).
mod1_log <- lm(log(precio_millon) ~ log(Area_contruida), data = datos_sub)
summary(mod1_log)
##
## Call:
## lm(formula = log(precio_millon) ~ log(Area_contruida), data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.89543 -0.16081 -0.01881 0.14323 0.81145
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.87401 0.09874 29.11 <2e-16 ***
## log(Area_contruida) 0.67970 0.01834 37.05 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2544 on 513 degrees of freedom
## Multiple R-squared: 0.728, Adjusted R-squared: 0.7275
## F-statistic: 1373 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
En un modelo log-log el coeficiente es una elasticidad. Si el área aumenta 1%, el precio aumenta en promedio 0.68%. Como es menor a 1, el precio no crece en la misma proporción que el área: las viviendas más grandes tienen un precio por m² más bajo. El R² sube a 0.728.
par(mfrow = c(2, 2))
plot(mod1_log)
shapiro.test(residuals(mod1_log))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod1_log)
## W = 0.98737, p-value = 0.0001956
Con la transformación los gráficos mejoran bastante. Los residuos quedan más repartidos alrededor de cero, el Q-Q está mucho más pegado a la línea y la casa de 1586 m² ya no influye tanto. La línea del Scale-Location queda mucho más plana, entonces la heterocedasticidad se reduce. El Shapiro todavía da menor a 0.05, pero el p-valor mejora mucho (pasa de menos de 2.2e-16 a 0.0002). Hay que tener en cuenta que con más de 500 datos esta prueba es muy estricta. El modelo log-log es mejor que el lineal.
mod2 <- lm(precio_millon ~ parqueaderos, data = datos_sub)
summary(mod2)
##
## Call:
## lm(formula = precio_millon ~ parqueaderos, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -724.7 -161.7 -88.7 100.8 950.3
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 256.71 24.98 10.28 <2e-16 ***
## parqueaderos 181.00 7.99 22.65 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 267.8 on 513 degrees of freedom
## Multiple R-squared: 0.5001, Adjusted R-squared: 0.4991
## F-statistic: 513.1 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
precio = 256.71 + 181.00 * parqueaderos
Cada parqueadero adicional aumenta el precio en promedio 181 millones. Este valor es alto porque las viviendas con más parqueaderos normalmente también son más grandes, entonces el coeficiente está recogiendo ese efecto. El intercepto (256.71) sería el precio promedio de una vivienda sin parqueadero. El R² es 0.50.
par(mfrow = c(2, 2))
plot(mod2)
shapiro.test(residuals(mod2))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod2)
## W = 0.8798, p-value < 2.2e-16
Como los parqueaderos son números enteros, los residuos se ven en columnas. Las columnas de más parqueaderos tienen más dispersión, y el Q-Q se sale en la parte de arriba. No se cumple normalidad (Shapiro p < 0.05) y la línea del Scale-Location sube, así que tampoco hay homocedasticidad.
Como hay viviendas con 0 parqueaderos y el logaritmo de 0 no existe, solo se le saca logaritmo al precio (modelo log-lin).
mod2_log <- lm(log(precio_millon) ~ parqueaderos, data = datos_sub)
summary(mod2_log)
##
## Call:
## lm(formula = log(precio_millon) ~ parqueaderos, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.04442 -0.22717 -0.05265 0.21405 1.01214
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.86986 0.03233 181.56 <2e-16 ***
## parqueaderos 0.23201 0.01034 22.43 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3466 on 513 degrees of freedom
## Multiple R-squared: 0.4952, Adjusted R-squared: 0.4942
## F-statistic: 503.3 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
En un modelo log-lin, cada parqueadero adicional aumenta el precio en (e^0.232 - 1) = 26% aproximadamente. El R² queda casi igual (0.495).
par(mfrow = c(2, 2))
plot(mod2_log)
shapiro.test(residuals(mod2_log))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod2_log)
## W = 0.98167, p-value = 4.438e-06
Con la transformación las columnas de residuos quedan con una dispersión más pareja. La línea del Scale-Location se mantiene estable en la mayoría de los datos y solo sube al final, donde hay muy pocas viviendas, entonces la heterocedasticidad mejora. La normalidad mejora (Shapiro pasa a 0.000004), pero todavía no se cumple. El modelo log-lin es mejor.
mod3 <- lm(precio_millon ~ Banos, data = datos_sub)
summary(mod3)
##
## Call:
## lm(formula = precio_millon ~ Banos, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -561.3 -192.9 -62.1 121.7 1146.3
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 46.032 39.448 1.167 0.244
## Banos 169.214 8.902 19.008 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 290.1 on 513 degrees of freedom
## Multiple R-squared: 0.4133, Adjusted R-squared: 0.4121
## F-statistic: 361.3 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
precio = 46.03 + 169.21 * Banos
Cada baño adicional aumenta el precio en promedio 169 millones. El intercepto no es significativo (p = 0.244), lo cual tiene sentido porque no hay viviendas sin baños. El R² es 0.413, el más bajo de los tres modelos.
par(mfrow = c(2, 2))
plot(mod3)
shapiro.test(residuals(mod3))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod3)
## W = 0.9041, p-value < 2.2e-16
Pasa algo parecido al modelo de parqueaderos: la dispersión aumenta con el número de baños y el Q-Q se sale en la cola de arriba. No se cumple normalidad ni homocedasticidad.
mod3_log <- lm(log(precio_millon) ~ Banos, data = datos_sub)
summary(mod3_log)
##
## Call:
## lm(formula = log(precio_millon) ~ Banos, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.14111 -0.24952 -0.06676 0.19517 1.20831
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.52794 0.04800 115.18 <2e-16 ***
## Banos 0.23404 0.01083 21.61 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.353 on 513 degrees of freedom
## Multiple R-squared: 0.4765, Adjusted R-squared: 0.4755
## F-statistic: 466.9 on 1 and 513 DF, p-value: < 2.2e-16
Interpretación
Cada baño adicional aumenta el precio en (e^0.234 - 1) = 26% aproximadamente. El R² sube de 0.413 a 0.477.
par(mfrow = c(2, 2))
plot(mod3_log)
shapiro.test(residuals(mod3_log))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod3_log)
## W = 0.9735, p-value = 4.919e-08
Aquí la transformación funciona todavía mejor: la línea del Scale-Location queda plana, o sea que la varianza de los errores es constante, y además mejora el R². La normalidad mejora aunque sigue sin cumplirse. El modelo log-lin es mejor.
| Modelo | R² | Normalidad (Shapiro) | Homocedasticidad (gráficos) |
|---|---|---|---|
| Precio ~ Área | 0.535 | No | No |
| log(Precio) ~ log(Área) | 0.728 | No, pero mejora | Mejora |
| Precio ~ Parqueaderos | 0.500 | No | No |
| log(Precio) ~ Parqueaderos | 0.495 | No, pero mejora | Mejora |
| Precio ~ Baños | 0.413 | No | No |
| log(Precio) ~ Baños | 0.477 | No, pero mejora | Sí |
En los tres casos el logaritmo ayudó a cumplir mejor los supuestos. El mejor modelo simple es el log-log con el área. Ninguna variable sola explica todo el precio, así que vale la pena usar varias al mismo tiempo.
Se incluyen todas las variables y el tipo de vivienda como dummy. R
crea la variable TipoCasa (1 si es casa, 0 si es
apartamento), así que la categoría base es
apartamento.
mod4 <- lm(precio_millon ~ Area_contruida + parqueaderos + Banos + Habitaciones + Estrato + Tipo,
data = datos_sub)
summary(mod4)
##
## Call:
## lm(formula = precio_millon ~ Area_contruida + parqueaderos +
## Banos + Habitaciones + Estrato + Tipo, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -868.70 -119.73 -41.72 71.04 1004.90
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -218.51794 167.44436 -1.305 0.1925
## Area_contruida 0.77798 0.08204 9.483 < 2e-16 ***
## parqueaderos 89.09425 9.00138 9.898 < 2e-16 ***
## Banos 64.58189 11.08122 5.828 9.97e-09 ***
## Habitaciones -4.52138 14.18274 -0.319 0.7500
## Estrato 49.46668 28.12833 1.759 0.0792 .
## TipoCasa -25.13991 25.49034 -0.986 0.3245
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 221.7 on 508 degrees of freedom
## Multiple R-squared: 0.6608, Adjusted R-squared: 0.6568
## F-statistic: 164.9 on 6 and 508 DF, p-value: < 2.2e-16
Interpretación
Cada coeficiente se interpreta dejando las demás variables constantes (ceteris paribus):
El R² ajustado es 0.657, mejor que cualquiera de los modelos simples lineales.
mod4_step <- step(mod4, direction = "both")
## Start: AIC=5570.16
## precio_millon ~ Area_contruida + parqueaderos + Banos + Habitaciones +
## Estrato + Tipo
##
## Df Sum of Sq RSS AIC
## - Habitaciones 1 4994 24966167 5568.3
## - Tipo 1 47794 25008968 5569.1
## <none> 24961174 5570.2
## - Estrato 1 151963 25113137 5571.3
## - Banos 1 1668965 26630139 5601.5
## - Area_contruida 1 4418786 29379960 5652.1
## - parqueaderos 1 4813743 29774916 5659.0
##
## Step: AIC=5568.27
## precio_millon ~ Area_contruida + parqueaderos + Banos + Estrato +
## Tipo
##
## Df Sum of Sq RSS AIC
## - Tipo 1 55197 25021365 5567.4
## <none> 24966167 5568.3
## - Estrato 1 157098 25123265 5569.5
## + Habitaciones 1 4994 24961174 5570.2
## - Banos 1 2164051 27130218 5609.1
## - Area_contruida 1 4443106 29409274 5650.6
## - parqueaderos 1 4811202 29777370 5657.0
##
## Step: AIC=5567.4
## precio_millon ~ Area_contruida + parqueaderos + Banos + Estrato
##
## Df Sum of Sq RSS AIC
## <none> 25021365 5567.4
## + Tipo 1 55197 24966167 5568.3
## - Estrato 1 159796 25181160 5568.7
## + Habitaciones 1 12397 25008968 5569.1
## - Banos 1 2172816 27194180 5608.3
## - Area_contruida 1 4441735 29463100 5649.6
## - parqueaderos 1 4757293 29778658 5655.0
summary(mod4_step)
##
## Call:
## lm(formula = precio_millon ~ Area_contruida + parqueaderos +
## Banos + Estrato, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -855.61 -115.12 -38.65 77.36 991.47
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -227.05086 163.27215 -1.391 0.1649
## Area_contruida 0.75608 0.07946 9.515 < 2e-16 ***
## parqueaderos 88.20024 8.95695 9.847 < 2e-16 ***
## Banos 59.50806 8.94200 6.655 7.33e-11 ***
## Estrato 50.57305 28.02252 1.805 0.0717 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 221.5 on 510 degrees of freedom
## Multiple R-squared: 0.6599, Adjusted R-squared: 0.6573
## F-statistic: 247.4 on 4 and 510 DF, p-value: < 2.2e-16
El método step va quitando variables mientras el AIC baje. Primero quitó Habitaciones, que probablemente aporta la misma información que los baños (su correlación es 0.72). Después quitó TipoCasa. El modelo final queda:
precio = -227.05 + 0.756 * Area + 88.20 * parqueaderos + 59.51 * Banos + 50.57 * Estrato
El R² ajustado se mantiene en 0.657 con menos variables, así que el modelo quedó más simple sin perder explicación.
par(mfrow = c(2, 2))
plot(mod4_step)
shapiro.test(residuals(mod4_step))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod4_step)
## W = 0.88376, p-value < 2.2e-16
Los problemas son parecidos a los de los modelos simples: los residuos forman un embudo y la línea del Scale-Location sube (heterocedasticidad). El Q-Q y el Shapiro (p < 0.05) muestran que los residuos no son normales.
Se repite el modelo con logaritmo en el precio y en el área. Las demás variables se dejan igual porque son conteos.
mod4_log <- lm(log(precio_millon) ~ log(Area_contruida) + parqueaderos + Banos + Habitaciones + Estrato + Tipo,
data = datos_sub)
mod4_log_step <- step(mod4_log, direction = "both")
## Start: AIC=-1495.58
## log(precio_millon) ~ log(Area_contruida) + parqueaderos + Banos +
## Habitaciones + Estrato + Tipo
##
## Df Sum of Sq RSS AIC
## <none> 27.466 -1495.6
## - Habitaciones 1 0.1535 27.619 -1494.7
## - Estrato 1 0.3015 27.767 -1492.0
## - Tipo 1 0.6601 28.126 -1485.3
## - Banos 1 1.3732 28.839 -1472.5
## - parqueaderos 1 2.9943 30.460 -1444.3
## - log(Area_contruida) 1 16.0716 43.537 -1260.3
summary(mod4_log_step)
##
## Call:
## lm(formula = log(precio_millon) ~ log(Area_contruida) + parqueaderos +
## Banos + Habitaciones + Estrato + Tipo, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.93691 -0.13285 -0.02248 0.12603 0.90688
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.957486 0.205245 14.410 < 2e-16 ***
## log(Area_contruida) 0.531217 0.030811 17.241 < 2e-16 ***
## parqueaderos 0.070888 0.009526 7.442 4.27e-13 ***
## Banos 0.060979 0.012100 5.040 6.50e-07 ***
## Habitaciones -0.025008 0.014843 -1.685 0.092643 .
## Estrato 0.069901 0.029601 2.361 0.018579 *
## TipoCasa -0.099058 0.028349 -3.494 0.000517 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2325 on 508 degrees of freedom
## Multiple R-squared: 0.775, Adjusted R-squared: 0.7724
## F-statistic: 291.7 on 6 and 508 DF, p-value: < 2.2e-16
Interpretación
En este caso el step no quitó ninguna variable. Dejando lo demás constante:
Lo interesante es que la dummy no era significativa en el modelo lineal y en el logarítmico sí. En porcentajes se ve mejor la diferencia entre casas y apartamentos. El R² ajustado es 0.772.
par(mfrow = c(2, 2))
plot(mod4_log_step)
shapiro.test(residuals(mod4_log_step))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod4_log_step)
## W = 0.97543, p-value = 1.315e-07
Con el logaritmo el embudo de los residuos casi desaparece y el Q-Q queda más cerca de la línea. El Shapiro todavía da menor a 0.05, pero el p-valor mejora mucho (de menos de 2.2e-16 a 0.0000001). Es mejor el modelo logarítmico.
En el gráfico del inicio se vio que la pendiente de las casas es diferente a la de los apartamentos. Para tener eso en cuenta, en este modelo se agrega una interacción entre el área y el tipo de vivienda, parecido al ejemplo de clase con zona y área.
mod5 <- lm(precio_millon ~ Area_contruida * Tipo + parqueaderos + Banos + Habitaciones + Estrato,
data = datos_sub)
mod5_step <- step(mod5, direction = "both")
## Start: AIC=5529.61
## precio_millon ~ Area_contruida * Tipo + parqueaderos + Banos +
## Habitaciones + Estrato
##
## Df Sum of Sq RSS AIC
## - Habitaciones 1 1848 22983573 5527.7
## - Estrato 1 62928 23044654 5529.0
## <none> 22981726 5529.6
## - Banos 1 731050 23712775 5543.7
## - Area_contruida:Tipo 1 1979448 24961174 5570.2
## - parqueaderos 1 4183361 27165087 5613.7
##
## Step: AIC=5527.65
## precio_millon ~ Area_contruida + Tipo + parqueaderos + Banos +
## Estrato + Area_contruida:Tipo
##
## Df Sum of Sq RSS AIC
## - Estrato 1 64885 23048458 5527.1
## <none> 22983573 5527.7
## + Habitaciones 1 1848 22981726 5529.6
## - Banos 1 934318 23917891 5546.2
## - Area_contruida:Tipo 1 1982594 24966167 5568.3
## - parqueaderos 1 4181891 27165464 5611.7
##
## Step: AIC=5527.11
## precio_millon ~ Area_contruida + Tipo + parqueaderos + Banos +
## Area_contruida:Tipo
##
## Df Sum of Sq RSS AIC
## <none> 23048458 5527.1
## + Estrato 1 64885 22983573 5527.7
## + Habitaciones 1 3804 23044654 5529.0
## - Banos 1 951182 23999640 5545.9
## - Area_contruida:Tipo 1 2074807 25123265 5569.5
## - parqueaderos 1 4218370 27266828 5611.7
summary(mod5_step)
##
## Call:
## lm(formula = precio_millon ~ Area_contruida + Tipo + parqueaderos +
## Banos + Area_contruida:Tipo, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -812.09 -119.88 -27.98 69.23 971.02
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -87.1978 37.0882 -2.351 0.0191 *
## Area_contruida 2.3194 0.2403 9.651 < 2e-16 ***
## TipoCasa 248.1720 47.2755 5.249 2.24e-07 ***
## parqueaderos 83.7615 8.6783 9.652 < 2e-16 ***
## Banos 43.6916 9.5330 4.583 5.77e-06 ***
## Area_contruida:TipoCasa -1.5768 0.2329 -6.769 3.58e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 212.8 on 509 degrees of freedom
## Multiple R-squared: 0.6868, Adjusted R-squared: 0.6837
## F-statistic: 223.2 on 5 and 509 DF, p-value: < 2.2e-16
Interpretación
El step quitó Habitaciones y Estrato. La interacción es significativa, entonces el efecto del área sí cambia según el tipo de vivienda:
O sea que en Ciudad Jardín el metro cuadrado de apartamento vale bastante más que el de casa. El R² ajustado es 0.684.
par(mfrow = c(2, 2))
plot(mod5_step)
shapiro.test(residuals(mod5_step))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod5_step)
## W = 0.895, p-value < 2.2e-16
Este modelo tiene los mismos problemas que el lineal anterior: los residuos no son normales (Shapiro p < 0.05) y se sigue viendo el embudo de heterocedasticidad.
mod5_log <- lm(log(precio_millon) ~ log(Area_contruida) * Tipo + parqueaderos + Banos + Habitaciones + Estrato,
data = datos_sub)
mod5_log_step <- step(mod5_log, direction = "both")
## Start: AIC=-1548.15
## log(precio_millon) ~ log(Area_contruida) * Tipo + parqueaderos +
## Banos + Habitaciones + Estrato
##
## Df Sum of Sq RSS AIC
## <none> 24.704 -1548.2
## - Habitaciones 1 0.0975 24.802 -1548.1
## - Estrato 1 0.1530 24.857 -1547.0
## - Banos 1 0.7041 25.409 -1535.7
## - log(Area_contruida):Tipo 1 2.7614 27.466 -1495.6
## - parqueaderos 1 3.3724 28.077 -1484.2
summary(mod5_log_step)
##
## Call:
## lm(formula = log(precio_millon) ~ log(Area_contruida) * Tipo +
## parqueaderos + Banos + Habitaciones + Estrato, data = datos_sub)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.84751 -0.12716 -0.01073 0.11442 0.86808
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.867886 0.242722 7.696 7.41e-14 ***
## log(Area_contruida) 0.784130 0.044545 17.603 < 2e-16 ***
## TipoCasa 1.709476 0.241741 7.072 5.11e-12 ***
## parqueaderos 0.075396 0.009063 8.319 8.23e-16 ***
## Banos 0.044456 0.011695 3.801 0.000161 ***
## Habitaciones -0.019956 0.014107 -1.415 0.157798
## Estrato 0.050013 0.028225 1.772 0.077005 .
## log(Area_contruida):TipoCasa -0.350697 0.046585 -7.528 2.37e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2207 on 507 degrees of freedom
## Multiple R-squared: 0.7976, Adjusted R-squared: 0.7948
## F-statistic: 285.5 on 7 and 507 DF, p-value: < 2.2e-16
Interpretación
En la versión logarítmica el step deja todas las variables. Ahora los coeficientes del área son elasticidades:
Se confirma lo mismo que en el modelo lineal: en las casas el tamaño pesa menos en el precio. Cada parqueadero sube el precio cerca de 7.8% y cada baño cerca de 4.5%. Este modelo tiene el R² ajustado más alto de todo el trabajo (0.795).
par(mfrow = c(2, 2))
plot(mod5_log_step)
shapiro.test(residuals(mod5_log_step))
##
## Shapiro-Wilk normality test
##
## data: residuals(mod5_log_step)
## W = 0.98103, p-value = 3.015e-06
De todos los modelos, este es el que tiene mejores gráficos. El Q-Q está casi sobre la línea y los residuos no muestran un patrón claro. La línea del Scale-Location sube un poco, pero mucho menos que en la versión lineal. El Shapiro sigue dando menor a 0.05, pero con un p-valor mucho más alto (0.000003).
Quiero estimar el precio de una casa y de un apartamento en Ciudad Jardín con estas características: 200 m², 2 parqueaderos, 3 baños, 3 habitaciones y estrato 6.
En los modelos con logaritmo la predicción sale en log, así que se
usa exp() para pasarla a millones.
nueva <- data.frame(Area_contruida = c(200, 200),
parqueaderos = c(2, 2),
Banos = c(3, 3),
Habitaciones = c(3, 3),
Estrato = c(6, 6),
Tipo = c("Casa", "Apartamento"))
# Modelo múltiple 1 lineal
predict(mod4_step, nueva, interval = "prediction")
## fit lwr upr
## 1 582.528 146.4991 1018.557
## 2 582.528 146.4991 1018.557
# Modelo múltiple 1 logarítmico
exp(predict(mod4_log_step, nueva, interval = "prediction"))
## fit lwr upr
## 1 567.9764 358.8851 898.8869
## 2 627.1199 396.3805 992.1763
# Modelo múltiple 2 logarítmico
exp(predict(mod5_log_step, nueva, interval = "prediction"))
## fit lwr upr
## 1 600.7597 388.4364 929.141
## 2 697.0260 450.5320 1078.381
Interpretación
En los modelos logarítmicos el apartamento sale más caro, lo cual coincide con lo que se encontró antes: con el mismo tamaño, el m² de apartamento vale más. Los intervalos también son más angostos que en el modelo lineal.
El MAE (error absoluto medio) dice en promedio cuántos millones se equivoca el modelo al predecir el precio. Entre más bajo, mejor.
Para calcularlo se dividen los datos: con el 80% se estiman los modelos y con el otro 20% se prueba qué tan bien predicen.
set.seed(123)
filas <- sample(1:nrow(datos_sub), size = round(0.8 * nrow(datos_sub)))
entrenamiento <- datos_sub[filas, ]
prueba <- datos_sub[-filas, ]
# Se vuelven a estimar los modelos con los datos de entrenamiento
e1 <- lm(precio_millon ~ Area_contruida, data = entrenamiento)
e2 <- lm(log(precio_millon) ~ log(Area_contruida), data = entrenamiento)
e3 <- lm(formula(mod4_step), data = entrenamiento)
e4 <- lm(formula(mod4_log_step), data = entrenamiento)
e5 <- lm(formula(mod5_log_step), data = entrenamiento)
real <- prueba$precio_millon
MAE <- data.frame(
Modelo = c("Simple lineal (área)", "Simple log-log (área)", "Múltiple 1 lineal",
"Múltiple 1 logarítmico", "Múltiple 2 logarítmico"),
MAE = c(mean(abs(real - predict(e1, prueba))),
mean(abs(real - exp(predict(e2, prueba)))),
mean(abs(real - predict(e3, prueba))),
mean(abs(real - exp(predict(e4, prueba)))),
mean(abs(real - exp(predict(e5, prueba))))))
MAE
| Modelo | MAE |
|---|---|
| Simple lineal (área) | 170.1133 |
| Simple log-log (área) | 148.4031 |
| Múltiple 1 lineal | 159.2722 |
| Múltiple 1 logarítmico | 132.6572 |
| Múltiple 2 logarítmico | 136.0722 |
Interpretación