Introducción

Elaborado por: Angélica Forero

Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.

Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:

Ayude a María a responder la solicitud, mediante técnicas modelación que usted conoce. Ella requiere le envíe un informe ejecutivo donde analice los dos casos y sus recomendaciones (Informe). Como soporte del informe debe anexar las estimaciones, validaciones y comparación de modelos requeridos (Anexos)

Pasos requeridos para la obtención de los resultados:

  • Filtro de datos y mapear zonas

  • Análisis Exploratorio

  • Modelo de regresión lineal múltiple

  • Validación de supuestos

  • Predicción precio vivienda

  • Solicitud vivienda 1

  • Solicitud vivienda 2

Cargar datos

Cargar Librerías

En primer lugar, se cargan las librerías que utilizaremos durante toda la actividad. Adicionalmente establecemos desde el principio la configuración de las opciones de formato número. En este caso que evite la notación científica y que muestre números en notación decimal estándar.

library(paqueteMODELOS)
library(tidyverse)
library(dplyr)
library(psych)
library(mice)
library(factoextra)
library(plotly)
library(ggplot2)
library(FactoMineR)
library(leaflet)
library(corrplot)
library(boot)
library(lmtest)
library(car)

options(scipen = 100, digits = 2)

Creación de funciones

Se crean funciones que nos ayudarán a limpiar la información.La primera función tiene como finalidad dejar los campos tipos caracter en mayúscula y eliminar los espacios.

LimpiarNombres <- function(s){
  x<-trimws(str_to_upper(gsub("([\\s])\\1+","\\1",s, perl=T)))
  return(x)
}

Cargar conjunto de datos

data(vivienda)

Exploración y modificación general

Se utiliza la función ‘glimpse’ que nos proporcionará una vista general de los datos. El número de registros, el número de campos y la clase a la que pertenecen esos campos.

glimpse(vivienda)
## Rows: 8,322
## Columns: 13
## $ id           <dbl> 1147, 1169, 1350, 5992, 1212, 1724, 2326, 4386, 1209, 159…
## $ zona         <chr> "Zona Oriente", "Zona Oriente", "Zona Oriente", "Zona Sur…
## $ piso         <chr> NA, NA, NA, "02", "01", "01", "01", "01", "02", "02", "02…
## $ estrato      <dbl> 3, 3, 3, 4, 5, 5, 4, 5, 5, 5, 6, 4, 5, 6, 4, 5, 5, 4, 5, …
## $ preciom      <dbl> 250, 320, 350, 400, 260, 240, 220, 310, 320, 780, 750, 62…
## $ areaconst    <dbl> 70, 120, 220, 280, 90, 87, 52, 137, 150, 380, 445, 355, 2…
## $ parqueaderos <dbl> 1, 1, 2, 3, 1, 1, 2, 2, 2, 2, NA, 3, 2, 2, 1, 4, 2, 2, 2,…
## $ banios       <dbl> 3, 2, 2, 5, 2, 3, 2, 3, 4, 3, 7, 5, 6, 2, 4, 4, 4, 3, 2, …
## $ habitaciones <dbl> 6, 3, 4, 3, 3, 3, 3, 4, 6, 3, 6, 5, 6, 2, 5, 5, 4, 3, 3, …
## $ tipo         <chr> "Casa", "Casa", "Casa", "Casa", "Apartamento", "Apartamen…
## $ barrio       <chr> "20 de julio", "20 de julio", "20 de julio", "3 de julio"…
## $ longitud     <dbl> -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -77, -7…
## $ latitud      <dbl> 3.4, 3.4, 3.4, 3.4, 3.5, 3.4, 3.4, 3.4, 3.5, 3.5, 3.4, 3.…
  • El conjunto de datos cuenta con 13 atributos y 8322 registros.

  • id: este dato es la identificación del registro por lo que tendremos en cuenta para saber si hay datos duplicados.

Identificar datos duplicados, vacíos y únicos.

Vacíos

colSums(is.na(vivienda))
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3

Duplicados

Con base en el id se identifican los duplicados

vivienda %>%
  group_by(id) %>% 
  summarise(contar=n()) %>% 
  filter(contar>1)
## # A tibble: 1 × 2
##      id contar
##   <dbl>  <int>
## 1    NA      3

Únicos

Algunos atributos categóricos y cuantitativos deberían tener muy pocas clases.Por lo tanto, seleccionamos los datos e identificamos los valores únicos que tienen.

var <- c("zona","piso", "estrato","parqueaderos","banios","habitaciones","tipo")
 for(variable in var){
  
unicos<- vivienda %>% 
  group_by(!!sym(variable)) %>% 
    summarise(registros = n())
   
 print(unicos)
 }
