# 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)

1 Introducción

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")
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

2 Propuesta 1: Casa

# Importación de datos

data("vivienda")
summary(vivienda)
##        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
# Exploración de Datos

md.pattern(vivienda)

##      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

2.1 Identificación de ubicaciones registradas - Vivienda 1

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.

2.1.1 Zona Norte

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")
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

2.1.2 Zona Centro

# 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

2.1.3 Zona Sur

# 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

2.1.4 Zona Oeste

# 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

2.1.5 Zona Oriente

# 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_oriente

Como 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)

mapa

2.2 Análisis Exploratorio de Datos - Vivienda 1

A partir de lo anterior, se adelanta un análisis exploratorio de los datos de las viviendas con tipología de casas.

# Visualización preliminar

summary(vivienda_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  
## 
# Visualización preliminar

md.pattern(vivienda_casas)

##      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")
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

2.2.1 Histograma de Precios

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")

2.2.2 Histograma de Área Construida

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")

2.2.3 Precio de viviendas en función del á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")

2.2.4 Precio de viviendas en función del estrato

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)

2.2.5 Precio de viviendas en función de la zona

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)

2.2.6 Precio de viviendas en función de la cantidad de baños y habitaciones

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 ") 

2.3 Modelo de Regresión Lineal Múltiple - Vivienda 1

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)\)

2.4 Validación de supuestos - Vivienda 1

Se validan los principales supuestos del Modelo de Regresión Lineal Múltiple.

# Generación de gráfico
par(mfrow = c(2, 2))
plot(modelo_RLM)

#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.

2.5 Predicción de precios - Vivienda 1

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 mínimo y máximo
Valores Precio de Casa
Valor mínimo 230
Valor máximo 350

2.6 Identificación de posibles ofertas - Vivienda 1

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).

cinco_ofertas_casas <- casa %>% sample_n(5)
print(cinco_ofertas_casas)
## # 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_casas

3 Propuesta 2: Apartamento

3.1 Identificación de ubicaciones registradas - Vivienda 2

Objetivo: 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.

3.1.1 Zona Sur

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")
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

3.1.2 Zona Norte

# Crear el mapa norte con leaflet
aptos_norte <- filter(vivienda, zona == "Zona Norte" & tipo == "Apartamento")

mapa_norte_apt <- leaflet(aptos_norte) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "#c14a30", radius = 2)

# Mostrar el mapa
mapa_norte_apt

3.1.3 Zona Centro

# 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

3.1.4 Zona Oeste

# 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

3.1.5 Zona Oriente

# 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_apt

Como 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_aptos

3.2 Análisis Exploratorio de Datos - Vivienda 2

A partir de lo anterior, se adelanta un análisis exploratorio de los datos de las viviendas con tipología de apartamentos

# Visualización preliminar

summary(vivienda_aptos)
##        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  
## 
# Visualización preliminar

md.pattern(vivienda_aptos)

##      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")
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

3.2.1 Histograma de Precios

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")

3.2.2 Histograma de Área Construida

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")

3.2.3 Precio de viviendas en función del á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")

3.2.4 Precio de viviendas en función del estrato

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)

3.2.5 Precio de viviendas en función de la zona

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)

3.2.6 Precio de viviendas en función de la cantidad de baños y habitaciones

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 ") 

3.3 Modelo de Regresión Lineal Múltiple - Vivienda 2

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)\)

3.4 Validación de supuestos - Vivienda 2

Se validan los principales supuestos del Modelo de Regresión Lineal Múltiple.

# Generación de gráfico
par(mfrow = c(2, 2))
plot(modelo_RLM_apt)

#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.

3.5 Predicción de precios - Vivienda 2

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 mínimo y máximo
Valores Precio de Apartamento
Valor mínimo 670
Valor máximo 730

3.6 Identificación de posibles ofertas - Vivienda 2

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.

apartamentos
mapa_aptos <- leaflet(apartamentos) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, lat = ~latitud, color ="#c14a30", radius = 4)


mapa_aptos

4 Conclusiones

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.