# Instalación y cargue de librerías
library(paqueteMODELOS)
library (kableExtra)
library(leaflet)
library(dplyr)
library(mice)
library(plotly)
library(GGally)
library(lmtest)
library(knitr)El presente ejercicio tiene como objetivo la generación de reflexiones que permitan tomar decisiones de negocio acertadas en el mercado inmobiliario de la ciudad de Santiago de Cali. Concretamente, se adelanta dicho análisis para hacer recomendaciones frente a la compra de dos inmuebles con características específicas:
tabla_viviendas <- data.frame(
Características = c("Tipo", "Área Construida", "Número de parqueaderos",
"Número de Baños", "Número de Habitaciones", "Estrato",
"Zona", "Crédito Pre-aprobado"),
Vivienda_1 = c("Casa", 200, 1, 2, 4, "4 o 5","Norte", "300 millones"),
Vivienda_2 = c("Apartamento",300, 3, 3, 5, "5 o 6","Sur", "850 millones")
)
# Mostrar la tabla usando kable
kable(tabla_viviendas, col.names = c("Características", "Vivienda 1", "Vivienda 2"),
caption = "Requisitos de Viviendas")| Características | Vivienda 1 | Vivienda 2 |
|---|---|---|
| Tipo | Casa | Apartamento |
| Área Construida | 200 | 300 |
| Número de parqueaderos | 1 | 3 |
| Número de Baños | 2 | 3 |
| Número de Habitaciones | 4 | 5 |
| Estrato | 4 o 5 | 5 o 6 |
| Zona | Norte | Sur |
| Crédito Pre-aprobado | 300 millones | 850 millones |
## id zona piso estrato
## Min. : 1 Length:8322 Length:8322 Min. :3.000
## 1st Qu.:2080 Class :character Class :character 1st Qu.:4.000
## Median :4160 Mode :character Mode :character Median :5.000
## Mean :4160 Mean :4.634
## 3rd Qu.:6240 3rd Qu.:5.000
## Max. :8319 Max. :6.000
## NA's :3 NA's :3
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 30.0 Min. : 1.000 Min. : 0.000
## 1st Qu.: 220.0 1st Qu.: 80.0 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 330.0 Median : 123.0 Median : 2.000 Median : 3.000
## Mean : 433.9 Mean : 174.9 Mean : 1.835 Mean : 3.111
## 3rd Qu.: 540.0 3rd Qu.: 229.0 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1999.0 Max. :1745.0 Max. :10.000 Max. :10.000
## NA's :2 NA's :3 NA's :1605 NA's :3
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8322 Length:8322 Min. :-76.59
## 1st Qu.: 3.000 Class :character Class :character 1st Qu.:-76.54
## Median : 3.000 Mode :character Mode :character Median :-76.53
## Mean : 3.605 Mean :-76.53
## 3rd Qu.: 4.000 3rd Qu.:-76.52
## Max. :10.000 Max. :-76.46
## NA's :3 NA's :3
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
## NA's :3
## preciom id zona estrato areaconst banios habitaciones tipo barrio longitud
## 4808 1 1 1 1 1 1 1 1 1 1
## 1909 1 1 1 1 1 1 1 1 1 1
## 876 1 1 1 1 1 1 1 1 1 1
## 726 1 1 1 1 1 1 1 1 1 1
## 1 1 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0
## 2 3 3 3 3 3 3 3 3 3
## latitud parqueaderos piso
## 4808 1 1 1 0
## 1909 1 1 0 1
## 876 1 0 1 1
## 726 1 0 0 2
## 1 0 0 0 12
## 2 0 0 0 13
## 3 1605 2638 4275
Objetivo: se busca encontrar si las ubicaciones de la tipología priorizada (Casa) están registradas de manera correcta según su localización en el mapa y la zona definida en la base de datos.
Teniendo en cuenta que el análisis está dirigido a casas en la zona norte de la ciudad, se presenta, además de la ubicación de los inmuebles, los primeros 3 registros con el fin de generar nociones generales de la información que se presenta.
# Filtrar la base de datos vivienda
vivienda_norte <- filter(vivienda, zona == "Zona Norte" & tipo == "Casa")
# Mostrar los primeros 3 registros en formato de tabla con kable
knitr::kable(head(vivienda_norte, 3), caption = "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 | NA | 7 | 6 | Casa | acopi | -76.52950 | 3.38527 |
# Crear el mapa norte con leaflet
mapa_norte <- leaflet(vivienda_norte) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#c14a30", radius = 2)
# Mostrar el mapa
mapa_norte# Crear el mapa centro con leaflet
vivienda_centro <- subset(vivienda, tipo=="Casa" & zona == "Zona Centro")
mapacentro <- leaflet(vivienda_centro) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#c19530", radius = 2)
mapacentro# Crear el mapa sur con leaflet
vivienda_sur <- subset(vivienda, tipo=="Casa" & zona == "Zona Sur")
mapa_sur <- leaflet(vivienda_sur) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#8ac130", radius = 2)
mapa_sur# Crear el mapa oeste con leaflet para la Zona Oeste
vivienda_oeste <- subset(vivienda, tipo == "Casa" & zona == "Zona Oeste")
mapa_oeste <- leaflet(vivienda_oeste) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#62162f", radius = 2)
mapa_oeste# Crear el mapa oriente con leaflet para la Zona Oriente
vivienda_oriente <- filter(vivienda, zona == "Zona Oriente" & tipo == "Casa")
mapa_oriente <- leaflet(vivienda_oriente) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#30abc1", radius = 2)
mapa_orienteComo se puede ver, en ninguna de las zonas hay uniformidad en cuanto a las localizaciones registradas de las casas, de manera que se requiere una mayor precisión de los datos para evitar superposiciones entre los puntos que simbolizan a las viviendas a lo largo de la ciudad. Lo cual se evidencia en el siguiente mapa:
#mostrar mapa de todas
vivienda_casas <- filter(vivienda, tipo == "Casa")
colores <- colorFactor(palette = c("#c14a30","#c19530", "#8ac130", "#62162f", "#30abc1"), domain = vivienda_casas$zona)
mapa<- leaflet(vivienda_casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(zona), radius = 3)
mapaA partir de lo anterior, se adelanta un análisis exploratorio de los datos de las viviendas con tipología de casas.
## id zona piso estrato
## Min. : 1 Length:3219 Length:3219 Min. :3.000
## 1st Qu.:1856 Class :character Class :character 1st Qu.:3.000
## Median :4190 Mode :character Mode :character Median :5.000
## Mean :3963 Mean :4.485
## 3rd Qu.:5862 3rd Qu.:5.000
## Max. :8319 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 77 Min. : 30.0 Min. : 1.00 Min. : 0.000
## 1st Qu.: 300 1st Qu.: 154.0 1st Qu.: 1.00 1st Qu.: 3.000
## Median : 430 Median : 240.0 Median : 2.00 Median : 4.000
## Mean : 540 Mean : 273.4 Mean : 2.29 Mean : 3.894
## 3rd Qu.: 670 3rd Qu.: 350.0 3rd Qu.: 3.00 3rd Qu.: 5.000
## Max. :1999 Max. :1745.0 Max. :10.00 Max. :10.000
## NA's :733
## habitaciones tipo barrio longitud
## Min. : 0.00 Length:3219 Length:3219 Min. :-76.59
## 1st Qu.: 3.00 Class :character Class :character 1st Qu.:-76.54
## Median : 4.00 Mode :character Mode :character Median :-76.53
## Mean : 4.61 Mean :-76.53
## 3rd Qu.: 5.00 3rd Qu.:-76.52
## Max. :10.00 Max. :-76.46
##
## latitud
## Min. :3.333
## 1st Qu.:3.383
## Median :3.413
## Mean :3.415
## 3rd Qu.:3.449
## Max. :3.496
##
## id zona estrato preciom areaconst banios habitaciones tipo barrio longitud
## 1626 1 1 1 1 1 1 1 1 1 1
## 860 1 1 1 1 1 1 1 1 1 1
## 339 1 1 1 1 1 1 1 1 1 1
## 394 1 1 1 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0 0 0 0
## latitud parqueaderos piso
## 1626 1 1 1 0
## 860 1 1 0 1
## 339 1 0 1 1
## 394 1 0 0 2
## 0 733 1254 1987
#Limpieza de datos
# Eliminación de variable "Piso", ya que no se requeire en el análisis solicitado y, además, su registro se adelantó de manera imprecisa e incompleta: no es funcional al análisis.
vivienda_casas_limpia <- subset(vivienda_casas, select = -c(piso))
# Eliminación de entradas que tienen datos de 0 habitaciones y de 0 baños
vivienda_casas_limpia <- vivienda_casas_limpia %>%
filter(habitaciones != 0 & banios != 0)
knitr::kable(head(vivienda_casas_limpia), caption = "Base de datos limpia")| id | zona | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1147 | Zona Oriente | 3 | 250 | 70 | 1 | 3 | 6 | Casa | 20 de julio | -76.51168 | 3.43382 |
| 1169 | Zona Oriente | 3 | 320 | 120 | 1 | 2 | 3 | Casa | 20 de julio | -76.51237 | 3.43369 |
| 1350 | Zona Oriente | 3 | 350 | 220 | 2 | 2 | 4 | Casa | 20 de julio | -76.51537 | 3.43566 |
| 5992 | Zona Sur | 4 | 400 | 280 | 3 | 5 | 3 | Casa | 3 de julio | -76.54000 | 3.43500 |
| 1209 | Zona Norte | 5 | 320 | 150 | 2 | 4 | 6 | Casa | acopi | -76.51341 | 3.47968 |
| 1592 | Zona Norte | 5 | 780 | 380 | 2 | 3 | 3 | Casa | acopi | -76.51674 | 3.48721 |
La mayoría de inmuebles registrados presentan precios inferiores a los 500 millones, de manera que el mercado inmobiliario de viviendas más caras, dada la estructura socioeconómica de la ciudad, se reduce a unas pocas unidades y concentrando la oferta y la demanda en precios más moderados.
plot_ly(vivienda_casas_limpia, x = ~preciom, type = "histogram")%>%
layout(title = "Histograma de precios")El área construida de los inmuebles tiende a concentrar a la izquierda del histograma, lo que implica que la mayoría son viviendas de tamaños en torno a los 200 metros cuadrados.
plot_ly(vivienda_casas_limpia, x = ~areaconst, type = "histogram")%>%
layout(title = "Histograma de área construida")Contrario a intuiciones preliminares, no se da, de manera estricta, que una mayor área construida implique mayores precios de las viviendas, considerando la posibilidad de que otras variables ligadas las condiciones físicas del inmueble o del barrio puedan estar afectando el precio comercial final de las viviendas.
plot_ly(vivienda_casas_limpia, x = ~preciom, y = ~areaconst, type = "histogram")%>%
layout(title = "Precio en función del Área construida")Como se puede esperar, las viviendas de estratos más altos, dado el poder adquisitivo de su público objetivo, tienden a tener precios más elevados.
plot_ly(vivienda_casas_limpia, x = ~estrato, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#c14a30")) %>%
layout(title = "Precio en función del estrato socioeconómico",
xaxis = list(title = "Estrato Socieconómico"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)Las viviendas más valorizadas se encuentran en la zona sur de la ciudad, abriendo la posibildad de pensar que los estratos más altos y amenidades que ofrece la ciudad, se puedan estar concentrando en este sector de la misma.
plot_ly(vivienda_casas_limpia, x = ~zona, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#62162f")) %>%
layout(title = "Precio en función de zona",
xaxis = list(title = "Zona"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)A mayor cantidad de baños y habitaciones, no necesariamente aumenta el precio de las viviendas, lo que denota que, en efecto, en un punto el exceso de estos dos elementos en un inmueble puede afectar negativamente su deseabilidad en el mercado.
# precio vs baños
plot_ly(vivienda_casas_limpia, x = ~banios, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#c19530")) %>%
layout(title = "Precio en función de cantidad de baños",
xaxis = list(title = "Cantidad de Baños"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)# precio vs habitaciones
plot_ly(vivienda_casas_limpia, x = ~habitaciones, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#8ac130")) %>%
layout(title = "Precio en función de cantidad de habitaciones",
xaxis = list(title = "Cantidad de Habitaciones"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)En cuanto a las correlaciones entre las distintas variables, se encuentra que las que más tienen incidencia en el precio de las viviendas son el estrato y el área construida, ambas con valores relativamente altos, reafirmando una relación estrecha entre dichas variables.
# Correlación
cor_1 <-vivienda_casas_limpia[,c("preciom","areaconst", "estrato", "banios","habitaciones")]
ggpairs(cor_1, title="GGally ") A continuación se aplica un modelo de regresión lineal múltiple para identificar la incidencia de las variables en el precio final de los inmuebles.
# Aplicar modelo de regresión lineal
vivienda_casas_limpia$estrato = as.numeric(vivienda_casas_limpia$estrato)
vivienda_casas_limpia <- vivienda_casas_limpia %>%
mutate(parqueaderos = ifelse(is.na(parqueaderos), 1, parqueaderos))
modelo_RLM=lm(preciom~ areaconst + parqueaderos + estrato + banios + habitaciones, vivienda_casas_limpia)
summary(modelo_RLM)##
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios +
## habitaciones, data = vivienda_casas_limpia)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1211.95 -108.71 -19.29 67.13 1176.48
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -386.38550 20.54710 -18.805 < 2e-16 ***
## areaconst 0.74887 0.02596 28.843 < 2e-16 ***
## parqueaderos 55.60110 3.12738 17.779 < 2e-16 ***
## estrato 112.31518 4.40898 25.474 < 2e-16 ***
## banios 40.28816 3.40857 11.820 < 2e-16 ***
## habitaciones -11.22987 2.70193 -4.156 3.32e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 199.9 on 3163 degrees of freedom
## Multiple R-squared: 0.6905, Adjusted R-squared: 0.69
## F-statistic: 1411 on 5 and 3163 DF, p-value: < 2.2e-16
De los resultados de la regresión, con un R cuadrado ajustado de 0.69, las categorias del modelo explican en un 69% las variaciones en los precios de las casas, de manera que, por ejemplo: por cada metro cuadrado adicional de área construida el precio aumenta en 749 mil pesos, mientras que por cada estrato que aumenta, el inmueble adquiere un costo de 112 millones de pesos adicionales. Esto, se explica a través de la siguiente fórmula:
\(precio= -386 + 0.7489 (areaconst) + 55.601 (parqueaderos) + 112.32 (estrato) + 40.288 (banios) + (-11.230)(habitaciones)\)
Se validan los principales supuestos del Modelo de Regresión Lineal Múltiple.
#Normalidad
res=modelo_RLM$residuals
shapiro_1=shapiro.test(res)
#Homoscedasticidad
breusch_1= lmtest::bptest(modelo_RLM)
# Linealidad
lineal_1=mean(modelo_RLM$residuals)
# No autocorrelación
durbin_1=lmtest::dwtest(modelo_RLM)
pruebas = c(
"Shapiro - Wilk:",
"Breusch-Pagan:",
"Promedio de residuos:",
"Durbin-Watson:"
)
valores_calculados = (c(shapiro_1$p.value, breusch_1$p.value, lineal_1, durbin_1$DW))
supuestos <- data.frame(
supuestos = c("Normalidad", "Homoscedasticidad", "Linealidad", "No autocorrelación"),
valores= paste(pruebas, valores_calculados)
)
tabla_1=kable(supuestos, col.names = c("Supuesto", "Prueba"))
tabla_1| Supuesto | Prueba |
|---|---|
| Normalidad | Shapiro - Wilk: 4.65389834594353e-44 |
| Homoscedasticidad | Breusch-Pagan: 3.94324225574717e-81 |
| Linealidad | Promedio de residuos: 1.58477833604971e-14 |
| No autocorrelación | Durbin-Watson: 4.65389834594353e-44 |
Normalidad: según el p-value de la prueba aplicada, se rechaza la hipótesis nula y se asume que los errores no tienen una distribución normal.
Homoscedasticidad: la varianza de los errores no es constante.
Linealidad: Se cumple el supuesto de linealidad en tanto el promedio de los residuos es muy cercano a 0, de manera que su relación con los valores reales tiende a ser lineal.
No - autocorrelación: con un valor inferior a 2, esta prueba revela que existe una ligera autocorrelación positiva en los residuos del modelo.
A partir del modelo construido, se predicen precios que puede tener la vivienda con las condiciones especificadas.
# Condiciones solicitadas
requisitos_1 <- data.frame(
areaconst = 200,
parqueaderos = 1,
estrato = c(4, 5),
banios = 2,
habitaciones = 4,
zona="Zona Norte"
)
# Predicción según condiciones
prediccion_1 = predict(modelo_RLM,requisitos_1)
print (prediccion_1)## 1 2
## 303.9076 416.2228
De esta forma, para estas características, se espera un valor del inmueble que oscila entre los 304 y 416 millones de pesos. Esto, contrasta con el valor mínimo y máximo del conjunto de casas que cumplen con todos los requisitos planteados.
# Creación de filtro de posibles inmuebles a ofertar
casa <- filter(vivienda_casas_limpia,
areaconst >= 200,
parqueaderos >= 1,
banios >= 2,
habitaciones >= 4,
zona == "Zona Norte",
estrato >= 4,
estrato <= 5,
preciom <= 350)
# Crear un data frame con los valores mínimo y máximo
valores_casa <- data.frame(
Descripción = c("Valor mínimo", "Valor máximo"),
Valor = c(min(casa$preciom), max(casa$preciom))
)
# Mostrar la tabla con kable
kable(valores_casa, col.names = c("Valores", "Precio de Casa"), caption = "Valores mínimo y máximo")| Valores | Precio de Casa |
|---|---|
| Valor mínimo | 230 |
| Valor máximo | 350 |
Se presentan posibles inmuebles que tienen las características solicitadas, planteadas como mínimo, ya que para encontrar una casa con las cantidades de salas exacta se dificulta en la medida que solo un registro cumple a cabalidad las condiciones (id=1943).
## # A tibble: 5 × 12
## id zona estrato preciom areaconst parqueaderos banios habitaciones tipo
## <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
## 1 766 Zona N… 5 321 249 1 5 5 Casa
## 2 2837 Zona N… 4 340 207 1 4 4 Casa
## 3 852 Zona N… 5 340 208 1 6 4 Casa
## 4 4267 Zona N… 5 335 202 1 4 5 Casa
## 5 1822 Zona N… 4 340 295 2 2 4 Casa
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>
mapa_casas <- leaflet(cinco_ofertas_casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color ="#c14a30", radius = 4)
mapa_casasObjetivo: se busca encontrar si las ubicaciones de la tipología priorizada (Apartamento) están registradas de manera correcta según su localización en el mapa y la zona definida en la base de datos.
Teniendo en cuenta que el análisis está dirigido a apartamentos en la zona sur de la ciudad, se presenta, además de la ubicación de los inmuebles, los primeros 3 registros con el fin de generar nociones generales de la información que se presenta.
# Filtrar la base de datos vivienda
aptos_sur <- subset(vivienda, tipo=="Apartamento" & zona == "Zona Sur")
# Mostrar los primeros 3 registros en formato de tabla con kable
knitr::kable(head(aptos_sur, 3), caption = "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 |
# Crear el mapa sur con leaflet
mapa_sur_apt <- leaflet(aptos_sur) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#8ac130", radius = 2)
mapa_sur_apt# Crear el mapa centro con leaflet
aptos_centro <- subset(vivienda, tipo=="Apartamento" & zona == "Zona Centro")
mapacentro_apt <- leaflet(aptos_centro) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#c19530", radius = 2)
mapacentro_apt# Crear el mapa oeste con leaflet para la Zona Oeste
aptos_oeste <- subset(vivienda, tipo == "Apartamento" & zona == "Zona Oeste")
mapa_oeste_apt <- leaflet(aptos_oeste) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#62162f", radius = 2)
mapa_oeste_apt# Crear el mapa oriente con leaflet para la Zona Oriente
aptos_oriente <- filter(vivienda, zona == "Zona Oriente" & tipo == "Apartamento")
mapa_oriente_apt <- leaflet(aptos_oriente) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#30abc1", radius = 2)
mapa_oriente_aptComo se puede ver, al igual como sucede con las casas, en ninguna de las zonas hay uniformidad en cuanto a las localizaciones registradas de las casas, de manera que se requiere una mayor precisión de los datos para evitar superposiciones entre los puntos que simbolizan a las viviendas a lo largo de la ciudad. Lo cual se evidencia en el siguiente mapa:
#mostrar mapa de todas
vivienda_aptos <- filter(vivienda, tipo == "Apartamento")
colores <- colorFactor(palette = c("#c14a30","#c19530", "#8ac130", "#62162f", "#30abc1"), domain = vivienda_aptos$zona)
mapa_aptos<- leaflet(vivienda_aptos) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(zona), radius = 3)
mapa_aptosA partir de lo anterior, se adelanta un análisis exploratorio de los datos de las viviendas con tipología de apartamentos
## id zona piso estrato
## Min. : 3 Length:5100 Length:5100 Min. :3.000
## 1st Qu.:2180 Class :character Class :character 1st Qu.:4.000
## Median :4158 Mode :character Mode :character Median :5.000
## Mean :4284 Mean :4.727
## 3rd Qu.:6556 3rd Qu.:6.000
## Max. :8317 Max. :6.000
##
## preciom areaconst parqueaderos banios
## Min. : 58.0 Min. : 35.0 Min. : 1.000 Min. :0.000
## 1st Qu.: 175.0 1st Qu.: 68.0 1st Qu.: 1.000 1st Qu.:2.000
## Median : 279.0 Median : 90.0 Median : 1.000 Median :2.000
## Mean : 366.9 Mean :112.8 Mean : 1.568 Mean :2.617
## 3rd Qu.: 430.0 3rd Qu.:130.0 3rd Qu.: 2.000 3rd Qu.:3.000
## Max. :1950.0 Max. :932.0 Max. :10.000 Max. :8.000
## NA's :869
## habitaciones tipo barrio longitud
## Min. :0.000 Length:5100 Length:5100 Min. :-76.59
## 1st Qu.:3.000 Class :character Class :character 1st Qu.:-76.54
## Median :3.000 Mode :character Mode :character Median :-76.53
## Mean :2.971 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :9.000 Max. :-76.46
##
## latitud
## Min. :3.334
## 1st Qu.:3.380
## Median :3.419
## Mean :3.419
## 3rd Qu.:3.453
## Max. :3.498
##
## id zona estrato preciom areaconst banios habitaciones tipo barrio longitud
## 3182 1 1 1 1 1 1 1 1 1 1
## 1049 1 1 1 1 1 1 1 1 1 1
## 537 1 1 1 1 1 1 1 1 1 1
## 332 1 1 1 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0 0 0 0
## latitud parqueaderos piso
## 3182 1 1 1 0
## 1049 1 1 0 1
## 537 1 0 1 1
## 332 1 0 0 2
## 0 869 1381 2250
#Limpieza de datos
# Eliminación de variable "Piso", ya que no se requeire en el análisis solicitado y, además, su registro se adelantó de manera imprecisa e incompleta: no es funcional al análisis.
vivienda_aptos_limpia <- subset(vivienda_aptos, select = -c(piso))
# Eliminación de entradas que tienen datos de 0 habitaciones y de 0 baños
vivienda_aptos_limpia <- vivienda_aptos_limpia %>%
filter(habitaciones != 0 & banios != 0)
knitr::kable(head(vivienda_aptos_limpia), caption = "Base de datos limpia")| id | zona | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1212 | Zona Norte | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento | acopi | -76.51350 | 3.45891 |
| 1724 | Zona Norte | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento | acopi | -76.51700 | 3.36971 |
| 2326 | Zona Norte | 4 | 220 | 52 | 2 | 2 | 3 | Apartamento | acopi | -76.51974 | 3.42627 |
| 4386 | Zona Norte | 5 | 310 | 137 | 2 | 3 | 4 | Apartamento | acopi | -76.53105 | 3.38296 |
| 7497 | Zona Norte | 6 | 520 | 98 | 2 | 2 | 2 | Apartamento | acopi | -76.54999 | 3.43505 |
| 5424 | Zona Norte | 4 | 320 | 108 | 2 | 3 | 3 | Apartamento | acopi | -76.53638 | 3.40770 |
Al igual que sucede con las casas, la mayoría de apartamentos registrados presentan precios inferiores a los 500 millones, de manera que el mercado inmobiliario de viviendas más caras, dada la estructura socioeconómica de la ciudad, se reduce a unas pocas unidades y concentrando la oferta y la demanda en precios más moderados.
plot_ly(vivienda_aptos_limpia, x = ~preciom, type = "histogram")%>%
layout(title = "Histograma de precios")El área construida de los inmuebles tiende a concentrarse a la izquierda del histograma, lo que implica que la mayoría son apartamentos de tamaños que tienden a no superar los 200 metros cuadrados.
plot_ly(vivienda_aptos_limpia, x = ~areaconst, type = "histogram")%>%
layout(title = "Histograma de área construida")Contrario a intuiciones preliminares, no se da, de manera estricta, que una mayor área construida implique mayores precios de las viviendas, de manera que, a diferencia de las casas, cuyos precios más elevados giran en torno a los 300 metros cuadrados, para apartamentos se da cerca de los 150 metros cuadrados
plot_ly(vivienda_aptos_limpia, x = ~preciom, y = ~areaconst, type = "histogram")%>%
layout(title = "Precio en función del Área construida")Como se puede esperar, los apartamentos de estratos más altos, dado el poder adquisitivo de su público objetivo, tienden a tener precios más elevados.
plot_ly(vivienda_aptos_limpia, x = ~estrato, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#c14a30")) %>%
layout(title = "Precio en función del estrato socioeconómico",
xaxis = list(title = "Estrato Socieconómico"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)Las viviendas más valorizadas se encuentran en la zona sur de la ciudad, abriendo la posibildad de pensar que los estratos más altos y amenidades que ofrece la ciudad, se puedan estar concentrando en este sector de la misma.
plot_ly(vivienda_aptos_limpia, x = ~zona, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#62162f")) %>%
layout(title = "Precio en función de zona",
xaxis = list(title = "Zona"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)A mayor cantidad de baños y habitaciones, no necesariamente aumenta el precio de las viviendas, lo que denota que, en efecto, en un punto el exceso de estos dos elementos en un apartamento puede afectar negativamente su deseabilidad en el mercado.
# precio vs baños
plot_ly(vivienda_aptos_limpia, x = ~banios, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#c19530")) %>%
layout(title = "Precio en función de cantidad de baños",
xaxis = list(title = "Cantidad de Baños"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)# precio vs habitaciones
plot_ly(vivienda_aptos_limpia, x = ~habitaciones, y = ~preciom, type = "bar",
mode = "markers", marker = list(color = "#8ac130")) %>%
layout(title = "Precio en función de cantidad de habitaciones",
xaxis = list(title = "Cantidad de Habitaciones"),
yaxis = list(title = "Precio (millones)"),
showlegend = FALSE)En cuanto a las correlaciones entre las distintas variables, a diferencia de las casas, en apartamentos se encuentra que las que más tienen incidencia en el precio de las viviendas son el área construida y la cantidad de baños, ambas con valores relativamente altos, reafirmando una relación estrecha entre dichas variables.
# Correlación
cor_2 <-vivienda_aptos_limpia[,c("preciom","areaconst", "estrato","banios","habitaciones")]
ggpairs(cor_2, title="GGally ") A continuación se aplica un modelo de regresión lineal múltiple para identificar la incidencia de las variables en el precio final de los inmuebles.
# Aplicar modelo de regresión lineal
vivienda_aptos_limpia$estrato = as.numeric(vivienda_aptos_limpia$estrato)
vivienda_aptos_limpia <- vivienda_aptos_limpia %>%
mutate(parqueaderos = ifelse(is.na(parqueaderos), 1, parqueaderos))
modelo_RLM_apt=lm(preciom~ areaconst + parqueaderos + estrato + banios + habitaciones, vivienda_aptos_limpia)
summary(modelo_RLM_apt)##
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios +
## habitaciones, data = vivienda_aptos_limpia)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1765.21 -50.53 1.48 44.20 1021.66
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -244.0901 13.0224 -18.74 <2e-16 ***
## areaconst 2.0862 0.0437 47.74 <2e-16 ***
## parqueaderos 93.0540 3.7862 24.58 <2e-16 ***
## estrato 50.9610 2.5309 20.14 <2e-16 ***
## banios 49.1179 3.0371 16.17 <2e-16 ***
## habitaciones -43.7476 3.3706 -12.98 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 129.5 on 5068 degrees of freedom
## Multiple R-squared: 0.7999, Adjusted R-squared: 0.7997
## F-statistic: 4052 on 5 and 5068 DF, p-value: < 2.2e-16
De los resultados de la regresión, con un R cuadrado ajustado de 0.79, las categorias del modelo explican en un 79% las variaciones en los precios de los apartamentos, con un poder predictivo ligeramente mayor al caso de las casas. Esto, denotando que, por ejemplo, por cada metro cuadrado adicional de área construida el precio aumenta en 2 millones de pesos, mientras que por cada estrato que aumenta, el inmueble adquiere un costo de 51 millones de pesos adicionales. Esto, se explica a través de la siguiente fórmula:
\(precio= -244.1 + 2.086 (areaconst) + 93.054 (parqueaderos) + 50.961 (estrato) + 49.118 (banios) + (-43.748)(habitaciones)\)
Se validan los principales supuestos del Modelo de Regresión Lineal Múltiple.
#Normalidad
#Debido a que la muestra resultante es mayor a 5000 (5074), se toma una muestra aleatoria de 5000 para poder aplicar la prueba Shapiro - Wilk
res_apt=modelo_RLM_apt$residuals
res_apt= sample(res_apt, 5000)
shapiro_2=shapiro.test(res_apt)
#Homoscedasticidad
breusch_2= lmtest::bptest(modelo_RLM_apt)
# Linealidad
lineal_2=mean(modelo_RLM_apt$residuals)
# No autocorrelación
durbin_2=lmtest::dwtest(modelo_RLM_apt)
valores_calculados_2 = (c(shapiro_2$p.value, breusch_2$p.value, lineal_2, durbin_2$statistic))
supuestos_2 <- data.frame(
supuestos_apt = c("Normalidad", "Homoscedasticidad", "Linealidad", "No autocorrelación"),
valores= paste(pruebas, valores_calculados_2)
)
tabla_2=kable(supuestos_2, col.names = c("Supuesto", "Prueba"))
tabla_2| Supuesto | Prueba |
|---|---|
| Normalidad | Shapiro - Wilk: 1.39848638103635e-59 |
| Homoscedasticidad | Breusch-Pagan: 1.48234003172932e-290 |
| Linealidad | Promedio de residuos: -5.63370355052917e-15 |
| No autocorrelación | Durbin-Watson: 1.6399763947977 |
Al igual que en el análisis de casas, se encuentran los siguientes resultados en el análisis de los supuestos:
Normalidad: según el p-value de la prueba aplicada, se rechaza la hipótesis nula y se asume que los errores no tienen una distribución normal.
Homoscedasticidad: la varianza de los errores no es constante.
Linealidad: Se cumple el supuesto de linealidad en tanto el promedio de los residuos es muy cercano a 0, de manera que su relación con los valores reales tiende a ser lineal.
No - autocorrelación: con un valor inferior a 2, esta prueba revela que existe una ligera autocorrelación positiva en los residuos del modelo.
A partir del modelo construido, se predicen precios que puede tener la vivienda con las condiciones especificadas.
# Condiciones solicitadas
requisitos_2 <- data.frame(
areaconst = 300,
parqueaderos = 3,
estrato = c(5, 6),
banios = 3,
habitaciones = 5,
zona="Zona Sur"
)
# Predicción según condiciones
prediccion_2 = predict(modelo_RLM_apt,requisitos_2)
print (prediccion_2)## 1 2
## 844.351 895.312
De esta forma, para estas características, se espera un valor del inmueble que oscila entre los 844 y 895 millones de pesos. Esto, contrasta con el valor mínimo y máximo del conjunto de casas que cumplen con todos los requisitos planteados.
# Creación de filtro de posibles inmuebles a ofertar
apartamentos <- filter(vivienda_aptos_limpia, areaconst >= 300, parqueaderos >= 3, banios >= 3, habitaciones >= 5, zona == "Zona Sur", estrato >= 5, estrato <= 6, preciom <= 850)
# Crear un data frame con los valores mínimo y máximo
valores_apto <- data.frame(
Descripción = c("Valor mínimo", "Valor máximo"),
Valor = c(min(apartamentos$preciom), max(apartamentos$preciom))
)
# Mostrar la tabla con kable
kable(valores_apto, col.names = c("Valores", "Precio de Apartamento"), caption = "Valores mínimo y máximo")| Valores | Precio de Apartamento |
|---|---|
| Valor mínimo | 670 |
| Valor máximo | 730 |
Se presentan posibles inmuebles que tienen las características solicitadas, planteadas como mínimo, ya que ninguna cumple con los requisitos exactos. Así mismo, solo dos inmuebles de la base de datos cumplen con dichos mínimos. Es importante anotar que las dos se encuentran a muy pocas cuadras de distancia, revelando información sobre microsectores con dinámicas inmobiliarias particulares dentro de la zona sur.
Se lograron identificar las necesidades del cliente según las especificaciones requeridas, teniendo como resultado una cantidad diferencial de posibles ofertas. Esto, debido a que la oferta de apartamentos del tamaño solicitado es más díficil de encontrar, sumado a otras características que hacen del inmueble requerido un bien escaso.
Así mismo, se recomienda ajustar y afinar la recolección y registro de datos, en especial frente a las latitudes y longitudes y la variable piso, que es bastante ambigua al no ser diferencial para casas y apartamentos.