## # A tibble: 6 × 2
##   zona         registros
##   <chr>            <int>
## 1 Zona Centro        124
## 2 Zona Norte        1920
## 3 Zona Oeste        1198
## 4 Zona Oriente       351
## 5 Zona Sur          4726
## 6 <NA>                 3
## # A tibble: 13 × 2
##    piso  registros
##    <chr>     <int>
##  1 01          860
##  2 02         1450
##  3 03         1097
##  4 04          607
##  5 05          567
##  6 06          245
##  7 07          204
##  8 08          211
##  9 09          146
## 10 10          130
## 11 11           84
## 12 12           83
## 13 <NA>       2638
## # A tibble: 5 × 2
##   estrato registros
##     <dbl>     <int>
## 1       3      1453
## 2       4      2129
## 3       5      2750
## 4       6      1987
## 5      NA         3
## # A tibble: 11 × 2
##    parqueaderos registros
##           <dbl>     <int>
##  1            1      3155
##  2            2      2475
##  3            3       520
##  4            4       384
##  5            5        68
##  6            6        68
##  7            7        18
##  8            8        17
##  9            9         4
## 10           10         8
## 11           NA      1605
## # A tibble: 12 × 2
##    banios registros
##     <dbl>     <int>
##  1      0        45
##  2      1       496
##  3      2      2946
##  4      3      1993
##  5      4      1456
##  6      5       890
##  7      6       314
##  8      7       107
##  9      8        48
## 10      9        15
## 11     10         9
## 12     NA         3
## # A tibble: 12 × 2
##    habitaciones registros
##           <dbl>     <int>
##  1            0        66
##  2            1        59
##  3            2       926
##  4            3      4097
##  5            4      1729
##  6            5       679
##  7            6       318
##  8            7       173
##  9            8       138
## 10            9        83
## 11           10        51
## 12           NA         3
## # A tibble: 3 × 2
##   tipo        registros
##   <chr>           <int>
## 1 Apartamento      5100
## 2 Casa             3219
## 3 <NA>                3

De esta primera parte podemos evidenciar que hay 1 valor que se repite 3 veces y es el id NA. El atributo piso está en tipo caracter y tiene 2638 registros NA. El atributo parqueadero tiene 1605 registros NA. Los demás atributos tienen 3 valores NA que corresponden a los valores que se están repitiendo.

Transformación de datos

Eliminación de datos

Eliminamos los datos NA

vivienda <-vivienda[!is.na(vivienda$id),]

Estandarización de los datos tipo caracter

vivienda <- vivienda %>%
  mutate_if(is.character, LimpiarNombres)

Cambiar el tipo de atributo piso

Se cambia el tipo de atributo del piso puesto que está como caracter

vivienda$piso <-as.numeric(vivienda$piso)

Corregir datos de Barrio

Los caracteres “É” y “Ú” los está tomando con los caracteres “√©” y “√∫”, respectivamente. Por lo tanto, hacemos los cambios. También algunos están tomando las tílde entonces dejamos todos los datos sin tíldes.

vivienda$barrio <- gsub("é", "E", vivienda$barrio)
vivienda$barrio <- gsub("√∫", "U", vivienda$barrio)
vivienda$barrio <- gsub("Á", "A", vivienda$barrio)
vivienda$barrio <- gsub("É", "E", vivienda$barrio)
vivienda$barrio <- gsub("Í", "I", vivienda$barrio)
vivienda$barrio <- gsub("Ó", "O", vivienda$barrio)
vivienda$barrio <- gsub("Ú", "U", vivienda$barrio)

Reemplezar NA parqueaderos

vivienda$parqueaderos[is.na(vivienda$parqueaderos)]<-0

1. BASE 1: Casas

1. Filtro

Realice un filtro a la base de datos e incluya solo las ofertas de: base1: casas, de la zona norte 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?).

casas <- vivienda %>% 
  filter(tipo =="CASA", zona=="ZONA NORTE") %>% 
  select(-id)
head(casas,3)
## # A tibble: 3 × 12
##   zona     piso estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 ZONA N…     2       5     320       150            2      4            6 CASA 
## 2 ZONA N…     2       5     780       380            2      3            3 CASA 
## 3 ZONA N…     2       6     750       445            0      7            6 CASA 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Verificación filtro

Para verificar el filtro extraemos los datos únicos de algunas variables

var <- c("zona","piso", "estrato","parqueaderos","banios","habitaciones","tipo")
 for(variable in var){
  
unicos<- casas %>% 
  group_by(!!sym(variable)) %>% 
    summarise(registros = n())
   
 print(unicos)
 }
## # A tibble: 1 × 2
##   zona       registros
##   <chr>          <int>
## 1 ZONA NORTE       722
## # A tibble: 6 × 2
##    piso registros
##   <dbl>     <int>
## 1     1        84
## 2     2       194
## 3     3        65
## 4     4         6
## 5     7         1
## 6    NA       372
## # A tibble: 4 × 2
##   estrato registros
##     <dbl>     <int>
## 1       3       235
## 2       4       161
## 3       5       271
## 4       6        55
## # A tibble: 11 × 2
##    parqueaderos registros
##           <dbl>     <int>
##  1            0       287
##  2            1       161
##  3            2       158
##  4            3        49
##  5            4        40
##  6            5        11
##  7            6         8
##  8            7         5
##  9            8         1
## 10            9         1
## 11           10         1
## # A tibble: 11 × 2
##    banios registros
##     <dbl>     <int>
##  1      0        10
##  2      1        17
##  3      2       165
##  4      3       187
##  5      4       171
##  6      5       101
##  7      6        46
##  8      7        11
##  9      8        11
## 10      9         1
## 11     10         2
## # A tibble: 11 × 2
##    habitaciones registros
##           <dbl>     <int>
##  1            0        20
##  2            1         2
##  3            2        12
##  4            3       171
##  5            4       222
##  6            5       137
##  7            6        60
##  8            7        42
##  9            8        29
## 10            9        14
## 11           10        13
## # A tibble: 1 × 2
##   tipo  registros
##   <chr>     <int>
## 1 CASA        722

