Iniciamos analizando los registros de ofertas de apartamentos del
conjunto de datos vivienda.
vivienda_apto <- subset(vivienda, tipo=="Apartamento")
kable(head(vivienda_apto, 3), "html") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1212 | Zona Norte | 01 | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento | acopi | -76.51350 | 3.45891 |
| 1724 | Zona Norte | 01 | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento | acopi | -76.51700 | 3.36971 |
| 2326 | Zona Norte | 01 | 4 | 220 | 52 | 2 | 2 | 3 | Apartamento | acopi | -76.51974 | 3.42627 |
Podemos observar la cantidad de registros que solo hacen referencia a ofertas de tipo apartamento.
conteo <- table(vivienda_apto$tipo)
kable(conteo, "html", caption = "Conteo en la variable Tipo") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| Var1 | Freq |
|---|---|
| Apartamento | 5100 |
Seleccionamos únicamente las variables de interés para el análisis:
preciom, areaconst, estrato,
banios, habitaciones,
parqueaderos, zona.
vivienda_apto <- select(vivienda_apto, preciom, areaconst, estrato, banios,
habitaciones, parqueaderos, zona)
kable(head(vivienda_apto,3), "html", caption = "Dataframe con columnas de interes") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | zona |
|---|---|---|---|---|---|---|
| 260 | 90 | 5 | 2 | 3 | 1 | Zona Norte |
| 240 | 87 | 5 | 3 | 3 | 1 | Zona Norte |
| 220 | 52 | 4 | 2 | 3 | 2 | Zona Norte |
Validamos los registros que tienen datos faltantes.
faltantes <- colSums(is.na(vivienda_apto)) %>%
as.data.frame()
kable(faltantes, "html", caption = "Conteo de faltantes") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| . | |
|---|---|
| preciom | 0 |
| areaconst | 0 |
| estrato | 0 |
| banios | 0 |
| habitaciones | 0 |
| parqueaderos | 869 |
| zona | 0 |
Se eliminan los 869 registros que tienen datos faltantes en la
variable parqueaderos. También se eliminan registros que
tienen datos atípicos en el número de baños y en el número de
habitaciones.
vivienda_apto <- subset(vivienda_apto, !is.na(parqueaderos))
vivienda_apto <- subset(vivienda_apto, banios!=0)
vivienda_apto <- subset(vivienda_apto, banios!=8)
vivienda_apto <- subset(vivienda_apto, habitaciones!=0)
vivienda_apto <- subset(vivienda_apto, habitaciones!=7)
vivienda_apto <- subset(vivienda_apto, habitaciones!=9)
kable(head(vivienda_apto,5), "html", caption = "Dataframe para analisis") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| preciom | areaconst | estrato | banios | habitaciones | parqueaderos | zona |
|---|---|---|---|---|---|---|
| 260 | 90 | 5 | 2 | 3 | 1 | Zona Norte |
| 240 | 87 | 5 | 3 | 3 | 1 | Zona Norte |
| 220 | 52 | 4 | 2 | 3 | 2 | Zona Norte |
| 310 | 137 | 5 | 3 | 4 | 2 | Zona Norte |
| 520 | 98 | 6 | 2 | 2 | 2 | Zona Norte |
plotly e interprete los resultados.Enfocándonos en las variables precio y área construida, podríamos ver las dispersiones segmentadas por zonas y detallar las características de cada punto.
plot_ly(data = vivienda_apto,
x = ~areaconst,
y = ~preciom,
type = 'scatter',
mode = 'markers',
color = ~zona,
text = ~paste("Estrato:", estrato, "<br>Banios:", banios,
"<br>Habitaciones:", habitaciones)) %>%
layout(title = "Relacion entre el Precio de la vivienda y Area Construida",
xaxis = list(title = "Area construida"),
yaxis = list(title = "Precio (millones)"))En el gráfico se puede observar un comportamiento natural de que a mayor área construida mayor precio de venta, lo que significa una relación casi lineal entre estas dos variables. También se pueden ver datos atípicos de viviendas con precios muy bajos para áreas construidas muy grandes.
En este caso realizaremos la observación usando un boxplot.
plot_ly(data = vivienda_apto,
x = ~as.factor(estrato),
y = ~preciom,
type = 'box',
color = ~as.factor(estrato)) %>%
layout(title = "Precio de la vivienda por estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones)"))A partir de la media del precio de la vivienda para cada estrato se puede validar que a medida en que aumenta el estrato también tiende a aumentar el precio de la vivienda. Para el estrato 3 la media es 128.5 millones, para el estrato 4 la media es 210 millones, para el estrato 5 la media es 300 millones y para el estrato 6 la media es 610 millones. Esto significa que existe cierto grado de relación positiva entre estas dos variables. Es importante tener en cuenta que hay muchos valores atípicos para cada estrato.
Se realiza el análisis usando un boxplot.
plot_ly(data = vivienda_apto,
x = ~as.factor(banios),
y = ~preciom,
type = 'box',
color = ~as.factor(banios)) %>%
layout(title = "Precio de la vivienda por numero de banios",
xaxis = list(title = "Numero de banios"),
yaxis = list(title = "Precio (millones)"))De igual forma es bastante evidente la relación positiva entre el precio de un inmueble y el número de baños, pues también se puede considerar el aumento en la media del precio para cada cantidad analizada, concluyendo que a mayor cantidad de baños mayor es el precio de la vivienda.
Se realiza el análisis usando un boxplot.
plot_ly(data = vivienda_apto,
x = ~as.factor(habitaciones),
y = ~preciom,
type = 'box',
color = ~as.factor(habitaciones)) %>%
layout(title = "Precio de la vivienda por numero de habitaciones",
xaxis = list(title = "Numero de habitaciones"),
yaxis = list(title = "Precio (millones)"))En este caso la relación entre el número de habitaciones y el precio de la vivienda no es muy directa, principalmente porque para las cantidades 1, 2 y 3 la media en el precio es muy similar. Puede notarse una tendencia un poco creciente a partir de 4 habitaciones.
Se realiza el análisis usando un boxplot.
plot_ly(data = vivienda_apto,
x = ~as.factor(zona),
y = ~preciom,
type = 'box',
color = ~as.factor(zona)) %>%
layout(title = "Precio de la vivienda por zonas",
xaxis = list(title = "Zonas"),
yaxis = list(title = "Precio (millones)"))Para la distribución de precios de las viviendas por zonas se ve claramente que la zona oeste tiene los apartamentos más costosos, la zona sur y la zona norte presenta valores similares en la media de los precios, en precios mínimos y en el precio máximo, así como en los valores atípicos. La zona oriente y la zona centro presentan las viviendas con menores precios. También se puede tener en cuenta que la mayoría de los apartamentos se encuentran en la zona sur, en la zona oeste y en la zona norte
conteo_zonas <- table(vivienda_apto$zona)
kable(conteo_zonas, "html", caption = "Conteo en la variable Zona") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| Var1 | Freq |
|---|---|
| Zona Centro | 10 |
| Zona Norte | 847 |
| Zona Oeste | 962 |
| Zona Oriente | 21 |
| Zona Sur | 2374 |
cor_matrix <- cor(select(vivienda_apto, preciom, areaconst, estrato, banios, habitaciones, parqueaderos))
cor_text <- round(cor_matrix, 3)
cor_text <- as.character(cor_text)
plot_ly(
x = colnames(cor_matrix),
y = rownames(cor_matrix),
z = cor_matrix,
type = "heatmap",
text = cor_text,
colorscale = "Viridis"
) %>%
add_annotations(
x = rep(colnames(cor_matrix), each = nrow(cor_matrix)),
y = rep(rownames(cor_matrix), times = ncol(cor_matrix)),
text = cor_text,
showarrow = FALSE,
font = list(color = "white", size = 12)
)En la matriz de correlación se observa que todas las variables presentan correlacion positiva con la variable precio. El área construida tiene una correlación lineal muy importante con la variable precio, y las variables cantidad de baños y cantidad de parqueaderos son muy significativas. El estrato también está relacionado linealmente con el precio, como lo vimos anteriormente, pero por el contrario la variable cantidad de habitaciones casi no tiene relación con el precio de las viviendas.
modelo1 <- lm(preciom ~ areaconst + as.factor(estrato)
+ habitaciones + parqueaderos + banios, data=vivienda_apto)
summary(modelo1)
Call:
lm(formula = preciom ~ areaconst + as.factor(estrato) + habitaciones +
parqueaderos + banios, data = vivienda_apto)
Residuals:
Min 1Q Median 3Q Max
-1717.57 -53.79 1.01 46.46 975.63
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -50.89668 12.58957 -4.043 0.0000538 ***
areaconst 2.06448 0.04785 43.148 < 0.0000000000000002 ***
as.factor(estrato)4 22.40280 8.81151 2.542 0.011 *
as.factor(estrato)5 38.22519 8.66813 4.410 0.0000106 ***
as.factor(estrato)6 162.96423 10.17599 16.015 < 0.0000000000000002 ***
habitaciones -43.70730 3.87466 -11.280 < 0.0000000000000002 ***
parqueaderos 81.68291 4.20303 19.434 < 0.0000000000000002 ***
banios 51.52874 3.37370 15.274 < 0.0000000000000002 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 132.3 on 4206 degrees of freedom
Multiple R-squared: 0.8009, Adjusted R-squared: 0.8006
F-statistic: 2418 on 7 and 4206 DF, p-value: < 0.00000000000000022
Intercepto (-50.896): Representa el valor estimado del precio de una vivienda cuando las variables independientes son cero. Contextualmente, en este caso no tiene sentido que las variables predictoras sean iguales a cero.
Área construída (2.064): Indica que por cada incremento por unidad de metros cuadrados el precio de la vivienda aumentará en 2.064 millones de pesos, mientras las demás variables se mantengan constantes. Resulta lógico interpretar que en promedio el precio de los apartamentos tendrán ese crecimiento constante dependiente del tamaño del inmueble.
Estrato: En la variable estrato se aplicó One-Hot Encoder, lo que generó una variable para cada valor de estrato: estrato_4, estrato_5 y estrato_6. Los valores en estas variables sólo podrán ser cero o uno, lo que indica que el estrato 4 tiene un peso de 22.4 millones en el valor de la vivienda, el estrato 5 tiene un peso de 38.22 millones en el precio de la vivienda y el estrato 6 presenta un peso mucho mayor de 162.96 millones de pesos en el valor de la vivienda.
Número de habitaciones (-43.707): Indica que por cada aumento por unidad en la cantidad de habitaciones el precio de la vivienda disminuirá en 43.707 millones de pesos, mientras las demás variables se mantengan constantes. Esto puede indicar que los datos muestran tendencias de disminución del precio cuando hay muchas habitaciones.
Número de parqueaderos (81.682): Indica que por cada aumento por unidad en la cantidad de parqueaderos el precio de la vivienda aumentará en 81.682 millones de pesos, mientras las demás variables se mantengan constantes. Esto tiene mucho sentido en el contexto del mercado inmobiliario debido a lo apetecidos que son los parqueaderos en una ciudad.
Número de baños (51.528): Indica que por cada aumento por unidad en la cantidad de baños el precio de la vivienda aumentará en 51.528 millones de pesos, mientras las demás variables se mantengan constantes. También es un factor común ante los deseos de los compradores de tener un inmueble con mayor cantidad de baños.
Según el resúmen del modelo se puede observar que todos los coeficientes son estadísticamente significativos ya que presentan un valor de \(p\) aproximadamente igual a cero, lo que permite rechazar la hipótesis nula de que cada coeficiente puede ser igual a cero.
El \(R^2\) tiene un valor de 0.8, lo que significa que el modelo puede explicar el \(80\%\) de la variabilidad del precio de las viviendas. Este valor es importante y puede indicar que el modelo es robusto y presenta un buen ajuste.
Una opción para intentar mejorar el ajuste del modelo puede ser la realización de transformaciones en las variables predictoras o en la variable dependiente. El modelo estudiado es un modelo Lin-Lin, por lo que las transformaciones pueden ser modelo Log-Lin, modelo Lin-Log y modelo Log-Log. También se puede realizar un análisis exhaustivo de los datos para identificar con mayor detalle datos atípicos, evaluar multicolinealidad, agregar otra variable que pueda ser significativa o realizar algún preprocesamiento en los datos para disminuir ruido.
Shapiro-Wilk normality test
data: resid(modelo1)
W = 0.84441, p-value < 0.00000000000000022
Las gráficas Normal Q-Q Plot y el histograma indican gráficamente la violación del supuesto de que los errores deben ser una variable aleatoria con distribución normal, y la prueba de Shapiro-Wilk lo confirma con un \(p-value<0.05\), indicando que se rechaza la hipótesis nula de que los residuales siguen una distribución normal. El no cumplimiento de este supuesto implica sesgo en los intervalos de confianza y pruebas de hipótesis no confiables, que podrían repercutir en un resultado erróneo en una predicción. Para corregir el no cumplimiento del supuesto de normalidad de los errores se podría realizar una transformación logarítmica o de raíz cuadrada en la variable dependiente precio de la vivienda.
Goldfeld-Quandt test
data: modelo1
GQ = 1.5523, df1 = 2099, df2 = 2099, p-value < 0.00000000000000022
alternative hypothesis: variance increases from segment 1 to 2
La prueba Goldfeld-Quandt presenta un \(p-value<0.05\), indicando que se rechaza la hipótesis nula de que la varianza de los residuos es constante. La violación del supuesto de homocedasticidad puede llevar a inferencias estadistica poco confiables. Una transformación logarítmica o de raíz cuadrada sobre la variable dependiente (preciom) podría ayudar a corregir la varianza no constante en los residuos
Durbin-Watson test
data: modelo1
DW = 1.7195, p-value < 0.00000000000000022
alternative hypothesis: true autocorrelation is greater than 0
La prueba Durbin-Watson presenta un \(p-value<0.05\), lo cual indica que se debe rechazar la hipótesis nula de independencia entre los errores, que a su vez significa que hay evidencia de autocorrelación entre los residuos del modelo. La autocorrelación en los residuos afecta directamente el error estándar en el cálculo de los coeficientes de regresión. Para corregir la violación de este supuesto se podría usar términos de desfase en las variables independeientes.
En la gráfica anterior podemos observar mucha varianza en cada una de las variables independiente del modelo, lo que sugiere que no hay una relación lineal precisa. Esto implica que el modelo no es capaz de relacionar adecuadamente las variables predictoras con la variable independiente, lo que conlleva a subajuste o sobreajuste. Para agregar linealidad al modelo se puede realizar alguna transformación logarítmica o cuadrática en algunas de las variables independientes.
set.seed(1)
split <- sample.split(vivienda_apto$preciom, SplitRatio=0.7)
train_data <- subset(vivienda_apto, split==TRUE)
test_data <- subset(vivienda_apto, split==FALSE)
modelo2 <- lm(preciom ~ areaconst + as.factor(estrato)
+ habitaciones + parqueaderos + banios, data = train_data)
summary(modelo2)
Call:
lm(formula = preciom ~ areaconst + as.factor(estrato) + habitaciones +
parqueaderos + banios, data = train_data)
Residuals:
Min 1Q Median 3Q Max
-1786.24 -53.38 1.02 45.99 965.05
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -56.09536 14.99910 -3.740 0.000188 ***
areaconst 2.14348 0.05696 37.629 < 0.0000000000000002 ***
as.factor(estrato)4 25.54393 10.35631 2.467 0.013700 *
as.factor(estrato)5 42.52222 10.15313 4.188 0.0000289 ***
as.factor(estrato)6 169.23115 11.95698 14.153 < 0.0000000000000002 ***
habitaciones -41.95271 4.65900 -9.005 < 0.0000000000000002 ***
parqueaderos 76.52107 4.92637 15.533 < 0.0000000000000002 ***
banios 50.14270 3.96264 12.654 < 0.0000000000000002 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 132.2 on 2980 degrees of freedom
Multiple R-squared: 0.8049, Adjusted R-squared: 0.8045
F-statistic: 1757 on 7 and 2980 DF, p-value: < 0.00000000000000022
El modelo entrenado con el \(70\%\) de los datos tiene un resultado similar al modelo entrenado con el total de los datos. Los residuos se distribuyen en rangos similares (min-max) y los valores de los coeficientes son casi idénticos, perdiendo significancia estadística únicamente en la variable dummy para el estrato 4. Este nuevo modelo también presenta un \(R^2\) igual a 0.80, indicando que puede explicar el \(80\%\) de la varianza del precio.
predicciones <- predict(modelo2, newdata=test_data)
resultados <- data.frame(preciom = test_data$preciom, prediccion = predicciones)
kable(head(resultados), "html", caption = "DF de puebas con predicciones") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| preciom | prediccion |
|---|---|
| 310 | 415.74320 |
| 820 | 1030.50928 |
| 450 | 469.57377 |
| 170 | 149.22017 |
| 78 | 72.40224 |
| 750 | 724.57376 |
# Error cuadrático medio
mse <- mse(test_data$preciom, predicciones)
# Error absoluto medio
mae <- mae(test_data$preciom, predicciones)
# R2 Score
media_precio <- mean(test_data$preciom)
SSR <- sum((test_data$preciom - predicciones)^2)
SST <- sum((test_data$preciom - media_precio)^2)
r2_score <- 1 - (SSR/SST)
metricas <- data.frame(
Modelo = "modelo2",
ECM = mse,
EAM = mae,
R2 = r2_score
)
kable(metricas, "html", caption = "Metricas de evaluación") %>%
kable_styling("striped", full_width = FALSE) %>%
row_spec(0, bold = TRUE)| Modelo | ECM | EAM | R2 |
|---|---|---|---|
| modelo2 | 17607.83 | 81.29649 | 0.7894513 |
Esta medida se puede interpretar como la diferencia cuadrática entre el valor predicho y el valor real esperado en promedio. En la evaluación del modelo con el conjunto de datos de pruebas obtenemos un valor de ECM igual a 17607.83, lo que significa que el modelo tiene un desviación aproximada de 17607.83 millones de pesos, con la escala elevada al cuadrado, por lo cual puede ser poco intuitivo para validar la exactitud de modelo. El ECM penaliza principalmente los errores grandes, por lo que un menor valor de ECM significa un mejor modelo.
El EAM es el promedio de todos los errores absolutos entre el valor real y el valor predicho, y que sirve para evaluar la precisión del modelo de regresión lineal. En la evaluación del modelo con el conjunto de datos de pruebas tenemos un EAM igual a 81.296, que indica en promedio que las predicciones se desvían 81.296 millones de pesos frente a los valores reales. Esta es una medida de más fácil interpretación dentro del contexto del problema, y se puede asumir que es un valor relativamente bajo en consideración a los valores de los precios de viviendas que se manejan en el conjunto de datos.
El coeficiente de determinación \(R^2\) con un valor de 0.789 indica que el ajuste del modelo de regresión lineal múltiple puede explicar el \(78.9\%\) de la variabilidad en el precio del inmueble a partir de la variabilidad de las variables predictoras área construída, estrato, número de habitaciones, número de parqueaderos y número de baños. El \(21.1\%\) restante puede depender de algunas variables no tenidas en cuenta en el modelo. En el contexto del mercado inmobiliario, el valor de \(R^2\) presentado por el modelo en la evaluación de los datos de prueba es significativo y puede sugerir que el modelo es robusto y eficiente en la predicción del precio de las viviendas.