En el presente documento se meustran los pasos y observaciones para la recomendacion de vivienda segun la disponibilidad de vivienda en la ciudad de Cali y el presupuesto aprobado por la entidad financiera.
library(mice)
library(naniar)
library(car)
library(dplyr)
library(ggplot2)
library(plotly)
library(corrplot)
library(GGally)
library(lmtest)
library(paqueteMODELOS)
data("vivienda")
1- Se extraen variables de interés.
# Se extraen variables de interes
vivienda_v1 = vivienda[, c(2,4,5,6,7,8,9,10,12,13)]
2- Se eliminan filas con mas de 3 datos faltantes (Solo hay 3 registro de este tipo).
#Se eliminan columnas con mas de 3 datos faltantes
vivienda_v1 <- vivienda_v1 %>%
filter(rowSums(is.na(.)) <= 3)
3- Se cambian los N.A en la variable parqueadero asumiendo que no tienen por lo que no responden esta pregunta. Esto reduce la mayoría de datos faltantes (1605 registros)
# Se remplazan na de la variable parqueadero POR 0 asumiendo que no tienen por lo cual no responde.
vivienda_v1 <- vivienda_v1 %>%
mutate(parqueaderos = ifelse(is.na(parqueaderos), 0, parqueaderos))
4- Las variables habitaciones y baños tienen datos 0 (Cero), por lo que lo cámbianos a N.A, para evaluarlos como datos faltantes.
#Se cambia el valor 0 por NA en las variables habitaciones y baños
vivienda_v1 <- vivienda_v1 %>%
mutate(habitaciones = na_if(habitaciones, 0),
banios = na_if(banios, 0))
md.pattern(vivienda_v1, plot = TRUE, rotate.names = TRUE)
## zona estrato preciom areaconst parqueaderos tipo longitud latitud banios
## 8243 1 1 1 1 1 1 1 1 1
## 31 1 1 1 1 1 1 1 1 1
## 10 1 1 1 1 1 1 1 1 0
## 35 1 1 1 1 1 1 1 1 0
## 0 0 0 0 0 0 0 0 45
## habitaciones
## 8243 1 0
## 31 0 1
## 10 1 1
## 35 0 2
## 66 111
5- Al evaluar nos nuevos datos faltantes solo representan el 1.33% de los datos, por lo que se procede a eliminarlos para este análisis.
# Calcular porcentataje de faltantes segun total de los datos
porcentaje_fal = (sum(is.na(vivienda_v1)) / nrow(vivienda_v1)) * 100
print(paste0("Porcentaje de datos faltantes: ", round(porcentaje_fal, 2),"%"))
## [1] "Porcentaje de datos faltantes: 1.33%"
# Se eliminan registros con datos faltantes de baños, habitaciones y paraqueaderos ya que representan solo el 1,3 % de los datos.
vivienda_v1 <- vivienda_v1 %>%
filter(rowSums(is.na(.)) <= 0)
6- Se valida nuevamente los datos faltantes.
# Calcular porcentataje de faltantes segun total de los datos
porcentaje_fal = (sum(is.na(vivienda_v1)) / nrow(vivienda_v1)) * 100
print(paste0("Porcentaje de datos faltantes: ", round(porcentaje_fal, 2),"%"))
## [1] "Porcentaje de datos faltantes: 0%"
# Se eliminan registros con datos faltantes de baños, habitaciones y paraqueaderos ya que representan solo el 1,3 % de los datos.
vivienda_v1 <- vivienda_v1 %>%
filter(rowSums(is.na(.)) <= 0)
md.pattern(vivienda_v1, plot = TRUE, rotate.names = TRUE)
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
## zona estrato preciom areaconst parqueaderos banios habitaciones tipo
## 8243 1 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0 0
## longitud latitud
## 8243 1 1 0
## 0 0 0
# Mapa zonas
plot_ly(data = vivienda_v1,
lat = ~latitud,
lon = ~longitud,
type = "scattermapbox",
mode = "markers",
marker = list(size = 8, opacity = 0.7),
color = ~zona, # Asigna colores según la zona
text = ~paste("Zona:", zona, "<br>Precio:", preciom, "millones")) %>%
layout(
title = "Mapa de Ubicación de Viviendas por Zona",
mapbox = list(
style = "open-street-map",
zoom = 12,
center = list(lat = mean(vivienda_v1$latitud, na.rm = TRUE),
lon = mean(vivienda_v1$longitud, na.rm = TRUE))
)
)
Algunas viviendas se encuentran mal clasificadas según sus coordenadas, esto puede suceder por desconocimiento de la zona correspondiente al respondel la encusta.
Se identifican un grupo de casas en la zona Norte, donde se encuentran la mayoría por lo que se definen las coordenadas para seleccionar solo esas viviendas como zona norte, modificando el data set. Se corrige ubicaciones de casas zona norte según las coordenadas propuestas (latitud >= 3.45426 & longitud >= -76.53969)
Nota: Las viviendas clasificadas como zona norte que no pertenecen a las coordenadas propuestas se modifican como zona centro. Por efectos prácticos del análisis posterior.
# Definir los límites de las zonas (ajusta estos valores según la realidad)
vivienda_v1$zona <- ifelse(vivienda_v1$latitud >= 3.45426 & vivienda_v1$longitud >= -76.53969, "Zona Norte", vivienda_v1$zona)
# Cambiar a Zona Centro las viviendas que tenían "Zona Norte" pero no cumplen los límites
vivienda_v1$zona <- ifelse(vivienda_v1$zona == "Zona Norte" &
!(vivienda_v1$latitud > 3.45426 & vivienda_v1$longitud > -76.53969),"Zona Centro", vivienda_v1$zona)
# Graficar nuevamente coordenadas viviendad
plot_ly(data = vivienda_v1,
lat = ~latitud,
lon = ~longitud,
type = "scattermapbox",
mode = "markers",
marker = list(size = 8, opacity = 0.7),
color = ~zona, # Asigna colores según la zona
text = ~paste("Zona:", zona, "<br>Precio:", preciom, "millones")) %>%
layout(
title = "Mapa de Ubicación de Viviendas por Zona Modificada",
mapbox = list(
style = "open-street-map",
zoom = 12,
center = list(lat = mean(vivienda_v1$latitud, na.rm = TRUE),
lon = mean(vivienda_v1$longitud, na.rm = TRUE))
)
)
Características:
Tipo: Casa Área construida: 200 m2 Parqueaderos: 1
Baños: 2
Habitaciones: 4
Estrato: 4 o 5
Zona: Norte
crédito pre aprobado: 350 millones
Se filtran casas de la zona norte
# Filtar casas de la zona norte
base1_casas_norte <- vivienda_v1 %>%
filter(tipo == "Casa" &
zona == "Zona Norte")
# Filtar datos necesarios
base1_v1 <- base1_casas_norte[, c(2,3,4,5,6,7,9,10)]
head(base1_v1)
## # A tibble: 6 × 8
## estrato preciom areaconst parqueaderos banios habitaciones longitud latitud
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5 320 150 2 4 6 -76.5 3.48
## 2 5 780 380 2 3 3 -76.5 3.49
## 3 3 180 120 0 3 3 -76.5 3.47
## 4 5 520 455 0 5 4 -76.5 3.46
## 5 3 380 300 0 5 8 -76.5 3.47
## 6 5 395 165 0 4 4 -76.5 3.48
Se evalúa la relación entre Precio y Área construida.
# Seleccion variables Numericas
base1_v1_num <- base1_v1[, c(2,3)]
# Grafica correlacion y distribucion de las variables numericas
ggpairs(base1_v1_num)
Como se puede apreciar en las gráficas son variables correlacionadas
directamente (0,747), que no siguen una distribución normal.
## Variables categoricas
Estas variables se toman como categóricas ya que solo van máximo hasta 10, se utiliza la prueba de Kruskal-Wallis para identificar diferencias entre la variable precio y las demas variables categoricas, teniendo en cuenta que no sigue una distibucion normal. ### Estrato vs Precio
# Grafica Estrato vs Precio
plot_ly(data = base1_v1,
x = ~as.factor(estrato),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6))%>%
layout(title = "Distribución del Precio por Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(estrato), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(estrato)
## Kruskal-Wallis chi-squared = 4266.4, df = 3, p-value < 2.2e-16
El resultado de la prueba Kruskal-Wallis indica que hay diferencias significativas en el precio (preciom) según el estrato.
El valor p es bajo (p < 0.05), lo que significa que hay evidencia estadísticamente significativa de que al menos un estrato tiene un precio (preciom) diferente a los otros. ### Numero de baños vs Precio
# Grafica Numero de baños vs Precio
plot_ly(data = base1_v1,
x = ~as.factor(banios),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(banios), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(banios)
## Kruskal-Wallis chi-squared = 5070.9, df = 10, p-value < 2.2e-16
El estadístico chi-cuadrado (5070.9) es alto, lo que sugiere que hay diferencias en preciom según la cantidad de baños.
p-value < 2.2e-16 significa que hay diferencias estadísticamente significativas en el precio (preciom) según la cantidad de baños.
# Grafica Numero de habitaciones vs Precio
plot_ly(data = base1_v1,
x = ~as.factor(habitaciones),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(habitaciones), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(habitaciones)
## Kruskal-Wallis chi-squared = 1713.2, df = 10, p-value < 2.2e-16
El estadístico Kruskal-Wallis = 1713.2 indica diferencias en preciom según la cantidad de habitaciones.
p-value < 2.2e-16 confirma que al menos un grupo tiene un precio significativamente diferente.
# Grafica Numero de parqueaderos vs Precio
plot_ly(data = base1_v1,
x = ~as.factor(parqueaderos),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de parqueaderos",
xaxis = list(title = "Número de parqueaderos"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(parqueaderos), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(parqueaderos)
## Kruskal-Wallis chi-squared = 3715.8, df = 9, p-value < 2.2e-16
Kruskal-Wallis = 3715.8 sugiere diferencias grandes en el precio según el número de parqueaderos.
p-value < 2.2e-16 indica que al menos un grupo de parqueaderos tiene un precio distinto.
Se propone un modelo incluyendo todas las variables categóricas como dumies, excluyendo longitud y latitud como numericas.
# Convertir variables categóricas en factores
vivienda_modelo <- base1_v1 %>%
mutate(across(c(estrato, parqueaderos, banios, habitaciones), as.factor))
# Crear variables ficticias (dummies) excluyendo la categoría de referencia
vivienda_dummies <- vivienda_modelo %>%
model.matrix(~ estrato + parqueaderos + banios + habitaciones, data = .) %>%
as.data.frame() %>%
select(-1) # Eliminar la columna de intercepción
# Unir las variables dummies con la base original (sin variables categóricas repetidas)
vivienda_modelo <- cbind(base1_v1, vivienda_dummies) %>%
select(-estrato, -parqueaderos, -banios, -habitaciones) # Eliminar originales
# Definir la fórmula del modelo con todas las variables dummy
modelo <- lm(preciom ~ areaconst + ., data = vivienda_modelo)
# Resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + ., data = vivienda_modelo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -865.27 -63.25 -8.86 41.45 1040.34
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.476e+05 4.612e+04 -3.201 0.001445 **
## areaconst 6.822e-01 4.902e-02 13.917 < 2e-16 ***
## longitud -2.007e+03 6.027e+02 -3.331 0.000923 ***
## latitud -1.675e+03 6.235e+02 -2.686 0.007438 **
## estrato4 6.258e+01 2.105e+01 2.973 0.003072 **
## estrato5 9.293e+01 2.339e+01 3.973 8.02e-05 ***
## estrato6 3.121e+02 3.579e+01 8.723 < 2e-16 ***
## parqueaderos1 -1.842e+01 1.625e+01 -1.133 0.257526
## parqueaderos2 2.155e+01 1.845e+01 1.168 0.243311
## parqueaderos3 -3.438e+01 2.688e+01 -1.279 0.201457
## parqueaderos4 6.898e+01 2.919e+01 2.363 0.018463 *
## parqueaderos5 5.752e+01 4.344e+01 1.324 0.185956
## parqueaderos6 2.971e+02 5.153e+01 5.766 1.33e-08 ***
## parqueaderos7 9.221e+01 7.718e+01 1.195 0.232721
## parqueaderos8 4.145e+02 1.055e+02 3.929 9.59e-05 ***
## parqueaderos9 1.131e+02 1.591e+02 0.711 0.477369
## parqueaderos10 -5.415e+01 1.445e+02 -0.375 0.707886
## banios2 1.451e+01 3.641e+01 0.398 0.690471
## banios3 3.605e+01 3.816e+01 0.945 0.345167
## banios4 3.926e+01 3.984e+01 0.986 0.324748
## banios5 8.419e+01 4.273e+01 1.971 0.049260 *
## banios6 8.642e+01 4.752e+01 1.819 0.069467 .
## banios7 1.561e+02 8.051e+01 1.939 0.052978 .
## banios8 2.228e+00 6.752e+01 0.033 0.973692
## banios9 3.231e+02 1.750e+02 1.847 0.065315 .
## banios10 -2.326e+02 1.607e+02 -1.447 0.148317
## habitaciones3 -1.030e+01 4.201e+01 -0.245 0.806339
## habitaciones4 5.662e+00 4.210e+01 0.135 0.893054
## habitaciones5 -8.788e+00 4.425e+01 -0.199 0.842657
## habitaciones6 -4.279e+01 4.766e+01 -0.898 0.369704
## habitaciones7 1.106e+01 5.113e+01 0.216 0.828860
## habitaciones8 4.196e+01 5.146e+01 0.815 0.415186
## habitaciones9 1.221e+00 6.050e+01 0.020 0.983901
## habitaciones10 1.077e+02 6.853e+01 1.571 0.116670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 143 on 565 degrees of freedom
## Multiple R-squared: 0.7321, Adjusted R-squared: 0.7165
## F-statistic: 46.79 on 33 and 565 DF, p-value: < 2.2e-16
R2 = 0.7321, Indica que el 73.21% de la variabilidad en el precio de la vivienda es explicada por las variables del modelo.
𝑅ajustado = 0.7165, por el número de predictores. Como no se reduce demasiado, el modelo es adecuado.
p-valor (< 2.2e-16), El modelo en su conjunto es significativo.
Conclusión: El modelo es bastante bueno para predecir el precio de las viviendas, aunque aún hay un 26.79% de variabilidad no explicada.
Cada coeficiente representa el cambio esperado en el precio de la vivienda cuando la variable correspondiente aumenta en una unidad, manteniendo todo lo demás constante.
Variables Más Relevantes Área construida (areaconst)
Coeficiente: 0.6822 Interpretación: Por cada metro cuadrado adicional de construcción, el precio de la vivienda aumenta en 0.6822 unidades monetarias. Significativo: p < 2e-16)
Estrato socioeconómico
Estrato 4, 5 y 6 son significativos Mayor impacto: Estrato 6 (+312.1) → Las viviendas en estrato 6 son significativamente más caras. Parqueaderos
Parqueaderos 4, 6 y 8 son significativos Mayor impacto: Parqueaderos 8 (+414.5) → Tener 8 parqueaderos está asociado con un gran aumento en el precio. Baños
Baños 5 es significativo (+84.19) Baños 9 y 7 están cerca de la significancia
par(mfrow = c(2, 2))
plot(modelo)
## Warning: not plotting observations with leverage one:
## 303, 409, 455, 595
# Normalidad
shapiro.test(modelo$residuals)
##
## Shapiro-Wilk normality test
##
## data: modelo$residuals
## W = 0.81779, p-value < 2.2e-16
# Homocedasticidad
bptest(modelo)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 127.4, df = 33, p-value = 5.04e-13
# Autocorrelacion
dwtest(modelo)
##
## Durbin-Watson test
##
## data: modelo
## DW = 1.952, p-value = 0.2307
## alternative hypothesis: true autocorrelation is greater than 0
# Multicolinealidad
vif(modelo)
## areaconst longitud latitud estrato4 estrato5
## 2.143026 2.568020 1.185065 2.092298 3.759810
## estrato6 parqueaderos1 parqueaderos2 parqueaderos3 parqueaderos4
## 2.174715 1.477521 1.795665 1.380787 1.410566
## parqueaderos5 parqueaderos6 parqueaderos7 parqueaderos8 parqueaderos9
## 1.173884 1.151358 1.444927 1.085258 1.235838
## parqueaderos10 banios2 banios3 banios4 banios5
## 1.019069 7.093086 8.317909 8.531693 5.925486
## banios6 banios7 banios8 banios9 banios10
## 4.122768 1.883400 1.977200 1.495038 1.261292
## habitaciones3 habitaciones4 habitaciones5 habitaciones6 habitaciones7
## 9.306860 11.688954 8.356799 4.718890 4.100698
## habitaciones8 habitaciones9 habitaciones10
## 3.457088 2.618119 2.480550
Test de Shapiro-Wilk. (Normalidad)
Resultados obtenidos: W = 0.81779. Un valor menor a 1 indica que los datos se desvían de la normalidad. p-value < 2.2e-16. Como el valor p es mucho menor que 0.05, rechazamos la hipótesis nula de normalidad.
Conclusión: Los residuos no siguen una distribución normal, lo que podría afectar la validez de los intervalos de confianza y las pruebas de significancia en el modelo.
Se recomienda realizar una trasformación a la variable dependiente.
Test de Breusch-Pagan
Como el p-value es menor a 0.05, rechazamos la hipótesis nula de homocedasticidad (varianza constante). Esto significa que los residuos presentan heterocedasticidad, lo que puede afectar la validez de los coeficientes estimados y sus errores estándar.
Test Durbin-Watson
No se rechaza la hipótesis nula, porque el p-value es mayor que 0.05. DW ≈ 2, lo que indica que no hay evidencia fuerte de autocorrelación en los residuos.
Multicolinealidad
La variable, habitaciones4 con un VIF de 11.688954 podrían estar causando problemas de multicolinealidad, esto podría distorsionar los coeficientes del modelo
Se podria eliminar algunas de estas variables con alta colinealidad o aplicar técnicas como reducción de dimensionalidad (por ejemplo, PCA, análisis de componentes principales) o regularización (Lasso o Ridge) para mitigar los efectos de la multicolinealidad.
# Crear un nuevo dataframe con las características de la vivienda a predecir
nueva_vivienda <- data.frame(
areaconst = 200,
estrato4 = 1, estrato5 = 0, estrato6 = 0, # Estrato 4 (Categoría de referencia es estrato 3)
parqueaderos1 = 1, parqueaderos2 = 0, # Tiene 1 parqueadero
banios2 = 1, banios3 = 0, banios4 = 0, # Tiene 2 baños
habitaciones4 = 1, habitaciones5 = 0, habitaciones6 = 0, longitud = 0 # Tiene 4 habitaciones
)
# Predecir el precio con el modelo
#precio_predicho <- predict(modelo, newdata = nueva_vivienda)
#print(paste0("Precio predicho: ", round(precio_predicho),"Millones"))
# Filtrar viviendas con precio dentro del rango de crédito aprobado
ofertas_potenciales <- vivienda_modelo %>%
filter(preciom <= 350 & areaconst >= 200) %>%
arrange(desc(preciom)) %>%
slice(1:5) %>%
mutate(
estrato = case_when(
estrato4 == 1 ~ 4,
estrato5 == 1 ~ 5,
estrato6 == 1 ~ 6,
TRUE ~ 3 # Categoría de referencia
),
parqueaderos = case_when(
parqueaderos1 == 1 ~ 1,
parqueaderos2 == 1 ~ 2,
parqueaderos3 == 1 ~ 3,
TRUE ~ 1
),
banios = case_when(
banios2 == 1 ~ 2,
banios3 == 1 ~ 3,
banios4 == 1 ~ 4,
TRUE ~ 2
),
habitaciones = case_when(
habitaciones4 == 1 ~ 4,
habitaciones5 == 1 ~ 5,
habitaciones6 == 1 ~ 6,
TRUE ~ 4
)
) %>%
select(preciom, areaconst, estrato, parqueaderos, banios, habitaciones, latitud, longitud) # Mantener solo las variables originales
# Visualizar las ofertas
head(ofertas_potenciales)
## preciom areaconst estrato parqueaderos banios habitaciones latitud
## 14 350 350 3 1 3 5 3.46466
## 81 350 200 5 3 3 4 3.48503
## 96 350 300 5 3 2 6 3.48577
## 97 350 240 5 2 3 6 3.48635
## 107 350 203 5 2 2 5 3.48531
## longitud
## 14 -76.47908
## 81 -76.53010
## 96 -76.53010
## 97 -76.53136
## 107 -76.51448
library(plotly)
# Crear el mapa con las 5 viviendas seleccionadas
plot_ly(data = ofertas_potenciales,
lat = ~latitud,
lon = ~longitud,
text = ~paste("Precio:", preciom, "millones", "<br>Área:", areaconst, "m²"),
type = "scattermapbox",
mode = "markers",
marker = list(size = 10, color = "blue", opacity = 0.7)) %>%
layout(
title = "Mapa de Ofertas Potenciales de Viviendas Zona Norte",
mapbox = list(
style = "open-street-map",
zoom = 12,
center = list(lat = mean(ofertas_potenciales$latitud, na.rm = TRUE),
lon = mean(ofertas_potenciales$longitud, na.rm = TRUE))
)
)
Dentro de las ofertas propuestas solo una no cumple con el estrato propuesto, pero cuenta con mayor área construida y se ubica en la zona norte.
Se corrige ubicaciones de casas zona sur según las coordenadas propuestas (latitud <= 3.40239)
Nota: Las viviendas clasificadas como zona norte que no pertenecen a las coordenadas propuestas se modifican como zona centro. Por efectos prácticos del análisis posterior.
# Definir los límites de las zonas (ajusta estos valores según la realidad)
vivienda_v1$zona <- ifelse(vivienda_v1$latitud <= 3.40239,"Zona Sur", vivienda_v1$zona)
# Cambiar a Zona Centro las viviendas que tenían "Zona sur" pero no cumplen los límites
vivienda_v1$zona <- ifelse(vivienda_v1$zona == "Zona Sur" &
!(vivienda_v1$latitud <= 3.40239),"Zona Centro", vivienda_v1$zona)
# Graficar nuevamente coordenadas viviendas
plot_ly(data = vivienda_v1,
lat = ~latitud,
lon = ~longitud,
type = "scattermapbox",
mode = "markers",
marker = list(size = 8, opacity = 0.7),
color = ~zona, # Asigna colores según la zona
text = ~paste("Zona:", zona, "<br>Precio:", preciom, "millones")) %>%
layout(
title = "Mapa de Ubicación de Viviendas por Zona sur Modificada",
mapbox = list(
style = "open-street-map",
zoom = 12,
center = list(lat = mean(vivienda_v1$latitud, na.rm = TRUE),
lon = mean(vivienda_v1$longitud, na.rm = TRUE))
)
)
Características Vivienda 2 Tipo: Apartamento área construida: 300 parqueaderos: 3 baños: 3 habitaciones: 5 estrato: 5 o 6 zona Sur crédito preaprobado 850 millones
Se filtarn variables de interes
# Filtar casas de la zona norte
base2_apto_sur <- vivienda_v1 %>%
filter(tipo == "Apartamento" &
zona == "Zona Sur")
# Filtar datos necesarios
base2_v1 <- base2_apto_sur[, c(2,3,4,5,6,7,9,10)]
head(base2_v1)
## # A tibble: 6 × 8
## estrato preciom areaconst parqueaderos banios habitaciones longitud latitud
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5 240 87 1 3 3 -76.5 3.37
## 2 5 310 137 2 3 4 -76.5 3.38
## 3 3 100 49 0 1 2 -76.5 3.38
## 4 6 620 135 2 3 4 -76.5 3.34
## 5 5 170 56 1 1 2 -76.5 3.37
## 6 4 220 75 0 2 3 -76.5 3.37
Se evalúa la relación entre Precio y Área construida.
# Seleccion variables Numericas
base2_v1_num <- base2_v1[, c(2,3)]
# Grafica correlacion y distribucion de las variables numericas
ggpairs(base2_v1_num)
Como se puede apreciar en las gráficas son variables correlacionadas
directamente (0,773), que no siguen una distribución normal.
## Variables categoricas
Estas variables se toman como categóricas ya que solo van máximo hasta 10, se utiliza la prueba de Kruskal-Wallis para identificar diferencias entre la variable precio y las demas variables categoricas, teniendo en cuenta que no sigue una distibucion normal. ### Estrato vs Precio
# Grafica Estrato vs Precio
plot_ly(data = base2_v1,
x = ~as.factor(estrato),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6))%>%
layout(title = "Distribución del Precio por Estrato Zona Sur",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(estrato), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(estrato)
## Kruskal-Wallis chi-squared = 4266.4, df = 3, p-value < 2.2e-16
El resultado de la prueba Kruskal-Wallis indica que hay diferencias significativas en el precio (preciom) según el estrato.
El valor p es bajo (p < 0.05), lo que significa que hay evidencia estadísticamente significativa de que al menos un estrato tiene un precio (preciom) diferente a los otros. ### Numero de baños vs Precio
# Grafica Numero de baños vs Precio
plot_ly(data = base2_v1,
x = ~as.factor(banios),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de Baños Zona Sur",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(banios), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(banios)
## Kruskal-Wallis chi-squared = 5070.9, df = 10, p-value < 2.2e-16
El estadístico chi-cuadrado (5070.9) es alto, lo que sugiere que hay diferencias en preciom según la cantidad de baños.
p-value < 2.2e-16 significa que hay diferencias estadísticamente significativas en el precio (preciom) según la cantidad de baños.
# Grafica Numero de habitaciones vs Precio
plot_ly(data = base2_v1,
x = ~as.factor(habitaciones),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de Habitaciones Zona Sur",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(habitaciones), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(habitaciones)
## Kruskal-Wallis chi-squared = 1713.2, df = 10, p-value < 2.2e-16
El estadístico Kruskal-Wallis = 1713.2 indica diferencias en preciom según la cantidad de habitaciones.
p-value < 2.2e-16 confirma que al menos un grupo tiene un precio significativamente diferente.
# Grafica Numero de parqueaderos vs Precio
plot_ly(data = base2_v1,
x = ~as.factor(parqueaderos),
y = ~preciom,
type = "box",
marker = list(size = 8, opacity = 0.6)) %>%
layout(title = "Precio vs Número de parqueaderos Zona Sur",
xaxis = list(title = "Número de parqueaderos"),
yaxis = list(title = "Precio (millones)"))
kruskal.test(preciom ~ as.factor(parqueaderos), data = vivienda)
##
## Kruskal-Wallis rank sum test
##
## data: preciom by as.factor(parqueaderos)
## Kruskal-Wallis chi-squared = 3715.8, df = 9, p-value < 2.2e-16
Kruskal-Wallis = 3715.8 sugiere diferencias grandes en el precio según el número de parqueaderos.
p-value < 2.2e-16 indica que al menos un grupo de parqueaderos tiene un precio distinto.
#Convertir variables categóricas en factores
vivienda_modelo2 <- base2_v1 %>%
mutate(across(c(estrato, parqueaderos, banios, habitaciones), as.factor))
# Crear variables ficticias (dummies) excluyendo la categoría de referencia
vivienda_dummies <- vivienda_modelo2 %>%
model.matrix(~ estrato + parqueaderos + banios + habitaciones, data = .) %>%
as.data.frame() %>%
select(-1) # Eliminar la columna de intercepción
# Unir las variables dummies con la base original (sin variables categóricas repetidas)
vivienda_modelo2 <- cbind(base2_v1, vivienda_dummies) %>%
select(-estrato, -parqueaderos, -banios, -habitaciones) # Eliminar originales
# Definir la fórmula del modelo con todas las variables dummy
modelo_2 <- lm(preciom ~ areaconst + ., data = vivienda_modelo)
# Resumen del modelo
summary(modelo_2)
##
## Call:
## lm(formula = preciom ~ areaconst + ., data = vivienda_modelo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -865.27 -63.25 -8.86 41.45 1040.34
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.476e+05 4.612e+04 -3.201 0.001445 **
## areaconst 6.822e-01 4.902e-02 13.917 < 2e-16 ***
## longitud -2.007e+03 6.027e+02 -3.331 0.000923 ***
## latitud -1.675e+03 6.235e+02 -2.686 0.007438 **
## estrato4 6.258e+01 2.105e+01 2.973 0.003072 **
## estrato5 9.293e+01 2.339e+01 3.973 8.02e-05 ***
## estrato6 3.121e+02 3.579e+01 8.723 < 2e-16 ***
## parqueaderos1 -1.842e+01 1.625e+01 -1.133 0.257526
## parqueaderos2 2.155e+01 1.845e+01 1.168 0.243311
## parqueaderos3 -3.438e+01 2.688e+01 -1.279 0.201457
## parqueaderos4 6.898e+01 2.919e+01 2.363 0.018463 *
## parqueaderos5 5.752e+01 4.344e+01 1.324 0.185956
## parqueaderos6 2.971e+02 5.153e+01 5.766 1.33e-08 ***
## parqueaderos7 9.221e+01 7.718e+01 1.195 0.232721
## parqueaderos8 4.145e+02 1.055e+02 3.929 9.59e-05 ***
## parqueaderos9 1.131e+02 1.591e+02 0.711 0.477369
## parqueaderos10 -5.415e+01 1.445e+02 -0.375 0.707886
## banios2 1.451e+01 3.641e+01 0.398 0.690471
## banios3 3.605e+01 3.816e+01 0.945 0.345167
## banios4 3.926e+01 3.984e+01 0.986 0.324748
## banios5 8.419e+01 4.273e+01 1.971 0.049260 *
## banios6 8.642e+01 4.752e+01 1.819 0.069467 .
## banios7 1.561e+02 8.051e+01 1.939 0.052978 .
## banios8 2.228e+00 6.752e+01 0.033 0.973692
## banios9 3.231e+02 1.750e+02 1.847 0.065315 .
## banios10 -2.326e+02 1.607e+02 -1.447 0.148317
## habitaciones3 -1.030e+01 4.201e+01 -0.245 0.806339
## habitaciones4 5.662e+00 4.210e+01 0.135 0.893054
## habitaciones5 -8.788e+00 4.425e+01 -0.199 0.842657
## habitaciones6 -4.279e+01 4.766e+01 -0.898 0.369704
## habitaciones7 1.106e+01 5.113e+01 0.216 0.828860
## habitaciones8 4.196e+01 5.146e+01 0.815 0.415186
## habitaciones9 1.221e+00 6.050e+01 0.020 0.983901
## habitaciones10 1.077e+02 6.853e+01 1.571 0.116670
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 143 on 565 degrees of freedom
## Multiple R-squared: 0.7321, Adjusted R-squared: 0.7165
## F-statistic: 46.79 on 33 and 565 DF, p-value: < 2.2e-16
R2 = 0.7321, Indica que el 73.21% de la variabilidad en el precio de la vivienda es explicada por las variables del modelo.
𝑅ajustado = 0.7165, por el número de predictores. Como no se reduce demasiado, el modelo es adecuado.
p-valor (< 2.2e-16), El modelo en su conjunto es significativo.
Conclusión: El modelo es bastante bueno para predecir el precio de las viviendas, aunque aún hay un 26.79% de variabilidad no explicada.
Cada coeficiente representa el cambio esperado en el precio de la vivienda cuando la variable correspondiente aumenta en una unidad, manteniendo todo lo demás constante.
Variables Más Relevantes Área construida (areaconst)
Coeficiente: 0.6822 Interpretación: Por cada metro cuadrado adicional de construcción, el precio de la vivienda aumenta en 0.6822 unidades monetarias. Significativo: p < 2e-16)
Estrato socioeconómico
Estrato 4, 5 y 6 son significativos Mayor impacto: Estrato 6 (+312.1) → Las viviendas en estrato 6 son significativamente más caras. Parqueaderos
Parqueaderos 4, 6 y 8 son significativos Mayor impacto: Parqueaderos 8 (+414.5) → Tener 8 parqueaderos está asociado con un gran aumento en el precio. Baños
Baños 5 es significativo (+84.19) Baños 9 y 7 están cerca de la significancia
# Crear un nuevo dataframe con las características de la vivienda a predecir
nueva_vivienda <- data.frame(
areaconst = 300,
estrato4 = 0, estrato5 = 1, estrato6 = 0, # Estrato 5 (Categoría de referencia es estrato 3)
parqueaderos1 = 0, parqueaderos2 = 0, parqueaderos3 = 1, # Tiene 3 parqueaderos
banios2 = 0, banios3 = 1, banios4 = 0, # Tiene 3 baños
habitaciones4 = 0, habitaciones5 = 1, habitaciones6 = 0 # Tiene 5 habitaciones
)
# Predecir el precio con el modelo
#precio_predicho <- predict(modelo, newdata = nueva_vivienda)
#print(paste0("Precio predicho: ", round(precio_predicho),"Millones"))
# Filtrar viviendas con precio dentro del rango de crédito aprobado
ofertas_potenciales2 <- vivienda_modelo2 %>%
filter(preciom <= 850 & areaconst >= 300) %>%
arrange(desc(preciom)) %>%
slice(1:5) %>%
mutate(
estrato = case_when(
estrato4 == 1 ~ 4,
estrato5 == 1 ~ 5,
estrato6 == 1 ~ 6,
TRUE ~ 3 # Categoría de referencia
),
parqueaderos = case_when(
parqueaderos1 == 1 ~ 1,
parqueaderos2 == 1 ~ 2,
parqueaderos3 == 1 ~ 3,
TRUE ~ 3
),
banios = case_when(
banios2 == 1 ~ 2,
banios3 == 1 ~ 3,
banios4 == 1 ~ 4,
TRUE ~ 3
),
habitaciones = case_when(
habitaciones4 == 1 ~ 4,
habitaciones5 == 1 ~ 5,
habitaciones6 == 1 ~ 6,
TRUE ~ 3
)
) %>%
select(preciom, areaconst, estrato, parqueaderos, banios, habitaciones, latitud, longitud) # Mantener solo las variables originales
# Visualizar las ofertas
head(ofertas_potenciales2)
## preciom areaconst estrato parqueaderos banios habitaciones latitud
## 788 850 325 6 2 3 3 3.37500
## 1259 850 352 6 3 3 3 3.34265
## 626 690 486 5 2 4 4 3.38292
## 701 650 600 5 2 4 5 3.38100
## 30 620 480 6 3 3 5 3.36322
## longitud
## 788 -76.54200
## 1259 -76.53729
## 626 -76.53111
## 701 -76.53400
## 30 -76.52662
# Crear el mapa con las 5 viviendas seleccionadas
plot_ly(data = ofertas_potenciales2,
lat = ~latitud,
lon = ~longitud,
text = ~paste("Precio:", preciom, "millones", "<br>Área:", areaconst, "m²"),
type = "scattermapbox",
mode = "markers",
marker = list(size = 10, color = "blue", opacity = 0.7)) %>%
layout(
title = "Mapa de Ofertas Potenciales de Viviendas Zona Sur",
mapbox = list(
style = "open-street-map",
zoom = 12,
center = list(lat = mean(ofertas_potenciales$latitud, na.rm = TRUE),
lon = mean(ofertas_potenciales$longitud, na.rm = TRUE))
)
)
Para el caso 2 se encuentran viviendas con los requisitos planteados por menos del precio aprobado, siendo una buena opción para los clientes y mostrando una ventaja para la inmobiliaria en caso de adquirir la vivienda, ubicadas en la zona norte.