Mapa

mapa_casa <- leaflet(casas) %>% 
  addTiles() %>% 
  addMarkers(lng = ~longitud,
             lat = ~latitud)


mapa_casa

Se puede observar que no todos los puntos están en la zona norte. No obstante, la mayoría de ellos sí se encuentran ubicados en esa área.

2. Análisis Exploratorio

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

Datos

Para entender la data de casas realizamos un summary

summary(casas)
##      zona                piso        estrato       preciom       areaconst   
##  Length:722         Min.   :1     Min.   :3.0   Min.   :  89   Min.   :  30  
##  Class :character   1st Qu.:2     1st Qu.:3.0   1st Qu.: 261   1st Qu.: 140  
##  Mode  :character   Median :2     Median :4.0   Median : 390   Median : 240  
##                     Mean   :2     Mean   :4.2   Mean   : 446   Mean   : 265  
##                     3rd Qu.:2     3rd Qu.:5.0   3rd Qu.: 550   3rd Qu.: 337  
##                     Max.   :7     Max.   :6.0   Max.   :1940   Max.   :1440  
##                     NA's   :372                                              
##   parqueaderos      banios      habitaciones      tipo          
##  Min.   : 0.0   Min.   : 0.0   Min.   : 0.0   Length:722        
##  1st Qu.: 0.0   1st Qu.: 2.0   1st Qu.: 3.0   Class :character  
##  Median : 1.0   Median : 3.0   Median : 4.0   Mode  :character  
##  Mean   : 1.3   Mean   : 3.6   Mean   : 4.5                     
##  3rd Qu.: 2.0   3rd Qu.: 4.0   3rd Qu.: 5.0                     
##  Max.   :10.0   Max.   :10.0   Max.   :10.0                     
##                                                                 
##     barrio             longitud      latitud   
##  Length:722         Min.   :-77   Min.   :3.3  
##  Class :character   1st Qu.:-77   1st Qu.:3.5  
##  Mode  :character   Median :-77   Median :3.5  
##                     Mean   :-77   Mean   :3.5  
##                     3rd Qu.:-77   3rd Qu.:3.5  
##                     Max.   :-76   Max.   :3.5  
## 

Análisis atributos

boxplot(casas$preciom, casas$areaconst,
        names = c("Precio", "Área"),
        main = "Diagrama de Caja",
        ylab = "Valores")

barplot(prop.table(table(casas$estrato)),
        ylab = "Proportion",
        xlab = "Estrato")

barplot(prop.table(table(casas$banios)),
        ylab = "Proportion",
        xlab = "Baños")

barplot(prop.table(table(casas$habitaciones)),
        ylab = "Proportion",
        xlab = "Habitaciones")

barplot(prop.table(table(casas$parqueaderos)),
        ylab = "Proportion",
        xlab = "Parqueaderos")

casa_ea <- casas %>% 
  select(preciom, areaconst, parqueaderos, estrato, banios, habitaciones)

matriz_cor_casa <- cor(casa_ea)

corrplot(matriz_cor_casa, method = "color", addCoef.col = "black")

ggpairs(casa_ea)

De acuerdo con la información previa, se observa que la mayoría de las casas se encuentran en el rango de estratos 3 a 5, con 2 a 4 baños, 3 a 5 habitaciones y 0 a 2 parqueaderos. Además, el promedio del precio de las casas es de 446 millones con un área promedio de 265. Al analizar las correlaciones, se destaca una relación lineal muy significativa entre el precio y el área (0.76), así como entre el precio y el estrato (0.62). En contraste, no se evidencia una correlación tan marcada entre el precio y el número de baños, habitaciones y paqueaderos. Por lo tanto, se puede inferir que el área y el estrato influyen considerablemente en el precio, indicando que a mayor área o estrato, mayor será el valor de la vivienda.

3. Modelo de Regresión Lineal Múltiple.

Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deben estár contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

set.seed(42)
modelo_casa <- lm(formula = preciom~areaconst+estrato+habitaciones+parqueaderos+banios, data = casa_ea)

summary(modelo_casa)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = casa_ea)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -964.0  -80.1  -17.1   50.1 1069.4 
## 
## Coefficients:
##               Estimate Std. Error t value             Pr(>|t|)    
## (Intercept)  -236.4755    30.3658   -7.79    0.000000000000024 ***
## areaconst       0.8268     0.0437   18.93 < 0.0000000000000002 ***
## estrato        86.4258     7.3975   11.68 < 0.0000000000000002 ***
## habitaciones    1.4444     4.1641    0.35                 0.73    
## parqueaderos   -1.6767     4.3150   -0.39                 0.70    
## banios         26.9798     5.3438    5.05    0.000000564653292 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 159 on 716 degrees of freedom
## Multiple R-squared:  0.651,  Adjusted R-squared:  0.648 
## F-statistic:  267 on 5 and 716 DF,  p-value: <0.0000000000000002

