Actividad 2

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.

head(data)
## # A tibble: 6 × 13
##      id zona        piso estrato preciom areaconst parquea banios habitac tipo  
##   <dbl> <chr>      <dbl>   <dbl>   <dbl>     <dbl>   <dbl>  <dbl>   <dbl> <chr> 
## 1  8312 Zona Oeste     4       6    1300       318       2      4       2 Apart…
## 2  8311 Zona Oeste     1       6     480       300       1      4       4 Casa  
## 3  8307 Zona Oeste    NA       5    1200       800       4      7       5 Casa  
## 4  8296 Zona Sur       2       3     220       150       1      2       4 Casa  
## 5  8297 Zona Oeste    NA       5     330       112       2      4       3 Casa  
## 6  8298 Zona Sur      NA       5    1350       390       8     10      10 Casa  
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
data$tipo = tolower(data$tipo)

data = data%>% mutate(tipo = ifelse(tipo == "apto","apartamento", tipo))
apartamento_df= subset(data, data$tipo=="apartamento")
head(apartamento_df, 3)
## # A tibble: 3 × 13
##      id zona        piso estrato preciom areaconst parquea banios habitac tipo  
##   <dbl> <chr>      <dbl>   <dbl>   <dbl>     <dbl>   <dbl>  <dbl>   <dbl> <chr> 
## 1  8312 Zona Oeste     4       6    1300       318       2      4       2 apart…
## 2  8299 Zona Sur       2       6     305       125       2      3       3 apart…
## 3  8300 Zona Oeste    NA       5     480       280       4      4       4 apart…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
pl = ggplot(apartamento_df, aes(x = zona, fill = tipo))
pl = pl + geom_bar(stat = "count")
pl

Con esta grafica podemos verificar que nuestra base de datos solo contiene apartamentos.

Verificar datos faltantes, imputacion de datos y eliminacion de variables que no se van a utilizar en el estudio.

faltantes = colSums(is.na(apartamento_df))
faltantes
##        id      zona      piso   estrato   preciom areaconst   parquea    banios 
##         0         0      1383         0         0         0       869         0 
##   habitac      tipo    barrio  longitud   latitud 
##         0         0         0         0         0

Tratamiento de datos faltantes

median_piso = median(apartamento_df$piso)
median_parquea = median(apartamento_df$parquea)
apartamento_df$piso[is.na(apartamento_df$piso)] = median_piso
apartamento_df$parquea[is.na(apartamento_df$parquea)] = median_parquea
faltantes2 = colSums(is.na(apartamento_df))
faltantes2
##        id      zona      piso   estrato   preciom areaconst   parquea    banios 
##         0         0         0         0         0         0         0         0 
##   habitac      tipo    barrio  longitud   latitud 
##         0         0         0         0         0

Eliminare las colunas id, barrio, longitud y latitud, ya que no se necesarias para este estudio. Tambien eliminare la columna Tipo ya que ha dejado de ser una variable y ya corroboramos que nuestra base de datos conitene solo apartamenetos.

apartamento_df = apartamento_df[,c(-1, -10, -11, -12, -13)] 
sapply(apartamento_df, class)
##        zona        piso     estrato     preciom   areaconst     parquea 
## "character"   "numeric"   "numeric"   "numeric"   "numeric"   "numeric" 
##      banios     habitac 
##   "numeric"   "numeric"

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, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

