Se hace el filtro sobre la base de datos para obtener unicamente los datos de apartamentos y se muestran los 3 primeros registros. Se retira la variable id pues no se va a utilizar dentro del modelo. Adicionalmente se retiran datos que tengan habitaciones en 0 o baños en 0 pues son datos atípicos.
data(vivienda)
apartamentos1 <- subset(vivienda, tipo == 'Apartamento' | tipo == 'apartamento')
apartamentos1 <- subset(apartamentos1, !is.na(apartamentos1$id))
apartamentos1 <- subset(apartamentos1, !is.na(apartamentos1$parqueaderos))
apartamentos1 <- subset(apartamentos1, !is.na(apartamentos1$piso))
apartamentos1 <- subset(apartamentos1, habitaciones != 0)
apartamentos1 <- subset(apartamentos1, banios != 0)
apartamentos <- apartamentos1 %>% select(-id)
summary(apartamentos)
## zona piso estrato preciom
## Length:3171 Length:3171 Min. :3.000 Min. : 58
## Class :character Class :character 1st Qu.:4.000 1st Qu.: 220
## Mode :character Mode :character Median :5.000 Median : 300
## Mean :4.887 Mean : 390
## 3rd Qu.:6.000 3rd Qu.: 450
## Max. :6.000 Max. :1900
## areaconst parqueaderos banios habitaciones
## Min. : 40 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.: 75 1st Qu.:1.000 1st Qu.:2.000 1st Qu.:3.000
## Median : 95 Median :1.000 Median :2.000 Median :3.000
## Mean :117 Mean :1.548 Mean :2.754 Mean :3.032
## 3rd Qu.:135 3rd Qu.:2.000 3rd Qu.:3.000 3rd Qu.:3.000
## Max. :932 Max. :7.000 Max. :7.000 Max. :7.000
## tipo barrio longitud latitud
## Length:3171 Length:3171 Min. :-76.59 Min. :3.334
## Class :character Class :character 1st Qu.:-76.54 1st Qu.:3.378
## Mode :character Mode :character Median :-76.53 Median :3.414
## Mean :-76.53 Mean :3.417
## 3rd Qu.:-76.52 3rd Qu.:3.452
## Max. :-76.46 Max. :3.498
head(apartamentos, 3)
## # A tibble: 3 × 12
## zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 Zona N… 01 5 260 90 1 2 3 Apar…
## 2 Zona N… 01 5 240 87 1 3 3 Apar…
## 3 Zona N… 01 4 220 52 2 2 3 Apar…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
En esta gráfica vemos que todos los datos son de tipo apartamento:
También vemos la cantidad de apartamentos por estrato
ggplot(apartamentos, aes(x = estrato)) +
geom_bar(width=0.5, colour="black", fill="red") +
labs(x="Estrato",y= "Cantidad") +
ylim(c(0, 1500)) +
theme_bw(base_size = 12) +
geom_text(aes(label=after_stat(count)), stat='count',
position=position_dodge(0.9),
vjust=-0.5,
size=5.0
) +
facet_wrap(~"Apartamentos por Estrato")
# Convertir variables categóricas en factores
apartamentos$zona <- as.factor(apartamentos$zona)
apartamentos$tipo <- as.factor(apartamentos$tipo)
apartamentos$piso <- as.factor(apartamentos$piso)
apartamentos$barrio <- as.factor(apartamentos$barrio)
summary(apartamentos)
## zona piso estrato preciom areaconst
## Zona Centro : 4 03 :480 Min. :3.000 Min. : 58 Min. : 40
## Zona Norte : 627 04 :476 1st Qu.:4.000 1st Qu.: 220 1st Qu.: 75
## Zona Oeste : 667 05 :448 Median :5.000 Median : 300 Median : 95
## Zona Oriente: 17 02 :414 Mean :4.887 Mean : 390 Mean :117
## Zona Sur :1856 01 :348 3rd Qu.:6.000 3rd Qu.: 450 3rd Qu.:135
## 06 :231 Max. :6.000 Max. :1900 Max. :932
## (Other):774
## parqueaderos banios habitaciones tipo
## Min. :1.000 Min. :1.000 Min. :1.000 Apartamento:3171
## 1st Qu.:1.000 1st Qu.:2.000 1st Qu.:3.000
## Median :1.000 Median :2.000 Median :3.000
## Mean :1.548 Mean :2.754 Mean :3.032
## 3rd Qu.:2.000 3rd Qu.:3.000 3rd Qu.:3.000
## Max. :7.000 Max. :7.000 Max. :7.000
##
## barrio longitud latitud
## valle del lili: 527 Min. :-76.59 Min. :3.334
## la flora : 211 1st Qu.:-76.54 1st Qu.:3.378
## ciudad jardín : 172 Median :-76.53 Median :3.414
## santa teresita: 154 Mean :-76.53 Mean :3.417
## pance : 149 3rd Qu.:-76.52 3rd Qu.:3.452
## los cristales : 107 Max. :-76.46 Max. :3.498
## (Other) :1851
De acuerdo al resumen de los datos, se encuentra que la mayor parte de los apartamentos se encuentra en el sur de la ciudad, seguido casi que en igual proporción por la zona oeste y la zona norte.
A continuación se grafica el precio contra el área construida por zona:
plot1 <- ggplot(apartamentos, aes(x = areaconst, y = preciom)) +
geom_point(aes(color = zona)) +
labs(title = "Precio vs Área Construida por Zona",
x = "Área Construida (m2)",
y = "Precio (millones)") +
theme_minimal()
ggplotly(plot1)
En la gráfica podemos ver que la mayor parte de los datos se concentran entre los 250 m^2 y los 1000 millones de pesos, siendo más comunes los apartamentos de menor área y ubicados en el sur. Los que se encuentran en la zona oeste son apartamentos mucho más costosos y con mayor área construida, lo cual es algo consistente pues en la zona oeste se encuentran los estratos más altos.
La siguiente gráfica de cajas y bigotes nos muestra la distribución de precios por estrato y zona.
plot2 <- ggplot(apartamentos, aes(x = estrato, y = preciom)) +
geom_boxplot(aes(fill = zona)) +
labs(title = "Distribución de Precios por Estrato y Zona",
x = "Estrato",
y = "Precio (millones)") +
theme_minimal()
ggplotly(plot2)
Se observa que la mayor parte de apartamentos del sur están ubicados en zonas de estrato 4, asi como también los del norte y centro. Mientras que en el oeste se observan la mayoría de los apartamentos de estrato 5. Se observan unos valores atípicos con precios mayores a 900 millones para apartamentos de estrato 5, esto puede deberse a que son apartamentos más grandes, con muchas habitaciones y/o parqueaderos, lo que hace que el precio aumente considerablemente.
La siguiente gráfica nos muestra el precio por baños y habitaciones.
plot3 <- ggplot(apartamentos, aes(x = banios, y = preciom)) +
geom_point(aes(color = factor(habitaciones))) +
labs(title = "Precio vs Número de Baños por Habitaciones",
x = "Número de Baños",
y = "Precio (millones)",
color = "Habitaciones") +
theme_minimal()
ggplotly(plot3)
En esta gráfica podemos ver que un dato que muestran los apartamentos de mayor precio es que tienen muchos baños, en algunos casos más baños que habitaciones, lo cual podría ser algo un poco raro pero no imposible, pues puede ser que cada habitación tiene baños y hay algunos que no están dentro de ninguna habitación.
Analisis correlacional
cor(apartamentos[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")], use="complete.obs")
## preciom areaconst estrato banios habitaciones
## preciom 1.0000000 0.8252069 0.6347688 0.7382931 0.3152145
## areaconst 0.8252069 1.0000000 0.5261113 0.7228843 0.4246333
## estrato 0.6347688 0.5261113 1.0000000 0.5777300 0.1968992
## banios 0.7382931 0.7228843 0.5777300 1.0000000 0.5384881
## habitaciones 0.3152145 0.4246333 0.1968992 0.5384881 1.0000000
Revisando la correlación entre las variables, podemos ver que hay una relación alta entre el número de baños y el precio asi como también con el área construida. También hay una relación con el estrato , y vemos que el número de habitaciones tiene una correlación muy baja con las otras variables.
# Modelo de regresión lineal múltiple
modelo <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = apartamentos)
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = apartamentos)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1689.35 -53.94 0.84 47.89 956.31
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -259.50257 17.80305 -14.576 <2e-16 ***
## areaconst 1.99914 0.05575 35.861 <2e-16 ***
## estrato 50.40145 3.43888 14.656 <2e-16 ***
## habitaciones -44.01795 4.42492 -9.948 <2e-16 ***
## parqueaderos 94.87201 4.96850 19.095 <2e-16 ***
## banios 56.61095 3.89484 14.535 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 132 on 3165 degrees of freedom
## Multiple R-squared: 0.7876, Adjusted R-squared: 0.7873
## F-statistic: 2347 on 5 and 3165 DF, p-value: < 2.2e-16
Interpretación de coeficientes del modelo
El coeficiente R^2 está dando un valor alto, lo cual nos indica que el modelo explica bien la variabilidad del precio. El ajustado también da un valor mayor a 0.7 lo cual nos dice que el modelo tiene un buen ajuste.
El modelo en general se ve bien, a excepción del caso de la variable habitaciones, pues no es coherente con el comportamiento esperado. Al revisar la colinealidad no se observa una relación significativa de esta variable con las demás. Es posible que esta variable tenga que tener alguna transformación para capturar mejor la relación con el precio. También podría haber apartamentos con un número de habitaciones inusualmente alto pero con un precio más bajo por otros factores no considerados en el modelo (como la ubicación o el estado de la propiedad). Esto podría sesgar el coeficiente hacia lo negativo.
Linealidad:
plot(fitted(modelo), rstandard(modelo))
abline(h = 0, col = "red")
La gráfica muestra una dispersión aleatoria alrededor de la línea horizontal, la forma en que esta disperso parece sugerir una relación no lineal entre las variables, ya que la distribución tiene una forma cónica. En ese caso para dar solución a problemas con la linealidad, se pueden aplicar transformaciones a la variable de respuesta o a las predictoras para poder facilitar la estabilización del modelo.
Independencia de errores:
Se aplica la prueba de Durbin-Watson:
dwtest(modelo)
##
## Durbin-Watson test
##
## data: modelo
## DW = 1.5891, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
El análisis muestra que hay autocorrelación positiva en los errores del modelo de regresión, lo cual sugiere que los errores no son completamente independientes, y esto podría indicar la necesidad de ajustar el modelo para tener en cuenta la dependencia en los errores. Para solucionar esto se podría revisar la estructura de los datos, o agregar variables omitidas (por ejemplo el barrio o la ubicación en latitud y longitud), o utilizar otros tipos de modelos que si permitan errores correlacionados.
Normalidad de los errores:
qqnorm(residuals(modelo))
qqline(residuals(modelo), col = "blue")
shapiro.test(residuals(modelo))
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo)
## W = 0.83091, p-value < 2.2e-16
La prueba de Shapiro-Wilk indica que los residuos del modelo de regresión no son normales, lo cual puede afectar la validez de las inferencias estadísticas. Para solucionar esto se debe considerar realizar transformaciones de las variables (como logaritmos, raíces cuadradas, etc.) a las variables independientes o a la variable dependiente para mejorar la normalidad de los residuos, o examinar la influencia de posibles valores atípicos para abordar este problema.
Homoscedasticidad:
Para comprobar si la varianza de los residuos es constante a lo largo de los valores ajustados se realiza la prueba de Breush-Pagan, graficando los residuos estandarizados vs. los valores ajustados.
plot(fitted(modelo), rstandard(modelo))
abline(h = 0, col = "purple")
bptest(modelo)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 975.56, df = 5, p-value < 2.2e-16
El resultado de la prueba de Breusch-Pagan indica que los errores del modelo de regresión no tienen varianza constante, lo que significa que el supuesto de homocedasticidad se ha violado. Esto es conocido como heterocedasticidad. Para solucionarlo se puede intentar aplicar transformaciones a las variables dependientes o independientes para reducir la heterocedasticidad. Por ejemplo, la transformación logarítmica es común en estos casos. También el explorar si la heterocedasticidad está asociada con alguna de las variables independientes puede ofrecer pistas para ajustar el modelo.
Calcular los valores de inflación de la varianza para comprobar si existe multicolinealidad entre las variables independientes:
vif(modelo)
## areaconst estrato habitaciones parqueaderos banios
## 2.601095 1.690200 1.469298 2.248557 2.987498
En este caso los valores dieron menores a 5 por lo tanto esto indica que no hay problemas de multicolinealidad.
set.seed(123) # Para hacer la partición reproducible
# Crear una partición con 70% de los datos para entrenamiento
train_index <- createDataPartition(apartamentos$preciom, p = 0.7, list = FALSE)
# Dividir los datos en conjunto de entrenamiento y prueba
train_data <- apartamentos[train_index, ]
test_data <- apartamentos[-train_index, ]
Se divide la base de datos en 70% para entrenamiento y 30% para pruebas, y se evalua el modelo con los datos de entrenamiento:
# Ajustar el modelo de regresión lineal múltiple con los datos de entrenamiento
modelo <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data)
# Mostrar un resumen del modelo ajustado
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = train_data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1569.79 -53.99 0.51 47.46 771.67
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -265.41893 20.78684 -12.769 < 2e-16 ***
## areaconst 1.85664 0.06198 29.957 < 2e-16 ***
## estrato 50.10041 4.05715 12.349 < 2e-16 ***
## habitaciones -39.10300 5.26743 -7.424 1.62e-13 ***
## parqueaderos 96.71814 5.83446 16.577 < 2e-16 ***
## banios 57.96920 4.54865 12.744 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 129.1 on 2216 degrees of freedom
## Multiple R-squared: 0.7887, Adjusted R-squared: 0.7882
## F-statistic: 1654 on 5 and 2216 DF, p-value: < 2.2e-16
Se realizan 6 predicciones utilizando los datos de prueba aplicados en el modelo definido:
# Hacer predicciones en el conjunto de prueba
predicciones <- predict(modelo, newdata = test_data)
# Mostrar las primeras 6 predicciones
head(predicciones)
## 1 2 3 4 5 6
## 223.5938 450.3749 385.5349 368.3829 128.7612 208.5679
# Calcular los errores de predicción
errores <- test_data$preciom - predicciones
# Calcular el Error Cuadrático Medio (MSE)
mse <- mean(errores^2)
# Calcular el Error Absoluto Medio (MAE)
mae <- mean(abs(errores))
# Calcular el R-cuadrado (R²)
r2 <- caret::postResample(pred = predicciones, obs = test_data$preciom)[2]
# Mostrar los resultados
cat("Error Cuadrático Medio (MSE):", mse, "\n")
## Error Cuadrático Medio (MSE): 19421.57
cat("Error Absoluto Medio (MAE):", mae, "\n")
## Error Absoluto Medio (MAE): 84.59011
cat("R-cuadrado (R²):", r2, "\n")
## R-cuadrado (R²): 0.7877905
Interpretación: Un MSE de 19,421.57 indica que, en promedio, el cuadrado de las diferencias entre los valores reales y predichos es 19,421.57. Como el MSE está en una escala diferente a la variable objetivo (precio), es difícil interpretarlo en términos absolutos. Sin embargo, este valor puede servir para comparar diferentes modelos: cuanto menor sea, mejor se ajusta el modelo.
Un MAE de 84.59 indica que, en promedio, las predicciones del modelo se desvían del valor real del precio de los apartamentos en 84.59 unidades monetarias (por ejemplo, millones de pesos, ya que se supone que esa es la unidad de medida). Esto sugiere que el modelo tiene una precisión razonable, aunque se debe considerar si este margen de error es aceptable según el contexto.
Un R² de 0.7878 significa que el modelo explica aproximadamente el 78.78% de la variabilidad en el precio de los apartamentos. Esto indica que el modelo captura la mayor parte de la variabilidad, pero aún queda un 21.22% que no está siendo explicado por las variables incluidas en el modelo.
En general, los resultados sugieren un modelo razonablemente bueno, pero con posibilidades de mejora.