title: “Asesoría para la compra de dos viviendas en la ciudad de Cali” author: “Gabriel Orozco Castaño” date: “2024-09-02” output: html_document —

Enunciado

Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.

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:

Características Vivienda 1 Vivienda 2 Tipo Casa Apartamento área construida 200 300 parqueaderos 1 3 baños 2 3 habitaciones 4 5 estrato 4 o 5 5 o 6 zona Norte Sur crédito preaprobado 350 millones 850 millones

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

Datos Los datos de los tres últimos meses se adjuntan en la base que puede obtener con el siguiente código en R

variable descripción zona ubicación de la vivienda : Zona Centro, Zona Norte,… piso piso que ocupa la vivienda : primer piso, segundo piso… estrato estrato socio-económico : 3,4,5,6 preciom precio de la vivienda en millones de pesos areaconst área construida parqueaderos número de parqueaderos banios número de baños habitaciones número de habitaciones tipo tipo de vivienda : Casa, Apartamento barrio barrio de ubicación de la vivienda : 20 de Julio, alamos,.. longitud coordenada geográfica latitud coordenada geográfica

#install.packages("devtools") # solo la primera vez
if (!requireNamespace("devtools", quietly = TRUE)) install.packages("devtools")
devtools::install_github("centromagis/paqueteMODELOS", force =TRUE)
## Downloading GitHub repo centromagis/paqueteMODELOS@HEAD
## digest (0.6.36 -> 0.6.37) [CRAN]
## curl   (5.2.1  -> 5.2.2 ) [CRAN]
## xfun   (0.46   -> 0.47  ) [CRAN]
## Installing 3 packages: digest, curl, xfun
## Installing packages into 'C:/Users/Gabriel_Orozco_C/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
## package 'digest' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'digest'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problema al copiar
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\00LOCK\digest\libs\x64\digest.dll
## a
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\digest\libs\x64\digest.dll:
## Permission denied
## Warning: restored 'digest'
## package 'curl' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'curl'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problema al copiar
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\00LOCK\curl\libs\x64\curl.dll
## a
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\curl\libs\x64\curl.dll:
## Permission denied
## Warning: restored 'curl'
## package 'xfun' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'xfun'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problema al copiar
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\00LOCK\xfun\libs\x64\xfun.dll
## a
## C:\Users\Gabriel_Orozco_C\AppData\Local\R\win-library\4.4\xfun\libs\x64\xfun.dll:
## Permission denied
## Warning: restored 'xfun'
## 
## The downloaded binary packages are in
##  C:\Users\Gabriel_Orozco_C\AppData\Local\Temp\RtmpADQoxM\downloaded_packages
## ── R CMD build ─────────────────────────────────────────────────────────────────
##          checking for file 'C:\Users\Gabriel_Orozco_C\AppData\Local\Temp\RtmpADQoxM\remotes30c87617530f\Centromagis-paqueteMODELOS-78ce06f/DESCRIPTION' ...  ✔  checking for file 'C:\Users\Gabriel_Orozco_C\AppData\Local\Temp\RtmpADQoxM\remotes30c87617530f\Centromagis-paqueteMODELOS-78ce06f/DESCRIPTION' (5.8s)
##       ─  preparing 'paqueteMODELOS': (10.9s)
##    checking DESCRIPTION meta-information ...  ✔  checking DESCRIPTION meta-information
##       ─  checking for LF line-endings in source and make files and shell scripts (565ms)
##       ─  checking for empty or unneeded directories
##       ─  building 'paqueteMODELOS_0.1.0.tar.gz'
##      
## 
## Installing package into 'C:/Users/Gabriel_Orozco_C/AppData/Local/R/win-library/4.4'
## (as 'lib' is unspecified)
library(paqueteMODELOS)
## Cargando paquete requerido: boot
## Cargando paquete requerido: broom
## Cargando paquete requerido: GGally
## Cargando paquete requerido: ggplot2
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Cargando paquete requerido: gridExtra
## Cargando paquete requerido: knitr
## Cargando paquete requerido: summarytools
data("vivienda")

Introducción

El presente informe ejecutivo proporcionará un análisis comparativo entre las vivienda tipo casa y apartamento de la ciduad de Cali, considerando las condiciones del mercado actual y las características específicas de las propiedades. Además se incluirán anexos con estimaciones, validaciones y comparaciones de modelos para apoyar las recomendaciones ofrecidas.

Hallazgos principales

El presente análisis consiste en identificar la mejor opción de la oferta de vivienda en cali, de acuerdo a las necesidades del cliente, las cual consiste en la compra de una vivienda tipo apartamento y otra tipo casa, de acuerdo con características propias.

Una vez realizado el análisis, se pudo encontrar una gran oferta de viviendas tipo casa que cumplen con las características solicitadas por el cliente; para el caso de las viviendas tipo apartamento, solo dos cumplen con las especificaciones dadas.

Análisis exploratorio y limpieza

A continuación se realiza un análisis exploratorio de los datos, permitiendo conocer las diferentes variables y tipos de datos que se encuentra en el dataset.

summary(vivienda) 
##        id           zona               piso              estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
##  Median :4160   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4160                                         Mean   :4.634  
##  3rd Qu.:6240                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :6.000  
##  NA's   :3                                            NA's   :3      
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
##  NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.605                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##  NA's   :3                                              NA's   :3       
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498  
##  NA's   :3
head(vivienda)
## # A tibble: 6 × 13
##      id zona    piso  estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1147 Zona O… <NA>        3     250        70            1      3            6
## 2  1169 Zona O… <NA>        3     320       120            1      2            3
## 3  1350 Zona O… <NA>        3     350       220            2      2            4
## 4  5992 Zona S… 02          4     400       280            3      5            3
## 5  1212 Zona N… 01          5     260        90            1      2            3
## 6  1724 Zona N… 01          5     240        87            1      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - 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>

A continuacion se muestra que hay datos faltantes en las diferentes variables, predominando en las variables piso y parquederos, con 2.638 y 1.605 datos faltantes respectivamente.

n_nas_por_columna <- colSums(is.na(vivienda))
print(n_nas_por_columna)
##           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

Con el fin de realizar la manipulación de los datos, se hace una copia del dataset original, llamado “vivienda_corregido”:

vivienda_corregido <- vivienda

Se realiza una imputacion de los datos con la moda, debido a que este método resulta ser menos sensible a valores atípicos en comparación con la media, así como sucede con las dos variables con mayor número de datos faltantes.

Por otro lado, la moda es recomendable cuando los datos tienen valores que se repiten con frecuencia, permitiendo mantener una coherencia en comparación a la distribución de los datos originales.

library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Función para calcular la moda
moda <- function(x) {
  uniq_x <- unique(x[!is.na(x)])
  uniq_x[which.max(tabulate(match(x, uniq_x)))]
}

# Calcular la moda para 'piso' y 'parqueadero' en cada combinación de 'tipo', 'zona', y 'estrato'
moda_imputacion <- vivienda_corregido %>%
  group_by(tipo, zona, estrato) %>%
  summarise(
    moda_piso = moda(piso),
    moda_parqueaderos = moda(parqueaderos),
    .groups = 'drop'
  )
# Unir los resultados de moda con los datos originales
vivienda_corregido <- vivienda_corregido %>%
  left_join(moda_imputacion, by = c("tipo", "zona", "estrato"))

# Imputar los valores faltantes con la moda
vivienda_corregido <- vivienda_corregido %>%
  mutate(
    piso = ifelse(is.na(piso), moda_piso, piso),
    parqueaderos = ifelse(is.na(parqueaderos), moda_parqueaderos, parqueaderos)
  ) %>%
  select(-moda_piso, -moda_parqueaderos) # Eliminar las columnas auxiliares
n_nas_por_columna <- colSums(is.na(vivienda_corregido))
print(n_nas_por_columna)
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3            9            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            6            3            3            3            3            3 
##      latitud 
##            3

Teniendo en cuenta que una vez imputados los datos faltantes con la moda quedan datos falntes de 9 en piso, 6 en parqueaderos y 3 en el resto de variables, se toma la decisiónd de eliminarlos, ya que son muy pocos.

# Identificar filas con al menos un valor NA
filas_con_na <- which(rowSums(is.na(vivienda_corregido)) > 0)