summary(apartamento_df)
##      zona                piso           estrato         preciom      
##  Length:5106        Min.   : 1.000   Min.   :3.000   Min.   :  58.0  
##  Class :character   1st Qu.: 3.000   1st Qu.:4.000   1st Qu.: 175.0  
##  Mode  :character   Median : 4.000   Median :5.000   Median : 280.0  
##                     Mean   : 4.463   Mean   :4.729   Mean   : 367.6  
##                     3rd Qu.: 5.000   3rd Qu.:6.000   3rd Qu.: 430.0  
##                     Max.   :12.000   Max.   :6.000   Max.   :1950.0  
##    areaconst        parquea           banios         habitac     
##  Min.   : 35.0   Min.   : 1.000   Min.   :0.000   Min.   :0.000  
##  1st Qu.: 68.0   1st Qu.: 1.000   1st Qu.:2.000   1st Qu.:3.000  
##  Median : 90.0   Median : 1.000   Median :2.000   Median :3.000  
##  Mean   :112.9   Mean   : 1.472   Mean   :2.619   Mean   :2.972  
##  3rd Qu.:130.0   3rd Qu.: 2.000   3rd Qu.:3.000   3rd Qu.:3.000  
##  Max.   :932.0   Max.   :10.000   Max.   :8.000   Max.   :9.000
summary_stats = aggregate(preciom~zona, data = apartamento_df, FUN = mean)
print(summary_stats)
##           zona  preciom
## 1  Zona Centro 186.5833
## 2   Zona Norte 285.2577
## 3   Zona Oeste 669.2676
## 4 Zona Oriente 152.5968
## 5     Zona Sur 297.3550

Variables Categoricas

Box Plot Precio por zona

boxplot(preciom ~ zona, data = apartamento_df, main = "Precio por Zona", xlab = "Zona", ylab = "Precio")

Box Plot precio / estrato

summary_stats2 = aggregate(preciom~estrato, data = apartamento_df, FUN = mean)
print(summary_stats2)
##   estrato  preciom
## 1       3 128.9765
## 2       4 206.9651
## 3       5 335.0475
## 4       6 703.7515
boxplot(preciom ~ estrato, data = apartamento_df, main = "Precio por estrato", xlab = "estrato", ylab = "Precio")

En el gráfico de precios por zona, se puede apreciar que el precio medio de los apartamentos en las zonas Centro, Norte y Sur se aproxima a los 200 millones, mostrando una notable similitud entre ellas. Por otro lado, la zona Oriente presenta el precio promedio más bajo, con un costo de 152 millones por apartamento. Además, las gráficas indican que en la zona Oeste se localizan los apartamentos más costosos, con un valor promedio de 669 millones.

En el gráfico de precios por estrato, se evidencia una relación directa entre el estrato y el valor del apartamento: a medida que se incrementa el estrato, también lo hace el precio.

Variables Numericas

Dot-Plot Area Construida Vs. Precio

fig1 = plot_ly(data = apartamento_df, 
               x = ~areaconst, 
               y = ~preciom, 
               type = 'scatter', 
               mode = 'markers') %>%
  layout(title = "Precio vs Área Construida")

fig1

Dot-Plot Piso Vs. Precio

fig2 = plot_ly(data = apartamento_df, 
               x = ~piso, 
               y = ~preciom, 
               type = 'scatter', 
               mode = 'markers') %>%
  layout(title = "Precio vs Piso")

fig2

Dot-Plot Parqueaderos Vs. Precio

fig3 = plot_ly(data = apartamento_df, 
               x = ~parquea, 
               y = ~preciom, 
               type = 'scatter', 
               mode = 'markers') %>%
  layout(title = "Precio vs Parqueaderos")

fig3

Dot-Plot Baños Vs. Precio

fig4 = plot_ly(data = apartamento_df, 
               x = ~banios, 
               y = ~preciom, 
               type = 'scatter', 
               mode = 'markers') %>%
  layout(title = "Precio vs Baños")

fig4

Dot-Plot Habitaciones Vs. Precio

fig5 = plot_ly(data = apartamento_df, 
               x = ~habitac, 
               y = ~preciom, 
               type = 'scatter', 
               mode = 'markers') %>%
  layout(title = "Precio vs Habitaciones")

fig5

Se puede observar una clara correlación lineal positiva entre el área construida, el precio, el número de parqueaderos y la cantidad de baños de un apartamento. Sin embargo, no se detecta ningún patrón entre el área construida y el piso, así como el número de habitaciones del apartamento.

A continuación, verificaremos si existe alguna correlación entre estas variables.

Analisis de correlacion (Variables cuantitativas)

corr = apartamento_df[, c("piso", "areaconst", "parquea", "banios", "habitac", "preciom")]
ggpairs(corr, title="GGally")

