Asesorar a la empresa C&A para brindarle la mejor opción de negocio a os de sus clienten que solicitan inmuebles con las siguientes características:
Inicialmente se procede a cargar la base de datos de viviendas en Cali y a limpiarla de información atípica o datos faltantes.
library(paqueteMODELOS)
data("vivienda")
vivienda
## # A tibble: 8,322 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1147 Zona … <NA> 3 250 70 1 3 6
## 2 1169 Zona … <NA> 3 320 120 1 2 3
## 3 1350 Zona … <NA> 3 350 220 2 2 4
## 4 5992 Zona … 02 4 400 280 3 5 3
## 5 1212 Zona … 01 5 260 90 1 2 3
## 6 1724 Zona … 01 5 240 87 1 3 3
## 7 2326 Zona … 01 4 220 52 2 2 3
## 8 4386 Zona … 01 5 310 137 2 3 4
## 9 1209 Zona … 02 5 320 150 2 4 6
## 10 1592 Zona … 02 5 780 380 2 3 3
## # ℹ 8,312 more rows
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Se identificó el grupo de viviendas ubicadas en la zona norte de la ciudad de Cali y se obtuvo el siguiente resultado:
library(dplyr)
library(leaflet)
library(knitr)
base1 <- datos_imputados %>%
filter(tolower(tipo) == "casa" & tolower(zona) == "zona norte")
head(base1, 3) %>% kable(caption = "Primeros 3 registros - Casas Zona Norte")
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1209 | Zona Norte | 02 | 5 | 320 | 150 | 2 | 4 | 6 | Casa | acopi | -76.51341 | 3.47968 |
| 1592 | Zona Norte | 02 | 5 | 780 | 380 | 2 | 3 | 3 | Casa | acopi | -76.51674 | 3.48721 |
| 4057 | Zona Norte | 02 | 6 | 750 | 445 | 2 | 7 | 6 | Casa | acopi | -76.52950 | 3.38527 |
table(base1$tipo, base1$zona) %>% kable(caption = "Comprobación de Tipo vs Zona")
| Zona Norte | |
|---|---|
| Casa | 722 |
leaflet(base1) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
popup = ~paste("Precio:", preciom, "M | Área:", areaconst, "m²"),
radius = 4, color = "red", stroke = FALSE, fillOpacity = 0.7
)
Al cargar la información en mapa se evidencia que a pesar de tener la clasificación como zona norte no todos los puntos se encuentran al norte de la ciudad. Para corregir esto se filtra según cuatro puntos extremos que pueden clasificarse como zona norte de la ciudad de Cali, lo que esté fuera de esos rangos sale del análisis.
library(leaflet)
library(htmltools)
base1_limpia <- base1 %>%
filter(
latitud >= 3.45 & latitud <= 3.50,
longitud >= -76.55 & longitud <= -76.50
)
leaflet(base1_limpia) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud, color = "green", radius = 4, fillOpacity = 0.8) %>%
addControl(
html = "<h4 style='margin:0; padding:5px; background:white; border-radius:4px;'>Casas Filtradas en Zona Norte</h4>",
position = "topright"
)
El análisis de correlación cruzada muestra que la variable con mayor poder explicativo respecto al precio del inmueble (preciom) es el área construida (areaconst), presentando una relación lineal positiva muy marcada. Las variables estrato, banos y parqueaderos también muestran una correlación positiva, mostrando que para el mercado es importante las viviendas amplias y el nivel socioeconómico de la ubicación.
library(plotly)
library(GGally)
vars_num <- base1_limpia %>%
select(preciom, areaconst, estrato, banios, habitaciones) %>%
na.omit()
p <- ggpairs(vars_num)
ggplotly(p)
El área construida vs precio muestra la correlación más alta, lo que quiere decir que a mayor metraje de la vivienda, mayor valor. Los baños y estrato tienen un impacto positivo considerable sobre el precio. Las habitaciones presenta una relación menos directa con el precio, ya que es más importante el tamaño de la vivienda que el número de habitaciones.
El modelo de regresión lineal múltiple estimado resultó ser significativo (p-valor < 0.05). Los coeficientes asociados a areaconst, estrato, banios y parqueaderos presentaron signos positivos y concuerdan con la teoría económica del mercado inmobiliario, donde cada unidad adicional incrementa el valor promedio de la vivienda en millones de pesos.
modelo_casas <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = base1_limpia)
base1_limpia <- base1_limpia %>%
mutate(prediccion_precio = predict(modelo_casas, newdata = .))
summary(modelo_casas)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base1_limpia)
##
## Residuals:
## Min 1Q Median 3Q Max
## -733.92 -81.57 -17.03 44.32 948.84
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -168.13161 47.15908 -3.565 0.000405 ***
## areaconst 0.85873 0.06285 13.662 < 2e-16 ***
## estrato 56.90315 10.49182 5.424 9.88e-08 ***
## habitaciones 8.32179 5.69505 1.461 0.144700
## parqueaderos 30.95801 6.37798 4.854 1.71e-06 ***
## banios 12.26769 7.26996 1.687 0.092259 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 166 on 420 degrees of freedom
## Multiple R-squared: 0.594, Adjusted R-squared: 0.5891
## F-statistic: 122.9 on 5 and 420 DF, p-value: < 2.2e-16
El coeficiente de determinación (R^2) indica la proporción de la variabilidad del precio explicada por el conjunto de covariables.
Al revisar la fórmula con pruebas detalladas, se evidenció que los precios de las casas tienen saltos muy grandes, algunas son muy baratas y otras muy caras y no se cumple la distribución normal. Esto hace que la fórmula cometa errores más grandes en las casas lujosas que en las normales. Para solucionarlo a futuro, se recomienda calcular los precios usando escalas proporcionales como porcentajes.
library(lmtest)
library(car)
par(mfrow = c(2, 2))
plot(modelo_casas)
par(mfrow = c(1,1))
bptest(modelo_casas)
##
## studentized Breusch-Pagan test
##
## data: modelo_casas
## BP = 61.267, df = 5, p-value = 6.65e-12
shapiro.test(residuals(modelo_casas)[1:5000])
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_casas)[1:5000]
## W = 0.83535, p-value < 2.2e-16
vif(modelo_casas)
## areaconst estrato habitaciones parqueaderos banios
## 1.517179 1.288074 1.662416 1.257304 1.865781
Se aplicó la fórmula para calcular cuánto debería costar la casa que pide el primer cliente con su presupuesto máximo de 350 millones, se filtró las 5 mejores opciones reales del mercado que se ajustan a lo que busca y que no se salen de su crédito pre-aprobado.
solicitud_1 <- data.frame(
areaconst = 200,
estrato = 4,
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
pred_1 <- predict(modelo_casas, newdata = solicitud_1, interval = "confidence")
pred_1
## fit lwr upr
## 1 320.0072 293.559 346.4553
Se presentan las opciones sugeridas según las indicaciones del primer cliente:
library(dplyr)
library(knitr)
precio_estimado_sol1 <- predict(modelo_casas, newdata = solicitud_1)
ofertas_sol1_especificas <- base1_limpia %>%
filter(!is.na(preciom)) %>%
filter(preciom <= 350) %>%
mutate(
diferencia_solicitud = abs(preciom - precio_estimado_sol1)
) %>%
arrange(diferencia_solicitud) %>%
slice_head(n = 5)
leaflet(ofertas_sol1_especificas) %>%
addTiles() %>%
addMarkers(
~longitud, ~latitud,
popup = ~paste0("<b>Tipo:</b>", tipo, "<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Parqueaderos:</b>", parqueaderos, "<br>",
"<b>Baños:</b>", banios, "<br>",
"<b>Habitaciones:</b> ", habitaciones, "<br>",
"<b>Estrato:</b>", estrato, "<br>",
"<b>Precio:</b> $", preciom, "M<br>")
)
El modelo encontró casas mucho más grandes, con más parqueaderos y en mejores estratos. Esto confirma que cuando el presupuesto sube, el mercado tiene en cuenta principalmente el espacio extra y la exclusividad de la ubicación.
Se presentan las opciones sugeridas según las indicaciones del segundo cliente:
library(dplyr)
library(knitr)
library(leaflet)
library(htmltools)
base2 <- datos_imputados %>%
filter(tolower(tipo) == "apartamento" & tolower(zona) == "zona sur")
head(base2, 3) %>% kable(caption = "Primeros 3 registros - Apartamentos Zona sur")
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5098 | Zona Sur | 05 | 4 | 290 | 96 | 1 | 2 | 3 | Apartamento | acopi | -76.53464 | 3.44987 |
| 698 | Zona Sur | 02 | 3 | 78 | 40 | 1 | 1 | 2 | Apartamento | aguablanca | -76.50100 | 3.40000 |
| 8199 | Zona Sur | NA | 6 | 875 | 194 | 2 | 5 | 3 | Apartamento | aguacatal | -76.55700 | 3.45900 |
# 3. Tabla de comprobación usando base2
table(base2$tipo, base2$zona) %>% kable(caption = "Comprobación de Tipo vs Zona")
| Zona Sur | |
|---|---|
| Apartamento | 2787 |
# 4. Mapa interactivo de los apartamentos con título
leaflet(base2) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
popup = ~paste("<b>Precio:</b> $", preciom, "M<br><b>Área:</b> ", areaconst, " m²"),
radius = 4, color = "blue", stroke = FALSE, fillOpacity = 0.7
) %>%
addControl(
html = "<h4 style='margin:0; padding:5px; background:white; border-radius:4px;'>Ubicación de Apartamentos - Zona sur</h4>",
position = "topright"
)
Para el caso del segundo cliente sucede lo mismo que en el primer cliente, a pesar de que las viviendas se ecuentren clasificadas como zona sur, no todas pertenecen a esa zona y se realiza el mismo procedimiento de definir unas coordenadas límite en Google maps para delimitar la zona sur, los puntos fuera de esos limites se eliminan.
library(leaflet)
library(htmltools)
base2_limpia <- base2 %>%
filter(
latitud >= 3.30 & latitud <= 3.39,
longitud >= -76.56 & longitud <= -76.50
)
leaflet(base2_limpia) %>%
addTiles() %>%
addCircleMarkers(~longitud, ~latitud, color = "green", radius = 4, fillOpacity = 0.8) %>%
addControl(
html = "<h4 style='margin:0; padding:5px; background:white; border-radius:4px;'>Apartamentos filtrados en Zona sur</h4>",
position = "topright"
)
library(plotly)
library(GGally)
vars_num_2 <- base2_limpia %>%
select(preciom, areaconst, estrato, banios, habitaciones) %>%
na.omit()
p <- ggpairs(vars_num_2)
ggplotly(p)
Para el caso del segundo cliente, se puede evidenciar que coincide con el primer caso en cuanto a que el precio va muy de la mano con el tamaño del inmueble. La segunda correlación fuerte es el número de baños, lo que quiere decir que entre más baños tenga un apartamento, más alto su valor. Depués encontramos que un estrato más alto si influye en el precio, pero no de la misma forma que las variables anteriores. Por último, las habitaciones no son una variable tan fuerte para definir el precio del apartamento.
modelo_apto <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = base2_limpia)
base2_limpia <- base2_limpia %>%
mutate(prediccion_precio = predict(modelo_apto, newdata = .))
summary(modelo_apto)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base2_limpia)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1244.93 -35.68 -3.21 36.95 869.68
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -270.7806 17.5785 -15.404 < 2e-16 ***
## areaconst 1.4726 0.0654 22.516 < 2e-16 ***
## estrato 50.0588 3.7187 13.461 < 2e-16 ***
## habitaciones -18.2428 4.6226 -3.946 8.27e-05 ***
## parqueaderos 89.7124 5.4825 16.364 < 2e-16 ***
## banios 52.3223 4.1218 12.694 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 96.26 on 1627 degrees of freedom
## Multiple R-squared: 0.8024, Adjusted R-squared: 0.8018
## F-statistic: 1321 on 5 and 1627 DF, p-value: < 2.2e-16
El valor p también es menor a 0.05, igual que con el caso del cliente 1, lo que significa que el conjunto de covariables significativa. También encontramos que todas las variables se encuentra de forma positiva, quiere decir que un aumento en ellas se traduce e un incremento del inmueble, pero en habitaciones, al aumentar el número de habitaciones sin que se incremente el metraje del apartamento puede llegar a disminuir su precio en 18.2 millones de pesos.
library(lmtest)
library(car)
par(mfrow = c(2, 2))
plot(modelo_apto)
par(mfrow = c(1,1))
bptest(modelo_apto)
##
## studentized Breusch-Pagan test
##
## data: modelo_apto
## BP = 667.11, df = 5, p-value < 2.2e-16
shapiro.test(residuals(modelo_apto)[1:5000])
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo_apto)[1:5000]
## W = 0.74687, p-value < 2.2e-16
vif(modelo_apto)
## areaconst estrato habitaciones parqueaderos banios
## 2.309762 1.772237 1.422627 2.225700 2.776279
Según Shapiro-Wilk se rechaza la hipótesis nula, dado que el valor p está por debajo de 0.05 y se considera que la distribución no es normal como en el caso del cliente 1. Y no existen problemas de multicolinealidad entre las variables predictoras, al encontrarse por debajo del umbral (<5).
solicitud_2 <- data.frame(
areaconst = 300,
estrato = 5,
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
pred_2 <- predict(modelo_apto, newdata = solicitud_2, interval = "confidence")
pred_2
## fit lwr upr
## 1 756.1842 728.2634 784.105
Según la predicción del precio de las caracteristicas del caso 2, el crédito preaprobado cumple satisfactoriamente con el posible precio del inmueble.
library(dplyr)
library(knitr)
precio_estimado_sol2 <- predict(modelo_apto, newdata = solicitud_2)
ofertas_sol2_especificas <- base2_limpia %>%
filter(!is.na(preciom)) %>%
filter(preciom <= 850) %>%
mutate(
diferencia_solicitud = abs(preciom - precio_estimado_sol2)
) %>%
arrange(diferencia_solicitud) %>%
slice_head(n = 5)
leaflet(ofertas_sol2_especificas) %>%
addTiles() %>%
addMarkers(
~longitud, ~latitud,
popup = ~paste0("<b>Tipo:</b>", tipo, "<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Parqueaderos:</b>", parqueaderos, "<br>",
"<b>Baños:</b>", banios, "<br>",
"<b>Habitaciones:</b> ", habitaciones, "<br>",
"<b>Estrato:</b>", estrato, "<br>",
"<b>Precio:</b> $", preciom, "M<br>")
)
Los datos categóricos no siempre reflejan la realidad espacial. Aunque un registro afirme pertenecer a la “Zona Norte”, el mapeo detallado reveló inconsistencias que requirieron una limpieza técnica basada en coordenadas.
El valor de las viviendas en la zona estudiada depende primordialmente del área construida, seguida por el estrato socioeconómico y la disponibilidad de baños y parqueaderos. El número de habitaciones no incrementa el valor, sino que debe estar respaldado por un tamaño en metros cuadrados.
Solicitud 1: Con un valor promedio estimado en los 320 millones y un límite de 346.5 millones, la solicitud es 100% viable frente al crédito pre-aprobado de 350 millones. Existe un margen cómodo para cubrir costos adicionales.
Solicitud 2: El crédito de 850 millones otorga un amplio poder de negociación en el mercado, permitiendo filtrar propiedades de mayor metraje, más parqueaderos y ubicaciones más exclusivas sin superar la capacidad financiera pre-aprobada.
Con los presupuestos de los dos clientes pueden encontrar viviendas según las características que ellos solicitan, así que pueden aceptar los créditos preaprobados para negociar las opciones que se les ha presentado.