# Ver las filas con datos faltantes
vivienda_corregido[filas_con_na, ]
## # A tibble: 10 × 13
##       id zona   piso  estrato preciom areaconst parqueaderos banios habitaciones
##    <dbl> <chr>  <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
##  1  1132 Zona … <NA>        5     325        89            1      2            3
##  2  4999 Zona … <NA>        6    1000       400           NA      5           10
##  3  5165 Zona … <NA>        5     180        98            1      2            3
##  4  6937 Zona … <NA>        6    1350       212            3      4            3
##  5  1214 Zona … 01          5     148       150           NA      1            3
##  6  5475 Zona … <NA>        5     650       129           NA      2            1
##  7   482 Zona … <NA>        5     350       152            1      4            3
##  8    NA <NA>   <NA>       NA      NA        NA           NA     NA           NA
##  9    NA <NA>   <NA>       NA      NA        NA           NA     NA           NA
## 10    NA <NA>   <NA>       NA     330        NA           NA     NA           NA
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Eliminar filas con datos faltantes
vivienda_corregido <- na.omit(vivienda_corregido)
summary(is.na(vivienda_corregido))
##      id             zona            piso          estrato       
##  Mode :logical   Mode :logical   Mode :logical   Mode :logical  
##  FALSE:8312      FALSE:8312      FALSE:8312      FALSE:8312     
##   preciom        areaconst       parqueaderos      banios       
##  Mode :logical   Mode :logical   Mode :logical   Mode :logical  
##  FALSE:8312      FALSE:8312      FALSE:8312      FALSE:8312     
##  habitaciones       tipo           barrio         longitud      
##  Mode :logical   Mode :logical   Mode :logical   Mode :logical  
##  FALSE:8312      FALSE:8312      FALSE:8312      FALSE:8312     
##   latitud       
##  Mode :logical  
##  FALSE:8312
n_nas_por_columna <- colSums(is.na(vivienda_corregido))
print(n_nas_por_columna)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0

Con el fin de determinar si la imputación a través de la moda fue correcta, se grafica a continuación a través de cajas y bigotes el antes y después de las dos variables que tuvieron más modificación, en este caso: piso y parqueaderos, lo anterior para verificar la consistencia en la distribución de los datos.

# Combinar los dataframes en uno solo

vivienda$origen <- "Original"
vivienda_corregido$origen <- "Corregido"

datos_combinados <- rbind(vivienda, vivienda_corregido)

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

# Crear el gráfico de cajas y bigotes
ggplot(datos_combinados, aes(x = origen, y = piso, fill = origen)) +
  geom_boxplot() +
  labs(title = "Distribución de Piso en Vivienda y Vivienda Modificado",
       x = "Origen de los Datos",
       y = "Piso") +
  scale_fill_manual(values = c("lightblue", "lightgreen")) +
  theme_minimal()
## Warning: Removed 2638 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

vivienda$origen <- "Original"
vivienda_corregido$origen <- "Corregido"

datos_combinados <- rbind(vivienda, vivienda_corregido)

# Asegurarse de que la columna 'parqueaderos' sea numérica
datos_combinados$parqueaderos <- as.numeric(datos_combinados$parqueaderos)

# Crear el gráfico de cajas y bigotes para la variable 'parqueaderos'
ggplot(datos_combinados, aes(x = origen, y = parqueaderos, fill = origen)) +
  geom_boxplot() +
  labs(title = "Distribución de Parqueaderos en Vivienda y Vivienda Corregido",
       x = "Origen de los Datos",
       y = "Parqueaderos") +
  scale_fill_manual(values = c("lightblue", "lightgreen")) +
  theme_minimal()
## Warning: Removed 1605 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

# Función para calcular la moda
calcular_moda <- function(x) {
  uniq_x <- unique(x)
  uniq_x[which.max(tabulate(match(x, uniq_x)))]
}

# Asegurarse de que la columna 'piso' sea numérica
vivienda$piso <- as.numeric(vivienda$piso)

# Calcular la media
media_piso <- mean(vivienda$piso, na.rm = TRUE)

# Calcular la mediana
mediana_piso <- median(vivienda$piso, na.rm = TRUE)

# Calcular la moda
moda_piso <- calcular_moda(vivienda$piso)

# Mostrar los resultados
cat("Media de Piso:", media_piso, "\n")
## Media de Piso: 3.770936
cat("Mediana de Piso:", mediana_piso, "\n")
## Mediana de Piso: 3
cat("Moda de Piso:", moda_piso, "\n")
## Moda de Piso: NA
# Código para vivienda corregido:

# Asegurarse de que la columna 'piso' sea numérica
vivienda_corregido$piso <- as.numeric(vivienda_corregido$piso)

# Calcular la media
media_piso_corregido <- mean(vivienda_corregido$piso, na.rm = TRUE)

# Calcular la mediana
mediana_piso_corregido <- median(vivienda_corregido$piso, na.rm = TRUE)

# Calcular la moda
moda_piso_corregido <- calcular_moda(vivienda_corregido$piso)

# Mostrar los resultados
cat("Media de Piso en la base imputada:", media_piso_corregido, "\n")
## Media de Piso en la base imputada: 3.461141
cat("Mediana de Piso en la base imputada:", mediana_piso_corregido, "\n")
## Mediana de Piso en la base imputada: 3
cat("Moda de Piso en la base imputada:", moda_piso_corregido, "\n")
## Moda de Piso en la base imputada: 2
# Función para calcular la moda
calcular_moda <- function(x) {
  uniq_x <- unique(x)
  uniq_x[which.max(tabulate(match(x, uniq_x)))]
}

# Asegurarse de que la columna 'parqueaderos' sea numérica
vivienda$parqueaderos <- as.numeric(vivienda$parqueaderos)

# Calcular la media
media_parqueaderos <- mean(vivienda$parqueaderos, na.rm = TRUE)

# Calcular la mediana
mediana_parqueaderos <- median(vivienda$parqueaderos, na.rm = TRUE)

# Calcular la moda
moda_parqueaderos <- calcular_moda(vivienda$parqueaderos)

# Mostrar los resultados
cat("Media de Piso:", media_parqueaderos, "\n")
## Media de Piso: 1.835194
cat("Mediana de Piso:", mediana_parqueaderos, "\n")
## Mediana de Piso: 2
cat("Moda de Piso:", moda_parqueaderos, "\n")
## Moda de Piso: 1
# Código para vivienda corregido:

# Asegurarse de que la columna 'piso' sea numérica
vivienda_corregido$parqueaderos <- as.numeric(vivienda_corregido$parqueaderos)

# Calcular la media
media_piso_corregido <- mean(vivienda_corregido$parqueaderos, na.rm = TRUE)

# Calcular la mediana
mediana_piso_corregido <- median(vivienda_corregido$parqueaderos, na.rm = TRUE)

# Calcular la moda
moda_piso_corregido <- calcular_moda(vivienda_corregido$parqueaderos)

# Mostrar los resultados
cat("Media de Piso en la base imputada:", media_parqueaderos, "\n")
## Media de Piso en la base imputada: 1.835194
cat("Mediana de Piso en la base imputada:", mediana_parqueaderos, "\n")
## Mediana de Piso en la base imputada: 2
cat("Moda de Piso en la base imputada:", moda_parqueaderos, "\n")
## Moda de Piso en la base imputada: 1

Como se evidencian con los gráficos anteriores de cajas y bigotes, así como el cálculo de la media, mediana y moda para las variables piso y parqueaderos, en el data set original vs el data set con los datos imputados, se muestra que la distribución de los datos son consistentes.

# Verificar las columnas del dataframe
print(names(vivienda_corregido))
##  [1] "id"           "zona"         "piso"         "estrato"      "preciom"     
##  [6] "areaconst"    "parqueaderos" "banios"       "habitaciones" "tipo"        
## [11] "barrio"       "longitud"     "latitud"      "origen"
# Convertir la variable 'piso' de character a numeric
vivienda_corregido$piso <- as.numeric(vivienda_corregido$piso)

# Verificar la conversión
str(vivienda_corregido$piso)
##  num [1:8312] 1 1 1 2 1 1 1 1 2 2 ...

Se revisan los registros en cero

# Contar cuántos registros tienen valor cero en cada variable
zero_counts <- sapply(vivienda_corregido, function(x) sum(x == 0, na.rm = TRUE))

# Mostrar los resultados
print(zero_counts)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0           45           66            0            0            0 
##      latitud       origen 
##            0            0

Según el código anterior, se evidencia que variables importantes como baños y habitaciones, cuentan con 45 y 66 registros en cero respectivamente, lo que hace necesario normalizarlas ya que no es posible que una vivienda cuente con cero baños y habitaciones y menos aún teniendo en cuenta la estratificación de las viviendas, las cuales oscilan entre 3 y 6. Por lo tanto, se imputa con la moda.

# Función para calcular la moda
calculate_mode <- function(x) {
  unique_x <- unique(x)
  unique_x[which.max(tabulate(match(x, unique_x)))]
}

# Calcular la moda para 'habitaciones' y 'banios' sin contar los ceros
mode_habitaciones <- calculate_mode(vivienda_corregido$habitaciones[vivienda_corregido$habitaciones != 0])
mode_banios <- calculate_mode(vivienda_corregido$banios[vivienda_corregido$banios != 0])

# Imputar la moda en 'habitaciones' y 'banios' donde el valor es 0
vivienda_corregido$habitaciones[vivienda_corregido$habitaciones == 0] <- mode_habitaciones
vivienda_corregido$banios[vivienda_corregido$banios == 0] <- mode_banios

Volvemos a verificar que las correcciones anteriores hayan quedado en el data set:

str(vivienda_corregido)
## spc_tbl_ [8,312 × 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8312] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8312] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : num [1:8312] 1 1 1 2 1 1 1 1 2 2 ...
##  $ estrato     : num [1:8312] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8312] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8312] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8312] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8312] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8312] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8312] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8312] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8312] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8312] 3.43 3.43 3.44 3.44 3.46 ...
##  $ origen      : chr [1:8312] "Corregido" "Corregido" "Corregido" "Corregido" ...
##  - 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> 
##  - attr(*, "na.action")= 'omit' Named int [1:10] 987 1005 4913 5427 5814 6023 6115 8320 8321 8322
##   ..- attr(*, "names")= chr [1:10] "987" "1005" "4913" "5427" ...
# Contar cuántos registros tienen valor cero en cada variable
zero_counts <- sapply(vivienda_corregido, function(x) sum(x == 0, na.rm = TRUE))

