Informe Actividad 2: Caso C&A

Carga de librerías e Importación de Datos

suppressWarnings({
library(ggplot2)
library(ggmap)
library(dplyr)
library(naniar)
library(mice)
library(plotly)
library(datasets)
library(gridExtra)
library(leaflet)
library(boot)
library(paqueteMODELOS)
data("vivienda")
})
## ℹ Google's Terms of Service: <https://mapsplatform.google.com>
##   Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service/>
##   OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles/>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
## 
## Attaching package: 'dplyr'
## 
## 
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## 
## 
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## 
## 
## 
## Attaching package: 'mice'
## 
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## 
## The following objects are masked from 'package:base':
## 
##     cbind, rbind
## 
## 
## 
## Attaching package: 'plotly'
## 
## 
## The following object is masked from 'package:ggmap':
## 
##     wind
## 
## 
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## 
## 
## The following object is masked from 'package:stats':
## 
##     filter
## 
## 
## The following object is masked from 'package:graphics':
## 
##     layout
## 
## 
## 
## Attaching package: 'gridExtra'
## 
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
## 
## 
## Loading required package: broom
## 
## Loading required package: GGally
## 
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## 
## Loading required package: knitr
## 
## Loading required package: summarytools

Análisis de compra de vivienda 1

Filtro a base de datos para ofertas de casas

Zona norte de la ciudad. Se Presentan los primeros 3 registros de los datos filtrados

vivCasaZonaNorte <- subset(vivienda,
                             tipo=="Casa" &
                             zona=="Zona Norte" &
                             (estrato == 4 | estrato == 5))
head(vivCasaZonaNorte, 3)
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1209 Zona N… 02          5     320       150            2      4            6
## 2  1592 Zona N… 02          5     780       380            2      3            3
## 3  4460 Zona N… 02          4     625       355            3      5            5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Visualización de tablas que comprueban el filtrado de las casas

rangoArea <- c(180, 220)
rangoPrecio <- c(300, 400)

Agrupación por área

resArea <- vivCasaZonaNorte %>%
  mutate(range_group = cut(areaconst, breaks = rangoArea)) %>%
  group_by(range_group) %>%
  summarize(count = n())
print(resArea)
## # A tibble: 2 × 2
##   range_group count
##   <fct>       <int>
## 1 (180,220]      39
## 2 <NA>          393

Se tienen 39 casas con un área construida entre 180 y 220m2 para los datos filtrados.

Agrupación por precio

resPrecio <- vivCasaZonaNorte %>%
  mutate(range_group = cut(preciom, breaks = rangoPrecio)) %>%
  group_by(range_group) %>%
  summarize(count = n())
print(resPrecio)
## # A tibble: 2 × 2
##   range_group count
##   <fct>       <int>
## 1 (300,400]     127
## 2 <NA>          305

Se tienen 127 casas con un precio entre $300 y $400 millones entre los datos filtrados.

Casas que cumplen las condiciones de venta

casasCondiciones <- subset(vivCasaZonaNorte, 
                           parqueaderos == 1 & 
                           banios == 2 & 
                           habitaciones == 4 & 
                           (estrato == 4 | estrato == 5))
print(casasCondiciones)
## # A tibble: 4 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1666 Zona N… 02          4     275       120            1      2            4
## 2  3889 Zona N… 01          4     520       350            1      2            4
## 3  2557 Zona N… <NA>        5     900       850            1      2            4
## 4  1943 Zona N… <NA>        5     350       346            1      2            4
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Las casas en zona norte que cumplen con las condiciones establecidas para estrato, parqueaderos, baños y habitaciones son las 4 que se pueden apreciar en la tabla anterior.

Se genera un mapa con la ubicación de todas las casas para apreciar si se encuentran ubicadas de manera coherente en términos geográficos. Posteriormente se realiza un gráfico smilar sólo para las casas filtradas.

viviendaCasas <- subset(vivienda, tipo=="Casa")
ggplot(data = viviendaCasas, aes(x = longitud, y = latitud, color = zona)) +
  geom_point() +
  scale_color_manual(values = rainbow(length(unique(viviendaCasas$zona)))) +
  labs(title = "Ubicación de casas según la zona",
       x = "Longitud",
       y = "Latitud",
       color = "Zona")

ggplot(data = vivCasaZonaNorte, aes(x = longitud, y = latitud, color = zona)) +
  geom_point() +
  scale_color_manual(values = rainbow(length(unique(vivCasaZonaNorte$zona)))) +
  labs(title = "Ubicación de casas filtradas",
       x = "Longitud",
       y = "Latitud",
       color = "Zona")

Discusión sobre la ubicación de las casas en zonas

Considerando los resultados de las gráficas anteriores, se puede deducir que no todas las casas se ubican en la zona correspondiente o, dicho de otra manera, algunas casas se ubican en otras zonas. Exite un número de razones por las que esto se puede presentar, entre ellas están: Errores en la entrada de los datos, errores de geocodificación, cambios en los límites, resolución espacial, características geográficas naturales o agregación de datos.

Análisis Exploratorio de Datos

Se realiza una visualización inicial de los atributos, tipo y número de registros

str(vivCasaZonaNorte)
## spc_tbl_ [432 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:432] 1209 1592 4460 6081 7824 ...
##  $ zona        : chr [1:432] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
##  $ piso        : chr [1:432] "02" "02" "02" "02" ...
##  $ estrato     : num [1:432] 5 5 4 5 4 5 5 5 4 4 ...
##  $ preciom     : num [1:432] 320 780 625 750 600 420 490 520 305 350 ...
##  $ areaconst   : num [1:432] 150 380 355 237 160 200 118 455 117 118 ...
##  $ parqueaderos: num [1:432] 2 2 3 2 1 4 2 NA NA NA ...
##  $ banios      : num [1:432] 4 3 5 6 4 4 4 5 3 3 ...
##  $ habitaciones: num [1:432] 6 3 5 6 5 5 4 4 4 3 ...
##  $ tipo        : chr [1:432] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:432] "acopi" "acopi" "acopi" "acopi" ...
##  $ longitud    : num [1:432] -76.5 -76.5 -76.5 -76.5 -76.6 ...
##  $ latitud     : num [1:432] 3.48 3.49 3.41 3.37 3.42 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

