Punto 1: Filtro de Base de Datos - Casas Zona Norte
Primero, verificamos los datos, ademas
Se filtra para que nos indique los datos
de la zona norte
Carga de Librerías y Datos
# Carga de librerías
library(devtools)
library(paqueteMODELOS)
library(dplyr)
library(leaflet)
library(knitr)
# Carga de datos
data("vivienda")
cat("Base de datos original:", nrow(vivienda), "registros\n")
## Base de datos original: 8322 registros
# Filtrar para obtener solo casas de la zona norte
base1 <- vivienda %>%
filter(tipo == "Casa", zona == "Norte")
cat("Base filtrada (casas zona norte):", nrow(base1), "registros\n")
## Base filtrada (casas zona norte): 0 registros
2. Exploración
Se realiza una exploración, para verificar el tipo de valores
Encontrando valores como “Casa”, “Apartamento” y las diferentes
zonas donde estan ubicadas.
Se observa en la tabla, las frecuencias de las casas, apartamentos
y
cada una de las zonas donde estan ubicadas. Esto con la finalidad,
de
verificar la oferta inmobiliara de la ciudad en especial de la zona
norte
y los tipos de vivienda.
cat("Valores únicos en 'tipo':\n")
## Valores únicos en 'tipo':
unique(vivienda$tipo)
## [1] "Casa" "Apartamento" NA
cat("\nValores únicos en 'zona':\n")
##
## Valores únicos en 'zona':
unique(vivienda$zona)
## [1] "Zona Oriente" "Zona Sur" "Zona Norte" "Zona Oeste" "Zona Centro"
## [6] NA
cat("\nFrecuencia de tipos de vivienda:\n")
##
## Frecuencia de tipos de vivienda:
table(vivienda$tipo) %>% kable()
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
## Warning in attr(x, "format"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
| Apartamento |
5100 |
| Casa |
3219 |
cat("\nFrecuencia de zonas:\n")
##
## Frecuencia de zonas:
table(vivienda$zona) %>% kable()
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
| Zona Centro |
124 |
| Zona Norte |
1920 |
| Zona Oeste |
1198 |
| Zona Oriente |
351 |
| Zona Sur |
4726 |
3.Filtro base de datos
Se filtra la base de datos para verificar cuantos registros
existe de interes para el estudio (zona norte)
# Filtrar con los valores exactos
base1 <- vivienda %>%
filter(tipo == "Casa", zona == "Zona Norte")
cat("Base filtrada (casas Zona Norte):", nrow(base1), "registros\n")
## Base filtrada (casas Zona Norte): 722 registros
# Verificar cuántos registros tenemos
if(nrow(base1) > 0) {
cat("¡Filtro exitoso! Se encontraron", nrow(base1), "casas en Zona Norte\n")
} else {
cat("No se encontraron registros. Revisar los valores en la exploración.\n")
}
## ¡Filtro exitoso! Se encontraron 722 casas en Zona Norte
4. Primeros registros
Se valida los primeros tres registros del dataset
if(nrow(base1) > 0) {
cat("Primeros 3 registros de la base filtrada:\n")
head(base1, 3) %>%
select(tipo, zona, preciom, areaconst, barrio, habitaciones, banios, parqueaderos, estrato) %>%
kable(caption = "Primeras tres casas de la Zona Norte")
} else {
cat("No hay registros para mostrar.\n")
}
## Primeros 3 registros de la base filtrada:
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
## Warning in attr(x, "format"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
Primeras tres casas de la Zona Norte
| Casa |
Zona Norte |
320 |
150 |
acopi |
6 |
4 |
2 |
5 |
| Casa |
Zona Norte |
780 |
380 |
acopi |
3 |
3 |
2 |
5 |
| Casa |
Zona Norte |
750 |
445 |
acopi |
6 |
7 |
NA |
6 |
5. Verificación de filtros
Se prueba la efectividad del filtro al buscar
los valores de casas de la zona norte, tambien se hace
un resumen estadistico para verificar valores faltantes o
que afecten el analisis.
if(nrow(base1) > 0) {
cat("Verificación del filtro por tipo de vivienda:\n")
table(base1$tipo) %>% kable(caption = "Distribución por tipo de vivienda")
cat("\nVerificación del filtro por zona:\n")
table(base1$zona) %>% kable(caption = "Distribución por zona")
cat("\nResumen de variables clave:\n")
summary(base1 %>% select(preciom, areaconst, habitaciones, banios, parqueaderos, estrato)) %>%
kable(caption = "Resumen estadístico de variables numéricas")
} else {
cat("No hay datos para verificar.\n")
}
## Verificación del filtro por tipo de vivienda:
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
##
## Verificación del filtro por zona:
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
##
## Resumen de variables clave:
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
## Warning in attr(x, "format"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
Resumen estadístico de variables numéricas
|
Min. : 89.0 |
Min. : 30.0 |
Min. : 0.000 |
Min. : 0.000 |
Min. : 1.000 |
Min. :3.000 |
|
1st Qu.: 261.2 |
1st Qu.: 140.0 |
1st Qu.: 3.000 |
1st Qu.: 2.000 |
1st Qu.: 1.000 |
1st Qu.:3.000 |
|
Median : 390.0 |
Median : 240.0 |
Median : 4.000 |
Median : 3.000 |
Median : 2.000 |
Median :4.000 |
|
Mean : 445.9 |
Mean : 264.9 |
Mean : 4.507 |
Mean : 3.555 |
Mean : 2.182 |
Mean :4.202 |
|
3rd Qu.: 550.0 |
3rd Qu.: 336.8 |
3rd Qu.: 5.000 |
3rd Qu.: 4.000 |
3rd Qu.: 3.000 |
3rd Qu.:5.000 |
|
Max. :1940.0 |
Max. :1440.0 |
Max. :10.000 |
Max. :10.000 |
Max. :10.000 |
Max. :6.000 |
|
NA |
NA |
NA |
NA |
NA’s :287 |
NA |
6. Mapa ubicaciones
Finalmente, se realiza un mapa con las ubicaciones
de cada una de las propieades en cada sector
if(nrow(base1) > 0) {
# Crear mapa interactivo
mapa_base1 <- leaflet(base1) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
popup = ~paste(
"<b>Precio:</b>", preciom, "millones<br>",
"<b>Barrio:</b>", barrio, "<br>",
"<b>Área:</b>", areaconst, "m²<br>",
"<b>Habitaciones:</b>", habitaciones, "<br>",
"<b>Baños:</b>", banios, "<br>",
"<b>Parqueaderos:</b>", parqueaderos, "<br>",
"<b>Estrato:</b>", estrato
),
color = "blue",
fillOpacity = 0.7,
radius = 6,
clusterOptions = markerClusterOptions()
) %>%
setView(
lng = mean(base1$longitud, na.rm = TRUE),
lat = mean(base1$latitud, na.rm = TRUE),
zoom = 12
) %>%
addControl(
"Casas - Zona Norte",
position = "topright"
)
# Mostrar mapa
mapa_base1
} else {
cat("No hay datos para crear el mapa.\n")
}
Análisis de Validación de Ubicación
Se verifica las coordenadas de las zonas
if(nrow(base1) > 0) {
# Verificar si hay puntos fuera de la zona norte
coordenadas_limite <- data.frame(
norte_max = max(base1$latitud, na.rm = TRUE),
norte_min = min(base1$latitud, na.rm = TRUE),
este_max = max(base1$longitud, na.rm = TRUE),
este_min = min(base1$longitud, na.rm = TRUE)
)
cat("Límites geográficos de las propiedades:\n")
kable(coordenadas_limite, caption = "Límites de coordenadas de las viviendas")
# Contar registros con coordenadas fuera de rangos esperados
latitud_media <- mean(base1$latitud, na.rm = TRUE)
longitud_media <- mean(base1$longitud, na.rm = TRUE)
cat("\nCoordenadas promedio de las viviendas:\n")
cat("Latitud promedio:", round(latitud_media, 4), "\n")
cat("Longitud promedio:", round(longitud_media, 4), "\n")
} else {
cat("No hay datos para validación de ubicación.\n")
}
## Límites geográficos de las propiedades:
## Warning in attr(x, "align"): 'xfun::attr()' está en desuso.
## Utilizar 'xfun::attr2()' en su lugar.
## Ver help("Deprecated")
##
## Coordenadas promedio de las viviendas:
## Latitud promedio: 3.4602
## Longitud promedio: -76.5171
PUNTO 2.
Se realiza la limpieza de la base de datos
En la primer figura, se verifica los precios
de cada zona en función del área,se puede observar
que la zona sur presenta los valores con mayores precios
y mayor área ocupada.
En el grafico de bigotes, notamos que los estratos 3 presenta los
precios más bajos
Mientras que el estrato 5, presenta la mayor variabilidad de
precios
y brechas en el mercado.
En el ultimo grafico, el precio se ve afectado en su mayoria, por el
estrato
y área construida.
# Paquetes para EDA
library(plotly)
## Warning: package 'plotly' was built under R version 4.4.3
##
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(tidyr)
library(forcats)
# Limpieza mínima y tipos
vivienda2 <- vivienda %>%
mutate(
zona = as.factor(zona),
tipo = as.factor(tipo),
estrato = as.integer(estrato)
) %>%
select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos, zona, tipo, barrio, latitud, longitud) %>%
filter(complete.cases(.))
# 2.1 Dispersión interactiva Precio vs Área (color = zona)
p_scatter_area <- plot_ly(
vivienda2,
x = ~areaconst, y = ~preciom, color = ~zona, type = "scatter", mode = "markers",
hoverinfo = "text",
text = ~paste("Barrio:", barrio,
"<br>Estrato:", estrato,
"<br>Baños:", banios,
"<br>Habitaciones:", habitaciones,
"<br>Parqueaderos:", parqueaderos)
)
p_scatter_area
# 2.2 Boxplot interactivo Precio por Estrato
p_box_estrato <- plot_ly(vivienda2, x = ~as.factor(estrato), y = ~preciom, type = "box")
p_box_estrato
# 2.3 Heatmap de correlaciones (solo numéricas)
num_vars <- vivienda2 %>% select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos)
corr_mat <- cor(num_vars, use = "pairwise.complete.obs")
p_heat <- plot_ly(
x = colnames(corr_mat), y = rownames(corr_mat),
z = corr_mat, type = "heatmap"
)
p_heat
PUNTO 3
En este item, se construye el modelo
Al realizar el modelo se obtiene un R cuadrado de 0.73
esto nos indica que el modelo logra predeccir los valores
Adecuadamente.
# Paquetes necesarios
library(broom) # Para organizar resultados del modelo
library(car) # Para VIF más adelante
## Warning: package 'car' was built under R version 4.4.3
## Cargando paquete requerido: carData
## Warning: package 'carData' was built under R version 4.4.3
##
## Adjuntando el paquete: 'car'
## The following object is masked from 'package:dplyr':
##
## recode
## The following object is masked from 'package:boot':
##
## logit
# Ajustar el modelo de regresión lineal múltiple
mod_lm <- lm(preciom ~ areaconst + estrato + banios + habitaciones + parqueaderos + zona,
data = vivienda2)
# Resumen del modelo
summary(mod_lm)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## parqueaderos + zona, data = vivienda2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1452.89 -85.65 -10.95 59.31 1018.96
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -254.03343 24.79054 -10.247 < 2e-16 ***
## areaconst 0.85161 0.02158 39.459 < 2e-16 ***
## estrato 85.63039 2.98804 28.658 < 2e-16 ***
## banios 59.79635 2.55941 23.363 < 2e-16 ***
## habitaciones -29.44326 2.22741 -13.219 < 2e-16 ***
## parqueaderos 74.71110 2.51343 29.725 < 2e-16 ***
## zonaZona Norte -97.09282 22.35596 -4.343 1.43e-05 ***
## zonaZona Oeste 27.75437 23.04952 1.204 0.2286
## zonaZona Oriente -50.96421 25.37667 -2.008 0.0447 *
## zonaZona Sur -90.21347 22.13048 -4.076 4.63e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 171.9 on 6707 degrees of freedom
## Multiple R-squared: 0.7371, Adjusted R-squared: 0.7368
## F-statistic: 2090 on 9 and 6707 DF, p-value: < 2.2e-16
# Tabla ordenada de coeficientes para el informe
tidy(mod_lm) %>% arrange(p.value)
## # A tibble: 10 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 areaconst 0.852 0.0216 39.5 2.06e-306
## 2 parqueaderos 74.7 2.51 29.7 1.66e-182
## 3 estrato 85.6 2.99 28.7 1.72e-170
## 4 banios 59.8 2.56 23.4 3.96e-116
## 5 habitaciones -29.4 2.23 -13.2 2.12e- 39
## 6 (Intercept) -254. 24.8 -10.2 1.84e- 24
## 7 zonaZona Norte -97.1 22.4 -4.34 1.43e- 5
## 8 zonaZona Sur -90.2 22.1 -4.08 4.63e- 5
## 9 zonaZona Oriente -51.0 25.4 -2.01 4.47e- 2
## 10 zonaZona Oeste 27.8 23.0 1.20 2.29e- 1
# R² y R² ajustado (qué tan bien explica el modelo)
glance(mod_lm)[, c("r.squared", "adj.r.squared", "sigma")]
## # A tibble: 1 × 3
## r.squared adj.r.squared sigma
## <dbl> <dbl> <dbl>
## 1 0.737 0.737 172.
PUNTO 4
En el item 4, se realiza la validación del modelo
el grafico de residuos vs predicciones, se verifica que no
tiene
un patrón (aleatorio) y en el grafico normaldiad de errores, estos
los
los puntos no forman una linea
Sin embargo al realizar la prueba de heterocedasticidad, el modelo
presenta errores
teniendo problemas con determinadas propieades. Mientras en la
prueba multicolineal,
se determian que las distintas variables aportan información
importante al modelo.
Es recomendable tener precaución con el modelo al determinar los
precios de las
PUNTO 5
Se realizan, nuevas predecciones con las variables de los estratos 4
y 5
# Crear nuevas observaciones con estrato 4 y 5
new_v1_e4 <- data.frame(
areaconst = 200, estrato = 4, banios = 2, habitaciones = 4, parqueaderos = 1,
zona = factor("Zona Norte", levels = levels(vivienda2$zona))
)
new_v1_e5 <- data.frame(
areaconst = 200, estrato = 5, banios = 2, habitaciones = 4, parqueaderos = 1,
zona = factor("Zona Norte", levels = levels(vivienda2$zona))
)
# Predicciones con intervalos de confianza
pred_v1_e4 <- predict(mod_lm, new_v1_e4, interval = "prediction")
pred_v1_e5 <- predict(mod_lm, new_v1_e5, interval = "prediction")
pred_v1_e4
## fit lwr upr
## 1 238.249 -98.91081 575.4088
pred_v1_e5
## fit lwr upr
## 1 323.8794 -13.32727 661.0861
PUNTO 6
Se realiza el filtro inteligente, en las propieades en la zona
norte
entre los distintos estratos, permitiendo así seleccionar la
adecuada
segun el precio, área y numero de habitaciones.
# Agregar columna con predicción para todas las viviendas
vivienda2$pred_lm <- predict(mod_lm, vivienda2)
# Filtrar candidatas (condiciones similares a Vivienda 1)
ofertas_v1 <- vivienda2 %>%
filter(tipo == "Casa",
zona == "Zona Norte",
areaconst >= 180 & areaconst <= 220, # rango alrededor de 200 m²
banios >= 2,
habitaciones >= 4,
parqueaderos >= 1,
pred_lm <= 350)
# Mostrar primeras 5
head(ofertas_v1, 5)
## # A tibble: 5 × 12
## preciom areaconst estrato banios habitaciones parqueaderos zona tipo barrio
## <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct> <fct> <chr>
## 1 270 196 3 2 4 1 Zona … Casa calima
## 2 400 186 5 3 5 1 Zona … Casa el bo…
## 3 355 190 4 3 5 2 Zona … Casa la fl…
## 4 365 198 4 3 5 2 Zona … Casa la me…
## 5 360 216 4 2 4 2 Zona … Casa la me…
## # ℹ 3 more variables: latitud <dbl>, longitud <dbl>, pred_lm <dbl>
# Mapa de las ofertas candidatas
leaflet(ofertas_v1) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud, lat = ~latitud,
popup = ~paste0("<b>Precio predicho:</b> ", round(pred_lm,1), " M<br>",
"<b>Precio real:</b> ", preciom, " M<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"<b>Habitaciones:</b> ", habitaciones,
" | <b>Baños:</b> ", banios,
" | <b>Parq:</b> ", parqueaderos,
"<br><b>Barrio:</b> ", barrio),
radius = 6, fillOpacity = 0.7, clusterOptions = markerClusterOptions()
)
PUNTO 7
Se realiza un filtro para los estratos 5 y 6, teniendo como base un
presupuesto de
850 millones. Se logra filtrar propieades que se encuentren dentro
de este presupesto
y que cumplan la condición de estrato
# Crear observaciones para estrato 5 y 6
new_v2_e5 <- data.frame(
areaconst = 300, estrato = 5, banios = 3, habitaciones = 5, parqueaderos = 3,
zona = factor("Zona Sur", levels = levels(vivienda2$zona))
)
new_v2_e6 <- data.frame(
areaconst = 300, estrato = 6, banios = 3, habitaciones = 5, parqueaderos = 3,
zona = factor("Zona Sur", levels = levels(vivienda2$zona))
)
# Predicciones
predict(mod_lm, new_v2_e5, interval = "prediction")
## fit lwr upr
## 1 595.6955 258.5521 932.8389
predict(mod_lm, new_v2_e6, interval = "prediction")
## fit lwr upr
## 1 681.3259 344.0971 1018.555
7.2
Finalmente, se realiza un mapa con las opciones que cumplan con
estas caracteristicas.
# Filtrar candidatas similares a Vivienda 2
ofertas_v2 <- vivienda2 %>%
filter(tipo == "Apartamento",
zona == "Zona Sur",
areaconst >= 270 & areaconst <= 330,
banios >= 3,
habitaciones >= 5,
parqueaderos >= 3,
pred_lm <= 850)
head(ofertas_v2, 5)
## # A tibble: 2 × 12
## preciom areaconst estrato banios habitaciones parqueaderos zona tipo barrio
## <dbl> <dbl> <int> <dbl> <dbl> <dbl> <fct> <fct> <chr>
## 1 370 300 3 6 5 3 Zona … Apar… melen…
## 2 670 300 5 5 6 3 Zona … Apar… semin…
## # ℹ 3 more variables: latitud <dbl>, longitud <dbl>, pred_lm <dbl>
# Mapa de las ofertas candidatas
leaflet(ofertas_v2) %>%
addTiles() %>%
addCircleMarkers(
lng = ~longitud, lat = ~latitud,
popup = ~paste0("<b>Precio predicho:</b> ", round(pred_lm,1), " M<br>",
"<b>Precio real:</b> ", preciom, " M<br>",
"<b>Área:</b> ", areaconst, " m²<br>",
"<b>Estrato:</b> ", estrato, "<br>",
"<b>Habitaciones:</b> ", habitaciones,
" | <b>Baños:</b> ", banios,
" | <b>Parq:</b> ", parqueaderos,
"<br><b>Barrio:</b> ", barrio),
radius = 6, fillOpacity = 0.7, clusterOptions = markerClusterOptions()
)