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