Carga de librerías e Importación de Datos
suppressWarnings({
library(ggplot2)
library(ggmap)
library(dplyr)
library(naniar)
library(mice)
library(plotly)
library(datasets)
library(gridExtra)
library(leaflet)
library(boot)
library(paqueteMODELOS)
data("vivienda")
})
## ℹ Google's Terms of Service: <https://mapsplatform.google.com>
## Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service/>
## OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles/>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
##
## Attaching package: 'dplyr'
##
##
## The following objects are masked from 'package:stats':
##
## filter, lag
##
##
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
##
##
##
## Attaching package: 'mice'
##
##
## The following object is masked from 'package:stats':
##
## filter
##
##
## The following objects are masked from 'package:base':
##
## cbind, rbind
##
##
##
## Attaching package: 'plotly'
##
##
## The following object is masked from 'package:ggmap':
##
## wind
##
##
## 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
##
##
##
## Attaching package: 'gridExtra'
##
##
## The following object is masked from 'package:dplyr':
##
## combine
##
##
## Loading required package: broom
##
## Loading required package: GGally
##
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
##
## Loading required package: knitr
##
## Loading required package: summarytools
Zona norte de la ciudad. Se Presentan los primeros 3 registros de los datos filtrados
vivCasaZonaNorte <- subset(vivienda,
tipo=="Casa" &
zona=="Zona Norte" &
(estrato == 4 | estrato == 5))
head(vivCasaZonaNorte, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4460 Zona N… 02 4 625 355 3 5 5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Visualización de tablas que comprueban el filtrado de las casas
rangoArea <- c(180, 220)
rangoPrecio <- c(300, 400)
Agrupación por área
resArea <- vivCasaZonaNorte %>%
mutate(range_group = cut(areaconst, breaks = rangoArea)) %>%
group_by(range_group) %>%
summarize(count = n())
print(resArea)
## # A tibble: 2 × 2
## range_group count
## <fct> <int>
## 1 (180,220] 39
## 2 <NA> 393
Se tienen 39 casas con un área construida entre 180 y 220m2 para los datos filtrados.
Agrupación por precio
resPrecio <- vivCasaZonaNorte %>%
mutate(range_group = cut(preciom, breaks = rangoPrecio)) %>%
group_by(range_group) %>%
summarize(count = n())
print(resPrecio)
## # A tibble: 2 × 2
## range_group count
## <fct> <int>
## 1 (300,400] 127
## 2 <NA> 305
Se tienen 127 casas con un precio entre $300 y $400 millones entre los datos filtrados.
Casas que cumplen las condiciones de venta
casasCondiciones <- subset(vivCasaZonaNorte,
parqueaderos == 1 &
banios == 2 &
habitaciones == 4 &
(estrato == 4 | estrato == 5))
print(casasCondiciones)
## # A tibble: 4 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1666 Zona N… 02 4 275 120 1 2 4
## 2 3889 Zona N… 01 4 520 350 1 2 4
## 3 2557 Zona N… <NA> 5 900 850 1 2 4
## 4 1943 Zona N… <NA> 5 350 346 1 2 4
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Las casas en zona norte que cumplen con las condiciones establecidas para estrato, parqueaderos, baños y habitaciones son las 4 que se pueden apreciar en la tabla anterior.
Se genera un mapa con la ubicación de todas las casas para apreciar si se encuentran ubicadas de manera coherente en términos geográficos. Posteriormente se realiza un gráfico smilar sólo para las casas filtradas.
viviendaCasas <- subset(vivienda, tipo=="Casa")
ggplot(data = viviendaCasas, aes(x = longitud, y = latitud, color = zona)) +
geom_point() +
scale_color_manual(values = rainbow(length(unique(viviendaCasas$zona)))) +
labs(title = "Ubicación de casas según la zona",
x = "Longitud",
y = "Latitud",
color = "Zona")
ggplot(data = vivCasaZonaNorte, aes(x = longitud, y = latitud, color = zona)) +
geom_point() +
scale_color_manual(values = rainbow(length(unique(vivCasaZonaNorte$zona)))) +
labs(title = "Ubicación de casas filtradas",
x = "Longitud",
y = "Latitud",
color = "Zona")
Considerando los resultados de las gráficas anteriores, se puede deducir que no todas las casas se ubican en la zona correspondiente o, dicho de otra manera, algunas casas se ubican en otras zonas. Exite un número de razones por las que esto se puede presentar, entre ellas están: Errores en la entrada de los datos, errores de geocodificación, cambios en los límites, resolución espacial, características geográficas naturales o agregación de datos.
Se realiza una visualización inicial de los atributos, tipo y número de registros
str(vivCasaZonaNorte)
## spc_tbl_ [432 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:432] 1209 1592 4460 6081 7824 ...
## $ zona : chr [1:432] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
## $ piso : chr [1:432] "02" "02" "02" "02" ...
## $ estrato : num [1:432] 5 5 4 5 4 5 5 5 4 4 ...
## $ preciom : num [1:432] 320 780 625 750 600 420 490 520 305 350 ...
## $ areaconst : num [1:432] 150 380 355 237 160 200 118 455 117 118 ...
## $ parqueaderos: num [1:432] 2 2 3 2 1 4 2 NA NA NA ...
## $ banios : num [1:432] 4 3 5 6 4 4 4 5 3 3 ...
## $ habitaciones: num [1:432] 6 3 5 6 5 5 4 4 4 3 ...
## $ tipo : chr [1:432] "Casa" "Casa" "Casa" "Casa" ...
## $ barrio : chr [1:432] "acopi" "acopi" "acopi" "acopi" ...
## $ longitud : num [1:432] -76.5 -76.5 -76.5 -76.5 -76.6 ...
## $ latitud : num [1:432] 3.48 3.49 3.41 3.37 3.42 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Se realiza una exploración de los datos con el fin de identificar tipo de variables, datos faltantes y datos atípicos
summary(vivCasaZonaNorte)
## id zona piso estrato
## Min. : 58 Length:432 Length:432 Min. :4.000
## 1st Qu.:1500 Class :character Class :character 1st Qu.:4.000
## Median :3006 Mode :character Mode :character Median :5.000
## Mean :3037 Mean :4.627
## 3rd Qu.:4330 3rd Qu.:5.000
## Max. :8319 Max. :5.000
##
## preciom areaconst parqueaderos banios
## Min. : 125.0 Min. : 45.0 Min. :1.000 Min. : 0.0
## 1st Qu.: 350.0 1st Qu.: 200.0 1st Qu.:1.000 1st Qu.: 3.0
## Median : 450.0 Median : 280.0 Median :2.000 Median : 4.0
## Mean : 508.2 Mean : 301.7 Mean :2.313 Mean : 3.9
## 3rd Qu.: 600.0 3rd Qu.: 360.0 3rd Qu.:3.000 3rd Qu.: 5.0
## Max. :1940.0 Max. :1188.0 Max. :9.000 Max. :10.0
## NA's :106
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:432 Length:432 Min. :-76.59
## 1st Qu.: 4.000 Class :character Class :character 1st Qu.:-76.53
## Median : 4.000 Mode :character Mode :character Median :-76.52
## Mean : 4.593 Mean :-76.52
## 3rd Qu.: 5.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.47
##
## latitud
## Min. :3.364
## 1st Qu.:3.458
## Median :3.474
## Mean :3.462
## 3rd Qu.:3.483
## Max. :3.493
##
faltantesCasas <- colSums(is.na(vivCasaZonaNorte))
faltantesCasas
## id zona piso estrato preciom areaconst
## 0 0 203 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 106 0 0 0 0 0
## latitud
## 0
Rango de valores u opciones de parqueadero por casa en los datos filtrados.
parqueaderos = unique(vivCasaZonaNorte$parqueaderos)
parqueaderos
## [1] 2 3 1 4 NA 6 5 8 7 9
Del resumen anterior es posible apreciar que los atributos de número de parqueaderos y piso presentan una cantidad considerable de datos NA. Debido a que el número de pisos no se encuentra entre las condiciones de compra, no es necesario realizar ningún tipo de procesamiento sobre este campo. Para el caso del número de parqueaderos se considera realizar una imputación de los registros faltantes basado en la moda. Esto teniendo en consideración que la condición indica que se tenga “1” parqueadero, lo cual garantiza que el procedimiento tendrá un impacto menor sobre los resultados dado que el rango de valores se encuentra entre 1 y 9 parqueaderos por casa.
Se crea un subconjunto de los datos consideranndo sólo los atributos dados en las condiciones de compra.
vivCasaZonaNortePro <- select(vivCasaZonaNorte, -zona, -tipo, -piso)
Se presenta a continuación, una visualización de los datos faltantes.
gg_miss_var(vivCasaZonaNortePro)
grafico <-md.pattern(vivCasaZonaNortePro, rotate.names = TRUE)
Se realiza imputación de datos reemplazando los valores nulos por la moda
moda_parqueaderos <- as.numeric(names(sort(table(vivCasaZonaNorte$parqueaderos), decreasing = TRUE)[1]))
moda_parqueaderos
## [1] 2
vivCasaZonaNortePro$parqueaderos[is.na(vivCasaZonaNortePro$parqueaderos)] <- moda_parqueaderos
# Se grafica de nuevo para verificar la no existencia de valores nulos
grafico <-md.pattern(vivCasaZonaNortePro, rotate.names = TRUE)
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
# Scatter plot de precio vs. area
plot_ly(data = vivCasaZonaNortePro, x = ~areaconst, y = ~preciom, mode = "markers", text = ~paste("Precio: $", preciom)) %>%
layout(title = "Precio vs. Área",
xaxis = list(title = "Área"),
yaxis = list(title = "Precio"))
## No trace type specified:
## Based on info supplied, a 'scatter' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#scatter
Existe una correlación positiva entre precio y área construida, ya que a medida que aumenta el área también lo hace la variable respuesta, sin embargo, esta relación es más notoría y clara para áreas y precios más bajos, ya que a medida que crecen ambas variables también aumenta su dispersión.
# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~habitaciones, y = ~preciom, type = "box", color = ~factor(habitaciones)) %>%
layout(title = "Precio según Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
De la gráfica se puede visualizar que existe una correlación positiva entre el número de habitaciones y el precio de las casas a partir de las casas con 3 hasta 8 habitaciones. En este rango se aprecia como la mediana del precio es de $350 millones para 3 habitaciones y aumenta hasta las 8 habitaciones ($600 millones). Se aprecia que no hay casas de 1 habitación y que las casas con 0 habitaciones tienen una mediana superior a las casas de 5 habitaciones. Se visualiza lo particular de las casas con 10 habitaciones, cuyo rango intercuartil es superior a los $1100 millones de pesos.
# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~banios, y = ~preciom, type = "box", color = ~factor(banios)) %>%
layout(title = "Precio según Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
Se puede apreciar de la gráfica que la media del precio de las casas aumenta con el númerode baños partir de los 2 baños y hasta los 7 baños. No se visualizan casas con 1 sólo baño y las casas con 0 baños tienen una mediana superior a las casas con 4 baños, además de tener un rango intercuartil de $550 millones.
# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~parqueaderos, y = ~preciom, type = "box", color = ~factor(parqueaderos)) %>%
layout(title = "Precio según Número de parqueaderos",
xaxis = list(title = "Número de parqueaderos"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
De la gráfica se puede apreciar como el precio aumenta si se tienen más parqueaderos, situación que se mantiene para las casas con 6 habitaciones si se tiene en cuenta la mediana del precio, auqnue con el caso particular de las casas con 5 habitaciones, cuya mediana del precio es inferior a las casas con 4 habitaciones.
# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~estrato, y = ~preciom, type = "box", color = ~factor(estrato)) %>%
layout(title = "Precio según el estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
De la gráfica se puede apreciar como la mediana del precio para las casas en estrato 5 supera en $100 millones la mediana de las casas en estrato 4.
Se utiliza el método de mínimos cuadrados ordinarios a través de la función lm
modRLM <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivCasaZonaNortePro)
summary(modRLM)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = vivCasaZonaNortePro)
##
## Residuals:
## Min 1Q Median 3Q Max
## -781.30 -86.39 -19.78 50.69 991.18
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -128.76609 80.21930 -1.605 0.1092
## areaconst 0.93571 0.05675 16.488 <2e-16 ***
## estrato 42.60892 16.98026 2.509 0.0125 *
## habitaciones 12.41024 5.82515 2.130 0.0337 *
## parqueaderos 11.09931 7.09860 1.564 0.1187
## banios 19.42247 7.39067 2.628 0.0089 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 167.2 on 426 degrees of freedom
## Multiple R-squared: 0.5286, Adjusted R-squared: 0.5231
## F-statistic: 95.54 on 5 and 426 DF, p-value: < 2.2e-16
En un Modelo de Regresión Lineal Múltiple (MRLM), los coeficientes representan el efecto estimado de cada variable independiente sobre la variable dependiente (en este caso el precio de las casas), manteniendo todas las demás variables constantes.
El Intercepto: El intercepto representa el valor estimado de la variable dependiente (precio casa) cuando todas las varaibles independientes son cero. En este caso el intercepto es de $-128.77, aunque este valor no es estadisticamente significativo debido a que p-value > 0.05. Esto sugiere que el intercepto no es significativamente diferente de cero y no es un dato que aporte en la explicación de los precios de las casas.
Cada coeficiente representa el cambio estimado en la variable dependiente (precio casa) debido al cambio de una unidad de la variable independiente correspondiente, manteniendo las demás variables constantes.
El coeficiente de la variable independiente de área construida indica que el incremento de una unidad, es decir, de 1 metro cuadrado aumenta el precio de la casa en $0.94 millones.
Para el caso del estrato, el cambio de éste de 4 a 5 significa un aumento en el precio de la casa de $42.6 millones.
El coeficiente obtenido para el número de habitaciones estima que el tener una habitación más incrementa el valor de la casa en $12.4 millones.
El incremento de 1 espacio de parqueadero significa el aumento del precio de la casa en $11.1 millones.
El contar con un baño más en este tipo de vivienda indica que el valor de la casa se incrementaría en $19.42 millones.
Los valores-t miden el grado de significancia de cada coeficiente o variable independiente. En este caso, se tiene el área construida tiene una significancia fuerte, el número de baños y el estrato tienen una siignificancia moderada, las habitaciones una significancia marginal y los parqueaderos no tendrían significancia estadística.
Según los resultados obtenidos de p-value, el área construida tiene una significancia estadística alta, el número de baños una significancia moderada, el estrato y las habitaciones una significancia leve y los parqueaderos no tienen significancia estadística.
El error estándar residual (RSE, por sus siglas en inglés) es una medida de la variabilidad de los valores observados sobre la regresión lineal. En este caso se estimó con 426 obseraciones o grados de libertad. El valor RSE de $167.2 millones indica la diferencia promedio entre los precios observados y los valores predichos. Aunque parece un valor de error bastante alto, se debe considerar que el rango de precios es de más de $1800 millones, con un valor mínimo de $125 millones y un valor máximo de $1940.
El R2 mide la proporción del total de la variación de la variable dependiente que es representada por la variación de las variables independientes. En otras palabraas, el R2 es una medida de qué tan bien las variables independientes explican la variabilidad de la variable dependiente en un modelo de regresión. El valor obtenido de 0.52 sugiere que las variables independientes explican una proporción moderada de la variabilidad de la variable dependiente de precio de la casa.
La primera medida remedial para mejorar el ajuste del modelo sería quitar la variable independiente de parqueaderos, esto debido a que los resultados obtenidos de t-value y p-value indican que no es una variable que estadisticamente proporcione información para explicar la variabilidad de los precios de las casas.
Otra alternativa sería aplicar transformaciones sobre la variable respuesta y/o sobre las variables predictoras.
Una última alternativa serían los llamados modelos lineales generalizados, los cuales permiten modelar respuestas que no se distribuyen de manera normal.
Se seleccionan los índices aleatorios que formarán el conjunto de entrenamiento.
set.seed(1)
train <- sample(x = 1:426, 426*0.7)
Se crea el modelo de entrenamiento
modRLM_train <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivCasaZonaNortePro, subset = train)
summary(modRLM_train)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = vivCasaZonaNortePro, subset = train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -769.48 -82.42 -23.43 55.51 949.09
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -71.09323 100.12633 -0.710 0.47825
## areaconst 0.95051 0.06715 14.154 < 2e-16 ***
## estrato 35.11993 20.93035 1.678 0.09443 .
## habitaciones 21.23055 7.13524 2.975 0.00317 **
## parqueaderos 6.30737 8.67489 0.727 0.46776
## banios 5.52591 9.49877 0.582 0.56118
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 168.6 on 292 degrees of freedom
## Multiple R-squared: 0.5398, Adjusted R-squared: 0.5319
## F-statistic: 68.5 on 5 and 292 DF, p-value: < 2.2e-16
Se estima para el conjunto de datos de casas en zona norte y para el subconjunto de entrenamiento de éste la raíz del error cuadrático medio comparando el precio actual con el precio que se obtiene del modelo.
pred_train <- predict(object = modRLM_train, newdata = vivCasaZonaNortePro[-train, ])
rmse_train <- sqrt(mean((vivCasaZonaNortePro$preciom[-train] - pred_train)^2))
pred <- predict(object = modRLM, newdata = vivCasaZonaNortePro)
rmse <- sqrt(mean((vivCasaZonaNortePro$preciom - pred)^2))
rmse
## [1] 166.0318
rmse_train
## [1] 167.1801
Al comparar los resultados obtenidos del subconjunto de entrenamiento respecto al conjunto de datos de casas en zona norte, se puede apreciar que los residuales en el conjunto de entrenamiento tienen un rango intercuartil inferior al conjunto original. Respecto a las variables independientes, en el subconjunto de entrenamiento dejó de tener significancia estadística el número de baños. Respecto al error estándar residual y el rmse se observa que el conjunto de entrenamiento presenta valores más altos de error. Entre las alternativas que se tienen para reducir la multicolinealidad están: la eliminación de variables, la transformación de variables, el análisis de componentes principales (PCA) o la regularización, ya sea a través de la regresión Ridge o la técnica Lasso.
El primer paso consiste en crear un Dataframe con los valores mencionados en las condiciones de venta.
new_data <- data.frame(
areaconst = 200,
estrato = c(4, 5),
habitaciones = 4,
parqueaderos = 1,
banios = 2
)
Se realiza la predicción
predCasaCondVenta <- predict(object = modRLM, newdata = new_data)
predCasaCondVenta
## 1 2
## 328.3975 371.0064
Los resultados predichos indican que el precio de una casa que cumpla con las condiciones de venta en estrato 4 tendría un valor aproximado de $328.4 millones y en estrato 5 de $371 millones.
Si se tiene presente que el preaprobado es de $350 millones, esto estaría indicando que el precio que se debe considerar para la búsqueda de las potenciales ofertas es el predicho para estrato 4, lo cual no significa que puedan aparecer opciones que cumplan las condiciones y que sean estrato 5. Se toman las condciones de venta como filtros de búsqueda para buscar las casas cuyo valor se acerque al predicho por el MLRM.
# Se establecen las condiciones de venta como condicionantes de búsqueda
max_price <- 350
min_area <- 200
min_rooms <- 4
min_baths <- 2
min_parking <- 1
# Se calcula las diferencias absolutas entre el precio predicho para estrato 4 y los precios actuales de las casas
absolute_differences <- abs(vivCasaZonaNortePro$preciom - predCasaCondVenta[1])
# Se buscan los índices que se acerquen al precio predicho y cumpla las condiciones venta
closest_indices <- which(vivCasaZonaNortePro$preciom <= max_price &
vivCasaZonaNortePro$areaconst >= min_area &
vivCasaZonaNortePro$habitaciones >= min_rooms &
vivCasaZonaNortePro$banios >= min_baths &
vivCasaZonaNortePro$parqueaderos >= min_parking &
absolute_differences <= min(absolute_differences))[1:5]
# Se extraen los registros basado en los índices
ofertas_casas <- vivCasaZonaNortePro[closest_indices, ]
print(ofertas_casas)
## # A tibble: 5 × 10
## id estrato preciom areaconst parqueaderos banios habitaciones barrio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 7471 4 330 240 2 4 4 acopi
## 2 952 4 330 275 2 3 5 la merced
## 3 1108 4 330 260 1 3 4 la merced
## 4 3043 5 330 275 2 3 5 la merced
## 5 1849 5 330 246 2 4 4 prados del n…
## # ℹ 2 more variables: longitud <dbl>, latitud <dbl>
Se presenta en la tabla 5 potenciales ofertas que cumplen las condiciones de venta y cuyo precio es similar en todas $330 millones. De las 5 casas sugeridas, 3 de ellas se encuentran en estrato 4 y dos en estrato 5. Si se tuvieran el área construida y el número de habitaciones como criterios de elección, las casas con id 952 y id 3043 son las más grandes con 275m2 y 5 habitaciones. Estas dos opciones tienen 3 baños, uno más de lo establecido en las condiciones. Algo similar sucede con los parqueaderos ya que se registran 2 parqueaderos y en las condiciones de venta se establece 1. Las otras 3 opciones cuentan con 4 habitaciones y dos de ellas tienen 4 baños (ids 7471 y 1849). A continuación, se presentan las ofertas potenciales en un mapa de la ciudad para conocer su ubicación.
oferta <- data.frame(
lat = ofertas_casas$latitud,
long = ofertas_casas$longitud
)
# Crea un mapa
map <- leaflet(oferta) %>%
addTiles() %>%
addMarkers(
lng = ~long,
lat = ~lat,
popup = ~as.character(lat)
)
map # Muestra el mapa
En el mapa es posible apreciar que la casa ubicada con latitud 3.39 se encuentra ubicada en una zona alejada a las otras 4 ofertas, esta casa corresponde al id 7471.
Zona sur de la ciudad. Se Presentan los primeros 3 registros de los datos filtrados
vivAptoZonaSur <- subset(vivienda,
tipo=="Apartamento" &
zona=="Zona Sur" &
(estrato == 5 | estrato == 6))
head(vivAptoZonaSur, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 8199 Zona S… <NA> 6 875 194 2 5 3
## 2 8102 Zona S… 05 5 344 107 2 2 3
## 3 7073 Zona S… 02 6 910 182 2 4 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Visualización de tablas que comprueban el filtrado de los apartamentos
rangoArea <- c(280, 320)
rangoPrecio <- c(800, 900)
Agrupación por área
resArea <- vivAptoZonaSur %>%
mutate(range_group = cut(areaconst, breaks = rangoArea)) %>%
group_by(range_group) %>%
summarize(count = n())
print(resArea)
## # A tibble: 2 × 2
## range_group count
## <fct> <int>
## 1 (280,320] 13
## 2 <NA> 1482
Se tienen 13 apartamentos con un área construida entre 280 y 320m2 para los datos filtrados.
Agrupación por precio
resPrecio <- vivAptoZonaSur %>%
mutate(range_group = cut(preciom, breaks = rangoPrecio)) %>%
group_by(range_group) %>%
summarize(count = n())
print(resPrecio)
## # A tibble: 2 × 2
## range_group count
## <fct> <int>
## 1 (800,900] 36
## 2 <NA> 1459
Se tienen 36 apartamentos con un precio entre $800 y $900 millones entre los datos filtrados.
Apartamentos que cumplen las condiciones de venta
aptosCondiciones <- subset(vivAptoZonaSur,
parqueaderos >= 3 &
banios >= 3 &
habitaciones >= 5 &
(estrato == 5 | estrato == 6))
print(aptosCondiciones)
## # A tibble: 7 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5248 Zona S… 08 6 1150 344 4 5 5
## 2 6023 Zona S… 08 6 1150 464 4 6 5
## 3 6086 Zona S… 10 6 1500 240 3 5 6
## 4 4 Zona S… <NA> 6 1280 346 4 6 5
## 5 7182 Zona S… <NA> 5 730 573 3 8 5
## 6 7512 Zona S… <NA> 5 670 300 3 5 6
## 7 8036 Zona S… <NA> 5 530 256 3 5 5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Como se puede apreciar existen 7 apartamentos en zona sur que cumplan con las condiciones de venta establecidas para estrato, parqueaderos, baños y habitaciones.
Se genera un mapa con la ubicación de todas los apartamentos para apreciar si se encuentran ubicados de manera coherente en términos geográficos. Posteriormente se realiza un gráfico smilar sólo para los apartamentos filtrados.
viviendaApto <- subset(vivienda, tipo=="Apartamento")
ggplot(data = viviendaApto, aes(x = longitud, y = latitud, color = zona)) +
geom_point() +
scale_color_manual(values = rainbow(length(unique(viviendaApto$zona)))) +
labs(title = "Ubicación de apartamentos según la zona",
x = "Longitud",
y = "Latitud",
color = "Zona")
ggplot(data = vivAptoZonaSur, aes(x = longitud, y = latitud, color = zona)) +
geom_point() +
scale_color_manual(values = rainbow(length(unique(vivAptoZonaSur$zona)))) +
labs(title = "Ubicación de apartamentos filtrados",
x = "Longitud",
y = "Latitud",
color = "Zona")
Considerando los resultados de las gráficas anteriores, se puede deducir que no todas los apartamentos se ubican en la zona correspondiente o, dicho de otra manera, algunos apartamentos se ubican en otras zonas. Exite un número de razones por las que esto se puede presentar, entre ellas están: Errores en la entrada de los datos, errores de geocodificación, cambios en los límites, resolución espacial, características geográficas naturales o agregación de datos.
Se realiza una visualización inicial de los atributos, tipo y número de registros
str(vivAptoZonaSur)
## spc_tbl_ [1,495 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ id : num [1:1495] 8199 8102 7073 8288 8294 ...
## $ zona : chr [1:1495] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
## $ piso : chr [1:1495] NA "05" "02" "01" ...
## $ estrato : num [1:1495] 6 5 6 5 5 5 6 5 5 5 ...
## $ preciom : num [1:1495] 875 344 910 310 175 285 305 185 280 164 ...
## $ areaconst : num [1:1495] 194 107 182 166 64 120 125 78 94 54 ...
## $ parqueaderos: num [1:1495] 2 2 2 2 1 2 2 1 1 1 ...
## $ banios : num [1:1495] 5 2 4 4 2 4 3 2 2 2 ...
## $ habitaciones: num [1:1495] 3 3 3 3 2 3 3 3 3 3 ...
## $ tipo : chr [1:1495] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
## $ barrio : chr [1:1495] "aguacatal" "altos de guadalupe" "arboleda" "bella suiza" ...
## $ longitud : num [1:1495] -76.6 -76.6 -76.5 -76.6 -76.6 ...
## $ latitud : num [1:1495] 3.46 3.41 3.45 3.41 3.41 ...
## - attr(*, "spec")=List of 3
## ..$ cols :List of 13
## .. ..$ id : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ zona : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ piso : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ estrato : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ preciom : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ areaconst : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ parqueaderos: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ banios : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ habitaciones: list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ tipo : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ barrio : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
## .. ..$ longitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## .. ..$ latitud : list()
## .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
## ..$ default: list()
## .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
## ..$ delim : chr ";"
## ..- attr(*, "class")= chr "col_spec"
## - attr(*, "problems")=<externalptr>
Se realiza una exploración de los datos con el fin de identificar tipo de variables, datos faltantes y datos atípicos
summary(vivAptoZonaSur)
## id zona piso estrato
## Min. : 3 Length:1495 Length:1495 Min. :5.000
## 1st Qu.:3149 Class :character Class :character 1st Qu.:5.000
## Median :4424 Mode :character Mode :character Median :5.000
## Mean :4489 Mean :5.309
## 3rd Qu.:5974 3rd Qu.:6.000
## Max. :8299 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 93.0 Min. : 43 Min. : 1.000 Min. :0.000
## 1st Qu.: 250.0 1st Qu.: 84 1st Qu.: 1.000 1st Qu.:2.000
## Median : 310.0 Median :102 Median : 2.000 Median :3.000
## Mean : 386.7 Mean :117 Mean : 1.605 Mean :2.868
## 3rd Qu.: 450.0 3rd Qu.:133 3rd Qu.: 2.000 3rd Qu.:3.000
## Max. :1750.0 Max. :932 Max. :10.000 Max. :8.000
## NA's :56
## habitaciones tipo barrio longitud
## Min. :0.000 Length:1495 Length:1495 Min. :-76.56
## 1st Qu.:3.000 Class :character Class :character 1st Qu.:-76.54
## Median :3.000 Mode :character Mode :character Median :-76.53
## Mean :3.091 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :6.000 Max. :-76.46
##
## latitud
## Min. :3.334
## 1st Qu.:3.365
## Median :3.380
## Mean :3.384
## 3rd Qu.:3.398
## Max. :3.497
##
faltantesAptos <- colSums(is.na(vivAptoZonaSur))
faltantesAptos
## id zona piso estrato preciom areaconst
## 0 0 338 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 56 0 0 0 0 0
## latitud
## 0
Rango de valores u opciones de parqueadero por casa en los datos filtrados.
parqueaderos = unique(vivAptoZonaSur$parqueaderos)
parqueaderos
## [1] 2 1 NA 3 4 10
Del resumen anterior es posible apreciar que los atributos de número de parqueaderos y piso presentan una cantidad considerable de datos NA. Debido a que el número de pisos no se encuentra entre las condiciones de compra, no es necesario realizar ningún tipo de procesamiento sobre este campo. Para el caso del número de parqueaderos se considera realizar una imputación de los registros faltantes basado en la moda.
Se crea un subconjunto de los datos consideranndo sólo los atributos dados en las condiciones de compra.
vivAptoZonaSurPro <- select(vivAptoZonaSur, -zona, -tipo, -piso)
Se presenta a continuación, una visualización de los datos faltantes.
gg_miss_var(vivAptoZonaSurPro)
grafico <-md.pattern(vivAptoZonaSurPro, rotate.names = TRUE)
Se realiza imputación de datos reemplazando los valores nulos por la moda
moda_parq_apto <- as.numeric(names(sort(table(vivAptoZonaSur$parqueaderos), decreasing = TRUE)[1]))
moda_parq_apto
## [1] 1
vivAptoZonaSurPro$parqueaderos[is.na(vivAptoZonaSurPro$parqueaderos)] <- moda_parq_apto
# Se grafica de nuevo para verificar la no existencia de valores nulos
grafico <-md.pattern(vivAptoZonaSurPro, rotate.names = TRUE)
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
# Scatter plot de precio vs. area
plot_ly(data = vivAptoZonaSurPro, x = ~areaconst, y = ~preciom, mode = "markers", text = ~paste("Precio: $", preciom)) %>%
layout(title = "Precio vs. Área",
xaxis = list(title = "Área"),
yaxis = list(title = "Precio"))
## No trace type specified:
## Based on info supplied, a 'scatter' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#scatter
Existe una correlación positiva entre precio y área construida, ya que a medida que aumenta el área también lo hace la variable respuesta, sin embargo, esta relación es más notoría y clara para áreas y precios más bajos, ya que a medida que crecen ambas variables también aumenta su dispersión.
# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~habitaciones, y = ~preciom, type = "box", color = ~factor(habitaciones)) %>%
layout(title = "Precio según Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio"))
De la gráfica se puede visualizar que existe una correlación positiva entre el número de habitaciones y el precio de los apartamentos a partir de los que tienen 2 hasta 6 habitaciones. En este rango se aprecia como la media del precio es de $247.5 millones para 1 habitación y aumenta hasta las 6 habitaciones ($1085 millones). Se aprecia que los apartamentos con 0 habitaciones tienen una media superior a los apartamentos con 5 habitaciones. Se visualiza lo particular de los apartamentos con 5 habitaciones, cuyo rango intercuartil es superior a los $600 millones de pesos.
# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~banios, y = ~preciom, type = "box", color = ~factor(banios)) %>%
layout(title = "Precio según Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors
Se puede apreciar de la gráfica que la media del precio de los apartamentos aumenta con el númerode baños partir de 1 baño y hasta los 6 baños. Los apartamentos con 0 baños tienen una mediana superior a los apartamentos con 4 baños, además de tener un rango intercuartil de $550 millones.
# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~parqueaderos, y = ~preciom, type = "box", color = ~factor(parqueaderos)) %>%
layout(title = "Precio según Número de parqueaderos",
xaxis = list(title = "Número de parqueaderos"),
yaxis = list(title = "Precio"))
De la gráfica se puede apreciar como el precio aumenta si se tienen más parqueaderos, situación que se mantiene para los apartamentos que tienen entre 1 a 4 parqueaderos.
# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~estrato, y = ~preciom, type = "box", color = ~factor(estrato)) %>%
layout(title = "Precio según el estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels
De la gráfica se puede apreciar como la mediana del precio para los apartamentos en estrato 6 supera en $300 millones la mediana de los apartamentos en estrato 5.
Se utiliza el método de mínimos cuadrados ordinarios a través de la función lm
modRLM_aptos <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivAptoZonaSurPro)
summary(modRLM_aptos)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = vivAptoZonaSurPro)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1022.84 -52.87 1.80 43.33 889.80
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -711.57312 39.27457 -18.118 < 2e-16 ***
## areaconst 1.24594 0.06299 19.779 < 2e-16 ***
## estrato 141.50801 7.46654 18.952 < 2e-16 ***
## habitaciones -16.07190 5.47219 -2.937 0.00336 **
## parqueaderos 75.47675 5.25944 14.351 < 2e-16 ***
## banios 45.79788 4.32974 10.578 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 110.7 on 1489 degrees of freedom
## Multiple R-squared: 0.7396, Adjusted R-squared: 0.7387
## F-statistic: 845.8 on 5 and 1489 DF, p-value: < 2.2e-16
En un Modelo de Regresión Lineal Múltiple (MRLM), los coeficientes representan el efecto estimado de cada variable independiente sobre la variable dependiente (en este caso el precio de los apartamentos), manteniendo todas las demás variables constantes.
El Intercepto: El intercepto representa el valor estimado de la variable dependiente (precio apartamento) cuando todas las varaibles independientes son cero. En este caso el intercepto es de $-711.57, valor que es estadisticamente significativo debido a que p-value < 0.05. Esto sugiere que el intercepto es un dato que aporte en la explicación de los precios de los apartamentos.
Cada coeficiente representa el cambio estimado en la variable dependiente (precio apartamento) debido al cambio de una unidad de la variable independiente correspondiente, manteniendo las demás variables constantes.
El coeficiente de la variable independiente de área construida indica que el incremento en una unidad, es decir, de 1 metro cuadrado aumenta el precio del apartamento en $1.24 millones.
Para el caso del estrato, el cambio de éste de 5 a 6 significa un aumento en el precio del apartamento de $141.5 millones.
El coeficiente obtenido para el número de habitaciones estima que el tener una habitación más disminuye el valor del apartamento en $16 millones. En este caso, se presenta una situación particular que no coincide con lo visto graficamente en la sección anterior, en dónde se apreciaba que el precio de los apartamentos aumentaba al tener más habitaciones. Este resultado en el coeficiente puede deberse a la cantidad de apartamentos registrados con 0 habitaciones, cuyo valor de la mediana del precio es superior a la mediana de precio de los apartamentos que tienen hasta 5 habitaciones.
El incremento de 1 espacio de parqueadero significa el aumento del precio del apartamento en $75.47 millones.
El contar con un baño más en este tipo de vivienda indica que el valor del apartamento se incrementaría en $45.8 millones.
Los valores-t miden el grado de significancia de cada coeficiente o variable independiente. En este caso, se tiene el número de habitaciones como la única variable con significancia baja.
Según los resultados obtenidos de p-value, todas las variables tienen una significancia estadística alta a excepción del número de habitaciones.
El error estándar residual (RSE) es una medida de la variabilidad de los valores observados sobre la regresión lineal. En este caso se estimó con 1489 obseraciones o grados de libertad. El valor RSE de $110.7 millones indica la diferencia promedio entre los precios observados y los valores predichos. Aunque parece un valor de error alto, se debe considerar que el rango de precios es de más de $1650 millones, con un valor mínimo de $93 millones y un valor máximo de $1750.
El R2 mide la proporción del total de la variación de la variable dependiente que es representada por la variación de las variables independientes. En otras palabras, el R2 es una medida de qué tan bien las variables independientes explican la variabilidad de la varaible dependiente en un modelo de regresión. El valor obtenido de 0.73 sugiere que las variables independientes explican en buena proporción la variabilidad de la variable dependiente de precio del apartamento.
La primera medida remedial para mejorar el ajuste del modelo sería quitar la variable independiente de número de habitaciones, esto debido a que los resultados obtenidos de t-value y p-value indican que, a diferencia de las otras variables, no es una variable que estadisticamente proporcione información para explicar la variabilidad de los precios de los apartamentos
Otra alternativa sería aplicar transformaciones sobre la variable respuesta y/o sobre las variables predictoras.
Una última alternativa serían los llamados modelos lineales generalizados, los cuales permiten modelar respuestas que no se distribuyen de manera normal.
Se seleccionan los índices aleatorios que formarán el conjunto de entrenamiento.
set.seed(1)
train_apto <- sample(x = 1:1489, 1489*0.7)
Se crea el modelo de entrenamiento
modRLM_train_apto <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivAptoZonaSurPro, subset = train_apto)
summary(modRLM_train_apto)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = vivAptoZonaSurPro, subset = train_apto)
##
## Residuals:
## Min 1Q Median 3Q Max
## -841.41 -53.92 -0.32 44.90 922.19
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -722.90926 46.47178 -15.556 <2e-16 ***
## areaconst 1.02992 0.06773 15.206 <2e-16 ***
## estrato 142.69409 8.77761 16.257 <2e-16 ***
## habitaciones -10.51341 6.52415 -1.611 0.107
## parqueaderos 79.24519 5.97209 13.269 <2e-16 ***
## banios 47.42097 4.98102 9.520 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 109.6 on 1036 degrees of freedom
## Multiple R-squared: 0.7378, Adjusted R-squared: 0.7366
## F-statistic: 583.1 on 5 and 1036 DF, p-value: < 2.2e-16
Se estima para el conjunto de datos de apartamentos en zona sur y para el subconjunto de entrenamiento de éste, la raíz del error cuadrático medio comparando el precio actual con el precio que se obtiene del modelo.
pred_train_apto <- predict(object = modRLM_train_apto, newdata = vivAptoZonaSurPro[-train_apto, ])
rmse_train_apto <- sqrt(mean((vivAptoZonaSurPro$preciom[-train_apto] - pred_train_apto)^2))
pred_apto <- predict(object = modRLM_aptos, newdata = vivAptoZonaSurPro)
rmse_apto <- sqrt(mean((vivAptoZonaSurPro$preciom - pred_apto)^2))
rmse_apto
## [1] 110.4636
rmse_train_apto
## [1] 114.6813
Al comparar los resultados obtenidos del subconjunto de entrenamiento respecto al conjunto de datos de apartamentos en zona sur, se puede apreciar que los residuales en el conjunto de entrenamiento tienen un rango intercuartil superior al conjunto original. Respecto a las variables independientes, en el subconjunto de entrenamiento pasó a tener aún menos significancia el número de habitaciones. Respecto al error estándar residual se puede apreciar uuna disminución en el conjunto de entrenamiento, mientras que el rmse se observa para el conjunto de entrenamiento un valor más alto de error. Entre las alternativas que se tienen para reducir la multicolinealidad están: la eliminación de variables, la transformación de variables, el análisis de componentes principales (PCA) o la regularización, ya sea a través de la regresión Ridge o la técnica Lasso.
El primer paso consiste en crear un Dataframe con los valores mencionados en las condiciones de venta.
new_data_apto <- data.frame(
areaconst = 300,
estrato = c(5, 6),
habitaciones = 5,
parqueaderos = 3,
banios = 3
)
Se realiza la predicción
predAptoCondVenta <- predict(object = modRLM_aptos, newdata = new_data_apto)
predAptoCondVenta
## 1 2
## 653.2136 794.7216
Los resultados predichos indican que el precio de un apartamento que cumpla con las condiciones de venta en estrato 5 tendría un valor aproximado de $653.2 millones y en estrato 6 de $794.7 millones.
Si se tiene presente que el preaprobado es de $850 millones, esto estaría indicando que es posible considerar los precios de ambos estratos en la búsqueda de las ofertas potenciales. Se toman las condciones de venta como filtros de búsqueda para buscar los apartamentos cuyo valor se acerque a los predichos por el MLRM.
# Se establecen las condiciones de venta como condicionantes de búsqueda
max_price <- 850
min_area <- 300
min_rooms <- 5
min_baths <- 3
min_parking <- 3
# Se calcula las diferencias absolutas entre el precio predicho para estrato 5 y los precios actuales de los apartamentos
absolute_diff_aptos <- abs(vivAptoZonaSurPro$preciom - predAptoCondVenta[2])
# Se buscan los índices que se acerquen al precio predicho y cumpla las condiciones venta
closest_ind_apto <- which(vivAptoZonaSurPro$preciom <= max_price &
vivAptoZonaSurPro$areaconst >= min_area &
vivAptoZonaSurPro$habitaciones >= min_rooms &
vivAptoZonaSurPro$banios >= min_baths &
vivAptoZonaSurPro$parqueaderos >= min_parking )
closest_ind_apto <- closest_ind_apto[order(absolute_diff_aptos[closest_ind_apto])][1:5]
# Se extraen los registros basado en los índices
ofertas_aptos <- vivAptoZonaSurPro[closest_ind_apto, ]
print(ofertas_aptos)
## # A tibble: 5 × 10
## id estrato preciom areaconst parqueaderos banios habitaciones barrio
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 7182 5 730 573 3 8 5 guadalupe
## 2 7512 5 670 300 3 5 6 seminario
## 3 NA NA NA NA NA NA NA <NA>
## 4 NA NA NA NA NA NA NA <NA>
## 5 NA NA NA NA NA NA NA <NA>
## # ℹ 2 more variables: longitud <dbl>, latitud <dbl>
Se presenta en la tabla as únicas 2 potenciales ofertas que cumplen las condiciones de venta y cuyo precio es inferior a $850 millones. De los 2 apartamentos sugeridos, el de mayor valor tiene un área de 573m2, 3 parqueaderos, 8 baños y 5 habitaciones, mientras que la otra opción tiene un área de 300m2, 3 parqueaderos, 5 baños y 6 habitaciones. A continuación, se presentan las ofertas potenciales en un mapa de la ciudad para conocer su ubicación.
oferta <- data.frame(
lat = ofertas_aptos$latitud,
long = ofertas_aptos$longitud
)
# Crea un mapa
map <- leaflet(oferta) %>%
addTiles() %>%
addMarkers(
lng = ~long,
lat = ~lat,
popup = ~as.character(lat)
)
## Warning in validateCoords(lng, lat, funcName): Data contains 3 rows with either
## missing or invalid lat/lon values and will be ignored
map # Muestra el mapa