# Mostrar los resultados
print(zero_counts)
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud       origen 
##            0            0

Limpieza y unificación de los nombres de los barrios.

Se revisan los diferentes datos que están dentro de la variable “barrio”:

summary(vivienda_corregido$barrio)
##    Length     Class      Mode 
##      8312 character character
tabla_barrio <- table(vivienda_corregido$barrio)
#print(tabla_barrio)

Tenieneo en cuenta que de las tres variables más importantes para realizar la clasificación y conjuntos de datos, la variable “barrio” cuenta con nombres diferentes para un solo lugar, se hace necesario crear un nuevo data frame en donde se unifiquen estos datos.

# Cargar las bibliotecas necesarias
library(dplyr)
library(ggplot2)
library(tidyr)

# Corrección de barrios
barrio_corregido <- table(vivienda_corregido$barrio) %>% data.frame()

# Se corrigen todos los nombres de los barrios y se llevan a minúscula
vivienda_corregido$barrio <- tolower(vivienda_corregido$barrio)

# Se eliminan las tildes
vivienda_corregido$barrio <- iconv(vivienda_corregido$barrio, to = "ASCII//TRANSLIT")

# Se hace limpieza de los nombres de los barrios
vivienda_corregido <- vivienda_corregido %>%
  mutate(barrio = ifelse(barrio == "agua blanca", "aguablanca", barrio),
         barrio = ifelse(barrio == "alf?crez real", "alferez real", barrio),
         barrio = ifelse(barrio == "alfonso lopez i", "alfonso lopez", barrio),
         barrio = ifelse(barrio == "arboledas", "arboleda", barrio),
         barrio = ifelse(barrio == "cali bella", "calibella", barrio),
         barrio = ifelse(barrio == "cali canto", "calicanto", barrio),
         barrio = ifelse(barrio == "calicanto viii", "calicanto", barrio),
         barrio = ifelse(barrio == "ciudad mel?cndez", "ciudadela melendez", barrio),
         barrio = ifelse(barrio == "ciudad melendez", "ciudadela melendez", barrio),
         barrio = ifelse(barrio == "ciudadela pasoancho", "ciudadela paso ancho", barrio),
         barrio = ifelse(barrio == "el tr?cbol", "el troncal", barrio),
         barrio = ifelse(barrio == "ingenio i", "el ingenio i", barrio),
         barrio = ifelse(barrio == "ingenio ii", "el ingenio ii", barrio),
         barrio = ifelse(barrio == "laflora", "la flora", barrio),
         barrio = ifelse(barrio == "las am?cricas", "las americas", barrio),
         barrio = ifelse(barrio == "las vegas de", "las vegas", barrio),
         barrio = ifelse(barrio == "mel?cndez", "melendez", barrio),
         barrio = ifelse(barrio == "pampalinda", "pampa linda", barrio),
         barrio = ifelse(barrio == "portales de comfandi", "portada de comfandi", barrio),
         barrio = ifelse(barrio == "rep??blica de israel", "republica de israel", barrio),
         barrio = ifelse(barrio == "rincon de la", "rincon de salomia", barrio),
         barrio = ifelse(barrio == "san judas", "san judas tadeo", barrio),
         barrio = ifelse(barrio == "tequendeme", "tequendama", barrio),
         barrio = ifelse(barrio == "valle de lili", "valle del lili", barrio),
         barrio = ifelse(barrio == "zona norte los", "zona norte", barrio))

Gráficos principales

El siguiente gráfico muestra el número de viviendas por cada una de las zonas, en donde la zona norte cuenta con el segundo grupo más alto de viviendas, con una participación del 23.1%, equivalente a 1.920 viviendas.

# Número de viviendas por zona

numero_viviendas_zona <- table(vivienda_corregido$zona)
suma_total_zona_vivienda <- sum(numero_viviendas_zona) # Se suman las viviendas por zona

barplot((numero_viviendas_zona), 
        col = c("skyblue", "grey", "darkblue", "lightblue", "darkgrey"),
        xlab = "Zona", ylab = "Cantidades",
        main = "Numero de viviendas por zona")

for (i in seq_along(numero_viviendas_zona)) {
  text(i, numero_viviendas_zona[i], label = paste(numero_viviendas_zona[i], sprintf("(%.1f%%)", numero_viviendas_zona[i]/suma_total_zona_vivienda*100)), pos = 1)
}

# Precio promedio de las viviendas por zona

promedio_valor_viviendas_zona <- tapply(vivienda_corregido$preciom, vivienda_corregido$zona, mean)

barplot((promedio_valor_viviendas_zona),
  col = c("skyblue", "grey", "darkblue", "lightblue", "darkgrey"),
  xlab = "Zona", ylab = "Promedio",
  main = "Precio promedio de las viviendas por zona")

# Agregamos valores al gráfico

text(
  barplot(promedio_valor_viviendas_zona, col = "skyblue", plot = FALSE),
  promedio_valor_viviendas_zona,
  labels = sprintf("%.0f", promedio_valor_viviendas_zona),
  pos = 1,
  col = "black",
  cex = 0.8
)

# Precio máximo de las viviendas por zona

precio_maximo_viviendas_zona <- tapply(vivienda_corregido$preciom, vivienda_corregido$zona, max)

barplot((precio_maximo_viviendas_zona),
  col = c("skyblue", "grey", "darkblue", "lightblue", "darkgrey"),
  xlab = "Zona", ylab = "Precio",
  main = "Precio maximo de las viviendas por zona")

# Agregamos valores al gráfico

text(
  barplot(precio_maximo_viviendas_zona, col = "skyblue", plot = FALSE),
  precio_maximo_viviendas_zona,
  labels = sprintf("%.0f", precio_maximo_viviendas_zona),
  pos = 1,
  col = "black",
  cex = 0.8
)

A continuación, se grafican los datos para las viviendas únicamente de la zona norte, las cuales son el foco del modelamiento, teniendo en cuenta las necesidades del cliente, en cuanto a conseguir una vivienda tipo apartamento y una vivienda tipo casa para albergar a dos familias de los empleados de la empresa.

# Filtrar los datos para la zona 'norte'
vivienda_norte <- subset(vivienda_corregido, zona == "Zona Norte")

# Calcular el número de viviendas por tipo en la zona 'norte'
numero_viviendas_tipo <- table(vivienda_norte$tipo)
suma_total_tipo_vivienda <- sum(numero_viviendas_tipo)  # Sumar las viviendas por tipo

# Crear el gráfico de barras
barplot(numero_viviendas_tipo,
        col = c("skyblue", "grey"),
        xlab = "Tipo",
        ylab = "Cantidades",
        main = "Número de Viviendas por Tipo en Zona Norte")

# Añadir etiquetas con el número de viviendas y porcentaje
for (i in seq_along(numero_viviendas_tipo)) {
  text(i, numero_viviendas_tipo[i], 
       labels = paste(numero_viviendas_tipo[i], sprintf("(%.1f%%)", numero_viviendas_tipo[i] / suma_total_tipo_vivienda * 100)), 
       pos = 1)
}

# Añadir una leyenda
legend("topright", 
       legend = names(numero_viviendas_tipo), 
       fill = c("skyblue", "grey"))

# Calcular el número de viviendas por estrato en la zona 'Zona Norte'
numero_viviendas_estrato <- table(vivienda_norte$estrato)
suma_total_estrato_vivienda <- sum(numero_viviendas_estrato)  # Sumar las viviendas por estrato

# Crear el gráfico de barras
barplot(numero_viviendas_estrato,
        col = c("skyblue", "grey", "darkblue", "lightblue", "darkgrey"),
        xlab = "Estrato",
        ylab = "Cantidades",
        main = "Número de Viviendas por Estrato en Zona Norte")

# Añadir etiquetas con el número de viviendas y porcentaje
for (i in seq_along(numero_viviendas_estrato)) {
  text(i, numero_viviendas_estrato[i], 
       labels = paste(numero_viviendas_estrato[i], sprintf("(%.1f%%)", numero_viviendas_estrato[i] / suma_total_estrato_vivienda * 100)), 
       pos = 3)  # Cambiar pos a 3 para colocar el texto encima de la barra
}

# Añadir una leyenda
legend("topright", 
       legend = names(numero_viviendas_estrato), 
       fill = c("skyblue", "grey", "darkblue", "lightblue", "darkgrey"))

# Calcular la media del precio por tipo
medias <- tapply(vivienda_norte$preciom, vivienda_norte$tipo, mean)
medias_df <- data.frame(tipo = names(medias), media = medias)

