Regresión Lineal MúltipleCon el fin de apoyar las decisiones administrativas y financieras de la inmobiliaria C&A (Casas y Apartamentos) en Cali, se desarrollará el siguiente proyecto el cual se basa en la construcción de un modelo de Regresión Lineal Multiple con la base de datos “vivienda”. Se trabajará especificamente en el tipo de vivienda “apartamentos” en relación a las variables predictoras como el área construida, el estrato, número de baños, habitaciones y la zona.
Para el desarrollo de este informe, se hará uso de técnicas y herramientas estadísticas para organizar, resumir y presentar los datos recopilados de acuerdo con la guía del curso de modelos estadisticos para la toma de decisiones, la cual abordará las siguientes etapas:
Análisis explotario y procesamiento de datos.
Modelo de Regresión Lineal Múltiple.
Conclusiones y recomendaciones.
En este apartado, se realizará la carga de la base de datos original, se realizará una primera visualización de los datos obtenidos, además de los paquetes a utilizar para el desarrollo de la actividad.
Consolidado de paquete de librerias a utilizar en el proyecto.
library(paqueteMETODOS)
library(kableExtra)
library(knitr)
library(GGally)
library(plotly)
library(car)
library(caret)
library(nortest)
library(lmtest)
library(dplyr)
Esta es una base de datos con la que hemos estado desarrollando informes anteriormente y se cuenta con algo de conocimiento sobre ella. La guía para esta segunda entrega nos pide realizar una serie de pasos pero antes, vamos a realizar unos ajustes previos a la BD.
La base de datos original cuenta con 8322 registros y 13 columnas, con variables de tipo numérico y categórico. Tambien hemos observado que existen dos variables con una alta pérdida de datos como lo son piso (32%) y parqueadero(19%) y, adicionalmente, hay 3 filas cuyos atributos no contienen informaciòn.
#Descarga de la base de datos original
#devtools::install_github("dgonxalex80/paqueteMETODOS, force = TRUE")
data(vivienda)
# Se crea una copia seguridad, con la que se iniciará el desarrollo.
viviwork = vivienda
El primer punto nos indica realizar un filtro para el procesamiento del modelo. Sin embargo, es necesario validar algunos elementos de la variable “tipo” de vivienda.
Se puede observar que solo hay dos tipos de vivienda (Casa y Apartamento), y todas están escritas en forma correcta, es decir no encontrar errores tipográficos. Al igual que se puede encontrar 5100 registros para apartamento, lo que será importante para el filtro de esta variable.
#Valores unicos de la variable tipo
unique(viviwork$tipo)
## [1] "Casa" "Apartamento" NA
table(viviwork$tipo)
##
## Apartamento Casa
## 5100 3219
Procedemos con el filtro de tipo apartamento, y observamos las primeras lineas para confirmar el procedimiento.
#Filtro apartamento
viviwork = viviwork[viviwork$tipo == "Apartamento", ]
#Se muestran las primeras lineas del filtro
viviwork %>%
head(3) %>%
kable("html", caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE, position = "center")
| 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 |
Con el siguiente gráfico se puede observar que, una vez realizado el filtro para apartamento, es constante la cantidad de datos perdidos tanto para la variable piso (27%) como parqueadero (17%). Aunque estas variables pueden llegar a ser importante para la realización del modelo de regresion lineal múltiple, solamente será tenida en cuenta la variable parqueadero.
#Conocer los datos faltantes de la nueva BD filtrada
faltantes = colSums(is.na(viviwork)) %>%
as.data.frame()
colnames(faltantes) = "Datos Faltantes 'Apartamento'"
kable(faltantes, caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "center")
| Datos Faltantes ‘Apartamento’ | |
|---|---|
| id | 3 |
| zona | 3 |
| piso | 1384 |
| estrato | 3 |
| preciom | 3 |
| areaconst | 3 |
| parqueaderos | 872 |
| banios | 3 |
| habitaciones | 3 |
| tipo | 3 |
| barrio | 3 |
| longitud | 3 |
| latitud | 3 |
Lo siguiente, es eliminar las variables que se considera no aportarán en el modelo de regresión multiple como lo son: “id”, “piso”, “barrio”, “longitud” y “latitud”.
#Eliminar variables
viviwork = subset(viviwork, select = -c (id, piso, barrio, longitud, latitud))
#visualización del nuevo dataset
viviwork %>%
sample_n(5) %>%
kable("html", caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE, position = "center")
| zona | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo |
|---|---|---|---|---|---|---|---|
| Zona Norte | 4 | 300 | 136 | 2 | 2 | 3 | Apartamento |
| Zona Oeste | 5 | 280 | 90 | 1 | 3 | 2 | Apartamento |
| Zona Sur | 3 | 160 | 65 | 1 | 2 | 3 | Apartamento |
| Zona Oeste | 6 | 1380 | 264 | 3 | 4 | 3 | Apartamento |
| Zona Norte | 4 | 314 | 68 | NA | 1 | 1 | Apartamento |
Posterior a ello, se procede a imputar variable parqueadero. En la realización del ejercicio, arrojo una validación con el promedio de 1.56 y la moda de 1 para el tipo apartamento. Lo cual coincide con el principio de realidad de la oferta.
#Convertir la variable a numérica
viviwork$parqueaderos = as.numeric(viviwork$parqueaderos)
#Calcular el promedio
promedio_parquea = mean(viviwork$parqueaderos, na.rm = TRUE)
print(paste("El promedio de parqueaderos es de: ", promedio_parquea))
## [1] "El promedio de parqueaderos es de: 1.56771448830064"
#Calcular la moda
moda = function(x) {
uniq_x = unique(x)
uniq_x[which.max(tabulate(match(x, uniq_x)))]
}
moda_parquea = moda(viviwork$parqueaderos)
print(paste("La moda de parqueaderos es de: ", moda_parquea))
## [1] "La moda de parqueaderos es de: 1"
Realizamos la imputación en la variable parqueaderos.
viviwork$parqueaderos[is.na(viviwork$parqueaderos)] = 1
A continuación, procedemos a eliminar las demas filas con datos NA en cualquier columna.
viviwork = na.omit(viviwork)
viviwork %>%
head() %>%
kable("html", caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE, position = "center")
| zona | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo |
|---|---|---|---|---|---|---|---|
| Zona Norte | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento |
| Zona Norte | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento |
| Zona Norte | 4 | 220 | 52 | 2 | 2 | 3 | Apartamento |
| Zona Norte | 5 | 310 | 137 | 2 | 3 | 4 | Apartamento |
| Zona Norte | 6 | 520 | 98 | 2 | 2 | 2 | Apartamento |
| Zona Norte | 4 | 320 | 108 | 2 | 3 | 3 | Apartamento |
En el dataset formado, encontramos datos de tipo numérico (estrato, precio, area, parqueadero, baños y habitaciones) y categórico (zona y tipo). Sin embargo, el estrato es de tipo categórico ordinal y se hará la modificación, al igual que se pasará a tipo factor, que segun la documentación favorece el desarollo del Modelo de RLM.
#Convertir estrato a variable categórico
viviwork$estrato = as.factor(viviwork$estrato)
#Convertir todas las variables chr a factor
viviwork = viviwork%>% mutate(across(where(is.character), as.factor))
sapply(viviwork, class)
## zona estrato preciom areaconst parqueaderos banios
## "factor" "factor" "numeric" "numeric" "numeric" "numeric"
## habitaciones tipo
## "numeric" "factor"
A continuación, realizamos el alistamiento para análisis de correlación de variables numéricas. En la correlación de Spearman se puede encontrar fuerte relación entre el precio y el area construida (0.89), asi tambien como los baños (0.77) y el parqueadero(0.73) para el filtro de apartamentos.
En esta matriz de gráficos e indicadores, podemos confirmar el analisis realizado para las variables categóricas. En donde hay una fuerte relación en torno al precio de los apartamentos. Además, se evidencia que algunas variables (areaconst, parqueaderos, banios) muestran una relación lineal con preciom, aunque no es perfecta para todas las combinaciones y ninguna de las variables parece seguir una distribución normal. Esto podría representar un desafio, especialmente si los residuos del modelo de regresión no son normales.
#filtro para variables numéricas
num_viviwork = viviwork[, sapply(viviwork, is.numeric)]
#Analisis de correlación
round(cor(x=num_viviwork, method = "spearman"), 3)
## preciom areaconst parqueaderos banios habitaciones
## preciom 1.000 0.890 0.736 0.772 0.326
## areaconst 0.890 1.000 0.706 0.800 0.468
## parqueaderos 0.736 0.706 1.000 0.647 0.302
## banios 0.772 0.800 0.647 1.000 0.489
## habitaciones 0.326 0.468 0.302 0.489 1.000
#Matriz de Correlaciones Var. Numéricas
ggpairs(num_viviwork, lower = list(continuous = "smooth"),
diag = list (continuous = "barDiag"), axisLabels = "none",
tittle= "Matríz de Correlaciones")
Ahora, mediante la implementación de gráficos con la libreria Plotly, realizaremos el análisis bivariado con la variable independiente del precio en millones de pesos COP… es variable dependiente.
Ahora, mediante la implementación de gráficos con la libreria Plotly, realizaremos el análisis bivariado con la variable independiente del precio en millones de pesos COP.
# Gráfico 1: Precio vs Área Construida y Estrato
plot_ly(data = viviwork, x = ~`areaconst`, y = ~preciom, color = ~factor(estrato)) %>%
add_markers(showlegend = TRUE, legendgroup = "Estrato") %>%
layout(title = "Relación Precio - Área Construida en Aptos",
xaxis = list(title = "Área Construida (m²)"),
yaxis = list(title = "Precio (en millones COP)"),
legend = list(
title = list(text = "Estrato"),
itemsizing = "constant",
itemwidth = 100, # Corrección: era 'itemwight'
itemheight = 20,
bgcolor = "rgba(255, 255, 255, 0.5)",
bordercolor = "rgba(0, 0, 0, 0.5)", # Corrección: era 'bardercolor'
borderwidth = 1,
x = 0.85,
y = 0.9
))
# Gráfico 2: Precio por Zona
plot_ly(data = viviwork, x = ~zona, y = ~preciom, type = 'box',
marker = list(color = '#001865')) %>%
layout(title = "Precio por Zona",
xaxis = list(title = "Zona"),
yaxis = list(title = "Precio (Millones de pesos)"),
width = 500,
height = 400)
# Gráfico 3: Precio por Estrato
plot_ly(data = viviwork, x = ~estrato, y = ~preciom, type = 'box',
marker = list(color = '#001865')) %>%
layout(title = "Precio por Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (Millones de pesos)"),
width = 500,
height = 400)
# Gráfico 4: Precio vs Parqueaderos
plot_ly(data = viviwork, x = ~parqueaderos, y = ~preciom, type = 'scatter', mode = 'markers',
marker = list(color = '#001865')) %>%
layout(title = "Precio vs Parqueaderos",
xaxis = list(title = "Número de Parqueaderos"),
yaxis = list(title = "Precio (Millones de pesos)"),
width = 500,
height = 400)
# Gráfico 5: Precio vs Número de Baños
plot_ly(data = viviwork, x = ~banios, y = ~preciom, type = 'scatter', mode = 'markers',
marker = list(color = '#001865')) %>%
layout(title = "Precio vs Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (Millones de pesos)"),
width = 500,
height = 400)
# Gráfico 6: Precio vs Número de Habitaciones
plot_ly(data = viviwork, x = ~habitaciones, y = ~preciom, type = 'scatter', mode = 'markers',
marker = list(color = '#001865')) %>%
layout(title = "Precio vs Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (Millones de pesos)"),
width = 500,
height = 400)
Tal como lo habíamos mencionado, hay solo dos variables para el análisis entre variables categóricas: zona y estrato. Para este, se utilizó el test de Chi-cuadrado de independencia para evaluar si existe una asociación significativa entre estas dos variables..
El valor p-value < 2.2e-16 indica que hay evidencia suficiente para rechazar la hipótesis nula de independencia. Lo cual significa que hay una relación significativa entre la zona y el estrato.
La Zona Sur tiene una alta concentración de viviendas en estratos 4 y 5, mientras que la Zona Oriente tiene una baja representación en todos los estratos.
cat_viviwork = viviwork[, sapply(viviwork, is.factor)]
chisq_test = chisq.test(table(viviwork$zona, viviwork$estrato))
print(chisq_test)
##
## Pearson's Chi-squared test
##
## data: table(viviwork$zona, viviwork$estrato)
## X-squared = 2174.4, df = 12, p-value < 2.2e-16
tabla = table(viviwork$zona, viviwork$estrato)
plot_ly(
x = colnames(tabla),
y = rownames(tabla),
z = tabla,
type = "heatmap"
) %>%
layout(title = "Heatmap de Zona vs Estrato")
Como parte final de preparación de nuestro dataset, se realizará el proceso de One Hot Encodin para las variables categóricas Zona y Estrato, creando variables dummy. Unificaremos todas las variables en un dataset final y realizaremos el proceso de estandarización de las variables.
viviwork_dummy = model.matrix(~ estrato -1, data = viviwork)
#combinar variables dummy con variables numericas
viviwork_final = cbind(viviwork_dummy, viviwork[, c("preciom",
"areaconst",
"parqueaderos",
"banios",
"habitaciones")])
viviwork_final = viviwork_final %>%
mutate(across(where(is.numeric), scale))
viviwork_final %>%
sample_n(5) %>%
kable("html", caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE, position = "center")
| estrato3 | estrato4 | estrato5 | estrato6 | preciom | areaconst | parqueaderos | banios | habitaciones | |
|---|---|---|---|---|---|---|---|---|---|
| 1066 | -0.3784354 | 1.6223324 | -0.7277295 | -0.5821234 | -0.5080694 | -0.5158959 | -0.6640895 | -0.5774194 | 0.04235103 |
| 4317 | -0.3784354 | 1.6223324 | -0.7277295 | -0.5821234 | -0.7915912 | -0.8907580 | -0.6640895 | -0.5774194 | 0.04235103 |
| 4828 | -0.3784354 | 1.6223324 | -0.7277295 | -0.5821234 | -0.6117969 | -0.5879848 | -0.6640895 | -0.5774194 | 0.04235103 |
| 3190 | 2.6419409 | -0.6162756 | -0.7277295 | -0.5821234 | -0.8538277 | -0.5447314 | -0.6640895 | -0.5774194 | -1.43703422 |
| 2270 | 2.6419409 | -0.6162756 | -0.7277295 | -0.5821234 | -0.9921311 | -0.9340114 | -0.6640895 | -1.5131801 | 0.04235103 |
Una vez lista la base de datos, se procede con la realización del modelo de RLM.
modelo = lm(preciom ~ ., data = viviwork_final)
summary(modelo)
##
## Call:
## lm(formula = preciom ~ ., data = viviwork_final)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.6003 -0.1625 0.0012 0.1445 3.4400
##
## Coefficients: (1 not defined because of singularities)
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.225e-15 6.152e-03 0.00 1
## estrato3 -2.094e-01 9.137e-03 -22.91 <2e-16 ***
## estrato4 -2.459e-01 9.832e-03 -25.01 <2e-16 ***
## estrato5 -2.248e-01 8.964e-03 -25.08 <2e-16 ***
## estrato6 NA NA NA NA
## areaconst 4.676e-01 9.912e-03 47.17 <2e-16 ***
## parqueaderos 2.049e-01 8.920e-03 22.97 <2e-16 ***
## banios 1.681e-01 1.070e-02 15.71 <2e-16 ***
## habitaciones -7.671e-02 7.354e-03 -10.43 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4393 on 5092 degrees of freedom
## Multiple R-squared: 0.8072, Adjusted R-squared: 0.807
## F-statistic: 3046 on 7 and 5092 DF, p-value: < 2.2e-16
Los resultados muestran que el valor del intercepto es de 1.225e-15, lo cual quiere decir que cuando todas las variables predictoras son cero, el valor predicho del precio es prácticamente cero. Todos los coeficientes asociados con el estrato son negativos y significativos con p-valores < 2e-16, sugiriendo que los precios de los apartamentos en estratos 3, 4, 5 y 6 son significativamente menores, lo cual podría ser contraintuitivo, pues se esperaría que un estrato mas alto, generalmente indica un nivel socioeconómico superior y que el precio de la vivienda sea más alto. Los coeficientes para las variables área construida, parqueaderos, baños y habitaciones son positivos y significativos, lo cual indica que un aumento en metros cuadrados de área, un parqueadero, baño o habitación adicional, están asociados con un incremento en el precio de la vivienda, lo cual puede ser coherente y esperado.
Precio = 1.225e-15 + (- 2.094e-01 (estrato3)) + (- 4.476e-01 (estrato4)) + (-2.248e-01 (estrato5)) + (- 2.479e-01 (estrato6)) + 4.67e-01(área construida) + 2.902e-01 (parqueaderos) + 6.801e-01 (baños) + 7.671e-01 (habitaciones) + E
El valor de R2 significa que aproximadamente el 80.72% de la variabilidad en los precios de las viviendas se explica por las variables independientes en el modelo. Esto sugiere que el modelo tiene un buen ajuste, pero aún podría mejorar. Algunas sugerencias son la eevisión de la calidad de los datos, realizar algunas transformaciones logaritmicas o cuadráticas en las variables que no tienen una relación lineal al precio, incluir algunas otras variables relevantes e influyentes en el precio, y el manejo de la multicolinealidad de algunas variables predictoras.
Para que los resultados del modelo de regresión lineal múltiple sean validos y las inferencias confiables, se procede a realizar la validación de los siguientes supuestos:
Gráfico de residuos vs valores ajustados.
plot(modelo$fitted.values, modelo$residuals)
abline(h = 0, col = "red")
Las pruebas de Homeodasticidad de Breusch-Pagan y Goldfeld-Quandt confirman la presencia de heterocedasticidad en el modelo de RLM. Esto significa que la variabilidad de los errores no es constante a lo largo de los valores predichos, lo que puede sesgar los estimadores estándar de error y afectar la validación de las hipótesis de los coeficientes.
bptest(modelo)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 1445.6, df = 7, p-value < 2.2e-16
gqtest(modelo)
##
## Goldfeld-Quandt test
##
## data: modelo
## GQ = 1.565, df1 = 2541, df2 = 2541, p-value < 2.2e-16
## alternative hypothesis: variance increases from segment 1 to 2
Histograma y Q-Q plot de los residuos
hist(modelo$residuals, breaks = 30, main = "Histograma de Residuos", xlab = "Residuos")
qqnorm(modelo$residuals)
qqline(modelo$residuals, col = "red")
Al aplicar el test de Anderson-Darling se ha detectado que los residuos no siguen una distribución normal, lo que sugiere considerar acciones correctivas para dar validez al modelo o evaluar si este es el más adecuado para los datos analizados.
Estadístico A = 211.74 resulta siendo un valor alto e indica una mayor desviación de la normalidad. Por su parte, p-valor < 2.2e-16 es extremadamente bajo (menor que 0.05) sugiere que la hipótesis nula de que los residuos siguen una distribución normal debe ser rechazada.
ad.test(modelo$residuals)
##
## Anderson-Darling normality test
##
## data: modelo$residuals
## A = 211.74, p-value < 2.2e-16
La prueba de Durbin-Watson (1.76) con un p-valor de 0.7603, indica que los residuos del modelo no presentan autocorrelación significativa de primer orden. Esto es positivo porque sugiere que se cumple la independencia de los errores.
dwtest(modelo)
##
## Durbin-Watson test
##
## data: modelo
## DW = 1.7166, p-value = 0.7603
## alternative hypothesis: true autocorrelation is greater than 0
Gráficamente, en la dispersión de residuales vs valores ajustados se puede observar un cierto patrón curvo de los residuos, lo cual sugiere una posible no linealidad en los datos (la relación de la variable independiente y dependiente, parece que no podría ser completamente capturada por un modelo lineal).
Por su parte, el gráfico Q-Q residuals, evidencia que los residuos no siguen una línea recta, especialmente en los extremos, lo cual indica que los residuos no se distribuyen normalmente.
par(mfrow = c(2, 2))
plot(modelo,
col = "#9abaf9",
pch = 20,
lwd = 1,
cex = 1)
abline(a=0,
b=1,
col = "#001865",
lty =2)
Calculo del VIF (Variance Inflation Factor (VIF); valores altos de VIF indican problemas de multicolinealidad)
#vif(modelo)
El modelo muestra el error “there are aliased coefficients in the model” lo cual indica que hay colinealidad perfecta o redundancia en las variables predictoras, una o más variables son combinaciones lineales exactas de otras. Por lo cual no corre renderiza el modelo, y es necesario realizar un ajuste.
Para ello, se utiliza el “alias” para conocer donde puede estar la multicolinealidad. El resultado muestra problema con la variable estrato.
alias(modelo)
## Model :
## preciom ~ estrato3 + estrato4 + estrato5 + estrato6 + areaconst +
## parqueaderos + banios + habitaciones
##
## Complete :
## (Intercept) estrato3 estrato4 estrato5
## estrato6 0 -1843889/2421792 -23702/23073 -1347/1231
## areaconst parqueaderos banios habitaciones
## estrato6 0 0 0 0
Se sugiere poder hacer un ajuste en el modelo, al no incluir esta variable. A continuación, se desarrolla el nuevo modelo y se hace validación de supuestos.
# Eliminar la variable problemática y reajustar el modelo
modelo_ajus = lm(preciom ~ areaconst + parqueaderos + banios + habitaciones, data = viviwork_final)
summary(modelo_ajus)
##
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + banios + habitaciones,
## data = viviwork_final)
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.1360 -0.1904 -0.0072 0.1619 3.5923
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.260e-15 6.603e-03 0.00 1
## areaconst 5.071e-01 1.051e-02 48.23 <2e-16 ***
## parqueaderos 2.680e-01 9.228e-03 29.05 <2e-16 ***
## banios 2.625e-01 1.061e-02 24.73 <2e-16 ***
## habitaciones -1.153e-01 7.683e-03 -15.01 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4715 on 5095 degrees of freedom
## Multiple R-squared: 0.7778, Adjusted R-squared: 0.7777
## F-statistic: 4459 on 4 and 5095 DF, p-value: < 2.2e-16
par(mfrow = c(2, 2))
plot(modelo_ajus,
col = "#9abaf9",
pch = 20,
lwd = 1,
cex = 1)
abline(a=0,
b=1,
col = "#001865",
lty =2)
Los valores calculados del VIF para el modelo ajustado están por debajo de 5, lo que es una buena señal. Sugiere que no hay un problema significativo de multicolinealidad entre las variables independientes en tu modelo.
Un VIF entre 1 y 5 generalmente indica una colinealidad moderada. Valores de VIF superiores a 10 suelen considerarse problemáticos, indicando una alta colinealidad entre variables independientes.
# Calcular nuevamente el VIF
library(car)
vif(modelo_ajus)
## areaconst parqueaderos banios habitaciones
## 2.535324 1.952796 2.583266 1.353780
set.seed(123)
indice = createDataPartition(viviwork_final$preciom, p = 0.7, list = FALSE)
# Crear conjuntos de entrenamiento y prueba
entrenamiento = viviwork_final[indice, ]
prueba = viviwork_final[-indice, ]
modelo_ajus_2 = lm(preciom ~ areaconst + parqueaderos + banios + habitaciones,
data = entrenamiento)
summary(modelo_ajus_2)
##
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + banios + habitaciones,
## data = entrenamiento)
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.0248 -0.1900 -0.0123 0.1585 3.6411
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.004779 0.007986 -0.598 0.55
## areaconst 0.498804 0.012615 39.539 <2e-16 ***
## parqueaderos 0.274509 0.011298 24.298 <2e-16 ***
## banios 0.251650 0.012815 19.637 <2e-16 ***
## habitaciones -0.110864 0.009179 -12.078 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.4772 on 3567 degrees of freedom
## Multiple R-squared: 0.7647, Adjusted R-squared: 0.7644
## F-statistic: 2897 on 4 and 3567 DF, p-value: < 2.2e-16
El modelo parece estar prediciendo razonablemente bien; los errores absolutos son relativamente pequeños y aparentemente no hay sesgo en el modelo. Es importante considerar la distribución de errores en todo el conjunto de prueba. y para tener una visión global y general del rendimiento modelo, se evaluarán la métricas como MAE, RMSE
predicciones = predict(modelo_ajus_2, newdata = prueba)
head(predicciones)
## 2 3 7 8 15 20
## -0.28700994 -0.38714060 -0.02036681 -0.86725120 -0.81690970 -0.94496166
comparacion = data.frame(Real = prueba$preciom, Predicho = predicciones)
head(comparacion)
## Real Predicho
## 2 -0.43891778 -0.28700994
## 3 -0.50806944 -0.38714060
## 7 0.06243174 -0.02036681
## 8 -0.92297939 -0.86725120
## 15 -0.68094859 -0.81690970
## 20 -0.81925190 -0.94496166
comparacion$Error_Absoluto = abs(comparacion$Real - comparacion$Predicho)
head(comparacion)
## Real Predicho Error_Absoluto
## 2 -0.43891778 -0.28700994 0.15190784
## 3 -0.50806944 -0.38714060 0.12092884
## 7 0.06243174 -0.02036681 0.08279855
## 8 -0.92297939 -0.86725120 0.05572819
## 15 -0.68094859 -0.81690970 0.13596111
## 20 -0.81925190 -0.94496166 0.12570976
Ahora, vamos a tener un supuesto de información para predecir el valor de una vivienda. En la actividad 2, hay unos datos que podríamos utilizar para ello. Estos serían los parámetros.
Condición 1: Área = 300 Parqueaderos = 2 Baños = 2 Habitaciones = 4
De acuerdo con el modelo de regresión lineal multiple, un apartamento con esas condiciones ronda los 159 millones de pesos. Sin embargo, hay que tener en cuenta que el modelo propuesto no incluye variables como el estrato y la ubicación por zona; aún así no deja de ser un excelente estimador para observar el movimiento de la vivienda en la ciudad de Cali.
# Dataframe para la condición
cond1 = data.frame(areaconst = 300, parqueaderos = 1, banios = 2, habitaciones = 4)
# Estandarizar la condición
cond1 = cond1 %>%
mutate(across(c("areaconst", "parqueaderos", "banios", "habitaciones"),
~ scale(. , center = attr(viviwork_final$areaconst, "scaled:center"),
scale = attr(viviwork_final$areaconst, "scaled:scale"))))
# Realizar la predicción
prediccion1 = predict(modelo_ajus_2, newdata = cond1)
# Recuperar los parámetros de estandarización
mean_areaconst = attr(viviwork_final$areaconst, "scaled:center")
sd_areaconst = attr(viviwork_final$areaconst, "scaled:scale")
# Deshacer la estandarización de la predicción
precio_predicho = as.numeric(prediccion1) * sd_areaconst + mean_areaconst
# Convertir a un dataframe con el valor en millones de pesos
resultados = data.frame(
"Condición" = "Predicción para la condición",
"Precio (Millones COP)" = format(precio_predicho, big.mark = ",", decimal.mark = ".", nsmall = 0)
)
colnames(resultados) = c("Condición", "Precio (Millones COP)")
resultados %>%
kable("html", caption = "Resultado de la Predicción", align = "c") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
full_width = FALSE,
position = "center",
font_size = 12)
| Condición | Precio (Millones COP) |
|---|---|
| Predicción para la condición | 159.3321 |
# Calcular el Error Cuadrático Medio (RMSE)
rmse = sqrt(mean((predicciones - prueba$preciom)^2))
# Calcular el Error Absoluto Medio (MAE)
mae = mean(abs(predicciones - prueba$preciom))
# Calcular R²
rss = sum((predicciones - prueba$preciom)^2)
tss = sum((prueba$preciom - mean(prueba$preciom))^2)
r2 = 1 - (rss/tss)
cat("RMSE: ", rmse, "\n")
## RMSE: 0.4587054
cat("MAE: ", mae, "\n")
## MAE: 0.2846605
cat("R²: ", r2, "\n")
## R²: 0.8047607
En general, estos resultados sugieren que el modelo de regresión lineal múltiple tiene un buen desempeño tanto en términos de ajuste general como en precisión de predicción.
De acuerdo con el análisis realizado con base en el modelo de regresión lineal multiple, podemos concluir que:
Es necesario poder realizar una buena elección de las variables predictoras, al igua que un excelente pre procesamiento de los mismos. Para ello, tambien urge poder medir la relación que tienen entre ellas, pues para este modelo resultó siendo un reto incluir variables categóricas como la zona y el estrato por su multicolinealidad. Aún así, el modelo logro tener un buen rendimiento predictivo.
Variable como el area, es de las que mayor ponderación tiene en torno al aumento relativo del valor del precio. De igual forma, variables predictoras como los baños, parqueaderos y habitaciones incrementan positivamente en relación al modelo. Algo inusual en el desarrollo, se encontró con la variable estrato cuyos coeficientes eran negativos.
En general, se consolida la idea general de conclusión que se ha venido desarrollando con los diferentes modelos trabajados en esta base de datos.