La inmobiliara A&C requiere la construcción de un modelo estadistico que ayude a estimar los precios de las viviendas ofrecidas. Para este propósito, dispone de datos recopilados de apartamentos ubicados en diferentes zonas de la ciudad, obtenidos de la plataforma Fincaraíz. Estos datos incluyen variables relevantes como el área construida en metros cuadrados, la ubicación de la zona de la vivienda, estrato, numero de baños, numero de habitaciones y su respectivo precio.
De acuerdo a este objetivo en este informe se pretende ajustar un modelo robusto y preciso que pueda ayudar a la inmobiliaria A&C a determinar los precios de los inmuebles a partir de multiples variables.
Inicialmente cargamos las librerias utilizadas en la estimación de los modelos a probar y procedemos a cargar la base de datos, la cual se encuentra en “paqueteMODELOS” con el nombre de vivienda.
Identificamos registros que corresponden a casas por lo que nos quedamos solo con los apartamentos:
bd_aptos <- subset(bd_viviendas, tipo == "Apartamento")
Con los datos filtrados procedemos a realizar el análisis exploratorio de las variables. Inicialmente visualizamos 3 registros de la base de datos:
head(bd_aptos,3)
Realizamos un resumen de toda la base de datos:
# Convertir la variable 'estrato' a character
bd_aptos$estrato <- as.character(bd_aptos$estrato)
# Convertir la variable 'piso' a numerico
bd_aptos$piso <- as.numeric(bd_aptos$piso)
# Seleccionar todas las variables categóricas
categoricas <- bd_aptos %>% select_if(is.character)%>% select(-barrio)
# Calcular y mostrar las frecuencias para cada variable categórica
for (var in names(categoricas)) {
cat("Frecuencias para la variable:", var, "\n")
print(bd_aptos %>% count(!!sym(var)))
cat("\n")
}
## Frecuencias para la variable: zona
## zona n
## 1 Zona Centro 24
## 2 Zona Norte 1198
## 3 Zona Oeste 1029
## 4 Zona Oriente 62
## 5 Zona Sur 2787
##
## Frecuencias para la variable: estrato
## estrato n
## 1 3 639
## 2 4 1404
## 3 5 1766
## 4 6 1291
##
## Frecuencias para la variable: tipo
## tipo n
## 1 Apartamento 5100
# Descripción de variables
skim_numeric <- bd_aptos %>% skim() %>% yank("numeric")
knit_print(skim_numeric)
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| id | 0 | 1.00 | 4284.03 | 2449.82 | 3.00 | 2179.75 | 4158.50 | 6556.25 | 8317.00 | ▆▇▆▆▇ |
| piso | 1381 | 0.73 | 4.63 | 2.81 | 1.00 | 2.00 | 4.00 | 6.00 | 12.00 | ▇▆▂▂▂ |
| preciom | 0 | 1.00 | 366.94 | 289.22 | 58.00 | 175.00 | 279.00 | 430.00 | 1950.00 | ▇▂▁▁▁ |
| areaconst | 0 | 1.00 | 112.78 | 69.36 | 35.00 | 68.00 | 90.00 | 130.00 | 932.00 | ▇▁▁▁▁ |
| parqueaderos | 869 | 0.83 | 1.57 | 0.74 | 1.00 | 1.00 | 1.00 | 2.00 | 10.00 | ▇▁▁▁▁ |
| banios | 0 | 1.00 | 2.62 | 1.07 | 0.00 | 2.00 | 2.00 | 3.00 | 8.00 | ▁▇▂▁▁ |
| habitaciones | 0 | 1.00 | 2.97 | 0.68 | 0.00 | 3.00 | 3.00 | 3.00 | 9.00 | ▁▇▂▁▁ |
| longitud | 0 | 1.00 | -76.53 | 0.02 | -76.59 | -76.54 | -76.53 | -76.52 | -76.46 | ▁▅▇▂▁ |
| latitud | 0 | 1.00 | 3.42 | 0.04 | 3.33 | 3.38 | 3.42 | 3.45 | 3.50 | ▂▇▅▇▅ |
Inicialmente, podemos identificar que la base de datos tiene 5100 registros y 13 variables, 5 de ellas son categoricas (incluyendo el estrato) y 8 son numericas. Evidenciamos tambien que las que más tienen datos perdidos corresponden a piso y parqueaderos. En la variable “zona”, se observa que la mayoría de los apartamentos se encuentran ubicados en la zona sur de la ciudad, con un total de 2.787 viviendas ofertadas, mientras que la zona centro muestra la menor oferta, con tan solo 24 viviendas disponibles. En cuanto al precio, se destaca que el valor promedio de la oferta es de 366 millones de pesos. En relación al área construida en metros cuadrados, se evidencia que el \(50\%\) de las viviendas tienen un área construida comprendida entre \(68\) y \(130 m^{2}\), según el primer y tercer cuartil. El tamaño promedio de las viviendas es de \(112 m^{2}\).
Para profundizar en la relación entre la variable respuesta “Precio de la vivienda” en función de las variables predictoras, se realiza un análisis bivariado:
Inicialmente vemos un gráfico de dispersión entre el precio y el área de la vivienda
fig1 <- plot_ly(data = bd_aptos, x = ~areaconst, y = ~preciom, type = 'scatter', mode = 'markers') %>%
layout(title = "Precio vs Área Construida",
xaxis = list(title = "Área de la Vivienda (m²)"),
yaxis = list(title = "Precio (COP)"))
fig1
Gráficamente, se evidencia una relación positiva entre estas dos variables, dado que cuando el área de la vivienda es mayor, también aumenta el precio de las viviendas. Ahora validaremos esta observación con la estimación de la correlación de pearson.
correlation <- cor(bd_aptos$areaconst, bd_aptos$preciom)
cat("La correlación entre el área y el precio de la vivienda es:", correlation)
## La correlación entre el área y el precio de la vivienda es: 0.8287437
Se evidencia una correlación positiva fuerte de \(0.8287\) entre la variable precio y área. Esto indica que conforme aumenta el área de un apartamento, también aumenta su precio.Sin embargo, a pesar de la fuerza de esta relación, es importante destacar que podemos realizar una prueba de hipótesis para determinar si la correlación entre estas variables es estadísticamente significativa.
cor.test(bd_aptos$areaconst,bd_aptos$preciom, method = "pearson")
##
## Pearson's product-moment correlation
##
## data: bd_aptos$areaconst and bd_aptos$preciom
## t = 105.74, df = 5098, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.8199479 0.8371482
## sample estimates:
## cor
## 0.8287437
El valor p obtenido es menor que \(2.2e -16\), prácticamente cero. Esto sugiere que la correlación de \(0.8287\) entre el área construida y el precio es estadísticamente significativa. En otras palabras, podemos rechazar la hipótesis nula de que no hay correlación entre estas dos variables.
fig2 <- plot_ly(data = bd_aptos, y = ~preciom, x = ~as.factor(estrato), type = 'box') %>%
layout(title = "Distribución del Precio por Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (COP)"))
fig2
Se puede evidenciar que a medida que incrementa el estrato socioeconómico, tamien la mediana del precio de la vivienda es mas alto.
fig3 <- plot_ly(data = bd_aptos, y = ~preciom, x = ~as.factor(banios), type = 'box') %>%
layout(title = "Distribución del Precio por Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (COP)"))
fig3
En el caso de las variable numero de baños y precio se puede evidenciar que a medida que incrementa el número de baños de las viviendas, tamien la mediana del precio de la vivienda es mas alto.
fig4 <- plot_ly(data = bd_aptos, y = ~preciom, x = ~as.factor(habitaciones), type = 'box') %>%
layout(title = "Distribución del Precio por Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (COP)"))
fig4
El gráfico muestra una relación entre el número de habitaciones de una propiedad y su precio. A mayor cantidad de habitaciones, mayor tiende a ser el valor de la vivienda. Sin embargo, se observa una considerable variabilidad en los precios, incluso para propiedades con el mismo número de habitaciones, lo que sugiere que otros factores como la ubicación, estrato u otrasvariables influyen significativamente en el precio final. Es decir, aunque el número de habitaciones es un factor importante, no es el único que determina el valor de una propiedad.
Ahora analizamos como es el comportamiento del precio de las viviendas segun la zona en la que se encuentran:
fig5 <- plot_ly(data = bd_aptos, y = ~preciom, x = ~zona, type = 'box') %>%
layout(title = "Distribución del Precio por Zona",
xaxis = list(title = "Zona"),
yaxis = list(title = "Precio (COP)"))
fig5
A pesar de que el numero de registros de cada zona es muy variable, teneniendo el mayor numero de registros en la zona sur y en la zona norte, se evidencia que los apartamentos de la zona Oeste son los que tienen los precios mas altos, seguidos por los apartamentos en la zona Norte.
# Filtrar sólo las variables numéricas para calcular la correlación
numerical_vars <- bd_aptos %>% select(preciom, areaconst, banios, habitaciones, parqueaderos)
correlation_matrix <- cor(numerical_vars, use = "complete.obs")
# Convertir la matriz en formato largo para su visualización
correlation_data <- as.data.frame(as.table(correlation_matrix))
# Heatmap de la matriz de correlación
fig6 <- plot_ly(z = correlation_data$Freq,
x = correlation_data$Var1,
y = correlation_data$Var2,
type = "heatmap") %>%
layout(title = "Heatmap de Correlaciones")
fig6
El heatmap de correlaciones revela una relación directa y significativa entre el precio de una propiedad y ciertas características específicas. Destacamos que las correlaciones más altas se encuentran entre el precio y el área construida, así como entre el precio y el número de parqueaderos. Esto indica que, a medida que aumenta el tamaño de una propiedad o la cantidad de parqueaderos, su valor tiende a elevarse de manera considerable. Por otro lado, las correlaciones mas bajas se presentan entre precio y número de habitaciones.
Se estima el modelo de regresión lineal múltiple entre \(precio=f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños )\), pero antes eliminamos filas con valores faltantes en las variables de interés:
# Convertir estrato en factor
bd_aptos$estrato <- as.factor(bd_aptos$estrato)
# Eliminar filas con valores faltantes en las variables de interés
bd_aptos_limpio <- bd_aptos %>%
select(preciom, areaconst, estrato, habitaciones, parqueaderos, banios) %>%
na.omit()
# Estimar el modelo de regresión lineal múltiple
modelo <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = bd_aptos_limpio)
# Resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = bd_aptos_limpio)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1635.50 -53.91 0.24 46.02 980.73
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -59.95783 12.43453 -4.822 1.47e-06 ***
## areaconst 1.96798 0.04726 41.644 < 2e-16 ***
## estrato4 25.53268 8.92944 2.859 0.00427 **
## estrato5 42.64309 8.77814 4.858 1.23e-06 ***
## estrato6 175.61949 10.25108 17.132 < 2e-16 ***
## habitaciones -37.98467 3.73083 -10.181 < 2e-16 ***
## parqueaderos 82.60882 4.08240 20.235 < 2e-16 ***
## banios 49.66742 3.35468 14.805 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 134.3 on 4223 degrees of freedom
## Multiple R-squared: 0.7953, Adjusted R-squared: 0.7949
## F-statistic: 2344 on 7 and 4223 DF, p-value: < 2.2e-16
### Coeficientes del modelo
#modelo$coefficients
Obtenemos la siguiente ecuación:
\(\widehat{preciom}_i = -59.95 + (1.97 \times areaconst_i) + (25.53 \times estrato4_i) + (42.64 \times estrato5_i) + (175.61 \times estrato6_i) -(37.98 \times habitaciones_i) + (82.61 \times parqueaderos_i) + (49.67 \times banios_i)\)
Donde todas las variables son estadísticamente significativas ya que el valor p es menor a un \(\alpha =0,001\), por ende sus interpretaciones serían:
El valor del R cuadrado ajustado indica que el modelo tiene un ajuste sólido, lo que sugiere que las variables incluidas (área construida, estrato, número de habitaciones, número de parqueaderos y número de baños) son relevantes y capturan la mayor parte de los factores clave que influyen en el precio de las casas. Sin embargo, se podrían considerar otras variables, como la zona, para evaluar su impacto en el R cuadrado ajustado y mejorar la capacidad explicativa del modelo. Además, es posible que algunas variables presenten relaciones no lineales con el precio, por lo que sería útil explorar transformaciones como logaritmos o polinomios para capturar estas posibles interacciones de manera más precisa.
Prueba de normalidad de Shapiro-Wilk:
# Residuales para modelo
residuos<- residuals(modelo)
# Crear un dataframe con los residuos
resi <- data.frame(residuos = residuos)
# Test de Shapiro - Wilk para modelo1
shapiro <- shapiro.test(residuos)
print(shapiro)
##
## Shapiro-Wilk normality test
##
## data: residuos
## W = 0.83994, p-value < 2.2e-16
Esta prueba se utiliza para determinar si una muestra de datos se ajusta a una distribución normal. En este caso, el valor p es muy pequeño (2.2e-16), lo que indica que podemos rechazar la hipótesis nula de que los residuos siguen una distribución normal. Por lo tanto, los residuos del modelo no parecen estar normalmente distribuidos
Prueba de Breusch-Pagan:
# Prueba Breusch-Pagan para la homocedasticidad para modelo
bptest <- bptest(modelo)
print(bptest)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 1254.8, df = 7, p-value < 2.2e-16
Esta prueba se utiliza para detectar la heterocedasticidad en un modelo de regresión lineal. En este caso, el valor p es muy pequeño (2.2e-16), lo que indica que podemos rechazar la hipótesis nula de homocedasticidad. Por lo tanto, parece haber heterocedasticidad en los residuos del modelo, lo que significa que la varianza de los errores no es constante.
Prueba de Durbin-Watson:
# Prueba Durbin-Watson para la no autocorrelación para modelo
dwtest <- dwtest(modelo)
print(dwtest)
##
## Durbin-Watson test
##
## data: modelo
## DW = 1.7189, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
Esta prueba se utiliza para detectar la presencia de autocorrelación en los residuos de un modelo de regresión. En este caso, el valor p es pequeño (2.2e-16), lo que indica que podemos rechazar la hipótesis nula de que no hay autocorrelación en los residuos. Por lo tanto, parece haber autocorrelación en los residuos del modelo.
Aplicar Transformaciones a las Variables: Es recomendable probar transformaciones en las variables dependientes o independientes. Por ejemplo, usar una transformación logarítmica puede mejorar la normalidad de los residuos y ayudar a estabilizar la varianza, lo que facilita cumplir con los supuestos del modelo.
Identificar y Manejar Observaciones Influyentes: Es importante detectar observaciones que influyen en exceso en el modelo, conocidas como puntos influyentes. Estas pueden ser identificadas mediante herramientas como la distancia de Cook, y luego se puede evaluar si deben ser eliminadas o ajustadas para reducir su impacto.
Considerar Modelos Alternativos: Si después de realizar las correcciones necesarias, los supuestos de la regresión lineal no se cumplen, es útil explorar otros modelos, como regresiones no lineales u otros métodos robustos, que no dependan estrictamente de los mismos supuestos.
A continuación, realizamos una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba y estimamos el modelo con la muestra del 70%.
set.seed(123)
y <- bd_aptos_limpio$preciom
# Crear la partición de los datos
particion <- createDataPartition(y, p = 0.7, list = FALSE,times = 1)
# Dividir en set de entrenamiento (70%) y prueba (30%)
train_data <- bd_aptos_limpio[particion, ] # 70% para entrenamiento
test_data <- bd_aptos_limpio[-particion, ] # 30% para prueba
Ajustamos el modelo de regresión lineal con el set de entrenamiento:
# Estimar el modelo con los datos de entrenamiento
modelo_train <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data)
# Mostrar los resultados del modelo
summary(modelo_train)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1526.70 -52.98 0.27 43.85 805.82
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -72.55412 14.79477 -4.904 9.90e-07 ***
## areaconst 1.83119 0.05484 33.389 < 2e-16 ***
## estrato4 26.18334 10.53876 2.484 0.013 *
## estrato5 45.92164 10.35592 4.434 9.57e-06 ***
## estrato6 173.15292 12.12014 14.286 < 2e-16 ***
## habitaciones -34.43228 4.49558 -7.659 2.52e-14 ***
## parqueaderos 81.85229 4.72988 17.305 < 2e-16 ***
## banios 55.70271 3.97284 14.021 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 133.6 on 2956 degrees of freedom
## Multiple R-squared: 0.7871, Adjusted R-squared: 0.7866
## F-statistic: 1561 on 7 and 2956 DF, p-value: < 2.2e-16
Ahora, usamos los datos de prueba para predecir el precio
# Realizar predicciones con los datos de prueba
predicciones <- predict(modelo_train, newdata = test_data)
# Crear un data frame con los valores reales y las predicciones
resultados_comparacion <- data.frame(
Real = test_data$preciom,
Predicho = predicciones
)
# Mostrar las primeras filas de la tabla
head(resultados_comparacion)
# Calcular el error cuadrático medio (RMSE)
rmse <- sqrt(mean((test_data$preciom - predicciones)^2))
cat("El RMSE del modelo es:", rmse)
## El RMSE del modelo es: 136.5155
mae <- mean(abs(test_data$preciom - predicciones))
mae <- mean(abs(test_data$preciom - predicciones))
cat("El MAE del modelo es:", mae)
## El MAE del modelo es: 84.00779
r2 <- cor(test_data$preciom, predicciones)^2
cat("El R-cuadrado del modelo es:", r2)
## El R-cuadrado del modelo es: 0.8120163