Se realiza una exploración de los datos con el fin de identificar tipo de variables, datos faltantes y datos atípicos

summary(vivCasaZonaNorte)
##        id           zona               piso              estrato     
##  Min.   :  58   Length:432         Length:432         Min.   :4.000  
##  1st Qu.:1500   Class :character   Class :character   1st Qu.:4.000  
##  Median :3006   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :3037                                         Mean   :4.627  
##  3rd Qu.:4330                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :5.000  
##                                                                      
##     preciom         areaconst       parqueaderos       banios    
##  Min.   : 125.0   Min.   :  45.0   Min.   :1.000   Min.   : 0.0  
##  1st Qu.: 350.0   1st Qu.: 200.0   1st Qu.:1.000   1st Qu.: 3.0  
##  Median : 450.0   Median : 280.0   Median :2.000   Median : 4.0  
##  Mean   : 508.2   Mean   : 301.7   Mean   :2.313   Mean   : 3.9  
##  3rd Qu.: 600.0   3rd Qu.: 360.0   3rd Qu.:3.000   3rd Qu.: 5.0  
##  Max.   :1940.0   Max.   :1188.0   Max.   :9.000   Max.   :10.0  
##                                    NA's   :106                   
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:432         Length:432         Min.   :-76.59  
##  1st Qu.: 4.000   Class :character   Class :character   1st Qu.:-76.53  
##  Median : 4.000   Mode  :character   Mode  :character   Median :-76.52  
##  Mean   : 4.593                                         Mean   :-76.52  
##  3rd Qu.: 5.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.47  
##                                                                         
##     latitud     
##  Min.   :3.364  
##  1st Qu.:3.458  
##  Median :3.474  
##  Mean   :3.462  
##  3rd Qu.:3.483  
##  Max.   :3.493  
## 

Revisión de Datos Faltantes (NA) para Casas Zona Norte

faltantesCasas <- colSums(is.na(vivCasaZonaNorte))
faltantesCasas
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          203            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          106            0            0            0            0            0 
##      latitud 
##            0

Rango de valores u opciones de parqueadero por casa en los datos filtrados.

parqueaderos = unique(vivCasaZonaNorte$parqueaderos)
parqueaderos
##  [1]  2  3  1  4 NA  6  5  8  7  9

Del resumen anterior es posible apreciar que los atributos de número de parqueaderos y piso presentan una cantidad considerable de datos NA. Debido a que el número de pisos no se encuentra entre las condiciones de compra, no es necesario realizar ningún tipo de procesamiento sobre este campo. Para el caso del número de parqueaderos se considera realizar una imputación de los registros faltantes basado en la moda. Esto teniendo en consideración que la condición indica que se tenga “1” parqueadero, lo cual garantiza que el procedimiento tendrá un impacto menor sobre los resultados dado que el rango de valores se encuentra entre 1 y 9 parqueaderos por casa.

Se crea un subconjunto de los datos consideranndo sólo los atributos dados en las condiciones de compra.

vivCasaZonaNortePro <- select(vivCasaZonaNorte, -zona, -tipo, -piso)

Se presenta a continuación, una visualización de los datos faltantes.

gg_miss_var(vivCasaZonaNortePro)

grafico <-md.pattern(vivCasaZonaNortePro, rotate.names = TRUE)

Se realiza imputación de datos reemplazando los valores nulos por la moda

moda_parqueaderos <- as.numeric(names(sort(table(vivCasaZonaNorte$parqueaderos), decreasing = TRUE)[1]))
moda_parqueaderos
## [1] 2
vivCasaZonaNortePro$parqueaderos[is.na(vivCasaZonaNortePro$parqueaderos)] <- moda_parqueaderos
# Se grafica de nuevo para verificar la no existencia de valores nulos
grafico <-md.pattern(vivCasaZonaNortePro, rotate.names = TRUE)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

Correlación entre precio de las casas y área construida

# Scatter plot de precio vs. area
plot_ly(data = vivCasaZonaNortePro, x = ~areaconst, y = ~preciom, mode = "markers", text = ~paste("Precio: $", preciom)) %>%
  layout(title = "Precio vs. Área",
         xaxis = list(title = "Área"),
         yaxis = list(title = "Precio"))
## No trace type specified:
##   Based on info supplied, a 'scatter' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#scatter

Existe una correlación positiva entre precio y área construida, ya que a medida que aumenta el área también lo hace la variable respuesta, sin embargo, esta relación es más notoría y clara para áreas y precios más bajos, ya que a medida que crecen ambas variables también aumenta su dispersión.

Influencia del número de habitaciones sobre el precio de las casas

# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~habitaciones, y = ~preciom, type = "box", color = ~factor(habitaciones)) %>%
  layout(title = "Precio según Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

De la gráfica se puede visualizar que existe una correlación positiva entre el número de habitaciones y el precio de las casas a partir de las casas con 3 hasta 8 habitaciones. En este rango se aprecia como la mediana del precio es de $350 millones para 3 habitaciones y aumenta hasta las 8 habitaciones ($600 millones). Se aprecia que no hay casas de 1 habitación y que las casas con 0 habitaciones tienen una mediana superior a las casas de 5 habitaciones. Se visualiza lo particular de las casas con 10 habitaciones, cuyo rango intercuartil es superior a los $1100 millones de pesos.

Influencia del número de baños sobre el precio de las casas

# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~banios, y = ~preciom, type = "box", color = ~factor(banios)) %>%
  layout(title = "Precio según Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

Se puede apreciar de la gráfica que la media del precio de las casas aumenta con el númerode baños partir de los 2 baños y hasta los 7 baños. No se visualizan casas con 1 sólo baño y las casas con 0 baños tienen una mediana superior a las casas con 4 baños, además de tener un rango intercuartil de $550 millones.

Influencia del número de parqueaderos sobre el precio de las casas

# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~parqueaderos, y = ~preciom, type = "box", color = ~factor(parqueaderos)) %>%
  layout(title = "Precio según Número de parqueaderos",
         xaxis = list(title = "Número de parqueaderos"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

De la gráfica se puede apreciar como el precio aumenta si se tienen más parqueaderos, situación que se mantiene para las casas con 6 habitaciones si se tiene en cuenta la mediana del precio, auqnue con el caso particular de las casas con 5 habitaciones, cuya mediana del precio es inferior a las casas con 4 habitaciones.

Influencia del estrato sobre el precio de las casas

# Box plot de precio por número de alcobas
plot_ly(data = vivCasaZonaNortePro, x = ~estrato, y = ~preciom, type = "box", color = ~factor(estrato)) %>%
  layout(title = "Precio según el estrato",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

De la gráfica se puede apreciar como la mediana del precio para las casas en estrato 5 supera en $100 millones la mediana de las casas en estrato 4.

Modelo de regresión lineal múltiple para estimar el precio de las casas en función de los atributos de las condiciones

Se utiliza el método de mínimos cuadrados ordinarios a través de la función lm

modRLM <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivCasaZonaNortePro)
summary(modRLM)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivCasaZonaNortePro)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -781.30  -86.39  -19.78   50.69  991.18 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -128.76609   80.21930  -1.605   0.1092    
## areaconst       0.93571    0.05675  16.488   <2e-16 ***
## estrato        42.60892   16.98026   2.509   0.0125 *  
## habitaciones   12.41024    5.82515   2.130   0.0337 *  
## parqueaderos   11.09931    7.09860   1.564   0.1187    
## banios         19.42247    7.39067   2.628   0.0089 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 167.2 on 426 degrees of freedom
## Multiple R-squared:  0.5286, Adjusted R-squared:  0.5231 
## F-statistic: 95.54 on 5 and 426 DF,  p-value: < 2.2e-16

Interpretación de los coeficientes obtenidos

En un Modelo de Regresión Lineal Múltiple (MRLM), los coeficientes representan el efecto estimado de cada variable independiente sobre la variable dependiente (en este caso el precio de las casas), manteniendo todas las demás variables constantes.

El Intercepto: El intercepto representa el valor estimado de la variable dependiente (precio casa) cuando todas las varaibles independientes son cero. En este caso el intercepto es de $-128.77, aunque este valor no es estadisticamente significativo debido a que p-value > 0.05. Esto sugiere que el intercepto no es significativamente diferente de cero y no es un dato que aporte en la explicación de los precios de las casas.

Coeficientes de las variables independientes:

Cada coeficiente representa el cambio estimado en la variable dependiente (precio casa) debido al cambio de una unidad de la variable independiente correspondiente, manteniendo las demás variables constantes.

El coeficiente de la variable independiente de área construida indica que el incremento de una unidad, es decir, de 1 metro cuadrado aumenta el precio de la casa en $0.94 millones.

Para el caso del estrato, el cambio de éste de 4 a 5 significa un aumento en el precio de la casa de $42.6 millones.

El coeficiente obtenido para el número de habitaciones estima que el tener una habitación más incrementa el valor de la casa en $12.4 millones.

El incremento de 1 espacio de parqueadero significa el aumento del precio de la casa en $11.1 millones.

El contar con un baño más en este tipo de vivienda indica que el valor de la casa se incrementaría en $19.42 millones.

Valores-t:

Los valores-t miden el grado de significancia de cada coeficiente o variable independiente. En este caso, se tiene el área construida tiene una significancia fuerte, el número de baños y el estrato tienen una siignificancia moderada, las habitaciones una significancia marginal y los parqueaderos no tendrían significancia estadística.

El valor-p

Según los resultados obtenidos de p-value, el área construida tiene una significancia estadística alta, el número de baños una significancia moderada, el estrato y las habitaciones una significancia leve y los parqueaderos no tienen significancia estadística.

El error estándar residual

El error estándar residual (RSE, por sus siglas en inglés) es una medida de la variabilidad de los valores observados sobre la regresión lineal. En este caso se estimó con 426 obseraciones o grados de libertad. El valor RSE de $167.2 millones indica la diferencia promedio entre los precios observados y los valores predichos. Aunque parece un valor de error bastante alto, se debe considerar que el rango de precios es de más de $1800 millones, con un valor mínimo de $125 millones y un valor máximo de $1940.

El coeficiente R2 y R2 ajustado

El R2 mide la proporción del total de la variación de la variable dependiente que es representada por la variación de las variables independientes. En otras palabraas, el R2 es una medida de qué tan bien las variables independientes explican la variabilidad de la variable dependiente en un modelo de regresión. El valor obtenido de 0.52 sugiere que las variables independientes explican una proporción moderada de la variabilidad de la variable dependiente de precio de la casa.

Ajuste del modelo e implicaciones

La primera medida remedial para mejorar el ajuste del modelo sería quitar la variable independiente de parqueaderos, esto debido a que los resultados obtenidos de t-value y p-value indican que no es una variable que estadisticamente proporcione información para explicar la variabilidad de los precios de las casas.

Otra alternativa sería aplicar transformaciones sobre la variable respuesta y/o sobre las variables predictoras.

Una última alternativa serían los llamados modelos lineales generalizados, los cuales permiten modelar respuestas que no se distribuyen de manera normal.

Validación de supuestos del modelo

Se seleccionan los índices aleatorios que formarán el conjunto de entrenamiento.

set.seed(1)
train <- sample(x = 1:426, 426*0.7)

Se crea el modelo de entrenamiento

modRLM_train <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivCasaZonaNortePro, subset = train)
summary(modRLM_train) 
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivCasaZonaNortePro, subset = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -769.48  -82.42  -23.43   55.51  949.09 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -71.09323  100.12633  -0.710  0.47825    
## areaconst      0.95051    0.06715  14.154  < 2e-16 ***
## estrato       35.11993   20.93035   1.678  0.09443 .  
## habitaciones  21.23055    7.13524   2.975  0.00317 ** 
## parqueaderos   6.30737    8.67489   0.727  0.46776    
## banios         5.52591    9.49877   0.582  0.56118    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 168.6 on 292 degrees of freedom
## Multiple R-squared:  0.5398, Adjusted R-squared:  0.5319 
## F-statistic:  68.5 on 5 and 292 DF,  p-value: < 2.2e-16

Se estima para el conjunto de datos de casas en zona norte y para el subconjunto de entrenamiento de éste la raíz del error cuadrático medio comparando el precio actual con el precio que se obtiene del modelo.

pred_train <- predict(object = modRLM_train, newdata = vivCasaZonaNortePro[-train, ])
rmse_train <- sqrt(mean((vivCasaZonaNortePro$preciom[-train] - pred_train)^2))
pred <- predict(object = modRLM, newdata = vivCasaZonaNortePro)
rmse <- sqrt(mean((vivCasaZonaNortePro$preciom - pred)^2))
rmse
## [1] 166.0318
rmse_train
## [1] 167.1801

Interpretación de resultados y validación de supuestos

Al comparar los resultados obtenidos del subconjunto de entrenamiento respecto al conjunto de datos de casas en zona norte, se puede apreciar que los residuales en el conjunto de entrenamiento tienen un rango intercuartil inferior al conjunto original. Respecto a las variables independientes, en el subconjunto de entrenamiento dejó de tener significancia estadística el número de baños. Respecto al error estándar residual y el rmse se observa que el conjunto de entrenamiento presenta valores más altos de error. Entre las alternativas que se tienen para reducir la multicolinealidad están: la eliminación de variables, la transformación de variables, el análisis de componentes principales (PCA) o la regularización, ya sea a través de la regresión Ridge o la técnica Lasso.

Predicción del precio de la casa

El primer paso consiste en crear un Dataframe con los valores mencionados en las condiciones de venta.

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

Se realiza la predicción

predCasaCondVenta <- predict(object = modRLM, newdata = new_data)
predCasaCondVenta
##        1        2 
## 328.3975 371.0064

Los resultados predichos indican que el precio de una casa que cumpla con las condiciones de venta en estrato 4 tendría un valor aproximado de $328.4 millones y en estrato 5 de $371 millones.

Potenciales ofertas de casas

Si se tiene presente que el preaprobado es de $350 millones, esto estaría indicando que el precio que se debe considerar para la búsqueda de las potenciales ofertas es el predicho para estrato 4, lo cual no significa que puedan aparecer opciones que cumplan las condiciones y que sean estrato 5. Se toman las condciones de venta como filtros de búsqueda para buscar las casas cuyo valor se acerque al predicho por el MLRM.

# Se establecen las condiciones de venta como condicionantes de búsqueda
max_price <- 350
min_area <- 200
min_rooms <- 4
min_baths <- 2
min_parking <- 1
# Se calcula las diferencias absolutas entre el precio predicho para estrato 4 y los precios actuales de las casas
absolute_differences <- abs(vivCasaZonaNortePro$preciom - predCasaCondVenta[1])
# Se buscan los índices que se acerquen al precio predicho y cumpla las condiciones venta
closest_indices <- which(vivCasaZonaNortePro$preciom <= max_price & 
                         vivCasaZonaNortePro$areaconst >= min_area & 
                         vivCasaZonaNortePro$habitaciones >= min_rooms & 
                         vivCasaZonaNortePro$banios >= min_baths & 
                         vivCasaZonaNortePro$parqueaderos >= min_parking & 
                         absolute_differences <= min(absolute_differences))[1:5]
# Se extraen los registros basado en los índices
ofertas_casas <- vivCasaZonaNortePro[closest_indices, ]
print(ofertas_casas)
## # A tibble: 5 × 10
##      id estrato preciom areaconst parqueaderos banios habitaciones barrio       
##   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>        
## 1  7471       4     330       240            2      4            4 acopi        
## 2   952       4     330       275            2      3            5 la merced    
## 3  1108       4     330       260            1      3            4 la merced    
## 4  3043       5     330       275            2      3            5 la merced    
## 5  1849       5     330       246            2      4            4 prados del n…
## # ℹ 2 more variables: longitud <dbl>, latitud <dbl>

Se presenta en la tabla 5 potenciales ofertas que cumplen las condiciones de venta y cuyo precio es similar en todas $330 millones. De las 5 casas sugeridas, 3 de ellas se encuentran en estrato 4 y dos en estrato 5. Si se tuvieran el área construida y el número de habitaciones como criterios de elección, las casas con id 952 y id 3043 son las más grandes con 275m2 y 5 habitaciones. Estas dos opciones tienen 3 baños, uno más de lo establecido en las condiciones. Algo similar sucede con los parqueaderos ya que se registran 2 parqueaderos y en las condiciones de venta se establece 1. Las otras 3 opciones cuentan con 4 habitaciones y dos de ellas tienen 4 baños (ids 7471 y 1849). A continuación, se presentan las ofertas potenciales en un mapa de la ciudad para conocer su ubicación.

oferta <- data.frame(
  lat = ofertas_casas$latitud,  
  long = ofertas_casas$longitud
)

# Crea un mapa
map <- leaflet(oferta) %>%
  addTiles() %>%
  addMarkers(
    lng = ~long,
    lat = ~lat,
    popup = ~as.character(lat) 
  )

map # Muestra el mapa

En el mapa es posible apreciar que la casa ubicada con latitud 3.39 se encuentra ubicada en una zona alejada a las otras 4 ofertas, esta casa corresponde al id 7471.

Análisis de compra de vivienda 2

Filtro a base de datos para ofertas de apartamentos

Zona sur de la ciudad. Se Presentan los primeros 3 registros de los datos filtrados

vivAptoZonaSur <- subset(vivienda,
                             tipo=="Apartamento" &
                             zona=="Zona Sur" &
                             (estrato == 5 | estrato == 6))
head(vivAptoZonaSur, 3)
## # A tibble: 3 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  8199 Zona S… <NA>        6     875       194            2      5            3
## 2  8102 Zona S… 05          5     344       107            2      2            3
## 3  7073 Zona S… 02          6     910       182            2      4            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Visualización de tablas que comprueban el filtrado de los apartamentos

rangoArea <- c(280, 320)
rangoPrecio <- c(800, 900)

Agrupación por área

resArea <- vivAptoZonaSur %>%
  mutate(range_group = cut(areaconst, breaks = rangoArea)) %>%
  group_by(range_group) %>%
  summarize(count = n())
print(resArea)
## # A tibble: 2 × 2
##   range_group count
##   <fct>       <int>
## 1 (280,320]      13
## 2 <NA>         1482

Se tienen 13 apartamentos con un área construida entre 280 y 320m2 para los datos filtrados.

Agrupación por precio

resPrecio <- vivAptoZonaSur %>%
  mutate(range_group = cut(preciom, breaks = rangoPrecio)) %>%
  group_by(range_group) %>%
  summarize(count = n())
print(resPrecio)
## # A tibble: 2 × 2
##   range_group count
##   <fct>       <int>
## 1 (800,900]      36
## 2 <NA>         1459

Se tienen 36 apartamentos con un precio entre $800 y $900 millones entre los datos filtrados.

Apartamentos que cumplen las condiciones de venta

aptosCondiciones <- subset(vivAptoZonaSur, 
                           parqueaderos >= 3 & 
                           banios >= 3 & 
                           habitaciones >= 5 & 
                           (estrato == 5 | estrato == 6))
print(aptosCondiciones)
## # A tibble: 7 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5248 Zona S… 08          6    1150       344            4      5            5
## 2  6023 Zona S… 08          6    1150       464            4      6            5
## 3  6086 Zona S… 10          6    1500       240            3      5            6
## 4     4 Zona S… <NA>        6    1280       346            4      6            5
## 5  7182 Zona S… <NA>        5     730       573            3      8            5
## 6  7512 Zona S… <NA>        5     670       300            3      5            6
## 7  8036 Zona S… <NA>        5     530       256            3      5            5
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Como se puede apreciar existen 7 apartamentos en zona sur que cumplan con las condiciones de venta establecidas para estrato, parqueaderos, baños y habitaciones.

Se genera un mapa con la ubicación de todas los apartamentos para apreciar si se encuentran ubicados de manera coherente en términos geográficos. Posteriormente se realiza un gráfico smilar sólo para los apartamentos filtrados.

viviendaApto <- subset(vivienda, tipo=="Apartamento")
ggplot(data = viviendaApto, aes(x = longitud, y = latitud, color = zona)) +
  geom_point() +
  scale_color_manual(values = rainbow(length(unique(viviendaApto$zona)))) +
  labs(title = "Ubicación de apartamentos según la zona",
       x = "Longitud",
       y = "Latitud",
       color = "Zona")

ggplot(data = vivAptoZonaSur, aes(x = longitud, y = latitud, color = zona)) +
  geom_point() +
  scale_color_manual(values = rainbow(length(unique(vivAptoZonaSur$zona)))) +
  labs(title = "Ubicación de apartamentos filtrados",
       x = "Longitud",
       y = "Latitud",
       color = "Zona")

Discusión sobre la ubicación de los apartamentos en zonas

Considerando los resultados de las gráficas anteriores, se puede deducir que no todas los apartamentos se ubican en la zona correspondiente o, dicho de otra manera, algunos apartamentos se ubican en otras zonas. Exite un número de razones por las que esto se puede presentar, entre ellas están: Errores en la entrada de los datos, errores de geocodificación, cambios en los límites, resolución espacial, características geográficas naturales o agregación de datos.

Análisis Exploratorio de Datos

Se realiza una visualización inicial de los atributos, tipo y número de registros

str(vivAptoZonaSur)
## spc_tbl_ [1,495 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:1495] 8199 8102 7073 8288 8294 ...
##  $ zona        : chr [1:1495] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
##  $ piso        : chr [1:1495] NA "05" "02" "01" ...
##  $ estrato     : num [1:1495] 6 5 6 5 5 5 6 5 5 5 ...
##  $ preciom     : num [1:1495] 875 344 910 310 175 285 305 185 280 164 ...
##  $ areaconst   : num [1:1495] 194 107 182 166 64 120 125 78 94 54 ...
##  $ parqueaderos: num [1:1495] 2 2 2 2 1 2 2 1 1 1 ...
##  $ banios      : num [1:1495] 5 2 4 4 2 4 3 2 2 2 ...
##  $ habitaciones: num [1:1495] 3 3 3 3 2 3 3 3 3 3 ...
##  $ tipo        : chr [1:1495] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
##  $ barrio      : chr [1:1495] "aguacatal" "altos de guadalupe" "arboleda" "bella suiza" ...
##  $ longitud    : num [1:1495] -76.6 -76.6 -76.5 -76.6 -76.6 ...
##  $ latitud     : num [1:1495] 3.46 3.41 3.45 3.41 3.41 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

Se realiza una exploración de los datos con el fin de identificar tipo de variables, datos faltantes y datos atípicos

summary(vivAptoZonaSur)
##        id           zona               piso              estrato     
##  Min.   :   3   Length:1495        Length:1495        Min.   :5.000  
##  1st Qu.:3149   Class :character   Class :character   1st Qu.:5.000  
##  Median :4424   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4489                                         Mean   :5.309  
##  3rd Qu.:5974                                         3rd Qu.:6.000  
##  Max.   :8299                                         Max.   :6.000  
##                                                                      
##     preciom         areaconst    parqueaderos        banios     
##  Min.   :  93.0   Min.   : 43   Min.   : 1.000   Min.   :0.000  
##  1st Qu.: 250.0   1st Qu.: 84   1st Qu.: 1.000   1st Qu.:2.000  
##  Median : 310.0   Median :102   Median : 2.000   Median :3.000  
##  Mean   : 386.7   Mean   :117   Mean   : 1.605   Mean   :2.868  
##  3rd Qu.: 450.0   3rd Qu.:133   3rd Qu.: 2.000   3rd Qu.:3.000  
##  Max.   :1750.0   Max.   :932   Max.   :10.000   Max.   :8.000  
##                                 NA's   :56                      
##   habitaciones       tipo              barrio             longitud     
##  Min.   :0.000   Length:1495        Length:1495        Min.   :-76.56  
##  1st Qu.:3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median :3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   :3.091                                         Mean   :-76.53  
##  3rd Qu.:3.000                                         3rd Qu.:-76.52  
##  Max.   :6.000                                         Max.   :-76.46  
##                                                                        
##     latitud     
##  Min.   :3.334  
##  1st Qu.:3.365  
##  Median :3.380  
##  Mean   :3.384  
##  3rd Qu.:3.398  
##  Max.   :3.497  
## 

Revisión de Datos Faltantes (NA) para Apartamentos Zona Sur

faltantesAptos <- colSums(is.na(vivAptoZonaSur))
faltantesAptos
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          338            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##           56            0            0            0            0            0 
##      latitud 
##            0

Rango de valores u opciones de parqueadero por casa en los datos filtrados.

parqueaderos = unique(vivAptoZonaSur$parqueaderos)
parqueaderos
## [1]  2  1 NA  3  4 10

Del resumen anterior es posible apreciar que los atributos de número de parqueaderos y piso presentan una cantidad considerable de datos NA. Debido a que el número de pisos no se encuentra entre las condiciones de compra, no es necesario realizar ningún tipo de procesamiento sobre este campo. Para el caso del número de parqueaderos se considera realizar una imputación de los registros faltantes basado en la moda.

Se crea un subconjunto de los datos consideranndo sólo los atributos dados en las condiciones de compra.

vivAptoZonaSurPro <- select(vivAptoZonaSur, -zona, -tipo, -piso)

Se presenta a continuación, una visualización de los datos faltantes.

gg_miss_var(vivAptoZonaSurPro)

grafico <-md.pattern(vivAptoZonaSurPro, rotate.names = TRUE)

Se realiza imputación de datos reemplazando los valores nulos por la moda

moda_parq_apto <- as.numeric(names(sort(table(vivAptoZonaSur$parqueaderos), decreasing = TRUE)[1]))
moda_parq_apto
## [1] 1
vivAptoZonaSurPro$parqueaderos[is.na(vivAptoZonaSurPro$parqueaderos)] <- moda_parq_apto
# Se grafica de nuevo para verificar la no existencia de valores nulos
grafico <-md.pattern(vivAptoZonaSurPro, rotate.names = TRUE)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

Correlación entre precio de los apartamentos y área construida

# Scatter plot de precio vs. area
plot_ly(data = vivAptoZonaSurPro, x = ~areaconst, y = ~preciom, mode = "markers", text = ~paste("Precio: $", preciom)) %>%
  layout(title = "Precio vs. Área",
         xaxis = list(title = "Área"),
         yaxis = list(title = "Precio"))
## No trace type specified:
##   Based on info supplied, a 'scatter' trace seems appropriate.
##   Read more about this trace type -> https://plotly.com/r/reference/#scatter

Existe una correlación positiva entre precio y área construida, ya que a medida que aumenta el área también lo hace la variable respuesta, sin embargo, esta relación es más notoría y clara para áreas y precios más bajos, ya que a medida que crecen ambas variables también aumenta su dispersión.

Influencia del número de habitaciones sobre el precio de los apartamentos

# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~habitaciones, y = ~preciom, type = "box", color = ~factor(habitaciones)) %>%
  layout(title = "Precio según Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio"))

De la gráfica se puede visualizar que existe una correlación positiva entre el número de habitaciones y el precio de los apartamentos a partir de los que tienen 2 hasta 6 habitaciones. En este rango se aprecia como la media del precio es de $247.5 millones para 1 habitación y aumenta hasta las 6 habitaciones ($1085 millones). Se aprecia que los apartamentos con 0 habitaciones tienen una media superior a los apartamentos con 5 habitaciones. Se visualiza lo particular de los apartamentos con 5 habitaciones, cuyo rango intercuartil es superior a los $600 millones de pesos.

Influencia del número de baños sobre el precio de los apartamentos

# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~banios, y = ~preciom, type = "box", color = ~factor(banios)) %>%
  layout(title = "Precio según Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

## Warning in RColorBrewer::brewer.pal(N, "Set2"): n too large, allowed maximum for palette Set2 is 8
## Returning the palette you asked for with that many colors

Se puede apreciar de la gráfica que la media del precio de los apartamentos aumenta con el númerode baños partir de 1 baño y hasta los 6 baños. Los apartamentos con 0 baños tienen una mediana superior a los apartamentos con 4 baños, además de tener un rango intercuartil de $550 millones.

Influencia del número de parqueaderos sobre el precio de los apartamentos

# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~parqueaderos, y = ~preciom, type = "box", color = ~factor(parqueaderos)) %>%
  layout(title = "Precio según Número de parqueaderos",
         xaxis = list(title = "Número de parqueaderos"),
         yaxis = list(title = "Precio"))

De la gráfica se puede apreciar como el precio aumenta si se tienen más parqueaderos, situación que se mantiene para los apartamentos que tienen entre 1 a 4 parqueaderos.

Influencia del estrato sobre el precio de los apartamentos

# Box plot de precio por número de alcobas
plot_ly(data = vivAptoZonaSurPro, x = ~estrato, y = ~preciom, type = "box", color = ~factor(estrato)) %>%
  layout(title = "Precio según el estrato",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio"))
## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

## Warning in RColorBrewer::brewer.pal(N, "Set2"): minimal value for n is 3, returning requested palette with 3 different levels

De la gráfica se puede apreciar como la mediana del precio para los apartamentos en estrato 6 supera en $300 millones la mediana de los apartamentos en estrato 5.

Modelo de regresión lineal múltiple para estimar el precio de los apartamentos en función de los atributos de las condiciones

Se utiliza el método de mínimos cuadrados ordinarios a través de la función lm

modRLM_aptos <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivAptoZonaSurPro)
summary(modRLM_aptos)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivAptoZonaSurPro)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1022.84   -52.87     1.80    43.33   889.80 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -711.57312   39.27457 -18.118  < 2e-16 ***
## areaconst       1.24594    0.06299  19.779  < 2e-16 ***
## estrato       141.50801    7.46654  18.952  < 2e-16 ***
## habitaciones  -16.07190    5.47219  -2.937  0.00336 ** 
## parqueaderos   75.47675    5.25944  14.351  < 2e-16 ***
## banios         45.79788    4.32974  10.578  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 110.7 on 1489 degrees of freedom
## Multiple R-squared:  0.7396, Adjusted R-squared:  0.7387 
## F-statistic: 845.8 on 5 and 1489 DF,  p-value: < 2.2e-16

Interpretación de los coeficientes obtenidos

En un Modelo de Regresión Lineal Múltiple (MRLM), los coeficientes representan el efecto estimado de cada variable independiente sobre la variable dependiente (en este caso el precio de los apartamentos), manteniendo todas las demás variables constantes.

El Intercepto: El intercepto representa el valor estimado de la variable dependiente (precio apartamento) cuando todas las varaibles independientes son cero. En este caso el intercepto es de $-711.57, valor que es estadisticamente significativo debido a que p-value < 0.05. Esto sugiere que el intercepto es un dato que aporte en la explicación de los precios de los apartamentos.

Coeficientes de las variables independientes:

Cada coeficiente representa el cambio estimado en la variable dependiente (precio apartamento) debido al cambio de una unidad de la variable independiente correspondiente, manteniendo las demás variables constantes.

El coeficiente de la variable independiente de área construida indica que el incremento en una unidad, es decir, de 1 metro cuadrado aumenta el precio del apartamento en $1.24 millones.

Para el caso del estrato, el cambio de éste de 5 a 6 significa un aumento en el precio del apartamento de $141.5 millones.

El coeficiente obtenido para el número de habitaciones estima que el tener una habitación más disminuye el valor del apartamento en $16 millones. En este caso, se presenta una situación particular que no coincide con lo visto graficamente en la sección anterior, en dónde se apreciaba que el precio de los apartamentos aumentaba al tener más habitaciones. Este resultado en el coeficiente puede deberse a la cantidad de apartamentos registrados con 0 habitaciones, cuyo valor de la mediana del precio es superior a la mediana de precio de los apartamentos que tienen hasta 5 habitaciones.

El incremento de 1 espacio de parqueadero significa el aumento del precio del apartamento en $75.47 millones.

El contar con un baño más en este tipo de vivienda indica que el valor del apartamento se incrementaría en $45.8 millones.

Valores-t:

Los valores-t miden el grado de significancia de cada coeficiente o variable independiente. En este caso, se tiene el número de habitaciones como la única variable con significancia baja.

El valor-p

Según los resultados obtenidos de p-value, todas las variables tienen una significancia estadística alta a excepción del número de habitaciones.

El error estándar residual

El error estándar residual (RSE) es una medida de la variabilidad de los valores observados sobre la regresión lineal. En este caso se estimó con 1489 obseraciones o grados de libertad. El valor RSE de $110.7 millones indica la diferencia promedio entre los precios observados y los valores predichos. Aunque parece un valor de error alto, se debe considerar que el rango de precios es de más de $1650 millones, con un valor mínimo de $93 millones y un valor máximo de $1750.

El coeficiente R2 y R2 ajustado

El R2 mide la proporción del total de la variación de la variable dependiente que es representada por la variación de las variables independientes. En otras palabras, el R2 es una medida de qué tan bien las variables independientes explican la variabilidad de la varaible dependiente en un modelo de regresión. El valor obtenido de 0.73 sugiere que las variables independientes explican en buena proporción la variabilidad de la variable dependiente de precio del apartamento.

Ajuste del modelo e implicaciones

La primera medida remedial para mejorar el ajuste del modelo sería quitar la variable independiente de número de habitaciones, esto debido a que los resultados obtenidos de t-value y p-value indican que, a diferencia de las otras variables, no es una variable que estadisticamente proporcione información para explicar la variabilidad de los precios de los apartamentos

Otra alternativa sería aplicar transformaciones sobre la variable respuesta y/o sobre las variables predictoras.

Una última alternativa serían los llamados modelos lineales generalizados, los cuales permiten modelar respuestas que no se distribuyen de manera normal.

Validación de supuestos del modelo

Se seleccionan los índices aleatorios que formarán el conjunto de entrenamiento.

set.seed(1)
train_apto <- sample(x = 1:1489, 1489*0.7)

Se crea el modelo de entrenamiento

modRLM_train_apto <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivAptoZonaSurPro, subset = train_apto)
summary(modRLM_train_apto) 
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivAptoZonaSurPro, subset = train_apto)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -841.41  -53.92   -0.32   44.90  922.19 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -722.90926   46.47178 -15.556   <2e-16 ***
## areaconst       1.02992    0.06773  15.206   <2e-16 ***
## estrato       142.69409    8.77761  16.257   <2e-16 ***
## habitaciones  -10.51341    6.52415  -1.611    0.107    
## parqueaderos   79.24519    5.97209  13.269   <2e-16 ***
## banios         47.42097    4.98102   9.520   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 109.6 on 1036 degrees of freedom
## Multiple R-squared:  0.7378, Adjusted R-squared:  0.7366 
## F-statistic: 583.1 on 5 and 1036 DF,  p-value: < 2.2e-16