El modelo muestra que las variables área, estrato y número de baños tienen un impacto significativo sobre el precio de la casa, mientras que el número de habitaciones y paqueaderos no parecen ser tan relevantes.

Los coeficientes proporcionan información adicional: por cada metro cuadrado adicional en una casa, el precio aumenta en 0.8268 unidades, lo que equivale aproximadamente a $826,000. Un nivel de estrato más alto se traduce en un incremento de aproximadamente $86 millones en el precio de la casa, y cada baño adicional contribuye a un aumento de $26 millones en el precio, manteniendo constantes las otras variables en cada caso.

Estos resultados tienen sentido: se esperaría que un estrato más elevado correspondiera a una zona más exclusiva, aumentando así el precio. Además, es lógico que una casa con mayor área tenga un precio más alto. Sin embargo, llama la atención que el número de baños tenga un impacto mayor que el área en el precio de la casa.

El valor R^2 nos indica que el modelo explica aproximadamente el 65.1% de la variabilidad observada en el precio de las casas.

Se pueden realizar técnicas de validación cruzada con el fin de evaluar la capacidad predictiva del modelo y seleccionar las mejores variables presictoras. Evitando el sobreajuste y mejorando la generalización del modelo.

Validación Cruzada

# Se define la función que devuelve el estadístico de interés, los coeficientes
# de regresión
set.seed(42)

fun_coeficientes <- function(data, index){
    return(coef(lm(formula = preciom~areaconst+estrato+habitaciones+parqueaderos+banios,
                   data = casa_ea, subset = index)
                )
           )
}

boot(data = casa_ea, statistic = fun_coeficientes, R = 9999)
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = casa_ea, statistic = fun_coeficientes, R = 9999)
## 
## 
## Bootstrap Statistics :
##     original  bias    std. error
## t1*  -236.48   2.120       32.21
## t2*     0.83   0.008        0.11
## t3*    86.43  -0.793        9.54
## t4*     1.44  -0.065        5.23
## t5*    -1.68  -0.136        5.46
## t6*    26.98  -0.166        7.55

La validación cruzada revela que, de las tres variables que influyen en el precio, el área es la variable que presenta menor sesgo (0.0087) y mayor precisión (0.11). En cuanto al estrato, la estimación del coeficiente tiende, en promedio, a ser aproximadamente 0.793 unidades más baja de lo que sería sin sesgo, mientras que para los baños este valor es de 0.166. Además, el error estándar indica que las estimaciones del coeficiente del estrato (86.43) pueden variar, en promedio, alrededor de 9.54 unidades de su valor estimado, mientras que para los baños este valor es de 7.55.

4. Validación de supuestos.

Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

Supuesto de normalidad

Se realiza la prueba de Shapiro-Wilk para evaluar formalmente si los residuos siguen una distribución normal.

print(shapiro.test(resid(modelo_casa)))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelo_casa)
## W = 0.8, p-value <0.0000000000000002

La prueba de Shapiro sugiere que los residuos en el modelo no siguen una distribución normal. Esto nos indica que el modelo posiblemente no está capturando completamente la estructura de los datos. El impacto de esto es que al final puede afectar la precisión de las predicciones. Existen varias técnicas que se pueden utilizar cuando los residuos del modelo no siguen una distribución normal:

  • Se podrían transformar las variables para que se ajusten mejor a una distribución normal, por ejmplo, utilizar una transformación logarítmica o Box-Cox.

  • Utilizar modelos de regresión que son menos sensibles a las violaciones de los supuestos de normalidad y homocedasticidad, por ejemplo, regresión robusta de mínimos cuadrados ordinarios (MCO) o la regresión robusta de mínimos cuadrados no paramétricos (MNCP)

Supuesto de varianza constante (homocedasticidad)

plot(fitted(modelo_casa), resid(modelo_casa), 
     xlab = "Valores Ajustados", ylab = "Residuos",
     main = "Gráfico de Residuos vs. Valores Ajustados")
abline(h = 0, col = "red", lty = 2)  # Agregar línea horizontal en y = 0

print(gqtest(modelo_casa))
## 
##  Goldfeld-Quandt test
## 
## data:  modelo_casa
## GQ = 1, df1 = 355, df2 = 355, p-value = 0.1
## alternative hypothesis: variance increases from segment 1 to 2

De acuerdo al gráfico y a la prueba de Goldfeld-Quandt, el supuesto de varianza constante no se viola en el modelo de regresión, es decir que los efectos de las variables predictoras (área, estrato, parqueadero, habitaciones, baños) en la varianza de los errores son consistentes en todo el rango de los datos. Esto proporciona una mayor confianza en las estimaciones y predicciones del modelo.

Supuesto de independencia