Podemos observar que la variable ‘preciom’ presenta una fuerte correlación lineal positiva con las variables ‘areaconst’ (r = 0.829), ‘parquea’ (r = 0.740) y ‘banios’ (r = 0.741). Por otro lado, no parece haber una correlación significativa entre ‘preciom’ y ‘piso’ (r = 0.192), ni con ‘habitac’ (r = 0.297).

En conclusión, la variable que más influye en el precio del apartamento es el área construida, seguida por el número de baños y parqueaderos. En cambio, las variables menos relevantes son ‘piso’ y ‘habitaciones’.

Por este motivo, no incluiremos las variables ‘piso’ y ‘habitaciones’ en nuestra regresión lineal múltiple.

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 están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

modelo = lm(preciom ~ areaconst + parquea + banios, data = apartamento_df)
summary(modelo)
## 
## Call:
## lm(formula = preciom ~ areaconst + parquea + banios, data = apartamento_df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1709.57   -51.10    -5.25    44.44  1084.21 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -180.52016    5.44533  -33.15   <2e-16 ***
## areaconst      2.04354    0.04453   45.89   <2e-16 ***
## parquea      115.06230    3.82759   30.06   <2e-16 ***
## banios        56.48425    2.76699   20.41   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 139.5 on 5102 degrees of freedom
## Multiple R-squared:  0.7688, Adjusted R-squared:  0.7687 
## F-statistic:  5656 on 3 and 5102 DF,  p-value: < 2.2e-16

Todos los t-values son considerablemente altos, lo que implica que la probabilidad de que estos resultados ocurran únicamente por casualidad es prácticamente cero. Por lo tanto, se rechaza la hipótesis nula que sostiene que el valor de Beta es igual a cero para cada una de las variables.

En esta primera estimación, se obtiene un coeficiente de determinación (R²) de 76.88%, lo que indica que el 76.88% de la variabilidad del precio de un apartamento en esta base de datos se explica mediante la correlación lineal múltiple.

Es lógico que el precio de un apartamento esté directamente relacionado con el área construida, así como con el número de comodidades adicionales, como un mayor número de baños y parqueaderos.

Estrategias para Mejorar el Modelo:

Incluir variables adicionales: Dado que hay un 23.12% de variabilidad no explicada, sería útil explorar otras variables que podrían impactar el precio, como la antigüedad del edificio o la cercanía a servicios públicos.

Transformaciones de variables: Si hay relaciones no lineales, transformar variables (como tomar logaritmos o raíces cuadradas) podría mejorar el ajuste del modelo.

Identificar y analizar posibles outliers en los datos puede ayudar a entender si hay apartamentos que tienen características inusuales que afectan el precio de manera desproporcionada.

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

par(mfrow=c(2,2))
plot(modelo)

# Check the length of residuals
n_residuals = length(modelo$residuals)
if (n_residuals < 3 || n_residuals > 5000) {
  cat("The sample size for the Shapiro-Wilk test should be between 3 and 5000. Current size:", n_residuals)
} else {
  shapiro_result <- shapiro.test(modelo$residuals)
  print(shapiro_result)
}
## The sample size for the Shapiro-Wilk test should be between 3 and 5000. Current size: 5106
# Set a seed for reproducibility
set.seed(123)

# Sample 5000 residuals randomly
sampled_residuals <- sample(modelo$residuals, size = 5000)

# Perform the Shapiro-Wilk test on the sampled residuals
shapiro_result <- shapiro.test(sampled_residuals)

# Print the result
print(shapiro_result)
## 
##  Shapiro-Wilk normality test
## 
## data:  sampled_residuals
## W = 0.83301, p-value < 2.2e-16

Ya que el p-val = 0 < 0.05. No se cumple el supuesto de normalidad de los residuos.

lmtest::bptest(modelo)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo
## BP = 1392.3, df = 3, p-value < 2.2e-16

Ya que el p-val = 0< 0.05. No se cumple el supuesto de homocedasticidad de los residuos.

lmtest::dwtest(modelo)
## 
##  Durbin-Watson test
## 
## data:  modelo
## DW = 1.7164, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

Ya que el p-val = 0 < 0.05. No se cumple el supuesto de no autocorrelación de errores de los residuos.