# Crear el boxplot con ggplot2
p <- ggplot(vivienda_corregido, aes(x = tipo, y = preciom, fill = tipo)) +
  geom_boxplot() +
  labs(title = "Distribución de Precio de las Viviendas por Tipo",
       x = "Tipo de Vivienda",
       y = "Precio") +
  scale_fill_manual(values = c("skyblue", "grey")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Añadir las medias al gráfico
p + geom_hline(data = medias_df, aes(yintercept = media, color = tipo), linetype = "dashed") +
  geom_text(data = medias_df, aes(x = tipo, y = media, label = sprintf("%.0f", media)), 
            color = "black", vjust = -0.5)

El gráfico boxplot anterior permite ver, además del promedio de los precios tanto para el conjunto de viviendas tipo apartamento como el de las casas, la distribución de los valores a lo largo de la escala de los precios, en donde se evidencia que la vivienda más cara es tipo casa y corresponde a un valor de 1.999.

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

A continuación se muestran algunos datos importantes de las viviendas tipo casa ubicadas en la zona norte, para lo cual, se cuenta con un total de 722 viviendas, ubicados en los diferentes estratos del 3 al 6, permitiendo tener una gran oferta de viviendas de tipo casa, en donde predominan las ubicadas en el estrato 5, con un total de 271, seguido del estrato 3 con 234, estrato 4 con 161 y por último las viviendas ubicadas en el estrato 6 con 55 viviendas.

library(dplyr)

vivienda_zona_norte_casa <- vivienda_corregido %>%
  filter(zona == "Zona Norte", tipo == "Casa")
head(vivienda_zona_norte_casa, 3)
## # A tibble: 3 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  1209 Zona N…     2       5     320       150            2      4            6
## 2  1592 Zona N…     2       5     780       380            2      3            3
## 3  4057 Zona N…     2       6     750       445            3      7            6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
tipo_table <- table(vivienda_zona_norte_casa$tipo)


resultado <- data.frame("Número de casas" = paste("Número de casas:", tipo_table))

# Muestra el resultado
print(resultado)
##        Número.de.casas
## 1 Número de casas: 722
piso_table <- table(vivienda_zona_norte_casa$estrato)


df_estrato <- as.data.frame(piso_table)


colnames(df_estrato) <- c("Estrato", "Cantidad")


df_estrato$Descripción <- paste(df_estrato$Cantidad, "Casas en estrato:", df_estrato$Estrato)

# Elimina la columna "Cantidad"
df_estrato <- df_estrato[, c("Estrato", "Descripción")]

print(df_estrato)
##   Estrato             Descripción
## 1       3 235 Casas en estrato: 3
## 2       4 161 Casas en estrato: 4
## 3       5 271 Casas en estrato: 5
## 4       6  55 Casas en estrato: 6
piso_table <- table(vivienda_zona_norte_casa$piso)

df_piso <- as.data.frame(piso_table)

colnames(df_piso) <- c("Número de pisos", "Cantidad")

df_piso$Descripción <- paste(df_piso$`Número de pisos`, "piso(s):", df_piso$Cantidad)

# Elimina la columna "Cantidad"
df_piso <- df_piso[, c("Número de pisos", "Descripción")]

print(df_piso)
##   Número de pisos    Descripción
## 1               1  1 piso(s): 84
## 2               2 2 piso(s): 566
## 3               3  3 piso(s): 65
## 4               4   4 piso(s): 6
## 5               7   7 piso(s): 1

Las tablas anteriores permite ver el número de viviendas por estrato por cantidad de pisos, en donde las casas de estrato 5 y viviendas con 2 pisos predominan.

1.1.1 Gráfico de correlación

# Realizamos gráfico de correlación

library(corrplot)
## corrplot 0.92 loaded
correlacion_matriz <- cor(vivienda_zona_norte_casa[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")])

corrplot(correlacion_matriz, method = "color", addCoef.col = "orange")

table_correlacion <- as.table(correlacion_matriz)
print(table_correlacion)
##                preciom areaconst   estrato    banios habitaciones
## preciom      1.0000000 0.7313480 0.6123503 0.5498030    0.3614330
## areaconst    0.7313480 1.0000000 0.4573818 0.4921797    0.4272849
## estrato      0.6123503 0.4573818 1.0000000 0.4214007    0.1038405
## banios       0.5498030 0.4921797 0.4214007 1.0000000    0.5925218
## habitaciones 0.3614330 0.4272849 0.1038405 0.5925218    1.0000000

No se evidencia una alta correlación entre las diferentes variables, por lo tanto no es posible realizar eliminación de alguna de ellas.

Las variables con más alta correlación son:

precio y área construída: 73.1% prcio y estrato: 61.2%. Baños y habitaciones: 59.3%.

# Tabla de frecuencia para ver los barrios

table(vivienda_zona_norte_casa$barrio)
## 
##                     acopi           alameda del rio                    alamos 
##                        70                         1                         3 
##         atanasio girardot              barranquilla        barrio tranquilo y 
##                         1                         3                         1 
##               base a?Crea                    berlin             brisas de los 
##                         2                         1                        22 
##        brisas del guabito                      cali                 calibella 
##                         1                        13                         1 
##                    calima             calimio norte                  cambulos 
##                         6                         3                         1 
##                centenario                 chapinero                chipichape 
##                         3                         1                         5 
##         ciudad los alamos        colinas del bosque                 cristales 
##                        11                         1                         1 
##                 el bosque                  el cedro           el gran limonar 
##                        37                         1                         1 
##                el guabito                   el sena                el tr?Cbol 
##                         1                         1                         1 
##           evaristo garcia          flora industrial                  floralia 
##                         1                         4                         3 
##                    gaitan                   granada      jorge eliecer gaitan 
##                         1                        10                         1 
##                 juanamb??                   la base                la campina 
##                        11                         1                         4 
##              la esmeralda                  la flora               la floresta 
##                         1                       100                         2 
##                 la merced                 la rivera               la rivera i 
##                        24                         9                         1 
##              la rivera ii                la riviera              la villa del 
##                         2                         1                         1 
##               las acacias             las am?Cricas                las ceibas 
##                         1                         1                         2 
##              las delicias               las granjas                 los andes 
##                         3                         1                        13 
##             los guaduales            los guayacanes                manzanares 
##                        10                         2                         1 
##                     menga   metropolitano del norte          nueva tequendama 
##                         2                         1                         1 
##         oasis de comfandi                 occidente                    pacara 
##                         1                         1                         2 
##     parque residencial el              paseo de los         paso del comercio 
##                         1                         2                         2 
##         poblado campestre                   popular       portada de comfandi 
##                         1                         5                         2 
##                  porvenir          prados del norte        quintas de salomia 
##                         2                        31                         1 
##             rozo la torre                   salomia                  san luis 
##                         1                        20                         3 
##               san vicente             santa barbara              santa monica 
##                        31                         1                        18 
##        santa monica norte  santa monica residencial                 santander 
##                         1                        20                         1 
##            tejares de san        torres de comfandi         union de vivienda 
##                         1                         2                         1 
## urbanizacion barranquilla     urbanizacion la flora    urbanizacion la merced 
##                         2                        23                         4 
##     urbanizacion la nueva            valle del lili                 versalles 
##                         1                         1                        16 
##            villa colombia         villa de veracruz           villa del prado 
##                         1                         4                        41 
##             villa del sol        villas de veracruz                    vipasa 
##                        12                         8                        30 
##                zona norte              zona oriente 
##                        19                         1
# Hacemos el mapa

#install.packages("leaflet")
library(leaflet)

map_vivienda <- leaflet(vivienda_zona_norte_casa) %>% 
  
  addTiles() %>% 
  addMarkers(lng = ~longitud, lat = ~latitud, popup = ~paste("ID:", id, "<br>Barrio:", barrio))

map_vivienda
#install.packages("dplyr")
library(dplyr)

zona_genera_casa <- vivienda %>% 
  filter(zona != "Zona Norte" & tipo == "Casa")
mapa_general <- leaflet() %>% 
  addTiles()

mapa_general<- mapa_general %>%
  addCircleMarkers(data = vivienda_zona_norte_casa, lng = ~longitud, lat = ~latitud, 
                   color = "blue", radius = 5, popup = ~paste("ID:", id, "<br>Barrio:", barrio))

mapa_general <- mapa_general %>%
  addCircleMarkers(data = zona_genera_casa , lng = ~longitud, lat = ~latitud, 
                   color = "red", radius = 5, popup = ~paste("ID:", id, "<br>Barrio:", barrio))
mapa_general

Con el gráfico anterior y utilizado las variables de longitud y latitud, se muetra la ubicación de las diferentes viviendas, en donde los puntos azules corresponden a aquellas ubicadas en la zona norte. Por otro lado, se puede identificar algunas viviendas que no corresponden a la zona norte, y están mas relacionadas a la zona sur; esto es debido a posibles errores en la variable latitud, para lo cual se sugiere hacer una corrección de estos datos, con el fin de garantizar la ubicación correcta de las diferentes viviendas.

1.2. 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, numero de habitaciones y zona donde se ubica la vivienda. Use gráficos interactivos con el paquete plotly e interprete los resultados.

# Grafico para el área construída

library(plotly)
## 
## Adjuntando el paquete: 'plotly'
## 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
library(ggplot2)

grafico_areaconst <- plot_ly(vivienda_zona_norte_casa, x = ~ areaconst, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_areaconst)

Se muestra un crecimiento en el precio promedio cuando el área construída incrementa.

# Gráfico en función del estrato

grafico_estrato <- plot_ly(vivienda_zona_norte_casa, x = ~ estrato, y = ~ preciom, type = "box")
grafico_estrato <- grafico_estrato %>%
  layout(title = "Relación entre precio y estrato", xaxis = list(title = "Estrato"), yaxis = list(title = "Precio"))

print(grafico_estrato)

Aunque se observa una relación entre el precio promedio y el estrato, en donde a estrato alto mayor precio promedio, también se evidencian datos atípicos, sobre todo en el estrato 4 y 5, en donde el valor de la mediana no se encuentra en la mitad del conjunto de datos.

# Gráficos en función del número de baños

grafico_baños <- plot_ly(vivienda_zona_norte_casa, x = ~ banios, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_baños)

No se cuenta con una relación clara entre el precio promedio y el número de baños.

# Gráficos en función al número de habitaciones

grafico_habitaciones <- plot_ly(vivienda_zona_norte_casa, x = ~ habitaciones, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_habitaciones)

No hay relación entre el número de habitaciones y el precio promedio.

# Gráfico en función de la zona

grafico_zona <- plot_ly(vivienda_zona_norte_casa, x = ~ zona, y = ~ preciom, type = "box")
grafico_zona <- grafico_zona %>%
  layout(title = "Relación entre precio y zona",
         xaxis = list(title = "Zona"), yaxis = list(title = "Precio"))

print(grafico_zona)

Como se muestra en el gráfico anterior, se cuenta con datos atípicos en el precio de las viviendas de la zona norte, lo que corresponde a pocas viviendas con un alto valor en el precio promedio; esto podría llegar a ocasionar ciertos problemas en la predicción del modelo, en cuanto a una posible sobreestimación del precio de las viviendas.

1.3. 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 )), interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están 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).