print(dwtest(modelo_casa))
## 
##  Durbin-Watson test
## 
## data:  modelo_casa
## DW = 2, p-value = 0.0000003
## alternative hypothesis: true autocorrelation is greater than 0

De acuerdo con los resultados de la prueba de Durbin-Watson, el modelo no cumple con el supuesto de independencia de los errores. Esto indica que el modelo puede estar subestimando o sobreestimando consistentemente los precios de las propiedades en función de las características del modelo. Se puede abordar este problema a través de:

  • Transformación de las variables, por ejmplo, utilizar una transformación logarítmica o Box-Cox.

  • Corrección de errores autocorrelacionados, por ejemplo, los modelos de autorregresión con errores ARIMA (ARIMA errors), los modelos de series temporales con términos autorregresivos de error o la inclusión de retardos adicionales de la variable dependiente en el modelo.

Multicolinealidad

A través del análisis de la varianza inflada (VIF), se puede calcular un factor para cada variable independiente. VIF mayor que 10 indica alta multicolinealidad.

print(vif(modelo_casa))
##    areaconst      estrato habitaciones parqueaderos       banios 
##          1.5          1.5          1.6          1.2          1.9

Los resultados del Factor de Inflación de la Varianza (VIF) para las variables en el modeloson bastante bajos, lo que indica que no hay evidencia de multicolinealidad significativa entre las variables independientes.

5. Predicción Precio de la vivienda

Según los requisitos de la casa, estas son las características:

  • Área construida: 200
  • Estrato: 4 o 5
  • Habitaciones: 4
  • Parqueaderos: 1
  • Baños: 2
# Estrato 4

datos_casa4 <- data.frame(areaconst=200,
                             estrato=4,
                             habitaciones=4,
                             parqueaderos=1,
                             banios=2)

prediccion_precioc4 <- predict(modelo_casa, datos_casa4)

print(prediccion_precioc4)
##   1 
## 333

De acuerdo al modelo, el precio con las características para la casa y en estrato 4 es de 333 millones

# Estrato 5

datos_casa5 <- data.frame(areaconst=200,
                             estrato=5,
                             habitaciones=4,
                             parqueaderos=1,
                             banios=2)

prediccion_precioc5 <- predict(modelo_casa, datos_casa5)

print(prediccion_precioc5)
##   1 
## 419

De acuerdo al modelo, el precio con las características para la casa y en estrato 5 es de 419 millones.

6. Ofertas Sugeridas

Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuenta que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

# Ofertas

ofertas_casas<- casas %>% 
  filter(preciom<=350,
         areaconst>=200,
         parqueaderos==1,
         banios>=2,
         habitaciones>=4,
         estrato%in%c(4,5)) %>% 
  arrange(preciom)
  

head(ofertas_casas, 7)
## # A tibble: 7 × 12
##   zona     piso estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 ZONA N…    NA       5     250       243            1      4            5 CASA 
## 2 ZONA N…    NA       5     321       249            1      5            5 CASA 
## 3 ZONA N…     2       4     330       260            1      3            4 CASA 
## 4 ZONA N…     1       5     335       202            1      4            5 CASA 
## 5 ZONA N…     2       5     342       250            1      4            6 CASA 
## 6 ZONA N…     3       4     350       350            1      4            5 CASA 
## 7 ZONA N…    NA       5     350       346            1      2            4 CASA 
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Mapa Ofertas

mapa_Ofertacasa <- leaflet(ofertas_casas) %>% 
  addTiles() %>% 
  addMarkers(lng = ~longitud,
             lat = ~latitud)


mapa_Ofertacasa

De acuerdo con la información se cuenta con 7 ofertas que se encuentran en la zona norte, con precios entre los 250 millones a los 350 millones, en estratos entre 4 y 5, área construida de 243 a 346, 1 parqueadero, entre 2 a 5 baños y entre 4 a 6 habitaciones. Así mismo, la oferta que se ajusta totalmente a las necesidades se encuentra en el barrio Vipasa y tiene un precio de 350 millones.

2. BASE 2: Apartamentos

1. Filtro

Realice un filtro a la base de datos e incluya solo las ofertas de 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?).

apartamentos <- vivienda %>% 
  filter(tipo =="APARTAMENTO", zona=="ZONA SUR") %>% 
  select(-id)

head(apartamentos,3)
## # A tibble: 3 × 12
##   zona     piso estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 ZONA S…     5       4     290        96            1      2            3 APAR…
## 2 ZONA S…     2       3      78        40            1      1            2 APAR…
## 3 ZONA S…    NA       6     875       194            2      5            3 APAR…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Verificación filtro

Para verificar el filtro extraemos los datos únicos de algunas variables

var <- c("zona","piso", "estrato","parqueaderos","banios","habitaciones","tipo")
 for(variable in var){
  
unicos<- apartamentos %>% 
  group_by(!!sym(variable)) %>% 
    summarise(registros = n())
   
 print(unicos)
 }