Se estima para el conjunto de datos de apartamentos en zona sur y para el subconjunto de entrenamiento de éste, la raíz del error cuadrático medio comparando el precio actual con el precio que se obtiene del modelo.

pred_train_apto <- predict(object = modRLM_train_apto, newdata = vivAptoZonaSurPro[-train_apto, ])
rmse_train_apto <- sqrt(mean((vivAptoZonaSurPro$preciom[-train_apto] - pred_train_apto)^2))
pred_apto <- predict(object = modRLM_aptos, newdata = vivAptoZonaSurPro)
rmse_apto <- sqrt(mean((vivAptoZonaSurPro$preciom - pred_apto)^2))
rmse_apto
## [1] 110.4636
rmse_train_apto
## [1] 114.6813

Interpretación de resultados y validación de supuestos

Al comparar los resultados obtenidos del subconjunto de entrenamiento respecto al conjunto de datos de apartamentos en zona sur, se puede apreciar que los residuales en el conjunto de entrenamiento tienen un rango intercuartil superior al conjunto original. Respecto a las variables independientes, en el subconjunto de entrenamiento pasó a tener aún menos significancia el número de habitaciones. Respecto al error estándar residual se puede apreciar uuna disminución en el conjunto de entrenamiento, mientras que el rmse se observa para el conjunto de entrenamiento un valor más alto de error. Entre las alternativas que se tienen para reducir la multicolinealidad están: la eliminación de variables, la transformación de variables, el análisis de componentes principales (PCA) o la regularización, ya sea a través de la regresión Ridge o la técnica Lasso.