A continuación, se muestra el resultado del modelo de regresión de las viviendas tipo casa, sin entrenamiento:

# Se crea modelo

modelo_casas_sin_entrenamiento <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivienda_zona_norte_casa)

summary(modelo_casas_sin_entrenamiento)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivienda_zona_norte_casa)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -914.44  -78.17  -16.76   44.77 1078.73 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -235.12846   30.85092  -7.621 7.98e-14 ***
## areaconst       0.78595    0.04448  17.669  < 2e-16 ***
## estrato        78.46054    7.55120  10.390  < 2e-16 ***
## habitaciones    3.01905    4.58968   0.658 0.510883    
## parqueaderos   19.10017    5.71284   3.343 0.000871 ***
## banios         25.78159    5.65364   4.560 6.01e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 157.3 on 716 degrees of freedom
## Multiple R-squared:  0.6587, Adjusted R-squared:  0.6563 
## F-statistic: 276.3 on 5 and 716 DF,  p-value: < 2.2e-16
# Obtener los residuos del modelo
residuos <- residuals(modelo_casas_sin_entrenamiento)

# Calcular el RMSE
mse <- mean(residuos^2)   # Error Cuadrático Medio
rmse <- sqrt(mse)        # Raíz del Error Cuadrático Medio

# Calcular el MAE
mae <- mean(abs(residuos))  # Error Absoluto Medio

# Mostrar los resultados
cat("RMSE:", rmse, "\n")
## RMSE: 156.6767
cat("MAE:", mae, "\n")
## MAE: 98.75491

Con el fin de poder tener entrenamiento y test (70% y 30%) respectivamente, se realiza lo siguiente:

set.seed(444)

n <- nrow(vivienda_zona_norte_casa)

train_size <- round(0.7 * n)
train_index <- sample(1:n, train_size)

train_data <- vivienda_zona_norte_casa[train_index, ]

test_data <- vivienda_zona_norte_casa[-train_index, ]
# Se crea modelo

modelo_casas <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data)

summary(modelo_casas)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = train_data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -883.43  -81.30  -14.51   59.27 1086.01 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -256.19181   39.76929  -6.442 2.78e-10 ***
## areaconst       0.76836    0.05367  14.315  < 2e-16 ***
## estrato        81.17482    9.38642   8.648  < 2e-16 ***
## habitaciones    1.52188    5.76629   0.264  0.79194    
## parqueaderos   22.46779    6.95438   3.231  0.00132 ** 
## banios         30.49435    6.83980   4.458 1.02e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 165.5 on 499 degrees of freedom
## Multiple R-squared:  0.6491, Adjusted R-squared:  0.6456 
## F-statistic: 184.6 on 5 and 499 DF,  p-value: < 2.2e-16
# Obtener los residuos del modelo
residuos <- residuals(modelo_casas)

# Calcular el RMSE
mse <- mean(residuos^2)   # Error Cuadrático Medio
rmse <- sqrt(mse)        # Raíz del Error Cuadrático Medio

# Calcular el MAE
mae <- mean(abs(residuos))  # Error Absoluto Medio

# Mostrar los resultados
cat("RMSE:", rmse, "\n")
## RMSE: 164.5031
cat("MAE:", mae, "\n")
## MAE: 104.151

Con la comparación del resultado obtenido en R-squared, en el modelo sin entrenamiento vs el modelo entrenado, se evidencia que resulta ser más eficiente cuando no se tiene un entrenamiento; lo anterior, debido a que el modelo está analizando todos los datos disponibles, incluyendo patrones y ruídos.

Con la creación del modelo anterior, se desea explicar la variable dependiente del precio promedio, usando las siguientes variables independientes: área construída, estrato, habitaciones, parqueaderos y baños.

Residuales: muestra el resultado de la diferencia entre los valores observados y los valores predichos, con una variabilidad entre -925.86 y 1.079. También presenta una media de -16.23; de esta forma se podría pensar en que el modelo tiende a sobre estimar o subestimar los precios de las viviendas, lo anterior teniendo en cuenta la variabilidad mínima y máxima, de -925.86 y 1.079 respectivamente. Por otro lado, al tener una media de -16.23,el modelo estaría valorizando un poco el precio de las viviendas, por lo tanto se estaría teniendo una sobre estimación en términos de promedio.

El resultado estimado en el intercepto no nos genera reelevancia, ya que no puede ser utilizado dentro del análisis, lo anterior debido a que que indica el precio de las viviendas cuando las variables independientes son cero, algo que no resulta lógico en el contexto de las viviendas.

El área construída presenta un estimado de 0.80527, lo que indica que al mantener constante el resto de variables, por cada metro construído se tiene un incremento en el valor por 0.805.

En cuanto al estrato, por cada incremento se tiene un aumento en el valor del precio en 78.11.

En cuanto a las habitaciones, teniendo en cuenta que el estimado es de 0.98 y en pr de 0.81, indica que no se tiene una relación clara en el cambio del precio por incremento en el número de las habitaciones.

Con relación a la variable parqueaderos, se tiene un incremento de 19.77 por cada parqueadero.

La relación de los baños es muy alta, en donde se indica que por cada baño adicional se tiene un incremento en el precio de 23.89.

El coeficiente R2 y R2 ajustado, el cual da como resultado 0.6587 y 0.6563 respectivamente, cifras que se encuentran muy cercanas, indica que aproximadamente el 65.87% de la variabilidad de los precios puede ser explicada por el modelo; este coeficiente se está viendo afectado por los datos atípicos, los cuales corresponden a pocas viviendas con precios muy altos; para esto, se podría pensar en hacer una limpieza de los datos extremos o atípicos, los cuales se visualizan en los precios elevados.

En cuanto al RMSE: 164.5031 y MAE: 104.151, resultan ser cifras altas en comparación con el modelo sin entrenamiento, lo que indica que puede ser menos preciso en la estimación de los precios de las viviendas, con propension a errores en las predicciones.

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

# Creamos el gráfico de la linealidad

plot(modelo_casas$fitted.values, modelo_casas$residuals, xlab = "Valores ajustados", ylab = "Residuales")

abline(h = 0, col = "red", lwd = 2)

El gráfico presenta una distribución aleatoria de los residuos, lo que indica que es algo positivo y que el modelo no requiere de ajuste.

# Gráfico para la homocedasticidad

plot(modelo_casas$fitted.values, sqrt(abs(modelo_casas$residuals)), xlab = "Valores ajustados", ylab = "Raíz de los valores residuales absolutos")

abline(h = 0, col = "red", lwd = 2)

Con el gráfico anterior, se evidencia una homocedasticidad, ya que no se visualiza un patrón de los residuos absolutos.

# Gráfico para la independencia de los errores

plot(modelo_casas$residuals, type = "l")

abline(h = 0, col = "red", lwd = 2)

No se evidencia patrones evidentes, por lo tanto se considera el supuesto de independencia del modelo, ya que cuenta con residuos aleatorios.

# Gráfico de normalidad de los errores

qqnorm(modelo_casas_sin_entrenamiento$residuals)

qqline(modelo_casas_sin_entrenamiento$residuals, col = 2)

# Gráfico de normalidad de los errores

qqnorm(modelo_casas$residuals)

qqline(modelo_casas$residuals, col = 2)

# Multicolinealidad