## # A tibble: 1 × 2
##   zona     registros
##   <chr>        <int>
## 1 ZONA SUR      2787
## # A tibble: 13 × 2
##     piso registros
##    <dbl>     <int>
##  1     1       255
##  2     2       319
##  3     3       329
##  4     4       327
##  5     5       353
##  6     6       131
##  7     7       113
##  8     8       129
##  9     9        73
## 10    10        73
## 11    11        29
## 12    12        34
## 13    NA       622
## # A tibble: 4 × 2
##   estrato registros
##     <dbl>     <int>
## 1       3       201
## 2       4      1091
## 3       5      1033
## 4       6       462
## # A tibble: 6 × 2
##   parqueaderos registros
##          <dbl>     <int>
## 1            0       406
## 2            1      1551
## 3            2       718
## 4            3        79
## 5            4        31
## 6           10         2
## # A tibble: 9 × 2
##   banios registros
##    <dbl>     <int>
## 1      0         6
## 2      1       167
## 3      2      1588
## 4      3       662
## 5      4       229
## 6      5       123
## 7      6        10
## 8      7         1
## 9      8         1
## # A tibble: 7 × 2
##   habitaciones registros
##          <dbl>     <int>
## 1            0         8
## 2            1        19
## 3            2       463
## 4            3      1902
## 5            4       366
## 6            5        24
## 7            6         5
## # A tibble: 1 × 2
##   tipo        registros
##   <chr>           <int>
## 1 APARTAMENTO      2787

Mapa

mapa_apartamento <- leaflet(apartamentos) %>% 
  addTiles() %>% 
  addMarkers(lng = ~longitud,
             lat = ~latitud)


mapa_apartamento

Se puede observar que no todos los puntos están en la zona sur. No obstante, la mayoría de ellos sí se encuentran ubicados en esa área.

2. Análisis Exploratorio

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

Datos

Para entender la data de apartamentos realizamos un summary

summary(apartamentos)
##      zona                piso        estrato       preciom       areaconst  
##  Length:2787        Min.   : 1    Min.   :3.0   Min.   :  75   Min.   : 40  
##  Class :character   1st Qu.: 2    1st Qu.:4.0   1st Qu.: 175   1st Qu.: 65  
##  Mode  :character   Median : 4    Median :5.0   Median : 245   Median : 85  
##                     Mean   : 4    Mean   :4.6   Mean   : 297   Mean   : 97  
##                     3rd Qu.: 6    3rd Qu.:5.0   3rd Qu.: 335   3rd Qu.:110  
##                     Max.   :12    Max.   :6.0   Max.   :1750   Max.   :932  
##                     NA's   :622                                             
##   parqueaderos      banios     habitaciones     tipo          
##  Min.   : 0.0   Min.   :0.0   Min.   :0     Length:2787       
##  1st Qu.: 1.0   1st Qu.:2.0   1st Qu.:3     Class :character  
##  Median : 1.0   Median :2.0   Median :3     Mode  :character  
##  Mean   : 1.2   Mean   :2.5   Mean   :3                       
##  3rd Qu.: 2.0   3rd Qu.:3.0   3rd Qu.:3                       
##  Max.   :10.0   Max.   :8.0   Max.   :6                       
##                                                               
##     barrio             longitud      latitud   
##  Length:2787        Min.   :-77   Min.   :3.3  
##  Class :character   1st Qu.:-77   1st Qu.:3.4  
##  Mode  :character   Median :-77   Median :3.4  
##                     Mean   :-77   Mean   :3.4  
##                     3rd Qu.:-77   3rd Qu.:3.4  
##                     Max.   :-76   Max.   :3.5  
## 

Análisis atributos

boxplot(apartamentos$preciom, apartamentos$areaconst,
        names = c("Precio", "Área"),
        main = "Diagrama de Caja",
        ylab = "Valores")

barplot(prop.table(table(apartamentos$estrato)),
        ylab = "Proportion",
        xlab = "Estrato")

barplot(prop.table(table(apartamentos$banios)),
        ylab = "Proportion",
        xlab = "Baños")

barplot(prop.table(table(apartamentos$habitaciones)),
        ylab = "Proportion",
        xlab = "Habitaciones")

barplot(prop.table(table(casas$parqueaderos)),
        ylab = "Proportion",
        xlab = "Parqueaderos")

apartamento_ea <- apartamentos %>% 
  select(preciom, areaconst, parqueaderos, estrato, banios, habitaciones)

matriz_cor_apartamento <- cor(apartamento_ea)

corrplot(matriz_cor_apartamento, method = "color", addCoef.col = "black")

ggpairs(apartamento_ea)

Según los datos previos, la mayoría de los apartamentos se ubican en el rango de estratos 4 a 5, con 2 a 3 baños, 3 habitaciones y de 0 a 2 parqueaderos. Además, el precio promedio de estos apartamentos es de 297 millones, con un área promedio de 97 metros cuadrados. Al examinar las correlaciones, se destaca una relación lineal muy significativa entre el precio y el área (0.76), así como entre el precio y el número de baños (0.72), el precio y el estrato (0.67), y el precio y el número de parqueaderos (0.67). En contraste, no se observa una correlación tan pronunciada entre el precio y el número de habitaciones (0.33). Por lo tanto, se puede inferir que el área, el estrato, el número de baños y los parqueaderos influyen considerablemente en el precio de los apartamentos.