Predicción del precio del apartamento

El primer paso consiste en crear un Dataframe con los valores mencionados en las condiciones de venta.

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

Se realiza la predicción

predAptoCondVenta <- predict(object = modRLM_aptos, newdata = new_data_apto)
predAptoCondVenta
##        1        2 
## 653.2136 794.7216

Los resultados predichos indican que el precio de un apartamento que cumpla con las condiciones de venta en estrato 5 tendría un valor aproximado de $653.2 millones y en estrato 6 de $794.7 millones.

Potenciales ofertas de apartamentos

Si se tiene presente que el preaprobado es de $850 millones, esto estaría indicando que es posible considerar los precios de ambos estratos en la búsqueda de las ofertas potenciales. Se toman las condciones de venta como filtros de búsqueda para buscar los apartamentos cuyo valor se acerque a los predichos por el MLRM.

# Se establecen las condiciones de venta como condicionantes de búsqueda
max_price <- 850
min_area <- 300
min_rooms <- 5
min_baths <- 3
min_parking <- 3
# Se calcula las diferencias absolutas entre el precio predicho para estrato 5 y los precios actuales de los apartamentos
absolute_diff_aptos <- abs(vivAptoZonaSurPro$preciom - predAptoCondVenta[2])
# Se buscan los índices que se acerquen al precio predicho y cumpla las condiciones venta
closest_ind_apto <- which(vivAptoZonaSurPro$preciom <= max_price & 
                         vivAptoZonaSurPro$areaconst >= min_area & 
                         vivAptoZonaSurPro$habitaciones >= min_rooms & 
                         vivAptoZonaSurPro$banios >= min_baths & 
                         vivAptoZonaSurPro$parqueaderos >= min_parking )
