Filtrado de base de Datos

Realice un filtro a la base de datos e incluya solo las ofertas de:

  • base1: casas, de la zona norte de la ciudad.

  • base2: apartamentos, de la zona sur de la ciudad.

Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).

# Import data
data("vivienda")
vivienda$zona <- as.factor(vivienda$zona)
vivienda$piso <- as.factor(vivienda$piso)
vivienda$estrato <- as.factor(vivienda$estrato)
vivienda$tipo <- as.factor(vivienda$tipo)
vivienda$banios <- as.factor(vivienda$banios)
vivienda$habitaciones <- as.factor(vivienda$habitaciones)

#Subset base1
base1 <- data.frame(subset(vivienda, tipo == "Casa" & zona == "Zona Norte"))

#Subset base2
base2 <- data.frame(subset(vivienda, tipo == "Apartamento" & zona == "Zona Sur"))

Base 1: Casas de la zona norte de la ciudad.

formattable(head(base1, 3))
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
summary(base1)
       id                   zona          piso     estrato    preciom      
 Min.   :  58.0   Zona Centro :  0   02     :194   3:235   Min.   :  89.0  
 1st Qu.: 766.2   Zona Norte  :722   01     : 84   4:161   1st Qu.: 261.2  
 Median :2257.0   Zona Oeste  :  0   03     : 65   5:271   Median : 390.0  
 Mean   :2574.6   Zona Oriente:  0   04     :  6   6: 55   Mean   : 445.9  
 3rd Qu.:4225.0   Zona Sur    :  0   07     :  1           3rd Qu.: 550.0  
 Max.   :8319.0                      (Other):  0           Max.   :1940.0  
                                     NA's   :372                           
   areaconst       parqueaderos        banios     habitaciones
 Min.   :  30.0   Min.   : 1.000   3      :187   4      :222  
 1st Qu.: 140.0   1st Qu.: 1.000   4      :171   3      :171  
 Median : 240.0   Median : 2.000   2      :165   5      :137  
 Mean   : 264.9   Mean   : 2.182   5      :101   6      : 60  
 3rd Qu.: 336.8   3rd Qu.: 3.000   6      : 46   7      : 42  
 Max.   :1440.0   Max.   :10.000   1      : 17   8      : 29  
                  NA's   :287      (Other): 35   (Other): 61  
          tipo        barrio             longitud         latitud     
 Apartamento:  0   Length:722         Min.   :-76.59   Min.   :3.333  
 Casa       :722   Class :character   1st Qu.:-76.53   1st Qu.:3.452  
                   Mode  :character   Median :-76.52   Median :3.468  
                                      Mean   :-76.52   Mean   :3.460  
                                      3rd Qu.:-76.50   3rd Qu.:3.482  
                                      Max.   :-76.47   Max.   :3.496  
                                                                      
leaflet(c(base1$latitud, base1$longitud)) %>%
  addTiles() %>% 
  addMarkers(lng=base1$longitud, lat=base1$latitud,
             popup = paste("Lat:", round(base1$latitud, 2),
                "Long:", round(base1$longitud, 2)))

Base 2: Apartamentos de la zona sur de la ciudad.

formattable(head(base2, 3))
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
summary(base2)
       id                 zona           piso     estrato     preciom      
 Min.   :   3   Zona Centro :   0   05     :353   3: 201   Min.   :  75.0  
 1st Qu.:2292   Zona Norte  :   0   03     :329   4:1091   1st Qu.: 175.0  
 Median :4004   Zona Oeste  :   0   04     :327   5:1033   Median : 245.0  
 Mean   :4131   Zona Oriente:   0   02     :319   6: 462   Mean   : 297.3  
 3rd Qu.:5876   Zona Sur    :2787   01     :255            3rd Qu.: 335.0  
 Max.   :8302                       (Other):582            Max.   :1750.0  
                                    NA's   :622                            
   areaconst       parqueaderos        banios      habitaciones 
 Min.   : 40.00   Min.   : 1.000   2      :1588   3      :1902  
 1st Qu.: 65.00   1st Qu.: 1.000   3      : 662   2      : 463  
 Median : 85.00   Median : 1.000   4      : 229   4      : 366  
 Mean   : 97.47   Mean   : 1.415   1      : 167   5      :  24  
 3rd Qu.:110.00   3rd Qu.: 2.000   5      : 123   1      :  19  
 Max.   :932.00   Max.   :10.000   6      :  10   0      :   8  
                  NA's   :406      (Other):   8   (Other):   5  
          tipo         barrio             longitud         latitud     
 Apartamento:2787   Length:2787        Min.   :-76.57   Min.   :3.334  
 Casa       :   0   Class :character   1st Qu.:-76.54   1st Qu.:3.370  
                    Mode  :character   Median :-76.53   Median :3.383  
                                       Mean   :-76.53   Mean   :3.390  
                                       3rd Qu.:-76.52   3rd Qu.:3.406  
                                       Max.   :-76.46   Max.   :3.497  
                                                                       
leaflet(c(base2$latitud, base2$longitud)) %>%
  addTiles() %>%
  addMarkers(lng = base2$longitud, lat = base2$latitud,
             popup = paste("Lat:", round(base2$latitud, 2),
                           "Long:", round(base2$longitud, 2)))

Análisis Exploratorio

Realice un análisis exploratorio de datos enfocado en la correlación entre la variable respuesta (precio) en función del área construida, estrato, numero de baños, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

Datos faltantes

Base1

gg_miss_var(base1, show_pct = TRUE) +
labs(title = "Datos Faltantes Casas Zona Sur") +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Se observa una gran cantidad de datos faltantes para el atributo piso y parqueaderos, por lo que se determinará la relación con los otros atributos.

Imputación del atributo piso

par(mfrow = c(2, 3), oma = c(0, 0, 3, 0))

# Plot empty pisos per habitaciones
piso_hab_bar1 <- barplot(piso_hab_base1, names.arg = hab_base1,
                         main = "Piso = NA por Habitaciones",
                         ylab = "Registros vacíos",
                         xlab = "Habitaciones",
                         las = 2)

# Plot empty pisos per estrato
piso_estr_bar1 <- barplot(piso_estr_base1, names.arg = estr_base1,
                          main = "Piso = NA por estrato",
                          ylab = "Registros vacíos",
                          xlab = "estrato",
                          las = 2)