3. Modelo de Regresión Lineal Múltiple.

Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deben estár contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

set.seed(42)
modelo_apartamento <- lm(formula = preciom~areaconst+estrato+habitaciones+parqueaderos+banios, data = apartamento_ea)

summary(modelo_apartamento)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = apartamento_ea)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1252.3   -42.1    -2.1    36.3   934.1 
## 
## Coefficients:
##               Estimate Std. Error t value             Pr(>|t|)    
## (Intercept)  -221.0461    13.4777  -16.40 < 0.0000000000000002 ***
## areaconst       1.4606     0.0488   29.96 < 0.0000000000000002 ***
## estrato        57.0061     2.7965   20.38 < 0.0000000000000002 ***
## habitaciones  -22.7179     3.3955   -6.69       0.000000000027 ***
## parqueaderos   48.3635     3.0234   16.00 < 0.0000000000000002 ***
## banios         48.6087     3.0405   15.99 < 0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 95 on 2781 degrees of freedom
## Multiple R-squared:  0.754,  Adjusted R-squared:  0.753 
## F-statistic: 1.7e+03 on 5 and 2781 DF,  p-value: <0.0000000000000002

El análisis revela la significancia de todas las variables consideradas (área, estrato, número de habitaciones, parqueaderos y baños).

Los coeficientes arrojan detalles adicionales: por cada metro cuadrado extra en un apartamento, el precio aumenta en 1.4606 unidades, aproximadamente $1’460.600. Un estrato más alto implica un incremento de alrededor de $57 millones en el precio del apartamento, mientras que cada habitación adicional reduce el precio en $22 millones. Por otro lado, cada parqueadero o baño adicional incrementa el precio en $48 millones, manteniendo las otras variables constantes.

Estos hallazgos son coherentes: se esperaría que un estrato más elevado estuviera asociado con una ubicación más exclusiva, lo que se traduce en un aumento en el precio. Además, resulta lógico que un apartamento más amplio tenga un precio más alto. Sin embargo, es notable que el número de baños y parqueaderos tenga un impacto más significativo en el precio que el área del apartamento, y que el número de habitaciones reduzca el precio del mismo.

El valor de R cuadrado (R^2) indica que el modelo explica aproximadamente el 75.4% de la variabilidad observada en el precio de los apartamentos.

Se pueden realizar técnicas de validación cruzada con el fin de evaluar la capacidad predictiva del modelo y seleccionar las mejores variables presictoras. Evitando el sobreajuste y mejorando la generalización del modelo.

Validación Cruzada

# Se define la función que devuelve el estadístico de interés, los coeficientes
# de regresión
set.seed(42)
fun_coeficientes <- function(data, index){
    return(coef(lm(formula = preciom~areaconst+estrato+habitaciones+parqueaderos+banios,
                   data = apartamento_ea, subset = index)
                )
           )
}

boot(data = apartamento_ea, statistic = fun_coeficientes, R = 9999)
## 
## ORDINARY NONPARAMETRIC BOOTSTRAP
## 
## 
## Call:
## boot(data = apartamento_ea, statistic = fun_coeficientes, R = 9999)
## 
## 
## Bootstrap Statistics :
##     original  bias    std. error
## t1*   -221.0    2.25       17.78
## t2*      1.5    0.05        0.32
## t3*     57.0   -0.37        3.54
## t4*    -22.7   -0.58        5.88
## t5*     48.4   -0.76        7.16
## t6*     48.6   -1.09        7.47

La validación cruzada revela que, entre las cinco variables que influyen en el precio, el área presenta el menor sesgo (0.05) y la mayor precisión (0.32). En cuanto al estrato, habitaciones, parqueaderos y baños, la estimación de cada coeficiente tiende, en promedio, a ser aproximadamente 0.37, 0.58, 0.76 y 1.09 unidades respectivamente más baja de lo que sería sin sesgo. Además, el error estándar indica que las estimaciones de los coeficientes del estrato, habitaciones, parqueaderos y baños pueden variar, en promedio, alrededor de 3.54, 5.88, 7.16 y 7.47 unidades respectivamente de su valor estimado.

4. Validación de supuestos.

Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

Supuesto de normalidad

Se realiza la prueba de Shapiro-Wilk para evaluar formalmente si los residuos siguen una distribución normal.

print(shapiro.test(resid(modelo_apartamento)))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(modelo_apartamento)
## W = 0.8, p-value <0.0000000000000002

La prueba de Shapiro sugiere que los residuos en el modelo no siguen una distribución normal. Esto nos indica que el modelo posiblemente no está capturando completamente la estructura de los datos. El impacto de esto es que al final puede afectar la precisión de la predicción del precio. Existen varias técnicas que se pueden utilizar cuando los residuos del modelo no siguen una distribución normal:

  • Se podrían transformar las variables para que se ajusten mejor a una distribución normal, por ejmplo, utilizar una transformación logarítmica o Box-Cox.

  • Utilizar modelos dde regresión que son menos sensibles a las violaciones de los supuestos de normalidad y homocedasticidad, por ejemplo, regresión robusta de mínimos cuadrados ordinarios (MCO) o la regresión robusta de mínimos cuadrados no paramétricos (MNCP)

