1. Realice un filtro a la base de datos e incluya sólo las ofertas de apartamentos. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta.

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")    

2. Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio de la casa) en función del área construida, estrato, número de baños, número de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

# 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.

3. Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber estar contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R² y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

# 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.

4. Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

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.

5. Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime el modelo con la muestra del 70%. Muestre los resultados.

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

6. Realice predicciones con el modelo anterior usando los datos de prueba (30%).

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

7. Calcule el error cuadrático medio, el error absoluto medio y el R², interprete.

# 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.