# install.packages("car")
library(car)
## Cargando paquete requerido: carData
## 
## Adjuntando el paquete: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
## The following object is masked from 'package:boot':
## 
##     logit
valores_vif <- vif(modelo_casas)
print(valores_vif)
##    areaconst      estrato habitaciones parqueaderos       banios 
##     1.589187     1.544386     1.755738     1.348679     1.918500
# install.packages("GGally")
library(GGally)

# Extraer las variables predictoras del modelo
variables_casas <- model.matrix(modelo_casas)[, -1]  # Elimina la columna del intercepto

# Convertir en data frame para generar el gráfico
variables_df_casas <- as.data.frame(variables_casas)

ggpairs(variables_df_casas)

Los residuos siguen una distribución normal, al estar alineado los residuos con la línea de referencia; sin embargo, se evidencia que los residuos tienen una distribución diferente a lo normal debido a sus colas largas, lo que indicaría que puede tomar valores un poco extremos y generar problemas en las estimaciones del modelo. Para corregir este aspecto, sería necesario aplicar transformaciones o considerar otros modelos alternativos, en donde se revisen aquellos valores atípicos para mejorar el ajuste del modelo.

En cuanto a la multicolinealidad, no se evidencia ningún problema en cuanto a las diferentes variables; lo anterior debido a los parámetros establecidos para esta revisión:

Si VIF <= 5, no hay problemas de multicolinealdiad. Si VIF está entre 5 y 10: hay problemas moderados de multicolinealidad. Si VIF > 10: hay problemas de multcolinealidad graves.

# Gráfico de colinealidad

colinealidad <- cor(vivienda_zona_norte_casa[, c("preciom", "areaconst", "habitaciones", "banios", "estrato")])

plot(colinealidad)

# install.packages("lmtest")
library(lmtest)
## Cargando paquete requerido: zoo
## 
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
# Gráfico no autorrelación

lmtest:: dwtest(modelo_casas)
## 
##  Durbin-Watson test
## 
## data:  modelo_casas
## DW = 1.9904, p-value = 0.4567
## alternative hypothesis: true autocorrelation is greater than 0

Teniendo en cuenta el test de Durbin - Watson, en donde DW es menor que 2 y el p-value es menor que 0,5, se evidencia una correlación positiva del modelo, conllevando a posibles errores en las estimaciones.

Sería necesario realizar ajustes al modelo con el fin de mejorar la estimación.

1.5. Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.

req <- data.frame(areaconst=c(200,200),estrato=c(4,5),habitaciones=c(4,4),parqueaderos=c(1,1),banios=c(2,2))
req
##   areaconst estrato habitaciones parqueaderos banios
## 1       200       4            4            1      2
## 2       200       5            4            1      2
predict(modelo_casas,req)
##        1        2 
## 311.7232 392.8981

Con las dos alternativas anteriores, en donde la diferencia es el estrato, la predicción para la vivienda en estrato 4 es de 311.7232; en cambio, para el estrato 5 se cuenta con una predicción de 392.8981.

1.6. Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra 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.

casas_fil <- filter(vivienda_zona_norte_casa, areaconst >= 200, parqueaderos >= 1, banios >= 2, habitaciones >= 4, estrato >=4, preciom <= 350)
observaciones_total<- nrow(casas_fil)
print(observaciones_total)
## [1] 39
primeras_5_casa <- head(casas_fil, 5)
print(primeras_5_casa)
## # A tibble: 5 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  7471 Zona N…     2       4     330       240            2      4            4
## 2  4210 Zona N…     1       5     350       200            3      3            4
## 3  4267 Zona N…     1       5     335       202            1      4            5
## 4  4800 Zona N…     1       5     340       250            2      4            4
## 5  4209 Zona N…     2       5     350       300            3      5            6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
mapa_vivienda1 <- leaflet(primeras_5_casa) %>%
  addTiles() %>%
  addMarkers(lng = ~longitud, lat = ~latitud, popup = ~paste("ID:", id, "<br>Barrio:", barrio))
mapa_vivienda1

Con el mapa anterior, se obtienen las 5 viviendas que cumplen con la solicitud por parte del cliente, para lo cual, se evidencia que el primer registro, de la vivienda ubicada en el barrio acopi, no cumple con el requisito de estar ubicada en la zona norte, esto debido a posibles problemas en la recorrecion de datos relacionados a la latidud; de esta forma se hace necesario excluir del modelo a esta vivienda y obtener nuevamente las 5 que cumplan con lo requerido.

id_a_modificar <- 7471
fila_a_modificar <- which(vivienda_zona_norte_casa$id == id_a_modificar)
vivienda_zona_norte_casa[fila_a_modificar, "zona"] <- "Zona Sur"
print(vivienda_zona_norte_casa[fila_a_modificar,])
## # A tibble: 1 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  7471 Zona S…     2       4     330       240            2      4            4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
casas_filtradas <- filter(vivienda_zona_norte_casa, zona == "Zona Norte", areaconst >= 200, parqueaderos >= 1, banios >= 2, habitaciones >= 4, estrato >=4, preciom <= 350)
observaciones_total<- nrow(casas_filtradas)
print(observaciones_total)
## [1] 38
primeras_5_casa <- head(casas_filtradas, 5)
print(primeras_5_casa)
## # A tibble: 5 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  4210 Zona N…     1       5     350       200            3      3            4
## 2  4267 Zona N…     1       5     335       202            1      4            5
## 3  4800 Zona N…     1       5     340       250            2      4            4
## 4  4209 Zona N…     2       5     350       300            3      5            6
## 5  4422 Zona N…     2       5     350       240            2      3            6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
mapa_vivienda1 <- leaflet(primeras_5_casa) %>%
  addTiles() %>%
  addMarkers(lng = ~longitud, lat = ~latitud, popup = ~paste("ID:", id, "<br>Barrio:", barrio))
mapa_vivienda1

En cuanto a la revisión de las diferentes viviendas, se evidencia que cualquiera puede llegar a ser una buena opción para el cliente; sin embargo, teniendo en cuenta el precio y el área construída, la opción 4 podría ser la más adecuada, en donde se cuenta con una área de 300 metros y seis habitaciones.

2. Realice los pasos del 1 al 6. Para la segunda solicitud que tiene un crédito pre-aprobado por valor de $850 millones.

2.1. Para apartamento: 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?).

# Se utiliza el dataset de vivienda corregido en donde se tiene la limpieza e imputación de los datos.

vivienda_zona_sur_apartamento <- vivienda_corregido %>% filter(zona == "Zona Sur" & tipo == "Apartamento")

head(vivienda_zona_sur_apartamento, 5)
## # A tibble: 5 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  5098 Zona S…     5       4     290        96            1      2            3
## 2   698 Zona S…     2       3      78        40            1      1            2
## 3  8199 Zona S…     2       6     875       194            2      5            3
## 4  1241 Zona S…     5       3     135       117            1      2            3
## 5  5370 Zona S…     5       3     135        78            1      1            3
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
table(vivienda_zona_sur_apartamento$tipo)
## 
## Apartamento 
##        2787
table(vivienda_zona_sur_apartamento$estrato)
## 
##    3    4    5    6 
##  201 1091 1033  462

Se encuentran 2.787 viviendas tipo apartamento ubicadas en la zona sur, distribuídos en estratos del 3 al 6; en donde en su mayoría corresponden a los estratos 4 y 5, con 1.091 y 1.033 viviendas respectivamente.

2.1.1. Gráfico de correlación

# Realizamos gráfico de correlación

library(corrplot)