# Plot empty pisos per banios
piso_ban_bar1 <- barplot(piso_ban_base1, names.arg = ban_base1,
                         main = "Piso = NA por banios",
                         ylab = "Registros vacíos",
                         xlab = "banios",
                         las = 2)

# Plot empty pisos per precio
piso_prec_bar1 <- barplot(piso_prec_base1, names.arg = breaks_prec_1,
                          main = "Piso = NA por precio",
                          ylab = "Registros vacíos",
                          xlab = "Precio",
                          las = 2)

# Plot empty pisos per area
piso_area_bar1 <- barplot(piso_area_base1, names.arg = breaks_area_1,
                          main = "Piso = NA por area",
                          ylab = "Registros vacíos",
                          xlab = "Area",
                          las = 2)

mtext("Distribución de valores vacíos de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que los valores faltantes del atributo piso no se comportan de manera aleatoria respecto a los demás atributos excepto para el estrato. A continuación, se determina la mediana para cada caso con el fin de establecer con qué valor se imputarán los datos faltantes del atributo piso

par(mfrow = c(2, 3),
    oma = c(0, 0, 3, 0),
    xpd = FALSE)

# Plot empty pisos per habitaciones
piso_hab_box1 <- boxplot(base1$piso ~ base1$habitaciones,
                         main = "Piso vs Habitaciones",
                         ylab = "Pisos",
                         xlab = "Habitaciones",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty pisos per estrato
piso_estr_box1 <- boxplot(base1$piso ~ base1$estrato,
                         main = "Piso vs Estrato",
                         ylab = "Pisos",
                         xlab = "Estrato",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty pisos per banios
piso_banios_box1 <- boxplot(base1$piso ~ base1$banios,
                         main = "Piso vs Banios",
                         ylab = "Pisos",
                         xlab = "Banios",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty pisos per preciom
piso_prec_box1 <- boxplot(base1$piso ~ base1$class_prec,
                         main = "Piso vs Preciom",
                         ylab = "Pisos",
                         xlab = NA,
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty pisos per area
piso_area_box1 <- boxplot(base1$piso ~ base1$class_area,
                         main = "Piso vs areaconst",
                         ylab = "Pisos",
                         xlab = NA,
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

mtext("Distribución de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que para la mayoría de los valores de los atributos, la mediana de los pisos es 2 excepto cuando:

  • El número de habitaciones es 9
  • El número de baños es 8 (la mediana de 1.5 pisos cuando los baños son 0 se aproxima al número par, que es 2)
  • El preciom está entre 1600-1800
  • El área construida está entre 1400-1500 o entre 700-800

Todos los registros con pisos vacíos que no contengan las condiciones anteriores, se imputaron con un valor de 2.

Luego de esa imputación, quedaron 27 valores vacíos en el atributo piso mostrados a continuación.

base1[is.na(base1$piso), c("piso", "banios", "habitaciones", "class_prec", "class_area")]
    piso banios habitaciones class_prec class_area
47    NA      8            0    600-800    300-400
63    NA      8            6    400-600    300-400
69    NA      0            0  1200-1400    700-800
114   NA      7            5  1600-1800    700-800
222   NA      4            7   800-1000    700-800
223   NA      5            9    600-800    300-400
229   NA      4            9    400-600    300-400
443   NA      8            8    400-600    300-400
456   NA      6            5    600-800    700-800
480   NA      8            5  1000-1200    500-600
510   NA      5            9    400-600    300-400
512   NA      5           10  1600-1800    700-800
513   NA      8           10  1800-2000    700-800
561   NA      5            9    200-400    300-400
604   NA      6            6  1200-1400    700-800

Para los registros faltantes, se anotan las medianas que cada atributo tiene para el atributo piso:

  • Los registros con 9 habitaciones:
    • Media de pisos teniendo en cuenta el nro de Habitaciones: 3
    • Media de pisos teniendo en cuenta el nro de Banios: 2
    • Media de pisos teniendo en cuenta la clase precio: 2
    • Media de pisos teniendo en cuenta la clase area: 2
    • Media total: 2
  • Los registros con 8 banios:
    • Media de pisos teniendo en cuenta el nro de Habitaciones: 2
    • Media de pisos teniendo en cuenta el nro de Banios: 3
    • Media de pisos teniendo en cuenta la clase precio: 2
    • Media de pisos teniendo en cuenta la clase area: 2
    • Media total: 2
  • Los registros con clase de preciom 1600-1800:
    • Media de pisos teniendo en cuenta el nro de Habitaciones: 2
    • Media de pisos teniendo en cuenta el nro de Banios: 2
    • Media de pisos teniendo en cuenta la clase precio: 1
    • Media de pisos teniendo en cuenta la clase area: 3
    • Media total: 2
  • Los registros con clase de area 700-800:
    • Media de pisos teniendo en cuenta el nro de Habitaciones: 2
    • Media de pisos teniendo en cuenta el nro de Banios: 2
    • Media de pisos teniendo en cuenta la clase precio: 2
    • Media de pisos teniendo en cuenta la clase area: 3
    • Media total: 2

Asi, todos los valores faltantes de piso, se imputaran por 2.

Imputación del atributo parqueadero

par(mfrow = c(2, 3), oma = c(0, 0, 3, 0))

# Plot empty parqueaderos per habitaciones
parq_hab_bar1 <- barplot(parq_hab_base1, names.arg = hab_base1,
                         main = "Parqueaderos = NA por Habitaciones",
                         ylab = "Registros vacíos",
                         xlab = "Habitaciones",
                         las = 2)

# Plot empty parqueaderos per estrato
parq_estr_bar1 <- barplot(parq_estr_base1, names.arg = estr_base1,
                          main = "Parqueaderos = NA por estrato",
                          ylab = "Registros vacíos",
                          xlab = "estrato",
                          las = 2)

# Plot empty parqueaderos per banios
parq_ban_bar1 <- barplot(parq_ban_base1, names.arg = ban_base1,
                         main = "Parqueaderos = NA por banios",
                         ylab = "Registros vacíos",
                         xlab = "banios",
                         las = 2)

# Plot empty parqueaderos per precio
parq_prec_bar1 <- barplot(parq_prec_base1, names.arg = breaks_prec_1,
                          main = "Parqueaderos = NA por precio",
                          ylab = "Registros vacíos",
                          xlab = "Precio",
                          las = 2)

# Plot empty parqueaderos per area
parq_area_bar1 <- barplot(parq_area_base1, names.arg = breaks_area_1,
                          main = "Parqueaderos = NA por area",
                          ylab = "Registros vacíos",
                          xlab = "Area",
                          las = 2)

mtext("Distribución de valores vacíos de parqueaderos por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que los valores faltantes del atributo parqueaderos no se comportan de manera aleatoria respecto a los demás atributos. A continuación, se determina la mediana para cada caso con el fin de establecer con qué valor se imputarán los datos faltantes.

par(mfrow = c(2, 3),
    oma = c(0, 0, 3, 0),
    xpd = FALSE)

# Plot empty parqueaderos per habitaciones
parq_hab_box1 <- boxplot(base1$parqueaderos ~ base1$habitaciones,
                         main = "Parqueaderos vs Habitaciones",
                         ylab = "Parqueaderos",
                         xlab = "Habitaciones",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty parqueaderos per estrato
parq_estr_box1 <- boxplot(base1$parqueaderos ~ base1$estrato,
                         main = "Parqueaderos vs Estrato",
                         ylab = "Parqueaderos",
                         xlab = "Estrato",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty parqueaderos per banios
parq_banios_box1 <- boxplot(base1$parqueaderos ~ base1$banios,
                         main = "Parqueaderos vs Banios",
                         ylab = "Parqueaderos",
                         xlab = "Banios",
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty parqueaderos per preciom
parq_prec_box1 <- boxplot(base1$parqueaderos ~ base1$class_prec,
                         main = "Parqueaderos vs Preciom",
                         ylab = "Parqueaderos",
                         xlab = NA,
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

# Plot empty parqueaderos per area
parq_area_box1 <- boxplot(base1$parqueaderos ~ base1$class_area,
                         main = "Parqueaderos vs areaconst",
                         ylab = "Parqueaderos",
                         xlab = NA,
                         las = 2)
abline(h = 2, col = "blue")
text(x = 2, y = 2, labels = "Mediana: 2", col = "blue", pos = 3)

mtext("Distribución de parqueaderos por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que para la mayoría de los valores de los atributos, la mediana de los parqueaderos es 2 excepto cuando:

  • El número de habitaciones es 1, 3, 9 o 10
  • El estrato es 3 o 6
  • El número de baños es 0, 1, 2 o de 6 en adelante
  • Las distintas clases de preciom mostradas
  • Las distintas clases de areaconst mostradas

Todos los registros con pisos vacíos que no contengan las condiciones anteriores, se imputaron con un valor de 2.

Luego de esa imputación, quedaron 244 valores vacíos.

Para los registros faltantes, se anotan las medianas que cada atributo tiene para el atributo parqueaderos:

  • Los registros con 1 habitaciones (1 registro):
    • Media teniendo en cuenta el nro de Habitaciones: 1
    • Media teniendo en cuenta el estrato: 1
    • Media teniendo en cuenta el nro de Banios: 1
    • Media teniendo en cuenta la clase precio: 2
    • Media teniendo en cuenta la clase area: 2
    • Media total: 1
  • Los registros con 3 habitaciones (96 registros):
    • Media teniendo en cuenta el nro de Habitaciones: 1
    • Media teniendo en cuenta el estrato (3): 1
    • Media teniendo en cuenta el nro de Banios (2): 1
    • Media teniendo en cuenta la clase precio: 1
    • Media teniendo en cuenta la clase area: 1
    • Media total: 1
  • Los registros con 9 habitaciones (3 registros):
    • Media teniendo en cuenta el nro de Habitaciones: 3
    • Media teniendo en cuenta el estrato (3): 1
    • Media teniendo en cuenta el nro de Banios (5): 2
    • Media teniendo en cuenta la clase precio: 2
    • Media teniendo en cuenta la clase area: 2
    • Media total: 2
  • Los registros con 10 habitaciones (3 registros):
    • Media teniendo en cuenta el nro de Habitaciones: 4
    • Media teniendo en cuenta el estrato (4): 2
    • Media teniendo en cuenta el nro de Banios (8): 4
    • Media teniendo en cuenta la clase precio: 3
    • Media teniendo en cuenta la clase area: 2
    • Media total: 3
  • Los registros con estrato 3 (88 registros):
    • Media teniendo en cuenta el nro de Habitaciones (4): 2
    • Media teniendo en cuenta el estrato: 1
    • Media teniendo en cuenta el nro de Banios (3): 2
    • Media teniendo en cuenta la clase precio: 2
    • Media teniendo en cuenta la clase area: 1
    • Media total: 2
  • Los registros con estrato 6 (18 registros):
    • Media teniendo en cuenta el nro de Habitaciones (5): 2
    • Media teniendo en cuenta el estrato: 3
    • Media teniendo en cuenta el nro de Banios (5): 2
    • Media teniendo en cuenta la clase precio: 3
    • Media teniendo en cuenta la clase area: 2
    • Media total: 2
  • Los registros con 0 banios (5 registros):
    • Mediana teniendo en cuenta el nro de Habitaciones (0): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Medianateniendo en cuenta el nro de Banios: 3
    • Medianateniendo en cuenta la clase precio: 2
    • Mediana teniendo en cuenta la clase area: 2
    • Mediana total: 2
  • Los registros con 2 banios (6 registros):
    • Mediana teniendo en cuenta el nro de Habitaciones (4): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Medianateniendo en cuenta el nro de Banios: 1
    • Medianateniendo en cuenta la clase precio: 2
    • Mediana teniendo en cuenta la clase area: 2
    • Mediana total: 2
  • Los registros con 6 banios (9 registros):
    • Mediana teniendo en cuenta el nro de Habitaciones (5): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Medianateniendo en cuenta el nro de Banios: 3
    • Medianateniendo en cuenta la clase precio: 2
    • Mediana teniendo en cuenta la clase area: 2
    • Mediana total: 2
  • Los registros con 7 banios (3 registros):
    • Mediana teniendo en cuenta el nro de Habitaciones (5): 2
    • Media teniendo en cuenta el estrato (5): 2
    • Medianateniendo en cuenta el nro de Banios: 4
    • Medianateniendo en cuenta la clase precio: 2
    • Mediana teniendo en cuenta la clase area: 2
    • Mediana total: 2
  • Los registros con 8 banios (1 registros):
    • Mediana teniendo en cuenta el nro de Habitaciones (0): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Medianateniendo en cuenta el nro de Banios: 4
    • Medianateniendo en cuenta la clase precio: 2
    • Mediana teniendo en cuenta la clase area: 2
    • Mediana total: 2
  • Los registros con clase de area 100-200:
    • Media teniendo en cuenta el nro de Habitaciones (4): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Media teniendo en cuenta el nro de Banios (3): 2
    • Media teniendo en cuenta la clase precio: 2
    • Media teniendo en cuenta la clase area: 1
    • Media total: 2
  • Los registros con clase de area 400-500:
    • Media teniendo en cuenta el nro de Habitaciones (6): 2
    • Media teniendo en cuenta el estrato (5): 2
    • Media teniendo en cuenta el nro de Banios (4): 2
    • Media teniendo en cuenta la clase precio: 3
    • Media teniendo en cuenta la clase area: 2
    • Media total: 2
  • Los registros con clase de area 600-700:
    • Media teniendo en cuenta el nro de Habitaciones (8): 2
    • Media teniendo en cuenta el estrato (4): 2
    • Media teniendo en cuenta el nro de Banios (4): 2
    • Media teniendo en cuenta la clase precio: 3
    • Media teniendo en cuenta la clase area: 2
    • Media total: 2

Base2

gg_miss_var(base2, show_pct = TRUE) +
labs(title = "Datos Faltantes Apartamentos Zona Sur") +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Se observa una gran cantidad de datos faltantes para el atributo piso y parqueaderos, por lo que se determinará la relación con los otros atributos.

Imputación del atributo piso

par(mfrow = c(2, 3), oma = c(0, 0, 3, 0))

# Plot empty pisos per habitaciones
piso_hab_bar2 <- barplot(piso_hab_base2, names.arg = hab_base2,
                         main = "Piso = NA por Habitaciones",
                         ylab = "Registros vacíos",
                         xlab = "Habitaciones",
                         las = 2)

# Plot empty pisos per estrato
piso_estr_bar2 <- barplot(piso_estr_base2, names.arg = estr_base2,
                          main = "Piso = NA por estrato",
                          ylab = "Registros vacíos",
                          xlab = "estrato",
                          las = 2)

# Plot empty pisos per banios
piso_ban_bar2 <- barplot(piso_ban_base2, names.arg = ban_base2,
                         main = "Piso = NA por banios",
                         ylab = "Registros vacíos",
                         xlab = "banios",
                         las = 2)

# Plot empty pisos per precio
piso_prec_bar2 <- barplot(piso_prec_base2, names.arg = breaks_prec_2,
                          main = "Piso = NA por precio",
                          ylab = "Registros vacíos",
                          xlab = "Precio",
                          las = 2)

# Plot empty pisos per area
piso_area_bar2 <- barplot(piso_area_base2, names.arg = breaks_area_2,
                          main = "Piso = NA por area",
                          ylab = "Registros vacíos",
                          xlab = "Area",
                          las = 2)

mtext("Distribución de valores vacíos de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que los valores faltantes del atributo piso no se comportan de manera aleatoria respecto a los demás atributos. A continuación, se determina la mediana para cada caso con el fin de establecer con qué valor se imputarán los datos faltantes del atributo piso.

par(mfrow = c(2, 3),
    oma = c(0, 0, 3, 0),
    xpd = FALSE)

# Plot empty pisos per habitaciones
piso_hab_box2 <- boxplot(base2$piso ~ base2$habitaciones,
                         main = "Piso vs Habitaciones",
                         ylab = "Pisos",
                         xlab = "Habitaciones",
                         las = 2)
abline(h = 4, col = "blue")
text(x = 2, y = 4, labels = "Mediana: 4", col = "blue", pos = 3)

# Plot empty pisos per estrato
piso_estr_box2 <- boxplot(base2$piso ~ base2$estrato,
                         main = "Piso vs Estrato",
                         ylab = "Pisos",
                         xlab = "Estrato",
                         las = 2)
abline(h = 4, col = "blue")
text(x = 2, y = 4, labels = "Mediana: 4", col = "blue", pos = 3)

# Plot empty pisos per banios
piso_banios_box2 <- boxplot(base2$piso ~ base2$banios,
                         main = "Piso vs Banios",
                         ylab = "Pisos",
                         xlab = "Banios",
                         las = 2)
abline(h = 4, col = "blue")
text(x = 2, y = 4, labels = "Mediana: 4", col = "blue", pos = 3)

# Plot empty pisos per preciom
piso_prec_box2 <- boxplot(base2$piso ~ base2$class_prec,
                         main = "Piso vs Preciom",
                         ylab = "Pisos",
                         xlab = NA,
                         las = 2)

# Plot empty pisos per area
piso_area_box2 <- boxplot(base2$piso ~ base2$class_area,
                         main = "Piso vs areaconst",
                         ylab = "Pisos",
                         xlab = NA,
                         las = 2)

mtext("Distribución de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se observa que para la mayoría de los valores de los atributos, la mediana de los pisos es 4 por lo que se imputarán los faltantes con ese valor.

base2[is.na(base2$piso), "piso"] <- 4

Imputación del atributo parqueaderos

par(mfrow = c(2, 3), oma = c(0, 0, 3, 0))

# Plot empty parqueaderos per habitaciones
parq_hab_bar2 <- barplot(parq_hab_base2, names.arg = hab_base2,
                         main = "parq = NA por Habitaciones",
                         ylab = "Registros vacíos",
                         xlab = "Habitaciones",
                         las = 2)

# Plot empty parqueaderos per estrato
parq_estr_bar2 <- barplot(parq_estr_base2, names.arg = estr_base2,
                          main = "parq = NA por estrato",
                          ylab = "Registros vacíos",
                          xlab = "estrato",
                          las = 2)

# Plot empty parqueaderos per banios
parq_ban_bar2 <- barplot(parq_ban_base2, names.arg = ban_base2,
                         main = "parq = NA por banios",
                         ylab = "Registros vacíos",
                         xlab = "banios",
                         las = 2)

# Plot empty parqueaderos per precio
parq_prec_bar2 <- barplot(parq_prec_base2, names.arg = breaks_prec_2,
                          main = "parq = NA por precio",
                          ylab = "Registros vacíos",
                          xlab = "Precio",
                          las = 2)

# Plot empty parqueaderos per area
parq_area_bar2 <- barplot(parq_area_base2, names.arg = breaks_area_2,
                          main = "parq = NA por area",
                          ylab = "Registros vacíos",
                          xlab = "Area",
                          las = 2)

mtext("Distribución de valores vacíos de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

par(mfrow = c(2, 3),
    oma = c(0, 0, 3, 0),
    xpd = FALSE)

# Plot empty parqueaderos per habitaciones
parq_hab_box2 <- boxplot(base2$parqueaderos ~ base2$habitaciones,
                         main = "parq vs Habitaciones",
                         ylab = "parqs",
                         xlab = "Habitaciones",
                         las = 2)
abline(h = 1, col = "blue")
text(x = 2, y = 1, labels = "Mediana: 1", col = "blue", pos = 3)

# Plot empty parqueaderos per estrato
parq_estr_box2 <- boxplot(base2$parqueaderos ~ base2$estrato,
                         main = "parq vs Estrato",
                         ylab = "parqs",
                         xlab = "Estrato",
                         las = 2)
abline(h = 1, col = "blue")
text(x = 2, y = 1, labels = "Mediana: 1", col = "blue", pos = 3)

# Plot empty parqueaderos per banios
parq_banios_box2 <- boxplot(base2$parqueaderos ~ base2$banios,
                         main = "parq vs Banios",
                         ylab = "parqs",
                         xlab = "Banios",
                         las = 2)
abline(h = 1, col = "blue")
text(x = 2, y = 1, labels = "Mediana: 1", col = "blue", pos = 3)

# Plot empty parqueaderos per preciom
parq_prec_box2 <- boxplot(base2$parqueaderos ~ base2$class_prec,
                         main = "parq vs Preciom",
                         ylab = "parqs",
                         xlab = NA,
                         las = 2)

# Plot empty parqueaderos per area
parq_area_box2 <- boxplot(base2$parqueaderos ~ base2$class_area,
                         main = "parq vs areaconst",
                         ylab = "parqs",
                         xlab = NA,
                         las = 2)

mtext("Distribución de piso por atributo",
      side = 3, outer = TRUE, cex =  1.2, font = 2)

par(mfrow = c(1, 1))

Se imputan los datos de parqueo con la mediana 1.

Datos atípicos

base1 <- read.csv("base1_imputed.csv")
base2 <- read.csv("base2_imputed.csv")

Base1

par(mfrow = c(1, 2), oma = c(0, 0, 1, 0), mar = c(7, 2, 1, 2))
k <- 2.4

box_base1 <- boxplot(base1[, c(3:4, 7:9)], range = k, las = 2)
for (j in seq(1, ncol(box_base1$stats))) {
  for (i in seq(1, nrow(box_base1$stats))) {
    text(x = j, y = box_base1$stats[i, j],
         labels = box_base1$stats[i, j],
         pos = 4, col = "blue")
  }
}

box2_base1 <- boxplot(base1[, c(5, 6)], range = k, las = 2)
for (j in seq(1, ncol(box2_base1$stats))) {
  for (i in seq(1, nrow(box2_base1$stats))) {
    text(x = j, y = box2_base1$stats[i, j],
         labels = box2_base1$stats[i, j],
         pos = 4, col = "blue")
  }
}

mtext("Datos atípicos Base1", side = 3, outer = TRUE, font = 2)

par(mfrow = c(1,1))

Se considerarán datos erróneos aquellos inmuebles cuyo número de habitaciones o baños sea 0. Para el resto de atributos, se considerarán datos atípicos los mostrados por el diagrama de cajas y bigotes excepto para el atributo piso ya que los outliers mostrados no se considerarán datos erróneos, pues es posible que haya casa diferentes a 2 pisos.

La base de datos limpia corresponde a un 83% de la base original

Base2

par(mfrow = c(1, 2), oma = c(0, 0, 1, 0), mar = c(7, 2, 1, 2))
k <- 2.4

box_base2 <- boxplot(base2[, c(3:4, 7:9)], range = 2.4, las = 2)
for (j in seq(1, ncol(box_base2$stats))) {
  for (i in seq(1, nrow(box_base2$stats))) {
    text(x = j, y = box_base2$stats[i, j],
         labels = box_base2$stats[i, j],
         pos = 4, col = "blue")
  }
}

box2_base2 <- boxplot(base2[, c(5, 6)], range = 2.4, las = 2)
for (j in seq(1, ncol(box2_base2$stats))) {
  for (i in seq(1, nrow(box2_base2$stats))) {
    text(x = j, y = box2_base2$stats[i, j],
         labels = box2_base2$stats[i, j],
         pos = 4, col = "blue")
  }
}

mtext("Datos atípicos Base2", side = 3, outer = TRUE, font = 2)

par(mfrow = c(1,1))

Se considerarán datos erróneos aquellos inmuebles cuyo número de habitaciones o baños sea 0. Para el resto de atributos, se considerarán datos atípicos los mostrados por el diagrama de cajas y bigotes excepto para el atributo piso y habitaciones, ya que los outliers mostrados no se considerarán datos erróneos, pues es posible que haya apartamentos en diferentes 2 pisos con hasta 6 habitaciones.

La base de datos limpia corresponde a un 86% de la base original

Relación de atributos

En las siguientes figuras se observa la relación entre los atributos numéricos.

Base 1

ggpairs(base1[, c("areaconst", "preciom")], title = "Correlación Base1")

Como era de esperarse, se observa una correlación positiva entre el área construida y el precio del inmueble, sin embargo, no explica de manera suficiente la variable de respuesta, por lo que será necesario considerar más atributos a la hora de predecir. En la siguiente imágen se observa la relación con el resto de atributos.

par(mfrow = c(3, 2), oma = c(0, 0, 2, 0), mar = c(4, 3, 1, 1))
k <- 2.4

piso_base1 <- boxplot(base1$preciom ~ base1$piso,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Pisos",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(piso_base1$stats))) {
  text(x = j, y = piso_base1$stats[3, j],
       labels = piso_base1$stats[3, j],
       pos = 3, col = "blue")
}

estrato_base1 <- boxplot(base1$preciom ~ base1$estrato,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Estrato",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(estrato_base1$stats))) {
  text(x = j, y = estrato_base1$stats[3, j],
       labels = estrato_base1$stats[3, j],
       pos = 3, col = "blue")
}

parqueaderos_base1 <- boxplot(base1$preciom ~ base1$parqueaderos,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Parqueaderos",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(parqueaderos_base1$stats))) {
  text(x = j, y = parqueaderos_base1$stats[3, j],
       labels = parqueaderos_base1$stats[3, j],
       pos = 3, col = "blue")
}

banios_base1 <- boxplot(base1$preciom ~ base1$banios,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Banios",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(banios_base1$stats))) {
  text(x = j, y = banios_base1$stats[3, j],
       labels = banios_base1$stats[3, j],
       pos = 3, col = "blue")
}

habitaciones_base1 <- boxplot(base1$preciom ~ base1$habitaciones,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Habitaciones",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(habitaciones_base1$stats))) {
  text(x = j, y = habitaciones_base1$stats[3, j],
       labels = habitaciones_base1$stats[3, j],
       pos = 3, col = "blue")
}

mtext("Comportamiento del Preciom vs Atributos", side = 3, outer = TRUE, font = 2)
par(mfrow = c(1,1))

Se observan relaciones interesentes en cuanto al precio del inmueble respecto a los demás atributos dentro de las cuales se listan:

  • Para las casas de la Zona Norte, la media del preciom no tiene una gran variación debido al piso, pues al observar las muescas de las cajas hay traslapamiento, lo que indica que, para un intérvalo del 95% de la media, será similar indistintamente del piso, excepto cuando su valor es 7.
  • El estrato, el parqueadero y el número de baños, son atributos que mueven la mediana del precio conforme aumentan.
  • Del número de habitaciones podríamos afirmar que cuando está entre 1 y 3 el precio se comporta de manera similar, mientras que, cuando asciende de 4 en adelante, el precio experimentará un aumento.
# Get Coordinates from variables
var_coords <- as.data.frame(mca_result$var$coord)
var_coords["Variable"] <- ifelse(startsWith(rownames(var_coords), "piso"),
                                 "piso",
                                 ifelse(startsWith(rownames(var_coords), "estrato"),
                                        "estrato",
                                        ifelse(startsWith(rownames(var_coords), "parqueaderos"),
                                               "parqueadero",
                                                ifelse(startsWith(rownames(var_coords), "banios"),
                                                       "banios",
                                                       ifelse(startsWith(rownames(var_coords), "habitaciones"),
                                                       "habitaciones",
                                                       "precio")))))
names(var_coords) <- c("Dim1", "Dim2", "Dim3", "Dim4", "Dim5", "Variable")

for (i in seq(35, 42)) {
  var_coords[i, "Variable"] <- "area"
}

# Create Graph
g <- ggplot(var_coords, aes(x = Dim1, y = Dim2,
                             color = Variable,
                             shape = Variable,
                             label = rownames(var_coords))) +
  geom_point() +
  geom_text(vjust = -0.5) +
  labs(title = "Análisis de Correspondencia Múltiple (MCA)",
       x = paste("Dimensión1(", round(mca_result$eig[1, 2], 2), "%)", sep = ""),
       y = paste("Dimensión2(", round(mca_result$eig[2, 2], 2), "%)", sep = "")) +
  ylim(-0.5, 2.5) +
  xlim(-2, 1.5) +
  theme_minimal()

ggplotly(g)

Interpretando los atributos estrato, parqueadero y discretizando los atributos precio y área, se realizó un análisis de correspondencia múltiple que deja las siguientes observaciones para las casas de la Zona Norte:

  • Las casas de estrato 3 tienden a tener de 2 a 3 habitaciones con 2 baños, 1 parqueadero, un área entre 100-200 m^2 con un costo inferior a 200.
  • En ese mismo estrato pueden conseguirse casas con 1 habitación, 1 baño, con un área inferior a 100m^2 y un costo inferior a 200.
  • En estrato 4 se tienden a conseguir casas de 200-300m^2, con 1 o 2 parqueaderos, de 4 a 6 habitaciones por un precio entre 200-400.
  • Para el estrato 5 tienden a encontrarse casas de 5 habitaciones en adelante, entre 200-400m^2, con 2 parqueaderos y un precio de 400-600.
  • Finalmente, en el estrato 6 se conseguirán generalmente casas de 500m^2 en adelante, con más de 3 parqueaderos con un precio superior a 600.

Base 2

ggpairs(base2[, c("areaconst", "preciom")], title = "Correlación Base2")

La correlación preciom/area de los apartamentos de la Zona Sur es similar respecto a las casas de la Zona Norte.

par(mfrow = c(3, 2), oma = c(0, 0, 2, 0), mar = c(4, 3, 1, 1))
k <- 2.4

piso_base2 <- boxplot(base2$preciom ~ base2$piso,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Pisos",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(piso_base2$stats))) {
  text(x = j, y = piso_base2$stats[3, j],
       labels = piso_base2$stats[3, j],
       pos = 3, col = "blue")
}

estrato_base2 <- boxplot(base2$preciom ~ base2$estrato,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Estrato",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(estrato_base2$stats))) {
  text(x = j, y = estrato_base2$stats[3, j],
       labels = estrato_base2$stats[3, j],
       pos = 3, col = "blue")
}

parqueaderos_base2 <- boxplot(base2$preciom ~ base2$parqueaderos,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Parqueaderos",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(parqueaderos_base2$stats))) {
  text(x = j, y = parqueaderos_base2$stats[3, j],
       labels = parqueaderos_base2$stats[3, j],
       pos = 3, col = "blue")
}

banios_base2 <- boxplot(base2$preciom ~ base2$banios,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Banios",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(banios_base2$stats))) {
  text(x = j, y = banios_base2$stats[3, j],
       labels = banios_base2$stats[3, j],
       pos = 3, col = "blue")
}

habitaciones_base2 <- boxplot(base2$preciom ~ base2$habitaciones,
                      range = 2.4,
                      ylab = NA,
                      xlab = "Habitaciones",
                      notch = TRUE,
                      las = 2)
for (j in seq(1, ncol(habitaciones_base2$stats))) {
  text(x = j, y = habitaciones_base2$stats[3, j],
       labels = habitaciones_base2$stats[3, j],
       pos = 3, col = "blue")
}

mtext("Comportamiento del Preciom vs Atributos", side = 3, outer = TRUE, font = 2)
par(mfrow = c(1,1))

Se observan relaciones interesentes en cuanto al precio del inmueble respecto a los demás atributos dentro de las cuales se listan:

  • El precio respecto al piso donde se ubica el apartamento se mantendrá estable, excepto para el piso 5, afectándolo de manera negativa, y del piso 11 en adelante, afectandolo de manera positiva.
  • Se observa nuevamente la suceptibilidad del precio respecto al estrato, parqueaderos y baños.
# Get Coordinates from variables
var_coords <- as.data.frame(mca_result$var$coord)
var_coords["Variable"] <- ifelse(startsWith(rownames(var_coords), "piso"),
                                 "piso",
                                 ifelse(startsWith(rownames(var_coords), "estrato"),
                                        "estrato",
                                        ifelse(startsWith(rownames(var_coords), "parqueaderos"),
                                               "parqueadero",
                                                ifelse(startsWith(rownames(var_coords), "banios"),
                                                       "banios",
                                                       ifelse(startsWith(rownames(var_coords), "habitaciones"),
                                                       "habitaciones",
                                                       "precio")))))
names(var_coords) <- c("Dim1", "Dim2", "Dim3", "Dim4", "Dim5", "Variable")

for (i in seq(35, 42)) {
  var_coords[i, "Variable"] <- "area"
}

# Create Graph
g <- ggplot(var_coords, aes(x = Dim1, y = Dim2,
                            color = Variable,
                            shape = Variable,
                            label = rownames(var_coords))) +
  geom_point() +
  geom_text(vjust = -0.5) +
  labs(title = "Análisis de Correspondencia Múltiple (MCA)",
       x = paste("Dimensión1(", round(mca_result$eig[1, 2], 2), "%)", sep = ""),
       y = paste("Dimensión2(", round(mca_result$eig[2, 2], 2), "%)", sep = "")) +
  theme_minimal()

ggplotly(g)

Interpretando los atributos estrato, parqueadero y discretizando los atributos precio y área, se realizó un análisis de correspondencia múltiple que deja las siguientes observaciones para los apartamentos de la Zona Sur:

  • En el estrato 3 se encontrarán generalmente apartamentos de menos de 50m^2, con 1 baño por un precio inferior a 100.
  • Para el estrato 4, se encontrarán apartamentos con 2 baños, 1 parqueadero, 2 habitaciones, área de 50-100m^2 con un precio de 100-200. Es posible encontrar en este estrato apartamentos de 3 habitaciones con un precio de 200-300. Un comportamiento similar tendrá el estrato 5.
  • Para el estrato 6, será más común encontrar apartamentos sobre los 100m^2 con más de 1 parqueo y más de 4 habitaciones, 3 baños por un precio superior a los 400.

Modelado de datos

Se realizará un modelo considerando los atributos area, estrato, habitaciones, parqueaderos y baños. Inicialmente se dividirá la base de datos en una proporción 70-30 donde el 70% se utilizará para entrenamiento, mientras que el 30% se utilizará para prueba.

Base 1: Casas Zona Norte

set.seed(123)
train_index <- sample(c(TRUE, FALSE),
                      nrow(base1),
                      replace = TRUE,
                      prob = c(0.70, 0.30))
train_base1 <- base1[train_index, ]
test_base1 <- base1[!train_index, ]
mod_base1 <- lm(preciom ~ areaconst
                + estrato
                + habitaciones
                + parqueaderos
                + banios,
                data = train_base1)

summary(mod_base1)

Call:
lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
    banios, data = train_base1)

Residuals:
    Min      1Q  Median      3Q     Max 
-294.53  -60.91  -13.25   45.83  581.54 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -179.30451   28.29196  -6.338 6.02e-10 ***
areaconst       0.73589    0.05617  13.102  < 2e-16 ***
estrato        80.05815    6.93742  11.540  < 2e-16 ***
habitaciones   -1.51347    4.52327  -0.335   0.7381    
parqueaderos   13.70358    7.48954   1.830   0.0680 .  
banios         15.80003    6.52812   2.420   0.0159 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 106.7 on 420 degrees of freedom
Multiple R-squared:  0.6974,    Adjusted R-squared:  0.6938 
F-statistic: 193.6 on 5 and 420 DF,  p-value: < 2.2e-16

Se observa un error en la variable de respuesta considerablemente alto entre -294.53 y 581.54 con una media de -13.25. Respecto a los coeficientes se observa que en su mayoría son significativos a excepción del número de habitaciones, mientras que el número de parqueaderos es el menos significativo dentro de los significativos. Con un intercepto negativo, el resto de parámetros aumentarán el precio del inmueble.

El coeficiente de determinación muestra que el modelo explica la variable de respuesta en un 70%, de igual manera, se observa que el modelo es significativo considerando el p-value de la prueba F.

plot(mod_base1$residuals, train_base1$preciom,
     main = "Preciom vs Valores Ajustados")

par(mfrow = c(2, 2), oma = c(0.5,0.5,4,0.5), mar = c(2,2,2,2))
plot(mod_base1)

par(mfrow = c(1, 1))
# Normality
shapiro.test(mod_base1$residuals)

    Shapiro-Wilk normality test

data:  mod_base1$residuals
W = 0.89861, p-value = 3.259e-16

# Constant variance
bptest(mod_base1)

    studentized Breusch-Pagan test

data:  mod_base1
BP = 41.883, df = 5, p-value = 6.22e-08

# Residuals independence
dwtest(mod_base1)

    Durbin-Watson test

data:  mod_base1
DW = 1.4292, p-value = 1.295e-09
alternative hypothesis: true autocorrelation is greater than 0

Al validar el supuesto de los modelos, se observa que no se cumple con la normalidad de los residuos mostrados en la gráfica qq residuals y corroborado con la prueba de hipótesis de shapiro-wilks; el criterio de homocedasticidad tampoco se cumple, pues al observar la varianza de los residuos en el gráfico Residuals vs Fitted, a medida que los valores ajustados son mayores, la varianza va aumentando, no se observan los residuos simétricos a la línea del 0, esto se corrobora con la prueba de Durbin-Whatson; finalmente, se observa en la gráfica de Preciom vs valores ajustados, que la calidad del modelo no es buena.

options(digits = 2, scipen = 999)
table_predictions1 <- data.frame(rbind(R2, MSE, df, RSE))
names(table_predictions1) <- "Value"
formattable(table_predictions1)
Value
R2 0.68
MSE 14607.44
df 170.00
RSE 120.86

Al evaluar la calidad de las predicciones, se observa que el coeficiente R^2 se mantiene, con 170 grados de libertad, se obtiene un error estándar residual de 120.86, aumentando un poco respecto al modelo de entrenamiento.

Predicción del Requerimiento 1

results1
        Estrato 4 Estrato 5
Preciom       327       407

Como se observa en las predicciones, sería conveniente que la empresa comprara una casa con las características referenciadas en un barrio de estrato 4 en la Zona Norte, pues las viviendas tendrán un valor estimado de $327 millones, monto que se ajusta al crédito preaprobado; por el contrario, en el estrato 5, el valor estimado estaría por encima del crédito. Ofertas que pueden ajustarse, se encuentran en el siguiente mapa.

offers1 <- subset(base1[order(base1$latitud), ],
                  preciom < 350
                  & class_area == "200-300"
                  & parqueaderos == 1
                  & banios >= 2
                  & habitaciones >= 4
                  & estrato >= 4)
leaflet(c(offers1$latitud, offers1$longitud)) %>%
  addTiles() %>%
  addMarkers(lng = offers1$longitud, lat = offers1$latitud,
             popup = paste("Precio:", offers1$preciom,
                           " Estrato:", offers1$estrato,
                           " Area:", offers1$areaconst,
                           " Hab:", offers1$habitaciones,
                           " Ban:", offers1$habitaciones,
                           " Parq:", offers1$parqueadero,
                           sep = ""))

Base 2: Apartamentos Zona Sur

set.seed(123)
train_index <- sample(c(TRUE, FALSE),
                      nrow(base2),
                      replace = TRUE,
                      prob = c(0.70, 0.30))
train_base2 <- base2[train_index, ]
test_base2 <- base2[!train_index, ]
mod_base2 <- lm(preciom ~ areaconst
                + estrato
                + habitaciones
                + parqueaderos
                + banios,
                data = train_base2)

summary(mod_base2)

Call:
lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
    banios, data = train_base2)

Residuals:
    Min      1Q  Median      3Q     Max 
-194.89  -27.20   -1.42   27.74  293.84 

Coefficients:
              Estimate Std. Error t value             Pr(>|t|)    
(Intercept)  -146.8409     9.4173  -15.59 < 0.0000000000000002 ***
areaconst       2.1348     0.0737   28.96 < 0.0000000000000002 ***
estrato        42.3806     1.9712   21.50 < 0.0000000000000002 ***
habitaciones  -11.8296     2.5642   -4.61            0.0000043 ***
parqueaderos   32.5082     3.5775    9.09 < 0.0000000000000002 ***
banios          7.8156     2.9099    2.69               0.0073 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 50 on 1674 degrees of freedom
Multiple R-squared:  0.742, Adjusted R-squared:  0.741 
F-statistic:  962 on 5 and 1674 DF,  p-value: <0.0000000000000002

Se observa un error en la variable de respuesta considerablemente alto entre -194.89 y 293.84 con una media de -1.42. Respecto a los coeficientes se observa que todos son significativos. Con un intercepto negativo, el resto de parámetros aumentarán el precio del inmueble a excepción de las habitaciones.

El coeficiente de determinación muestra que el modelo explica la variable de respuesta en un 74%, de igual manera, se observa que el modelo es significativo considerando el p-value de la prueba F.

plot(mod_base2$residuals, train_base2$preciom,
     main = "Preciom vs Valores Ajustados")

par(mfrow = c(2, 2), oma = c(0.5,0.5,4,0.5), mar = c(2,2,2,2))
plot(mod_base2)

par(mfrow = c(1, 1))
# Normality
shapiro.test(mod_base2$residuals)

    Shapiro-Wilk normality test

data:  mod_base2$residuals
W = 1, p-value <0.0000000000000002

# Constant variance
bptest(mod_base2)

    studentized Breusch-Pagan test

data:  mod_base2
BP = 348, df = 5, p-value <0.0000000000000002

# Residuals independence
dwtest(mod_base2)

    Durbin-Watson test

data:  mod_base2
DW = 2, p-value <0.0000000000000002
alternative hypothesis: true autocorrelation is greater than 0

Al validar el supuesto de los modelos, se observa que no se cumple con la normalidad de los residuos mostrados en la gráfica qq residuals y corroborado con la prueba de hipótesis de shapiro-wilks; el criterio de homocedasticidad tampoco se cumple, pues al observar la varianza de los residuos en el gráfico Residuals vs Fitted, a medida que los valores ajustados son mayores, la varianza va aumentando, no se observan los residuos simétricos a la línea del 0, esto se corrobora con la prueba de Durbin-Whatson; finalmente, se observa en la gráfica de Preciom vs valores ajustados, que la calidad del modelo no es buena.

options(digits = 2, scipen = 999)
table_predictions2 <- data.frame(rbind(R2, MSE, df, RSE))
names(table_predictions2) <- "Value"
formattable(table_predictions2)
Value
R2 0.76
MSE 2506.77
df 700.00
RSE 50.07

Al evaluar la calidad de las predicciones, se observa que el coeficiente R^2 aumenta un 2%, con 700 grados de libertad, se obtiene un error estándar residual de 50.07, manteniendose respecto al modelo de entrenamiento.

Predicción del Requerimiento 2

results2
        Estrato 5 Estrato 6
Preciom       725       756

Como se observa en las predicciones, la empresa podria adquirir el inmueble con las características deseadas en estrato 5 y 6, sin embargo, no hay apartamentos disponibles con 300 metros cuadrados ni de 5 habitaciones. Ofertas que pueden ajustarse, se encuentran en el siguiente mapa.

offers2 <- subset(base2[order(base2$latitud), ],
                  preciom < 850
                  & parqueaderos >= 3
                  & banios >= 3
                  & estrato >= 5)

offers2 <- head(offers2)

leaflet(c(offers2$latitud, offers2$longitud)) %>%
  addTiles() %>%
  addMarkers(lng = offers2$longitud, lat = offers2$latitud,
             popup = paste("Precio:", offers2$preciom,
                           " Estrato:", offers2$estrato,
                           " Area:", offers2$areaconst,
                           " Hab:", offers2$habitaciones,
                           " Ban:", offers2$habitaciones,
                           " Parq:", offers2$parqueadero,
                           sep = ""))