Contexto

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.

Punto 1: Base de datos

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

Punto 2: Análisis exploratorio bivariado de datos

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:

Precio vs Area construida

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.

Distribución del precio por Estrato socioeconómico

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.

Precio de la vivienda vs Número de baños

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.

Precio de la vivienda vs Número de habitaciones

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.

Distribución del Precio por Zona

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.

Correlaciones

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

Punto 3: Estimación modelo

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:

  • \(\widehat{\textbf{preciom}}:\) es la variable dependiente estimada, es decir, la predicción del modelo para el precio de un apartamento.
  • $ _0 = -59.95$: Es la constante o intercepto estimado del modelo, indica el precio promedio del apartamento cuando todas las variables independientes están en cero (aunque en la práctica, no tiene mucho sentido que algunas variables como areaconst, habitaciones, parqueaderos, y baños sean cero).
  • \(\textbf{Área construida:}\) Por cada aumento de un metro cuadrado en el área construida, el precio de la casa aumenta en promedio 1,97 millones COP.
  • \(\textbf{Estrato 4:}\) Este valor indica que, en promedio, las casas ubicadas en el estrato 4 tienen un precio que es 25.53 millones COP más alto que las casas en el estrato 3, asumiendo que todas las demás variables en el modelo (como areaconst, habitaciones, parqueaderos, y banios) se mantienen constantes.
  • \(\textbf{Estrato 5:}\) Este valor indica que, en promedio, las casas ubicadas en el estrato 5 tienen un precio que es 42.64 millones COP más alto que las casas en el estrato 3, asumiendo que todas las demás variables en el modelo (como areaconst, habitaciones, parqueaderos, y banios) se mantienen constantes.
  • \(\textbf{Estrato 6:}\) Este valor indica que, en promedio, las casas ubicadas en el estrato 6 tienen un precio que es 175.61 millones COP más alto que las casas en el estrato 3, asumiendo que todas las demás variables en el modelo (como areaconst, habitaciones, parqueaderos, y banios) se mantienen constantes.
  • \(\textbf{Habitaciones:}\) En este caso, se obresrva un coeficiente negativo (-37.98) indicando una relación inversa entre el número de habitaciones y el precio de la casa. Es decir, a medida que aumenta el número de habitaciones, el precio de la casa disminuye, según el modelo. Puntualmente lo podemos interpretar como de la siguiente manera, en promedio, por cada habitación adicional que tiene una casa, el precio de la casa disminuye en 37.98 millones COP, asumiendo que todas las demás variables del modelo (areaconst, estrato, parqueaderos, y banios) se mantienen constantes.
  • \(\textbf{Parqueaderos:}\)En promedio, por cada parqueadero adicional que tiene un apartamento, el precio del mismo aumenta en 82.61 unidades monetarias, asumiendo que todas las demás variables del modelo (areaconst, estrato, habitaciones, y banios) se mantienen constantes.
  • \(\textbf{Baños:}\)En promedio, por cada baño adicional que tiene un apartamento, el precio del mismo aumenta en 49.67 millones COP, asumiendo que todas las demás variables del modelo (areaconst, estrato, habitaciones, y banios) se mantienen constantes.
  • \(R^2 Ajustado:\) El R cuadrado ajustado del modelo es 0.7949, lo que indica que aproximadamente el 76.5% de la variabilidad en el precio de los apartamentos se puede explicar por las variables incluidas en el modelo.

Discusión:

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.

Supuestos del modelo

Normalidad

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

Homocedasticidad

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.

Autocorrelación

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.

Sugerencias

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

Punto 5. Partición:

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

Punto 6: Evaluar el modelo con el set de prueba

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)

Punto 7: Metricas set de prueba

Error cuadratico medio

# 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))
  • RMSE (136.52): El Error Cuadrático Medio de 136.52 indica que, en promedio, las predicciones del precio de las casas se desvían en aproximadamente 136.52 millones de los valores reales. Este valor representa la magnitud promedio del error de predicción, donde los errores grandes tienen más peso debido a la naturaleza cuadrática del cálculo del RMSE. Un RMSE relativamente bajo sugiere que el modelo tiene un buen rendimiento, aunque siempre depende del rango y la escala de los datos.

Error absoluto medio

mae <- mean(abs(test_data$preciom - predicciones))

cat("El MAE del modelo es:", mae)
## El MAE del modelo es: 84.00779
  • MAE (84.01): El Error Absoluto Medio de 84.01 sugiere que, en promedio, las predicciones del modelo difieren de los valores reales en aproximadamente 84.01 millones. A diferencia del RMSE, el MAE no penaliza los errores grandes de manera desproporcionada. Este valor más bajo que el RMSE confirma que los errores grandes no son extremadamente frecuentes, lo que significa que el modelo es bastante preciso en la mayoría de los casos.

Error absoluto medio

r2 <- cor(test_data$preciom, predicciones)^2
cat("El R-cuadrado del modelo es:", r2)
## El R-cuadrado del modelo es: 0.8120163
  • \(R^2 (0.812)\): El R-cuadrado de 0.812 indica que el modelo explica el 81.2% de la variabilidad del precio de las casas. Esto sugiere que el modelo captura gran parte de las relaciones entre las variables independientes (área construida, estrato, número de habitaciones, parqueaderos y baños) y el precio de las casas. Un valor cercano a 1 es ideal, por lo que 0.812 refleja un ajuste sólido y efectivo del modelo, aunque todavía hay un 18.8% de la variabilidad que no está siendo explicada, lo cual podría deberse a factores no considerados o no lineales.