correlacion_matriz_apartamento <- cor(vivienda_zona_sur_apartamento[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")])

corrplot(correlacion_matriz_apartamento, method = "color", addCoef.col = "orange")

tabla_correlacion_apartamento <- as.table(correlacion_matriz_apartamento)
print(tabla_correlacion_apartamento)
##                preciom areaconst   estrato    banios habitaciones
## preciom      1.0000000 0.7579955 0.6727067 0.7288773    0.3454114
## areaconst    0.7579955 1.0000000 0.4815593 0.6748986    0.4522052
## estrato      0.6727067 0.4815593 1.0000000 0.5721277    0.2133095
## banios       0.7288773 0.6748986 0.5721277 1.0000000    0.5199280
## habitaciones 0.3454114 0.4522052 0.2133095 0.5199280    1.0000000

No se cuenta con una correlación alta entre la diferentes variables (>85%); por lo tanto no es necesario eliminar variables.

Las variables con más correlación, son:

Precio y área construída: 75.8% Precio y baño: 72.9% Área construída y baños: 67.5%

table(vivienda_zona_sur_apartamento$barrio)
## 
##                         acopi                    aguablanca 
##                             1                             1 
##                     aguacatal                       alameda 
##                             1                             2 
##                 alf?Crez real                  alferez real 
##                             4                             1 
##                   alto jordan            altos de guadalupe 
##                             1                             1 
##                      arboleda             belisario caicedo 
##                             1                             2 
##                   bella suiza           bloques del limonar 
##                             5                             1 
##                     bochalema           bosques del limonar 
##                            33                            12 
##            brisas del limonar                  buenos aires 
##                             1                             4 
##                        caldas                          cali 
##                             1                             3 
##                     calicanto                      cambulos 
##                             2                             2 
##                   camino real                     campestre 
##                            15                             1 
##                   canasgordas                  canaveralejo 
##                             5                             9 
##                   canaverales       canaverales los samanes 
##                            19                             1 
##                         caney                caney especial 
##                            58                             1 
##                         capri                   cataya real 
##                            43                             1 
##               cerro cristales                    champagnat 
##                             1                             1 
##                   ciudad 2000              ciudad bochalema 
##                            19                            48 
##                  ciudad capri                 ciudad jardin 
##                             9                           227 
##           ciudad jardin pance              ciudad mel?Cndez 
##                             1                             1 
##               ciudad pacifica          ciudad universitaria 
##                             3                             1 
##            ciudadela comfandi            ciudadela melendez 
##                             2                             2 
##          ciudadela paso ancho               colinas del sur 
##                             3                             3 
##                    colseguros              colseguros andes 
##                            22                             1 
##                     cristales               cristobal colon 
##                             1                             2 
##               cuarto de legua                 departamental 
##                            30                            16 
##                      el caney                     el dorado 
##                           125                             6 
##               el gran limonar                     el guabal 
##                             3                             4 
##                    el ingenio                  el ingenio 3 
##                           128                             1 
##                  el ingenio i                 el ingenio ii 
##                            13                            10 
##                el ingenio iii                     el jordan 
##                            10                             1 
##                       el lido                    el limonar 
##                            33                            59 
##                    el refugio                 fuentes de la 
##                            77                             1 
##                  gran limonar                     guadalupe 
##                             8                            10 
##                       ingenio                   la alborada 
##                             1                             4 
##                    la cascada                      la flora 
##                             2                             1 
##                   la hacienda                      la luisa 
##                           109                             1 
##                      la selva                   las acacias 
##                             7                             1 
##                  las camelias                   las granjas 
##                             1                             7 
##                     las vegas                  los cambulos 
##                             1                            22 
##                los farallones             mayapan las vegas 
##                             2                            31 
##                     mel?Cndez                      melendez 
##                            19                            40 
##                    miraflores                   multicentro 
##                             1                            27 
##                       napoles                     normandia 
##                            13                             1 
##              nueva tequendama            oasis de pasoancho 
##                            36                             1 
##                   pampa linda                  panamericano 
##                            16                             2 
##                         pance           parcelaciones pance 
##                           205                            18 
##                     pasoancho                         ponce 
##                             5                             1 
##            prados del limonar               primero de mayo 
##                             4                            24 
##                quintas de don                       refugio 
##                            58                             1 
##                       samanes          samanes de guadalupe 
##                             1                             1 
##                     san bosco                  san fernando 
##                             2                            19 
##            san fernando nuevo            san fernando viejo 
##                             4                             5 
##                   san joaquin                   santa anita 
##                             1                            38 
##                   santa elena                  santa isabel 
##                             1                             7 
##                santa teresita                 santo domingo 
##                             6                             1 
##              sector aguacatal sector canaveralejo guadalupe 
##                             1                             2 
##                     seminario                      templete 
##                            22                             2 
##                    tequendama                unicentro cali 
##                            14                             1 
##       urbanizacion colseguros        urbanizacion gratamira 
##                             2                             1 
##    urbanizacion nueva granada         urbanizacion rio lili 
##                             1                             3 
##       urbanizacion tequendama                valle del lili 
##                             2                           839 
##                     versalles                 villa del sur 
##                             1                             1 
##                        vipasa                      zona sur 
##                             1                            32
# Hacemos el mapa para ver la distribución de las viviendas tipo apartamento en la zona sur

#install.packages("leaflet")
library(leaflet)

map_vivienda_apartamento <- leaflet(vivienda_zona_sur_apartamento) %>% 
  
  addTiles() %>% 
  addMarkers(lng = ~longitud, lat = ~latitud, popup = ~paste("ID:", id, "<br>Barrio:", barrio))

map_vivienda_apartamento
library(leaflet)
zona_general_apartamento <- vivienda_corregido %>%
  filter(zona != "Zona Sur" & tipo == "Apartamento")
mapa_general_apartamento <- leaflet() %>%
  addTiles()
mapa_general_apartamento <- mapa_general_apartamento %>%
  addCircleMarkers(data = vivienda_zona_sur_apartamento, lng = ~longitud, lat = ~latitud, color = "blue", popup = ~paste("ID:", id, "<br>Barrio:", barrio))
mapa_general_apartamento <- mapa_general_apartamento %>%
  addCircleMarkers(data = zona_general_apartamento, lng = ~longitud, lat = ~latitud, color = "red", popup = ~paste("ID:", id, "<br>Barrio:", barrio))

mapa_general_apartamento

Se muestran viviendas tipo apartamento en diferentes zonas de la ciudad de Cali, por lo tanto, sería adecuado realizar una corrección de las variables longitud o latitud.

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

# Grafico para el área construída

library(plotly)
library(ggplot2)

grafico_areaconst_apartamento <- plot_ly(vivienda_zona_sur_apartamento, x = ~ areaconst, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_areaconst_apartamento)

Se evidencian datos atípicos en la relación del área construída y el precio.

# Gráfico en función del estrato

grafico_estrato_apartamento <- plot_ly(vivienda_zona_sur_apartamento, x = ~ estrato, y = ~ preciom, type = "box")
grafico_estrato <- grafico_estrato %>%
  layout(title = "Relación entre precio y estrato", xaxis = list(title = "Estrato"), yaxis = list(title = "Precio"))

print(grafico_estrato_apartamento)

Se evidencia que entre el estrato aumenta, se tienen más datos atípicos, esto debido a que hay apartamentos con precios muy altos en comparación con el resto.

# Gráficos en función del número de baños

grafico_baños_apartamento <- plot_ly(vivienda_zona_sur_apartamento, x = ~ banios, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_baños_apartamento)

Se evidencia un leve patrón entre el número de baños y el precio de las viviendas.

# Gráficos en función al número de habitaciones

grafico_habitaciones_apartamento <- plot_ly(vivienda_zona_sur_apartamento, x = ~ habitaciones, y = ~ preciom, type = "scatter", mode = "markers", marker = list(size = 10, opacity = 0.6))

print(grafico_habitaciones_apartamento)

No se logra evidenciar una relacion clara entre el número de habitaciones y el precio promedio de las viviendas.

# Gráfico en función de la zona

grafico_zona_apartamento <- plot_ly(vivienda_zona_sur_apartamento, x = ~ zona, y = ~ preciom, type = "box")
grafico_zona_apartamento <- grafico_zona_apartamento %>%
  layout(title = "Relación entre precio y zona",
         xaxis = list(title = "Zona"), yaxis = list(title = "Precio"))

print(grafico_zona_apartamento)

Se evidencian precios atípicos en las viviendas tipo apartamento de la zona sur.

2.3. Para los apartamentos: 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 )), interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están 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).

library(MASS)
## 
## Adjuntando el paquete: 'MASS'
## The following object is masked from 'package:plotly':
## 
##     select
## The following object is masked from 'package:dplyr':
## 
##     select
modelo_apartamento_sin_entrenamiento <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = vivienda_zona_sur_apartamento)

summary(modelo_apartamento_sin_entrenamiento)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = vivienda_zona_sur_apartamento)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1129.59   -37.74    -2.89    37.96   924.93 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -260.39880   13.14519 -19.809  < 2e-16 ***
## areaconst       1.33416    0.04901  27.221  < 2e-16 ***
## estrato        58.06534    2.67323  21.721  < 2e-16 ***
## habitaciones  -19.45520    3.44917  -5.641 1.87e-08 ***
## parqueaderos   74.24566    3.69204  20.110  < 2e-16 ***
## banios         46.44876    3.04467  15.256  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 92.81 on 2781 degrees of freedom
## Multiple R-squared:  0.7657, Adjusted R-squared:  0.7652 
## F-statistic:  1817 on 5 and 2781 DF,  p-value: < 2.2e-16
# Obtener los residuos del modelo
residuos <- residuals(modelo_apartamento_sin_entrenamiento)

# Calcular el RMSE
mse <- mean(residuos^2)   # Error Cuadrático Medio
rmse <- sqrt(mse)        # Raíz del Error Cuadrático Medio

# Calcular el MAE
mae <- mean(abs(residuos))  # Error Absoluto Medio

# Mostrar los resultados
cat("RMSE:", rmse, "\n")
## RMSE: 92.70855
cat("MAE:", mae, "\n")
## MAE: 55.92704

Con el fin de poder tener entrenamiento y test (70% y 30%) respectivamente, se realiza lo siguiente:

set.seed(444)

n <- nrow(vivienda_zona_sur_apartamento)

train_size <- round(0.7 * n)
train_index <- sample(1:n, train_size)

train_data_apartamento <- vivienda_zona_sur_apartamento[train_index, ]

test_data_apartamento <- vivienda_zona_sur_apartamento[-train_index, ]
# Se crea modelo

modelo_apartamento <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data_apartamento)