closest_ind_apto <- closest_ind_apto[order(absolute_diff_aptos[closest_ind_apto])][1:5]
# Se extraen los registros basado en los índices
ofertas_aptos <- vivAptoZonaSurPro[closest_ind_apto, ]
print(ofertas_aptos)
## # A tibble: 5 × 10
##      id estrato preciom areaconst parqueaderos banios habitaciones barrio   
##   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl> <chr>    
## 1  7182       5     730       573            3      8            5 guadalupe
## 2  7512       5     670       300            3      5            6 seminario
## 3    NA      NA      NA        NA           NA     NA           NA <NA>     
## 4    NA      NA      NA        NA           NA     NA           NA <NA>     
## 5    NA      NA      NA        NA           NA     NA           NA <NA>     
## # ℹ 2 more variables: longitud <dbl>, latitud <dbl>

Se presenta en la tabla as únicas 2 potenciales ofertas que cumplen las condiciones de venta y cuyo precio es inferior a $850 millones. De los 2 apartamentos sugeridos, el de mayor valor tiene un área de 573m2, 3 parqueaderos, 8 baños y 5 habitaciones, mientras que la otra opción tiene un área de 300m2, 3 parqueaderos, 5 baños y 6 habitaciones. A continuación, se presentan las ofertas potenciales en un mapa de la ciudad para conocer su ubicación.

oferta <- data.frame(
  lat = ofertas_aptos$latitud,  
  long = ofertas_aptos$longitud
)

# Crea un mapa
map <- leaflet(oferta) %>%
  addTiles() %>%
  addMarkers(
    lng = ~long,
    lat = ~lat,
    popup = ~as.character(lat) 
  )
## Warning in validateCoords(lng, lat, funcName): Data contains 3 rows with either
## missing or invalid lat/lon values and will be ignored
map # Muestra el mapa