Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad.
Las solicitudes incluyen las siguientes condiciones:
| Características | Vivienda 1 | Vivienda 2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| Área construida | 200 | 300 |
| Parqueaderos | 1 | 3 |
| Baños | 2 | 3 |
| Habitaciones | 4 | 5 |
| Estrato | 4 o 5 | 5 o 6 |
| Zona | Norte | Sur |
| Crédito preaprobado | 350 millones | 850 millones |
Ayude a María a responder la solicitud, mediante técnicas de modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos) .
#install.packages("devtools") # solo la primera vez
#devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
library(paqueteMODELOS)
library(dplyr)
library(summarytools)
library(knitr)
library(corrplot)
library(cluster)
library(factoextra)
library(FactoMineR)
library(leaflet)
library(plotly)
library(lmtest)
library(DT)
library(Metrics)
data("vivienda")
Se inicia con la preparación de datos definida en la Actividad 1, en la cual se realizan los siguientes ajustes:
Reclasificar la variable piso como discreta.
Reclasificar la variable estrato como categórica.
Eliminar los 3 registros en los que se observa que en dos de ellos hay 13 variables sin datos y en el otro registro hay 12 variables sin datos.
Se considera que el valor de 0 baños es inconsistente para una vivienda, por lo que se imputa el valor mínimo de 1 en esos registros.
Imputar los datos faltantes de las variables categóricas y/o discretas con la moda.
#Reclasificar la variable piso como discreta.
vivienda$piso <- as.integer(vivienda$piso)
#Reclasificar la variable estrato como categórica.
vivienda$estrato <- as.character(vivienda$estrato)
# Contar cuántos NA hay por fila
na_por_fila <- apply(is.na(vivienda), 1, sum)
#Eliminación de los 3 registros con mayor cantidad de datos faltantes
v_sna <- vivienda[na_por_fila < 12, ]
#Imputación de los registros con 0 banios
v_sna$banios[v_sna$banios == 0] <- 1
#Imputación de datos faltantes en piso y parqueaderos con la moda
# Función para calcular la moda
moda <- function(x) {
ux <- na.omit(unique(x))
ux[which.max(tabulate(match(x, ux)))]
}
# Imputar NA con la moda en todas las columnas que tengan datos faltantes
for (col in names(v_sna)[colSums(is.na(v_sna)) > 0]) {
v_sna[[col]][is.na(v_sna[[col]])] <- moda(v_sna[[col]])
}
#
cat(capture.output(glimpse(v_sna)), sep = "\n")
## Rows: 8,319
## Columns: 13
## $ id <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
## $ zona <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
## $ piso <int> 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, …
## $ estrato <chr> "3", "3", "3", "4", "5", "5", "4", "5", "5", "5", "6", "4…
## $ preciom <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
## $ areaconst <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
## $ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, 1, 3, 2, 2, 1, 4, 2, 2, 2, …
## $ banios <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
## $ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
## $ tipo <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
## $ barrio <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
## $ longitud <dbl> -76.51168, -76.51237, -76.51537, -76.54000, -76.51350, -7…
## $ latitud <dbl> 3.43382, 3.43369, 3.43566, 3.43500, 3.45891, 3.36971, 3.4…
A continuación se presentan los primeros 3 registros de la base filtrada para el análisis de la vivienda 1. Adicionalmente se muestran los valores de las variables zona y tipo para verificar el correcto filtrado de los datos.
v=v_sna[v_sna$zona=="Zona Norte" & v_sna$tipo == "Casa",]
head(v, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 2 5 320 150 2 4 6
## 2 1592 Zona N… 2 5 780 380 2 3 3
## 3 4057 Zona N… 2 6 750 445 1 7 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
#Valores únicos de las variables filtradas
unique(v$zona)
## [1] "Zona Norte"
unique(v$tipo)
## [1] "Casa"
Localización de las casas identificadas en la Zona Norte
# Localización de las casas
leaflet(v) %>%
addTiles() %>% # Mapa base
addCircleMarkers(~longitud, ~latitud,
popup = ~as.character(barrio),
radius = 4, # tamaño del punto (puedes probar 2, 3, 5...)
color = "blue", # color del borde
fillColor = "lightblue", # color de relleno
fillOpacity = 0.7)
Geográficamente las casas clasificadas al Norte de la ciudad, realmente se distribuyen en diferentes zonas. Lo cual puede ocurrir por errores en la digitación de las coordenadas o en la identificación de la zona.
# Selección de variables de interés, se omite zona ya que solo corresponde a la Zona Norte y se agrega número de parqueaderos al ser mencionada en el punto 3.
df <- v %>%
select(id,preciom, areaconst, estrato, banios, habitaciones, parqueaderos)
#Se deja estrato como numérica al ser ordinal
df$estrato <- as.numeric(df$estrato)
# Correlaciones numéricas
# Usamos Spearman (robusto a no linealidades y ordinalidad de estrato)
cors <- cor(df[, !(names(df) %in% c("id"))],
method = "spearman",
use = "pairwise.complete.obs")
# Heatmap interactivo
heatmap_cor <- plot_ly(
x = colnames(cors), y = rownames(cors),
z = cors, type = "heatmap",
text = round(cors, 2), hovertemplate = "X: %{x}<br>Y: %{y}<br>rho: %{text}<extra></extra>"
) %>%
layout(title = "Matriz de correlaciones (Spearman)")
heatmap_cor
Las variables que presentan una correlación fuerte con el precio de la vivienda son el área construida y el estrato. Los baños presentan una correlación positiva un poco menos fuerte, pero a la vez significativa.
No se observa una correlación fuerte por parte de las variables habitaciones y parqueaderos con el precio de la vivienda.
**Análisis bivariado
# Precio vs Área construida
plot_ly(df, x = ~areaconst, y = ~preciom, color = ~estrato,
type = "scatter", mode = "markers",
text = ~paste("Estrato:", estrato)) %>%
layout(title = "Precio vs Área construida (color por estrato)")
Se identifica gráficamente la correlación positiva entre el área construida y el precio de vivienda, evidenciando adicionalmente que los valores extremos de precio y área de viviendas se presentan predominantemente en los estratos 5 y 6.
#Modelo de regresión líneal múltiple
mod <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = df)
summary(mod)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = df)
##
## Residuals:
## Min 1Q Median 3Q Max
## -941.57 -78.42 -18.26 45.19 1082.73
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -234.54892 29.62212 -7.918 9.21e-15 ***
## areaconst 0.81276 0.04381 18.550 < 2e-16 ***
## estrato 83.90147 7.28818 11.512 < 2e-16 ***
## banios 28.16985 5.46747 5.152 3.33e-07 ***
## habitaciones 0.58410 4.10505 0.142 0.887
## parqueaderos 5.51724 5.24937 1.051 0.294
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 158.7 on 716 degrees of freedom
## Multiple R-squared: 0.6526, Adjusted R-squared: 0.6501
## F-statistic: 268.9 on 5 and 716 DF, p-value: < 2.2e-16
A partir del modelo estimado con las variables de interés se pueden realizar las siguientes afirmaciones:
Todas las variables consideradas tienen una relación directa frente al precio de los inmuebles. Es decir, que a medida que aumenta el valor de la variable tambien aumenta en alguna medida el precio de la vivienda.
No obstante, para cada variable se presenta el valor p, que permite establecer la influencia real de cada variable en el precio del inmueble. Las variables que realmente influyen en el precio de los inmuebles son areaconst, estrato, y banios. Mientras que habitaciones y parqueaderos no presentan suficiente evidencia de influencia en el precio.
Los resultados frente a la influencia en el precio de cada variable, son coherentes frente al análisis de correlaciones en el que no se identificó correlación fuerte por parte de las variables habitaciones y parqueaderos con el precio de la vivienda.
El R2= 0,65 indica que el modelo explica el 65% de la variabilidad con las variables consideradas, lo cual indica que hay otros factores relevantes que no se están incluyendo.
Algunas mejoras que se pueden realizar al modelo serían eliminar una a una las variables no influyentes para evaluar su aporte real y considerar la inclusión de variables adicionales que recojan la variabilidad que no está siendo explicada y puedan mejorar la predicción.
Evaluación de los supuestos del modelo
# Obtener residuales estandarizados
residuales_estandarizados <- rstandard(mod)
par(mfrow = c(1, 2))
# Generar el QQ-Plot para evaluar normalidad
qqnorm(residuales_estandarizados, main="QQ-Plot de los Residuales", col="blue",
cex.main = 1, # Tamaño del título
cex.axis = 0.8)
qqline(residuales_estandarizados, col="red", lwd=2) # Agregar línea de referencia
# Determinar residuos ordinarios y aplicar test de normalidad
ei <- residuals(mod)
shapiro.test(ei)
##
## Shapiro-Wilk normality test
##
## data: ei
## W = 0.83618, p-value < 2.2e-16
Hipótesis del test de normalidad:
H0: Los errores siguen una distribución normal.
H1: Los errores no siguen una distribución normal.
Al aplicar el test de Shapiro-Wilk se obtiene un valor p menor a 0.05 sobre los residuales del modelo. Por lo tanto, se rechaza la hipótesis nula, evidenciando que los errores no siguen una distribución normal. Esto reafirma lo que evidencia el QQ-Plot al presentar una dispersión en el extremo superior del modelo. Esto puede deberse a una gran presencia de valores atípicos en los precios de las casas de la Zona norte.
2. Los errores tienen varianza constante
(homocedasticidad)
#prueba Breusch-Pagan
bptest(mod)
##
## studentized Breusch-Pagan test
##
## data: mod
## BP = 135.9, df = 5, p-value < 2.2e-16
Prueba de hipótesis:
H0: Los errores tienen varianza constante (homocedasticidad).
H1: Los errores no tienen varianza constante (heterocedasticidad).
Aplicando la prueba de Breusch-Pagan, se rechaza la hipótesis nula, por lo tanto se evidencia la presencia de heterocedasticidad para el modelo.
3. No hay correlación entre los errores
(independencia).
# Durbin-Watson Test
# Evalúa si los errores presentan autocorrelación de primer orden
dw_test <- dwtest(mod)
print(dw_test)
##
## Durbin-Watson test
##
## data: mod
## DW = 1.6212, p-value = 1.353e-07
## alternative hypothesis: true autocorrelation is greater than 0
# Breusch-Godfrey Test
# Permite evaluar autocorrelación de orden superior en los errores
bg_test <- bgtest(mod)
print(bg_test)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: mod
## LM test = 26.146, df = 1, p-value = 3.166e-07
H0: Los errores son independientes, es decir, no presentan autocorrelación.
H1: Los errores no son independientes, es decir, presentan autocorrelación.
Aplicando las pruebas de Durbin-Watson y Breusch-Godfrey, se rechaza la hipótesis nula. Por lo tanto en el modelo no hay evidencia de independencia en los errores.
Los supuestos de normalidad e independencia de los errores no se cumplen para el modelo, por lo tanto las inferencias realizadas para los precios de las casa de la Zona Norte no serán confiables.
Algunas sugerencias de ajustes sobre el modelo podrían ser, eliminacion de atipicos, eliminación de variables no significativas que pueden estar correlacionadas con otras variables. O realizar una transformación que mejore el ajuste y el cumplimiento de los supuestos del modelo.
Precio estrato 4
# Definir la nueva vivienda
nueva_vivienda <- data.frame(
areaconst = 200,
estrato = 4,
banios = 2,
habitaciones = 4,
parqueaderos = 1
)
# Hacer la predicción
predict(mod, newdata = nueva_vivienda)
## 1
## 327.8023
Precio estrato 5
# Definir la nueva vivienda
nueva_vivienda2 <- data.frame(
areaconst = 200,
estrato = 5,
banios = 2,
habitaciones = 4,
parqueaderos = 1
)
# Hacer la predicción
predict(mod, newdata = nueva_vivienda2)
## 1
## 411.7037
Predicciones usando set de prueba
set.seed(123) # para reproducibilidad
# 1. Separar datos en entrenamiento y prueba (70/30, por ejemplo)
n <- nrow(df)
train_index <- sample(1:n, size = 0.7*n)
train <- df[train_index, ] # datos de entrenamiento
test <- df[-train_index, ] # datos de prueba
# 2. Ajustar el modelo con los datos de entrenamiento
modelo <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = train)
# 3. Hacer predicciones sobre el set de prueba
test$predicciones <- predict(modelo, newdata = test)
# 4. Evaluar el desempeño
head(test[, c("preciom", "predicciones")]) # ver precios reales vs predichos
## # A tibble: 6 × 2
## preciom predicciones
## <dbl> <dbl>
## 1 320 437.
## 2 780 590.
## 3 750 856.
## 4 625 544.
## 5 420 473.
## 6 230 212.
Evaluación de métricas MAE y RMSE
# Métricas
mae <- mae(test$preciom, test$predicciones)
rmse <- rmse(test$preciom, test$predicciones)
mae
## [1] 108.6988
rmse
## [1] 180.1705
Visualmente comparando las predicciones sobre el set de prueba se evidencian diferencias significativas frente al precio de mercado.
Los valores de las métricas del error absoluto promedio MAE=108 y la raiz del error cuadrático medio RMSE=180 evidencian que hay diferencias muy grandes en la predicción y que hay gran influencia de valores atípicos
6. Con las predicciones del modelo sugiera potenciales
ofertas que responda a la solicitud de la vivienda 1. Tenga en cuenta
que la empresa tiene crédito pre-aprobado de máximo 350 millones de
pesos. Realice un análisis y presente en un mapa al menos 5 ofertas
potenciales que debe discutir.
#Generación de predicciones
df$pred_precio <- predict(mod, newdata = df)
#Filtro de viviendas de interés, se amplian las condiciones para considerar más opciones
ofertas <- df %>%
filter(areaconst >= 180 & areaconst <= 250, # área aproximada a 200
banios %in% c(2, 3),
habitaciones %in% c(3, 4, 5),
parqueaderos %in% c(1, 2),
estrato %in% c(4, 5),
pred_precio <= 400) # se amplia el precio ya que el precio de mercado tiende a
#estar por debajo del precio de predicción
Se presentan 8 alternativas flexibilizando algunas variables de interés
#Se seleccionan aquellas 8 ofertas cuyos valores de mercado son más bajos
ofertas_top5 <- ofertas %>%
arrange(preciom) %>%
head(8)
ofertas_top5
## # A tibble: 8 × 8
## id preciom areaconst estrato banios habitaciones parqueaderos pred_precio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4684 271 226 4 3 3 1 377.
## 2 4511 275 190 4 2 3 1 319.
## 3 5412 280 180 4 3 3 1 339.
## 4 515 280 180 4 3 4 1 340.
## 5 3586 330 240 4 2 3 1 360.
## 6 3891 355 190 4 3 5 2 354.
## 7 6807 360 240 4 2 3 1 360.
## 8 1222 360 216 4 2 4 2 346.
#Se agregan variables de latitud y longitud
ofertas_top5 <- ofertas_top5 %>%
left_join(v %>% select(id, longitud, latitud), by = "id")
Localización de casas tentativas
leaflet(ofertas_top5) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
popup = ~paste0("Precio estimado: ", round(pred_precio,1), " M",
"<br>Precio mercado: ", preciom, " m²",
"<br>Área: ", areaconst, " m²",
"<br>Estrato: ", estrato,
"<br>Baños: ", banios,
"<br>Habitaciones: ", habitaciones,
"<br>id: ", id),
radius = 6,
color = "blue",
fillOpacity = 0.7)
Las potenciales ofertas corresponden a las seleccionadas en la zona
norte cuyos id corresponden a: 515, 1222, 3586, 4511 y 4684
A continuación se presentan los primeros 3 registros de la base filtrada para el análisis de la vivienda 2. Adicionalmente se muestran los valores de las variables zona y tipo para verificar el correcto filtrado de los datos.
v2=v_sna[v_sna$zona=="Zona Sur" & v_sna$tipo == "Apartamento",]
head(v2, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <int> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5098 Zona S… 5 4 290 96 1 2 3
## 2 698 Zona S… 2 3 78 40 1 1 2
## 3 8199 Zona S… 2 6 875 194 2 5 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
#Valores únicos de las variables filtradas
unique(v2$zona)
## [1] "Zona Sur"
unique(v2$tipo)
## [1] "Apartamento"
Localización de las casas identificadas en la Zona Sur
# Localización de los apartamentos
leaflet(v2) %>%
addTiles() %>% # Mapa base
addCircleMarkers(~longitud, ~latitud,
popup = ~as.character(barrio),
radius = 4, # tamaño del punto (puedes probar 2, 3, 5...)
color = "blue", # color del borde
fillColor = "lightblue", # color de relleno
fillOpacity = 0.7)
Geográficamente las casas clasificadas al Sur de la ciudad, realmente se distribuyen en diferentes zonas. Lo cual puede ocurrir por errores en la digitación de las coordenadas o en la identificación de la zona.
# Selección de variables de interés, se omite zona ya que solo corresponde a la Zona Sur y se agrega número de parqueaderos al ser mencionada en el punto 3.
df2 <- v2 %>%
select(id,preciom, areaconst, estrato, banios, habitaciones, parqueaderos)
#Se deja estrato como numérica al ser ordinal
df2$estrato <- as.numeric(df2$estrato)
# Correlaciones numéricas
# Usamos Spearman (robusto a no linealidades y ordinalidad de estrato)
cors <- cor(df2[, !(names(df2) %in% c("id"))],
method = "spearman",
use = "pairwise.complete.obs")
# Heatmap interactivo
heatmap_cor <- plot_ly(
x = colnames(cors), y = rownames(cors),
z = cors, type = "heatmap",
text = round(cors, 2), hovertemplate = "X: %{x}<br>Y: %{y}<br>rho: %{text}<extra></extra>"
) %>%
layout(title = "Matriz de correlaciones (Spearman)")
heatmap_cor
Las variables que presentan una correlación fuerte con el precio de la vivienda son el área construida, el estrato, la cantidad de baños y la cantidad de parqueaderos.
No se observa una correlación fuerte por parte de la variable habitaciones con el precio de la vivienda.
# Precio vs Área construida
plot_ly(df2, x = ~areaconst, y = ~preciom, color = ~estrato,
type = "scatter", mode = "markers",
text = ~paste("Estrato:", estrato)) %>%
layout(title = "Precio vs Área construida (color por estrato)")
Se identifica gráficamente la correlación positiva entre el área construida y el precio de vivienda, evidenciando adicionalmente una gran dispersión frente a los valores extremos de precio y área en las viviendas.
#Modelo de regresión líneal múltiple
mod2 <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = df2)
summary(mod2)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos, data = df2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1143.63 -37.88 -2.65 38.05 923.94
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -268.14679 12.86750 -20.839 < 2e-16 ***
## areaconst 1.35004 0.04864 27.757 < 2e-16 ***
## estrato 59.22882 2.66505 22.224 < 2e-16 ***
## banios 45.43316 3.02871 15.001 < 2e-16 ***
## habitaciones -17.54937 3.31804 -5.289 1.32e-07 ***
## parqueaderos 72.74628 3.66473 19.850 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 92.97 on 2781 degrees of freedom
## Multiple R-squared: 0.7649, Adjusted R-squared: 0.7644
## F-statistic: 1809 on 5 and 2781 DF, p-value: < 2.2e-16
A partir del modelo estimado con las variables de interés se pueden realizar las siguientes afirmaciones:
Con la excepción de la variable habitaciones, el resto de variables tienen una relación directa frente al precio de los inmuebles. Es decir, que a medida que aumenta el valor de la variable tambien aumenta en alguna medida el precio de la vivienda.
Para el caso de las habitaciones, a medida que aumenta la cantidad de habitaciones, disminuye el precio de la vivienda.
No obstante, para cada variable se presenta el valor p, que permite establecer la influencia real de cada variable en el precio del inmueble. En este caso todas las variables influyen en el precio de los inmuebles.
Los resultados frente a la influencia en el precio de cada variable, son coherentes frente al análisis de correlaciones. No se esperaba significancia frente a la variable de habitaciones, no obstante el modelo confirma que su presencia aporta al entendimiento de la variabilidad del precio de la vivienda.
El R2= 0,76 indica que el modelo explica el 76% de la variabilidad con las variables consideradas, lo cual indica que hay otros factores relevantes que no se están incluyendo.
El modelo se puede mejorar incluyendo variables adicionales que recojan la variabilidad que no está siendo explicada y puedan mejorar la predicción.
Evaluación de los supuestos del modelo
# Obtener residuales estandarizados
residuales_estandarizados <- rstandard(mod2)
par(mfrow = c(1, 2))
# Generar el QQ-Plot para evaluar normalidad
qqnorm(residuales_estandarizados, main="QQ-Plot de los Residuales", col="blue",
cex.main = 1, # Tamaño del título
cex.axis = 0.8)
qqline(residuales_estandarizados, col="red", lwd=2) # Agregar línea de referencia
# Determinar residuos ordinarios y aplicar test de normalidad
ei <- residuals(mod2)
shapiro.test(ei)
##
## Shapiro-Wilk normality test
##
## data: ei
## W = 0.78265, p-value < 2.2e-16
Hipótesis del test de normalidad:
H0: Los errores siguen una distribución normal.
H1: Los errores no siguen una distribución normal.
Al aplicar el test de Shapiro-Wilk se obtiene un valor p menor a 0.05 sobre los residuales del modelo. Por lo tanto, se rechaza la hipótesis nula, evidenciando que los errores no siguen una distribución normal. Esto reafirma lo que evidencia el QQ-Plot al presentar una dispersión en el extremo superior del modelo. Esto puede deberse a una gran presencia de valores atípicos en los precios de los apartamentos de la Zona Sur.
2. Los errores tienen varianza constante
(homocedasticidad)
#prueba Breusch-Pagan
bptest(mod2)
##
## studentized Breusch-Pagan test
##
## data: mod2
## BP = 890.48, df = 5, p-value < 2.2e-16
Prueba de hipótesis:
H0: Los errores tienen varianza constante (homocedasticidad).
H1: Los errores no tienen varianza constante (heterocedasticidad).
Aplicando la prueba de Breusch-Pagan, se rechaza la hipótesis nula, por lo tanto se evidencia la presencia de heterocedasticidad para el modelo.
3. No hay correlación entre los errores
(independencia).
# Durbin-Watson Test
# Evalúa si los errores presentan autocorrelación de primer orden
dw_test <- dwtest(mod2)
print(dw_test)
##
## Durbin-Watson test
##
## data: mod2
## DW = 1.5434, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
# Breusch-Godfrey Test
# Permite evaluar autocorrelación de orden superior en los errores
bg_test <- bgtest(mod2)
print(bg_test)
##
## Breusch-Godfrey test for serial correlation of order up to 1
##
## data: mod2
## LM test = 146.15, df = 1, p-value < 2.2e-16
H0: Los errores son independientes, es decir, no presentan autocorrelación.
H1: Los errores no son independientes, es decir, presentan autocorrelación.
Aplicando las pruebas de Durbin-Watson y Breusch-Godfrey, se rechaza la hipótesis nula. Por lo tanto en el modelo no hay evidencia de independencia en los errores.
Los supuestos de normalidad e independencia de los errores no se cumplen para el modelo, por lo tanto las inferencias realizadas para los precios de los apartamentos de la Zona Sur no serán confiables.
Algunas sugerencias de ajustes sobre el modelo podrían ser, eliminacion de atípicos o realizar una transformación que mejore el ajuste y el cumplimiento de los supuestos del modelo.
Precio estrato 5
# Definir la nueva vivienda
nueva_vivienda <- data.frame(
areaconst = 300,
estrato = 5,
banios = 3,
habitaciones = 5,
parqueaderos = 3
)
# Hacer la predicción
predict(mod2, newdata = nueva_vivienda)
## 1
## 699.8008
Precio estrato 6
# Definir la nueva vivienda
nueva_vivienda2 <- data.frame(
areaconst = 300,
estrato = 6,
banios = 3,
habitaciones = 5,
parqueaderos = 3
)
# Hacer la predicción
predict(mod2, newdata = nueva_vivienda2)
## 1
## 759.0297
Predicciones usando set de prueba
set.seed(123) # para reproducibilidad
# 1. Separar datos en entrenamiento y prueba (70/30, por ejemplo)
n <- nrow(df2)
train_index <- sample(1:n, size = 0.7*n)
train <- df2[train_index, ] # datos de entrenamiento
test <- df2[-train_index, ] # datos de prueba
# 2. Ajustar el modelo con los datos de entrenamiento
modelo2 <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos, data = train)
# 3. Hacer predicciones sobre el set de prueba
test$predicciones <- predict(modelo2, newdata = test)
# 4. Evaluar el desempeño
head(test[, c("preciom", "predicciones")]) # ver precios reales vs predichos
## # A tibble: 6 × 2
## preciom predicciones
## <dbl> <dbl>
## 1 875 671.
## 2 220 181.
## 3 150 118.
## 4 305 486.
## 5 115 51.9
## 6 175 173.
Evaluación de métricas MAE y RMSE
# Métricas
mae <- mae(test$preciom, test$predicciones)
rmse <- rmse(test$preciom, test$predicciones)
mae
## [1] 55.11728
rmse
## [1] 89.10835
Visualmente comparando las predicciones sobre el set de prueba se evidencian diferencias significativas frente al precio de mercado.
Los valores de las métricas del error absoluto promedio MAE=55 y la raiz del error cuadrático medio RMSE=89 evidencian que hay diferencias muy grandes en la predicción y que hay gran influencia de valores atípicos.
6. Con las predicciones del modelo sugiera potenciales
ofertas que responda a la solicitud de la vivienda 2. Tenga en cuenta
que la empresa tiene crédito pre-aprobado de máximo 850 millones de
pesos. Realice un análisis y presente en un mapa al menos 5 ofertas
potenciales que debe discutir.
#Generación de predicciones
df2$pred_precio <- predict(mod2, newdata = df2)
#Filtro de viviendas de interés, se amplian las condiciones para considerar más opciones
ofertas2 <- df2 %>%
filter(areaconst >= 250 & areaconst <= 350, # área aproximada a 200
banios %in% c(3, 4),
habitaciones %in% c(3, 4, 5),
parqueaderos %in% c(3, 4),
estrato %in% c(5, 6),
pred_precio <= 1000) # se amplia el precio ya que el precio de mercado tiende a
#estar por debajo del precio de predicción
Se presentan 7 alternativas flexibilizando algunas variables de interés
#Se seleccionan aquellas 7 ofertas cuyos valores de mercado son más bajos
ofertas_top7 <- ofertas2 %>%
arrange(preciom) %>%
head(7)
ofertas_top7
## # A tibble: 7 × 8
## id preciom areaconst estrato banios habitaciones parqueaderos pred_precio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6175 350 270 5 3 4 3 677.
## 2 6205 350 260 5 3 3 3 681.
## 3 7680 450 267 5 3 3 3 690.
## 4 4958 950 329 6 3 3 4 906.
## 5 4962 980 274 6 4 4 3 787.
## 6 3809 980 306 6 4 3 4 920.
## 7 4870 980 306 6 4 3 4 920.
#Se agregan variables de latitud y longitud
ofertas_top7 <- ofertas_top7 %>%
left_join(v2 %>% select(id, longitud, latitud), by = "id")
Localización de apartamentos tentativos
leaflet(ofertas_top7) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud,
popup = ~paste0("Precio estimado: ", round(pred_precio,1), " M",
"<br>Precio mercado: ", preciom, " m²",
"<br>Área: ", areaconst, " m²",
"<br>Estrato: ", estrato,
"<br>Baños: ", banios,
"<br>Habitaciones: ", habitaciones,
"<br>id: ", id),
radius = 6,
color = "blue",
fillOpacity = 0.7)
Para mejorar la precisión de las predicciones y la consistencia de los modelos frente a los supuestos se recomienda:
Evaluar valores extremos de modo que se incluyan variables asociadas a estos valores que aporten en su explicación dentro del modelo o que se eliminen dada su influencia en la alta variabilidad de la predicción del modelo.
Eliminar del modelo las variables no influyentes, ya que no aportan en la predicción y si aumentan la complejidad de la interpretación del modelo.
Evaluar la inclusión de variables adicionales que recojan la variabilidad que no está siendo explicada y que puedan mejorar la predicción.
Realizar una transformación que mejore el ajuste y el cumplimiento de los supuestos del modelo.
Utilizar un modelo de predicción que no cumple con los supuestos
y que no explica una variabilidad significativa del fenómeno de interés
no es confiable y no permitirá realizar recomendaciones acertadas frente
a una decisión comercial.
Los datos de los tres últimos meses presentan las siguientes variables:
| Variable | Descripción |
|---|---|
| zona | Ubicación de la vivienda: Zona Centro, Zona Norte,… |
| piso | Piso que ocupa la vivienda: primer piso, segundo piso… |
| estrato | Estrato socio-económico: 3, 4, 5, 6 |
| preciom | Precio de la vivienda en millones de pesos |
| areaconst | Área construida |
| parqueaderos | Número de parqueaderos |
| banios | Número de baños |
| habitaciones | Número de habitaciones |
| tipo | Tipo de vivienda: Casa, Apartamento |
| barrio | Barrio de ubicación de la vivienda: 20 de Julio, Álamos,… |
| longitud | Coordenada geográfica |
| latitud | Coordenada geográfica |
Los precios de las casas ubicadas en la zona norte de la ciudad se encuentran directamente relacionados con el área construida, el estrato y la cantidad de baños.
El modelo indica que el aumento de estrato 4 a 5 puede significar un incremento de 84 millones en el precio de la casa, para la zona de interés el valor de referencia presenta mayor coincidencia con viviendas de estrato 4.
A continuación se presentan 5 ofertas tentativas de casas localizadas en la zona norte cuyo precio de mercado es acorde al crédito preaprobado y que cuentan con las condiciones mínimas requeridas para consideración del cliente.
# Filtras por los ids de interés
ids_interes <- c(515, 1222, 3586, 4511, 4684)
ofertas_filtradas <- ofertas_top5 %>%
filter(id %in% ids_interes)
datatable(
ofertas_filtradas %>%
select(id, pred_precio, preciom, areaconst, estrato, banios, habitaciones, parqueaderos),
options = list(
pageLength = 5,
autoWidth = TRUE,
scrollX = TRUE
),
rownames = FALSE
)
# Mapa solo con esos puntos
leaflet(ofertas_filtradas) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
popup = ~paste0("Precio estimado: ", round(pred_precio,1), " M",
"<br>Precio mercado: ", preciom, " M",
"<br>Área: ", areaconst, " m²",
"<br>Estrato: ", estrato,
"<br>Baños: ", banios,
"<br>Habitaciones: ", habitaciones,
"<br>ID: ", id),
radius = 6,
color = "blue",
fillOpacity = 0.7
) %>%
addLabelOnlyMarkers(
~longitud, ~latitud,
label = ~paste(preciom, "M"),
labelOptions = labelOptions(
noHide = TRUE, # 👈 mantiene el label siempre visible
direction = "top", # posición del texto
textOnly = TRUE,
style = list(
"color" = "black",
"font-size" = "12px",
"font-weight" = "bold"
)
)
)
Los precios de los apartamentos ubicados en la zona sur de la ciudad se encuentran directamente relacionados con el área construida, el estrato, la cantidad de parqueaderos y la cantidad de baños. A diferencia de la zona norte en esta zona la cantidad de parqueaderos tienen una gran injerencia en el precio de las viviendas.
Por otro lado la relación del precio con respecto a las habitaciones es inversa.
El modelo indica que el aumento de estrato 5 a 6 puede significar un incremento de 59 millones en el precio del apartamento, para la zona de interés el valor de referencia presenta mayor coincidencia con viviendas de estrato 5.
A continuación se presentan 7 ofertas tentativas de apartamentos localizados en la zona sur cuyo precio de mercado es acorde al crédito preaprobado o puede ser superior considerando que los precios de vivienda pueden ser negociables y que no hay muchas ofertas que se ajusten al límite presupuestal dadas las condiciones requeridas por el cliente.
datatable(
ofertas_top7 %>%
select(id, pred_precio, preciom, areaconst, estrato, banios, habitaciones, parqueaderos),
options = list(
pageLength = 5,
autoWidth = TRUE,
scrollX = TRUE
),
rownames = FALSE
)
# Mapa solo con esos puntos
leaflet(ofertas_top7) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
popup = ~paste0("Precio estimado: ", round(pred_precio,1), " M",
"<br>Precio mercado: ", preciom, " M",
"<br>Área: ", areaconst, " m²",
"<br>Estrato: ", estrato,
"<br>Baños: ", banios,
"<br>Habitaciones: ", habitaciones,
"<br>ID: ", id),
radius = 6,
color = "blue",
fillOpacity = 0.7
) %>%
addLabelOnlyMarkers(
~longitud, ~latitud,
label = ~paste(preciom, "M"),
labelOptions = labelOptions(
noHide = TRUE, # 👈 mantiene el label siempre visible
direction = "top", # posición del texto
textOnly = TRUE,
style = list(
"color" = "black",
"font-size" = "12px",
"font-weight" = "bold"
)
)
)
Es necesario verificar la precisión de la información de referencia que utiliza la empresa C&A, ya que de su veracidad, depende la calidad del servicio prestado, la credibilidad hacia los clientes y el prestigio que permitirá una consolidación en el mercado inmobiliario.
La información de zona no es consistente al revisar su localización en el mapa, esta información se puede validar con datos complementarios de barrio para mejorar su precisión y disminuir los casos que puedan requerir validación de campo.