summary(modelo_apartamento)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = train_data_apartamento)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1093.63   -38.24    -2.09    38.45   926.48 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -255.95065   16.04846 -15.949   <2e-16 ***
## areaconst       1.29091    0.05607  23.023   <2e-16 ***
## estrato        55.45913    3.26036  17.010   <2e-16 ***
## habitaciones  -21.09334    4.14694  -5.086    4e-07 ***
## parqueaderos   76.22798    4.55098  16.750   <2e-16 ***
## banios         51.73836    3.60237  14.362   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 93.43 on 1945 degrees of freedom
## Multiple R-squared:  0.7626, Adjusted R-squared:  0.762 
## F-statistic:  1249 on 5 and 1945 DF,  p-value: < 2.2e-16
# Obtener los residuos del modelo
residuos <- residuals(modelo_apartamento)

# Calcular el RMSE
mse <- mean(residuos^2)   # Error Cuadrático Medio
rmse <- sqrt(mse)        # Raíz del Error Cuadrático Medio

# Calcular el MAE
mae <- mean(abs(residuos))  # Error Absoluto Medio

# Mostrar los resultados
cat("RMSE:", rmse, "\n")
## RMSE: 93.28868
cat("MAE:", mae, "\n")
## MAE: 55.80861

Similar al modelo de las viviendas tipo casa de la zona norte, sucede en las de tipo apartamento de la zona sur, ya que el modelo cuenta con una mejor eficiencia en términos de R-squared cuando no se entrena el modelo, en comparación de cuando si.

El coeficiente en el área construída es significativo, lo que indica que por cada metro adicional en área construída, se tiene un incremento en el valor de 1.33.

En cuanto al estrato, también se tiene un incremento de 58.065 por cada incremento en el estrato.

en la variable de habitaciones se evidencia una disminución en el precio por cada habitación adicional, lo que podría resultar inusual para efectos del modelo, lo cual sería necesario ejercer una revisión adicional. Con el dato actual, se podría pensar que aquellas viviendas con precio más alto, tieneden a tener menos habitacion de gran tamaño, en donde se involucra la combinación de: precio alto, pocas habitaciones y gran tamaño; ocasionando datos atípicos que genera inconsistencia al modelo. Para el caso de los apartamentos de precio promedio bajo, estaría conformado de alto número de habitaciones de menor tamaño.

En el parqueadero se tiene un aumento de 74.25 por cada parqueadero adicional.

En cuanto al baño, se tiene un coeficiente positivo y significativo: por cada baño adicional se tiene un aumento en el precio de 46.45.

R-squared y R ajustado, tiene un valor similar, correpondiente a 0.7657 y 0.7652 respectivamente, lo que indica que el modelo explica aproximadamente el 76.57% de la variación en los precios de los apartamentos.

2.4. Para los apartamentos: 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).

# Creamos el gráfico de la linealidad

plot(modelo_apartamento$fitted.values, modelo_apartamento$residuals, xlab = "Valores ajustados", ylab = "Residuales")

abline(h = 0, col = "red", lwd = 2)

El gráfico presenta una distribución aleatoria de los residuos, lo que indica que es algo positivo y que el modelo no requiere de ajuste.

# Gráfico para la homocedasticidad

plot(modelo_apartamento$fitted.values, sqrt(abs(modelo_apartamento$residuals)), xlab = "Valores ajustados", ylab = "Raíz de los valores residuales absolutos")

abline(h = 0, col = "red", lwd = 2)

Con el gráfico anterior, se evidencia una homocedasticidad, ya que no se visualiza un patrón de los residuos absolutos.

# Gráfico para la independencia de los errores

plot(modelo_apartamento$residuals, type = "l")

abline(h = 0, col = "red", lwd = 2)

No se evidencia patrones evidentes, por lo tanto se considera el supuesto de independencia del modelo, ya que cuenta con residuos aleatorios.

# Gráfico de normalidad de los errores

qqnorm(modelo_apartamento$residuals)

qqline(modelo_apartamento$residuals, col = 2)

# Multicolinealidad

# install.packages("car")
library(car)

valores_vif_apartamento <- vif(modelo_apartamento)
print(valores_vif_apartamento)
##    areaconst      estrato habitaciones parqueaderos       banios 
##     1.991380     1.652219     1.417214     1.784140     2.467238
# install.packages("GGally")
library(GGally)

# Extraer las variables predictoras del modelo
variables <- model.matrix(modelo_apartamento)[, -1]  # Elimina la columna del intercepto

# Convertir en data frame para generar el gráfico
variables_df_apartamento <- as.data.frame(variables)

ggpairs(variables_df_apartamento)

Los residuos siguen una distribución normal, al contar una alineación con la línea de referencia; sin embargo, similar a las viviendas tipo casa, se evidencia que los residuos tienen una distribución diferente a lo normal debido a sus colas largas, lo que indicaría que puede tomar valores un poco extremos y generar problemas en las estimaciones del modelo. Para corregir este aspecto, sería necesario aplicar transformaciones o considerar otros modelos alternativos, en donde se revisen aquellos valores atípicos para mejorar el ajuste del modelo.

No se evidencia multicolinealidad en las diferentes variables seleccionadas.

# Gráfico de colinealidad

colinealidad_apartamento <- cor(vivienda_zona_sur_apartamento[, c("preciom", "areaconst", "habitaciones", "banios", "estrato")])

plot(colinealidad_apartamento)

# install.packages("lmtest")
library(lmtest)

# Gráfico no autorrelación

lmtest:: dwtest(modelo_apartamento)
## 
##  Durbin-Watson test
## 
## data:  modelo_apartamento
## DW = 1.9208, p-value = 0.03987
## alternative hypothesis: true autocorrelation is greater than 0

Teniendo en cuenta el test de Durbin - Watson, en donde DW es menor que 2 y el p-value es menor que 0,5, se evidencia una correlación positiva del modelo, conllevando a posibles errores en las estimaciones.

Para tener un modelo con una alta predicción, sería necesario realizar más entrenamiento y posiblemente cambiar el modelo, ya que este no logra predicir con alta exactitud el precio de las viviendas.

2.5. Para los apartamentos: Con el modelo identificado debe predecir el precio de la vivienda con las características de la primera solicitud.

req <- data.frame(areaconst=c(300,300),estrato=c(5,6),habitaciones=c(5,5),parqueaderos=c(3,3),banios=c(3,3))
req
##   areaconst estrato habitaciones parqueaderos banios
## 1       300       5            5            3      3
## 2       300       6            5            3      3
predict(modelo_apartamento,req)
##        1        2 
## 687.0496 742.5087

Con las dos alternativas anteriores, en donde la diferencia es el estrato, la predicción para la vivienda tipo apartamento en estrato 5 es de 687.0496; en cambio, para el estrato 6 se cuenta con una predicción de 742.5087.

2.6. Para los apartamentos: Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 2. Tenga encuentra 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.

apartamento_fil <- filter(vivienda_zona_sur_apartamento, areaconst >= 300, parqueaderos >= 3, banios >= 3, habitaciones >= 5, estrato >=5, preciom <= 850)
observaciones_total_apartamento <- nrow(apartamento_fil)
print(observaciones_total_apartamento)
## [1] 2
primeros_5_apartamentos <- head(apartamento_fil, 5)
print(primeros_5_apartamentos)
## # A tibble: 2 × 14
##      id zona     piso estrato preciom areaconst parqueaderos banios habitaciones
##   <dbl> <chr>   <dbl>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
## 1  7182 Zona S…     3       5     730       573            3      8            5
## 2  7512 Zona S…     3       5     670       300            3      5            6
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## #   origen <chr>
mapa_vivienda1_apartamento <- leaflet(primeros_5_apartamentos) %>%
  addTiles() %>%
  addMarkers(lng = ~longitud, lat = ~latitud, popup = ~paste("ID:", id, "<br>Barrio:", barrio))
mapa_vivienda1_apartamento

Teniendo en cuenta las dos opciones anteriores, cualquiera de ellas sería viable para el cliente; sin embargo, teniendo en cuenta el valor limite de 850, la opción 1 podría ser la mejor opción, debido a que tiene una área de construcción grande y tiene menos habitaciones que la segunda opción, lo que hace que puede llegar a tener mayor espacio o mejor zonas comunes para compartir en familia.

Conclusiones

En cuanto a la solicitud por parte de la empresa, relacionada en obtener una oferta de viviendas tipo casa en la zona norte y tipo apartamento en la zona sur, de acuerdo con algunas características importantes, se puede concluir que se cuentan con las 5 viviendas tipo casa; sin embargo, para la oferta de los apartamentos solo hay 2 que cumplan con las características solicitadas.

Por otro lado, es importante mencionar que si bien el modelo no cuenta con una alta eficiencia en su pronósticos, debido a factores de datos atípicos relacionados a características propias de las viviendas, como es el caso de aquellas muy pocas con valores muy altos, o la incorrecta clasificación de los datos en la variable latitud que genera inconsistencias en la ubicación de la viviendas, lo que hace necesario poder tener una revisión detallada de los datos para poder tener un mejoramiento en el modelo.

Recomendaciones

Se hace necesario realizar un proceso de validación y revisión de los datos de la base, ya que se presentan inconsistencias relacionadas a la ubicación de las viviendas, así como una gran cantidad de datos nulos en las variable piso y parqueadero, también aparecen datos en cero en las variables baños y habitaciones. Lo anterior podría generar una mejora en la eficiencia de la predicción de los modelos.