Supuesto de varianza constante (homocedasticidad)

plot(fitted(modelo_apartamento), resid(modelo_apartamento), 
     xlab = "Valores Ajustados", ylab = "Residuos",
     main = "Gráfico de Residuos vs. Valores Ajustados")
abline(h = 0, col = "red", lty = 2)  # Agregar línea horizontal en y = 0

print(gqtest(modelo_apartamento))
## 
##  Goldfeld-Quandt test
## 
## data:  modelo_apartamento
## GQ = 0.9, df1 = 1388, df2 = 1387, p-value = 0.9
## alternative hypothesis: variance increases from segment 1 to 2

De acuerdo al gráfico y a la prueba de Goldfeld-Quandt, el supuesto de varianza constante no se viola en el modelo de regresión, es decir que los efectos de las variables predictoras (área, estrato, parqueadero, habitaciones, baños) en la varianza de los errores son consistentes en todo el rango de los datos. Esto proporciona una mayor confianza en las estimaciones y predicciones del modelo.

Supuesto de independencia

print(dwtest(modelo_apartamento))
## 
##  Durbin-Watson test
## 
## data:  modelo_apartamento
## DW = 2, p-value <0.0000000000000002
## alternative hypothesis: true autocorrelation is greater than 0

De acuerdo con los resultados de la prueba de Durbin-Watson, el modelo no cumple con el supuesto de independencia de los errores. Esto indica que el modelo puede estar subestimando o sobreestimando consistentemente los precios de las propiedades en función de las características del modelo. Se puede abordar este problema a través de:

  • Transformación de las variables, por ejmplo, utilizar una transformación logarítmica o Box-Cox.

  • Corrección de errores autocorrelacionados, por ejemplo, los modelos de autorregresión con errores ARIMA (ARIMA errors), los modelos de series temporales con términos autorregresivos de error o la inclusión de retardos adicionales de la variable dependiente en el modelo.

Multicolinealidad

A través del análisis de la varianza inflada (VIF), se puede calcular un factor para cada variable independiente. VIF mayor que 10 indica alta multicolinealidad.

print(vif(modelo_apartamento))
##    areaconst      estrato habitaciones parqueaderos       banios 
##          2.0          1.7          1.4          1.8          2.5

Los resultados del Factor de Inflación de la Varianza (VIF) para las variables en el modelo son bastante bajos, lo que indica que no hay evidencia de multicolinealidad significativa entre las variables independientes.

5. Predicción Precio del apartamento

Según los requisitos del apartamento, estas son las características:

  • Área construida: 300
  • Estrato: 5 o 6
  • Habitaciones: 5
  • Parqueaderos: 3
  • Baños: 3
# Estrato 5

datos_apto5 <- data.frame(areaconst=300,
                             estrato=5,
                             habitaciones=5,
                             parqueaderos=3,
                             banios=3)

prediccion_precioa5 <- predict(modelo_apartamento, datos_apto5)

print(prediccion_precioa5)
##   1 
## 679

De acuerdo al modelo, el precio con las características para el apartamento y en estrato 5 es de 679 millones.

# Estrato 6

datos_apto6  <- data.frame(areaconst=300,
                             estrato=6,
                             habitaciones=5,
                             parqueaderos=3,
                             banios=3)

prediccion_precioa6 <- predict(modelo_apartamento, datos_apto6)

print(prediccion_precioa6)
##   1 
## 737

De acuerdo al modelo, el precio con las características para el apartamento y en estrato 5 es de 737 millones.

6. Ofertas Sugeridas

Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 2. Tenga encuenta que la empresa tiene crédito pre-aprobado de máximo 850 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

# Ofertas

ofertas_aptos<- apartamentos %>% 
  filter(preciom<=850,
         parqueaderos>=3,
         habitaciones>=5,
         estrato%in%c(5,6)) %>% 
  arrange(preciom)
  

head(ofertas_aptos, 3)
## # A tibble: 3 × 12
##   zona     piso estrato preciom areaconst parqueaderos banios habitaciones tipo 
##   <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>
## 1 ZONA S…    NA       5     530       256            3      5            5 APAR…
## 2 ZONA S…    NA       5     670       300            3      5            6 APAR…
## 3 ZONA S…    NA       5     730       573            3      8            5 APAR…
## # ℹ 3 more variables: barrio <chr>, longitud <dbl>, latitud <dbl>

Mapa Ofertas

mapa_Ofertaapto <- leaflet(ofertas_aptos) %>% 
  addTiles() %>% 
  addMarkers(lng = ~longitud,
             lat = ~latitud)


mapa_Ofertaapto

De acuerdo con la información se cuenta con 3 ofertas que se encuentran en la zona sur, con precios entre los 530 a 730 millones, en estratos 5, área construida de 256 a 573, 3 parqueadero, entre 5 a 8 baños y entre 5 a 6 habitaciones.