Conclusión: Dado que los supuestos de normalidad, homocedasticidad y no autocorrelación de los residuos no se cumplen, es importante revisar el modelo de regresión lineal múltiple utilizado. Se podria hacer una transformación de variables mediante el uso de potencias, raíces o logaritmos, lo cual puede ayudar a estabilizar la variabilidad de los residuos y mejorar el modelo.

Se podria optar por utilizar métodos estadísticos alternativos que sean más robustos ante estas violaciones de supuestos, tales como el modelos de mínimos cuadrados generalizados (GLS) o árboles de decisión.

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.

# crear indices aleatorios par la particion 

indices = sample(1:nrow(apartamento_df), size = 0.7*nrow(apartamento_df))
#crear conjuntos de entrenamiento y prueba
train_data = apartamento_df[indices, ]
test_data = apartamento_df[indices, ]
# estimar el modelo con la muestra de entrenamiento
modelo2 = lm(preciom ~ areaconst + parquea + banios, data = train_data)
summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + parquea + banios, data = train_data)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1559.67   -51.45    -6.11    45.22  1106.09 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -187.63851    6.61724  -28.36   <2e-16 ***
## areaconst      1.86677    0.05284   35.33   <2e-16 ***
## parquea      127.27188    4.70078   27.07   <2e-16 ***
## banios        59.73696    3.34156   17.88   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 141.7 on 3570 degrees of freedom
## Multiple R-squared:  0.7622, Adjusted R-squared:  0.762 
## F-statistic:  3814 on 3 and 3570 DF,  p-value: < 2.2e-16

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

# Realizaar predicciones con el modelo
predicciones = predict(modelo2, newdata = test_data)

# agregar predicciones al conjunto de prueba para comaracion 
resultados = data.frame(Real = test_data$preciom, Predicho = predicciones)
print(head(resultados))
##   Real Predicho
## 1 1350 989.7532
## 2  350 430.9260
## 3  480 623.2034
## 4  153 171.1133
## 5  160 169.2465
## 6  270 223.3827

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

Error Absoluto medio (MAE)

mae = mean((abs(resultados$Real - resultados$Predicho)))
cat("Error Absoluto Medio (MAE):", mae, "\n")
## Error Absoluto Medio (MAE): 85.26666

Este valor indica que, en promedio, las predicciones del modelo se desvían de los valores reales en aproximadamente 85.27 Millones de pesos.

Error Medio Cuadratico (RMSE)

rmse = sqrt(mean((resultados$Real - resultados$Predicho)^2))
cat("Error Cuadratico Medio (RMSE):", rmse, "\n")
## Error Cuadratico Medio (RMSE): 141.6249

El RMSE de aproximadamente 141.62 indica que la raíz cuadrada del promedio de las diferencias al cuadrado entre las predicciones y los valores reales es de 141.62 unidades. Este valor es más alto que el MAE, lo que puede sugerir que existen algunos errores significativamente grandes que están influyendo en el RMSE. Esto podría indicar que el modelo tiene algunos casos atípicos o predicciones que se desvían mucho de los valores reales.

R-squeared

r2 = 1 - (sum((resultados$Real - resultados$Predicho)^2)/ sum((resultados$Real - mean(resultados$Real))^2))
cat("R-Squared:", r2, "\n")
## R-Squared: 0.7621873

Un R² de 0.7621 significa que aproximadamente el 76.21% de la variabilidad en los datos reales se explica por el modelo. Este es un buen valor, ya que sugiere que el modelo tiene una capacidad razonable para predecir los resultados. Sin embargo, hay un 23.79% de la variabilidad que no se explica, lo que puede ser debido a factores no considerados en el modelo o a la naturaleza aleatoria de los datos.

Conclusion:

  • Los resultados indican que el modelo tiene un desempeño aceptable. El MAE y el RMSE son relativamente altos, lo que sugiere que hay espacio para mejorar en la precisión de las predicciones, tal vez a través de la optimización del modelo o la inclusión de variables adicionales.

  • El R² indica que el modelo es capaz de explicar una buena parte de la variabilidad de los datos, lo que sugiere que hay una relación significativa entre las variables analizadas.