El objetivo de este trabajo es realizar un modelo de regresión lineal múltiple utilizando la base de datos de viviendas de la ciudad de Cali, con el fin de analizar dos solicitudes realizadas por una compañía internacional que desea ubicar a dos de sus empleados y sus familias en la ciudad.
Para la primera solicitud se busca una casa en la zona norte, con un área construida de 200 m², 1 parqueadero, 2 baños, 4 habitaciones, estrato 4 o 5 y un crédito preaprobado máximo de 350 millones de pesos. Para la segunda solicitud se busca un apartamento en la zona sur, con un área construida de 300 m², 3 parqueaderos, 3 baños, 5 habitaciones, estrato 5 o 6 y un crédito preaprobado máximo de 850 millones de pesos. A partir de estas características se realizarán los filtros correspondientes, el análisis de los datos y la estimación de los modelos, para finalmente identificar potenciales ofertas que se ajusten a las necesidades y al presupuesto de cada solicitud.
A continuación, se carga la base de datos con la que se trabajará durante el desarrollo de esta actividad.
library(paqueteMODELOS)
library(dplyr)
library(leaflet)
library("plotly")
library(factoextra)
data("vivienda")
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>
La base de datos cuenta con 8322 registros y 13 variables que describen diferentes características de las viviendas registradas. A continuación, se presentan las variables junto con su clasificación según el tipo de dato.
Antes de realizar el filtro, se verifican los valores registrados en las variables tipo y zona ( varibles cualitativas), con el fin de identificar posibles errores de digitación o diferencias en la forma en que fueron registradas las categorías.Adicionalmente, se verifica la presencia de valores faltantes (NA) en las variables tipo y zona antes de realizar el filtro.
unique(vivienda$tipo)
## [1] "Casa" "Apartamento" NA
unique(vivienda$zona)
## [1] "Zona Oriente" "Zona Sur" "Zona Norte" "Zona Oeste" "Zona Centro"
## [6] NA
sum(is.na(vivienda$tipo))
## [1] 3
sum(is.na(vivienda$zona))
## [1] 3
vivienda %>%
filter(is.na(tipo))
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 NA <NA> <NA> NA NA NA NA NA NA
## 2 NA <NA> <NA> NA NA NA NA NA NA
## 3 NA <NA> <NA> NA 330 NA NA NA NA
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
vivienda %>%
filter(is.na(zona))
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 NA <NA> <NA> NA NA NA NA NA NA
## 2 NA <NA> <NA> NA NA NA NA NA NA
## 3 NA <NA> <NA> NA 330 NA NA NA NA
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
#Calculemos la proporcion que representan esos 3 datos respecto a la base completa
n_total <- nrow(vivienda)
n_incompletos <- sum(is.na(vivienda$tipo) & is.na(vivienda$zona))
porcentaje <- (n_incompletos / n_total) * 100
n_total
## [1] 8322
n_incompletos
## [1] 3
porcentaje
## [1] 0.03604903
Se identificaron 3 registros con valores faltantes en las variables tipo y zona. Estos registros corresponden a las mismas observaciones y presentan además ausencia de información en la mayoría de las variables. Representan aproximadamente el 0.03 % del total de la base, por lo cual se decide excluirlos del análisis. Se procede entonces a crear una base vivienda limpia.
# Nuestra base de datos completa depurada ahora es vivienda_limpia
vivienda_limpia <- vivienda %>%
filter(!(is.na(tipo) & is.na(zona)))
sum(is.na(vivienda_limpia$tipo))
## [1] 0
sum(is.na(vivienda_limpia$zona))
## [1] 0
unique(vivienda_limpia$tipo)
## [1] "Casa" "Apartamento"
unique(vivienda_limpia$zona)
## [1] "Zona Oriente" "Zona Sur" "Zona Norte" "Zona Oeste" "Zona Centro"
length(unique(vivienda_limpia$id))
## [1] 8319
# Verificamos registros vivienda limpia
nrow(vivienda_limpia)
## [1] 8319
Al eliminar estos tres registros, tenemos entonces un total de 8319 observaciones, sin valores faltantes y con categorias unicas para las variables de tipo y zona. Finalmente, se revisan los valores mínimos y máximos de las variables numéricas y para la varibale estrato ( que es cualitativa ordinal, registrada como cuantitativa), con el fin de identificar posibles valores fuera de los rangos esperados.
summary(vivienda_limpia)
## id zona piso estrato
## Min. : 1 Length:8319 Length:8319 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
##
## 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 :1602
## habitaciones tipo barrio longitud
## Min. : 0.000 Length:8319 Length:8319 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
##
## latitud
## Min. :3.333
## 1st Qu.:3.381
## Median :3.416
## Mean :3.418
## 3rd Qu.:3.452
## Max. :3.498
##
Al revisar la base de datos se identificaron algunos aspectos que deben tenerse en cuenta antes de realizar el análisis:
Para el caso 1 se busca una vivienda tipo casa, con un area construida de 200 metros cuadrados, 1 parqueadero, 2 baños, 4 habitaciones, estrato 4 o 5, en zona norte y con un credito pre aprobado de 350 millones.
Para analizar la primera solicitud se realiza un filtro de la base, seleccionando únicamente las viviendas de tipo Casa ubicadas en la Zona Norte de la ciudad.
base1 <- vivienda_limpia %>%
filter(tipo == "Casa",
zona == "Zona Norte")
head(base1, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4057 Zona N… 02 6 750 445 NA 7 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
dim(base1)
## [1] 722 13
En resumen, para la base 1, correspondiente a viviendas tipo Casa ubicadas en la Zona Norte de la ciudad, se obtienen 722 registros y 13 variables. Como verificación del filtro realizado, se presentan los tres primeros registros de la base 1.
A continuación, se presenta la ubicación geográfica de las viviendas correspondientes a la base 1, utilizando las coordenadas de latitud y longitud disponibles en la base de datos. Esto permite verificar visualmente la distribución de las ofertas registradas como pertenecientes a la Zona Norte de Cali. Adicionalmente, se identificaron los valores mínimo y máximo de la variable latitud ( circulos rojos), ya que esta coordenada permite observar la variación de los puntos en dirección norte-sur. Estos dos puntos se resaltan en el mapa con el fin de observar qué tan alejadas se encuentran las viviendas ubicadas en los extremos de la base 1.
extremos_latitud <- base1 %>%
filter(
latitud == min(latitud, na.rm = TRUE) |
latitud == max(latitud, na.rm = TRUE)
)
extremos_latitud
## # A tibble: 2 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 655 Zona N… <NA> 3 280 148 2 4 4
## 2 6906 Zona N… 02 6 1080 650 2 0 7
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
leaflet(base1) %>%
addTiles() %>%
# Todas las viviendas
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 2
) %>%
# Puntos con latitud mínima y máxima
addCircleMarkers(
data = extremos_latitud,
lng = ~longitud,
lat = ~latitud,
radius = 7,
color = "red",
fillColor = "red",
fillOpacity = 1,
popup = ~paste(
"Latitud:", latitud,
"<br>Longitud:", longitud
)
)
Al observar el mapa se puede ver que una gran cantidad de los puntos se encuentran localizados en la Zona Norte de Cali; sin embargo, también se observa una proporción considerable de puntos ubicados en otras zonas de la ciudad, a pesar de estar clasificados en la base como Zona Norte.al comparar los extremos de la latitud, se tiene un valor máximo de 3.49584 y un mínimo de 3.33308, presentando una variación aproximada de 0.16°. Esta diferencia permite observar qué tan dispersos se encuentran los registros en dirección norte-sur. La presencia de puntos en otras zonas podría deberse a errores en las coordenadas de algunos registros o a una asignación incorrecta de la zona al momento de construir la base de datos. Debido a esto, es necesario realizar un filtro de la base 1, puesto que el objetivo del caso 1 es una vivienda localizada en la Zona Norte.
Para realizar un correcto filtrado de la base, se procede a realizar un analisis de conglomerados con el fin de observar agrupaciones de vivienda de acuerdo a las variables longitud y latitud. Pirmeramente se realizara el metodo del codo con el fin de elegir correctamente la cantidad de clusters y posteriomente se procede a realizar el PCA.
fviz_nbclust(
base1[, c("longitud", "latitud")],
kmeans,
method = "wss",
k.max = 10
) +
labs(
title = "Método del codo",
x = "Número de conglomerados",
y = "Suma de cuadrados dentro de los grupos"
)
El método del codo sugiere, en primera instancia, trabajar con 2 conglomerados. Esta selección resulta conveniente para el propósito del análisis, ya que se busca identificar un grupo que represente las viviendas realmente ubicadas en la Zona Norte y separar aquellas observaciones que, aunque se encuentran categorizadas como Zona Norte en la base de datos, presentan una ubicación geográfica diferente.
set.seed(123)
conglomerados1 <- kmeans(
base1[, c("longitud", "latitud")],
centers = 2
)
base1$conglomerado <- as.factor(conglomerados1$cluster)
table(base1$conglomerado)
##
## 1 2
## 606 116
Al representar los conglomerados sobre el mapa con la ubicación geográfica de las observaciones de la base 1, se evidencia una clara separación entre los registros realmente ubicados en la Zona Norte de Cali y aquellos que se encuentran en otras zonas de la ciudad. De esta manera, es posible identificar el conglomerado que debe conservarse y excluir las observaciones que no cumplen con la ubicación requerida para el caso 1.
Por esta razón, se procede a excluir de la base 1 los registros pertenecientes al conglomerado que no corresponde geográficamente a la Zona Norte de Cali. De esta forma, la base queda conformada únicamente por las viviendas ubicadas en el sector de interés, y sobre esta nueva base se continuará con la revisión de calidad de los datos, el análisis exploratorio y la estimación del modelo de regresión.
## [1] 606 13
Con base en los problemas de calidad de los datos detectados al inicio del documento, se procederá a evaluar si existen valores faltantes (NA) en las variables de interés de la base 1, así como datos inconsistentes en el número de baños y habitaciones.
# Valores faltantes
sum(is.na(base1$preciom))
## [1] 0
sum(is.na(base1$areaconst))
## [1] 0
sum(is.na(base1$estrato))
## [1] 0
sum(is.na(base1$parqueaderos))
## [1] 209
sum(is.na(base1$banios))
## [1] 0
sum(is.na(base1$habitaciones))
## [1] 0
# Valores iguales a cero
sum(base1$parqueaderos == 0, na.rm = TRUE)
## [1] 0
sum(base1$banios == 0, na.rm = TRUE)
## [1] 7
sum(base1$habitaciones == 0, na.rm = TRUE)
## [1] 11
Al analizar las variables predictoras de la base 1, se identificaron algunos aspectos relacionados con la calidad de los datos:
Las demás variables predictoras analizadas no presentan valores faltantes. Estos resultados serán tenidos en cuenta para definir posteriormente el tratamiento más adecuado antes de estimar el modelo de regresión.
Para los 209 valores faltantes encontrados en la variable parqueaderos se plantean tres posibles opciones para su tratamiento:
Para elegir entre la opción 1 y la opción 2, se procede a calcular la mediana del número de parqueaderos para cada estrato presente en la base 1 y, adicionalmente, la mediana general de toda la base. Esto con el fin de verificar qué tanto varía la mediana entre los diferentes estratos y determinar cuál de los dos métodos de imputación resulta más adecuado.
base1 %>%
group_by(estrato) %>%
summarise(
n = n(),
NA_parqueaderos = sum(is.na(parqueaderos)),
mediana_parqueaderos = median(parqueaderos, na.rm = TRUE)
)
## # A tibble: 4 × 4
## estrato n NA_parqueaderos mediana_parqueaderos
## <dbl> <int> <int> <dbl>
## 1 3 210 136 1
## 2 4 129 36 2
## 3 5 233 29 2
## 4 6 34 8 2.5
median(base1$parqueaderos, na.rm = TRUE)
## [1] 2
Los resultados muestran que la mediana del número de parqueaderos presenta algunas diferencias entre los estratos. Para el estrato 3 se obtiene una mediana de 1 parqueadero, para los estratos 4 y 5 una mediana de 2 y para el estrato 6 una mediana de 2.5. Por otro lado, la mediana general de la base 1 es de 2 parqueaderos. Debido a estas diferencias, se considera más adecuado realizar la imputación de los valores faltantes utilizando la mediana correspondiente a cada estrato, ya que de esta manera se tienen en cuenta las características de cada grup.Para el estrato 6 se obtuvo una mediana de 2.5 parqueaderos. Debido a que el número de parqueaderos es una variable discreta y no puede tomar este valor en la práctica, se revisó la distribución de los datos del estrato 6. El valor más frecuente corresponde a 3 parqueaderos, por lo que se utilizará este valor para imputar los registros faltantes de este estrato.
base1 <- base1 %>%
mutate(
parqueaderos = case_when(
is.na(parqueaderos) & estrato == 3 ~ 1,
is.na(parqueaderos) & estrato == 4 ~ 2,
is.na(parqueaderos) & estrato == 5 ~ 2,
is.na(parqueaderos) & estrato == 6 ~ 3,
TRUE ~ parqueaderos
)
)
## id zona piso estrato preciom areaconst
## 0 0 279 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 0 0 0 0 0 0
## latitud
## 0
Después de realizar la imputación de la variable parqueaderos, se verificó nuevamente la presencia de valores faltantes. Se encontraron 279 valores NA correspondientes a la variable piso; sin embargo, debido a que esta variable no será utilizada como predictora en el modelo, estos valores no requieren ningún tratamiento adicional para el presente análisis.
AAhora bien, se debe resolver la situación de los registros que presentan 0 baños y/o 0 habitaciones, ya que estos valores no son usuales para una casa. Para analizar estos casos, se revisará cuántos registros presentan 0 baños y 0 habitaciones al mismo tiempo, cuántos tienen 0 baños pero sí tienen habitaciones y cuántos tienen 0 habitaciones pero sí tienen baños.
# 0 baños y 0 habitaciones
sum(base1$banios == 0 & base1$habitaciones == 0)
## [1] 6
# 0 baños pero sí tiene habitaciones
sum(base1$banios == 0 & base1$habitaciones > 0)
## [1] 1
# 0 habitaciones pero sí tiene baños
sum(base1$habitaciones == 0 & base1$banios > 0)
## [1] 5
registros_cero <- sum(
base1$banios == 0 | base1$habitaciones == 0
)
registros_cero
## [1] 12
porcentaje_cero <- registros_cero / nrow(base1) * 100
porcentaje_cero
## [1] 1.980198
En total se identificaron 12 registros con valores iguales a 0 en baños y/o habitaciones. Estos representan aproximadamente el 2 % de las 606 observaciones de la base 1. Debido a que estos valores no son usuales para una vivienda y corresponden a una proporción pequeña de la base, se decide eliminar estos registros, considerando que su exclusión no representa una pérdida considerable de información.
base1 <- base1 %>%
filter(banios > 0,
habitaciones > 0)
sum(base1$banios == 0)
## [1] 0
sum(base1$habitaciones == 0)
## [1] 0
dim(base1)
## [1] 594 13
summary(base1)
## id zona piso estrato
## Min. : 88.0 Length:594 Length:594 Min. :3.000
## 1st Qu.: 627.5 Class :character Class :character 1st Qu.:3.000
## Median :1644.5 Mode :character Mode :character Median :4.000
## Mean :2206.1 Mean :4.158
## 3rd Qu.:3587.5 3rd Qu.:5.000
## Max. :8319.0 Max. :6.000
## preciom areaconst parqueaderos banios
## Min. : 89.0 Min. : 30.0 Min. : 1.000 Min. : 1.000
## 1st Qu.: 240.8 1st Qu.: 138.5 1st Qu.: 1.000 1st Qu.: 2.000
## Median : 380.0 Median : 238.5 Median : 2.000 Median : 3.000
## Mean : 429.6 Mean : 260.5 Mean : 1.909 Mean : 3.557
## 3rd Qu.: 547.5 3rd Qu.: 336.8 3rd Qu.: 2.000 3rd Qu.: 4.000
## Max. :1940.0 Max. :1440.0 Max. :10.000 Max. :10.000
## habitaciones tipo barrio longitud
## Min. : 1.00 Length:594 Length:594 Min. :-76.59
## 1st Qu.: 3.00 Class :character Class :character 1st Qu.:-76.53
## Median : 4.00 Mode :character Mode :character Median :-76.52
## Mean : 4.67 Mean :-76.51
## 3rd Qu.: 5.00 3rd Qu.:-76.50
## Max. :10.00 Max. :-76.48
## latitud
## Min. :3.437
## 1st Qu.:3.465
## Median :3.473
## Mean :3.472
## 3rd Qu.:3.483
## Max. :3.496
A continuación, se realiza un análisis exploratorio de la base 1 con el fin de observar la relación entre el precio de las viviendas y algunas de sus principales características, como el área construida, estrato, número de baños, habitaciones y parqueaderos.
A continuación se grafica el histograma de la variable objetivo precio con el fin de observar graficamente sy distribución. Asi mismo, se realiza la prueba Shapiro-Wilk con el fin de evaluar si los datos se comportan como una distribucion normal. Se plantea para esta: Ho: los datos siguen una distibucion normal H1: los datos no siguen una distribucion normal
plot_ly(
base1,
x = ~preciom,
type = "histogram"
)
shapiro.test(base1$preciom)
##
## Shapiro-Wilk normality test
##
## data: base1$preciom
## W = 0.85179, p-value < 2.2e-16
La base 1 está conformada por 594 viviendas, cuyos precios varían entre 89 y 1.940 millones de pesos. La mediana del precio es de 380 millones y la media de 429,6 millones. Se observa que la mayor parte de las viviendas se concentra en los rangos de precios más bajos y medios, mientras que existen algunas viviendas con precios considerablemente altos. Esto genera una distribución asimétrica hacia la derecha, lo cual también se observa en el histograma.La prueba de Shapiro-Wilk obtuvo un valor p menor a 0,05, por lo que rechazamos H0 y se concluye que la variable precio no presenta una distribución normal.
Debido a que la prueba de Shapiro-Wilk indicó que la variable precio no presenta una distribución normal (p < 0.05), para el análisis de las relaciones entre las variables se utilizará el coeficiente de correlación de Kendall, siguiendo un enfoque no paramétrico.
Se analiza la relacion entre el precio de las viviendas y el area construida.
plot_ly(base1,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "markers")
cor(base1$areaconst, base1$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.6566387
Se observa una relación positiva entre el área construida y el precio de las viviendas. Debido a que la variable precio no presenta una distribución normal, se utilizó el coeficiente de correlación de Kendall, obteniendo un valor de 0.65. Este resultado indica una relación positiva relativamente fuerte, es decir, en general las viviendas con mayor área construida tienden a presentar precios más altos. También se observan algunos datos que se alejan del comportamiento general. También se observan dos datos que se alejan bastante del comportamiento general.
Debido a que la variable estrato es cualitativa ordinal, realizar un gráfico de dispersión no es lo más adecuado. Para observar si existe alguna relación entre el estrato y el precio, se realizarán diagramas de cajas para cada uno de los estratos, comparándolos con el precio de las viviendas.
plot_ly(base1,
x = ~factor(estrato),
y = ~preciom,
type = "box")
cor(base1$estrato, base1$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.5665472
Se observa una relación positiva moderada entre el estrato y el precio. La correlación de Kendall fue de 0.56, indicando que, en general, a medida que aumenta el estrato también tiende a aumentar el precio de las viviendas. También se observan algunos valores que se alejan considerablemente del comportamiento general.
Debido a que el número de baños es una variable cuantitativa discreta, se realiza un diagrama de cajas para observar cómo cambia el precio de las viviendas según el número de baños.
plot_ly(base1,
x = ~factor(banios),
y = ~preciom,
type = "box")
cor(base1$banios, base1$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.5238742
table(base1$banios)
##
## 1 2 3 4 5 6 7 8 9 10
## 15 145 156 140 83 37 6 9 1 2
Aunque existe bastante variación en los precios, se observa una tendencia de aumento del precio a medida que aumenta el número de baños. La correlación de Kendall fue de 0.52, lo que indica una relación positiva moderada. También se observa que existen muy pocos registros de viviendas con 9 y 10 baños, con solo 1 y 2 viviendas respectivamente.
plot_ly(base1,
x = ~factor(habitaciones),
y = ~preciom,
type = "box")
cor(base1$habitaciones, base1$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.3569346
Se observa que la relación entre el número de habitaciones y el precio no es muy fuerte. La correlación de Kendall fue de 0.35, lo que indica una relación positiva baja.
plot_ly(base1,
x = ~factor(parqueaderos),
y = ~preciom,
type = "box")
cor(base1$parqueaderos, base1$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.4638594
table(base1$parqueaderos)
##
## 1 2 3 4 5 6 7 8 9 10
## 279 205 50 34 11 7 5 1 1 1
La correlación de Kendall fue de 0.46, lo que indica una relación positiva moderada entre el número de parqueaderos y el precio. Sin embargo, en el gráfico no se observa una tendencia tan clara, especialmente para las viviendas con mayor número de parqueaderos, debido a que existen muy pocos registros en estas categorías. La mayoría de las viviendas cuenta con 1 o 2 parqueaderos.
Antes de realizar el modelo de regresión lineal múltiple, se analizará la correlación entre las variables predictoras con el fin de identificar si existen relaciones fuertes entre ellas que puedan afectar el modelo.
library(corrplot)
cor_predictoras <- cor(
base1[, c(
"areaconst",
"estrato",
"habitaciones",
"parqueaderos",
"banios"
)],
method = "kendall",
use = "complete.obs"
)
corrplot(
cor_predictoras,
method = "color",
type = "upper",
addCoef.col = "black",
tl.col = "black",
tl.srt = 45,
diag = FALSE
)
Entre las variables predictoras se espera encontrar correlaciones que no
sean demasiado altas, con el fin de evitar posibles problemas de
multicolinealidad. En este caso, las correlaciones obtenidas mediante
Kendall son bajas o moderadas. Las mayores se presentan entre estrato y
parqueaderos, y entre habitaciones y baños, ambas con un valor de 0.51.
Estos valores no parecen lo suficientemente altos como para considerar
inicialmente la eliminación de alguna variable.
A continuación, se presenta el modelo de regresión lineal múltiple correspondiente a la base 1. Para evaluar los resultados del modelo se tendrá en cuenta la significancia individual de las variables mediante la prueba t y la significancia global del modelo mediante la prueba F.
Para la prueba t de cada coeficiente se plantean las siguientes hipótesis: H₀: βⱼ = 0: la variable no es estadísticamente significativa en el modelo. H₁: βⱼ ≠ 0: la variable es estadísticamente significativa en el modelo.
Para la prueba F global se plantean las siguientes hipótesis: H₀: β₁ = β₂ = β₃ = β₄ = β₅ = 0: ninguna de las variables predictoras es significativa en el modelo. H₁: al menos un βⱼ ≠ 0: al menos una de las variables predictoras es significativa en el modelo.
Para ambas pruebas se utilizará un nivel de significancia de 0.05. Cuando el p-value sea menor a 0.05 se rechazará la hipótesis nula.
modelo1 <- lm(
preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = base1
)
summary(modelo1)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -908.45 -67.78 -14.11 36.82 1076.70
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -221.13740 33.88020 -6.527 1.45e-10 ***
## areaconst 0.75136 0.04929 15.245 < 2e-16 ***
## estrato 68.86834 8.28013 8.317 6.28e-16 ***
## habitaciones 9.59644 5.04910 1.901 0.057840 .
## parqueaderos 21.04821 5.79935 3.629 0.000309 ***
## banios 23.50442 6.32594 3.716 0.000222 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 153.6 on 588 degrees of freedom
## Multiple R-squared: 0.6614, Adjusted R-squared: 0.6585
## F-statistic: 229.7 on 5 and 588 DF, p-value: < 2.2e-16
Con base en los resultados anteriores se puede concluir que:
confint(modelo1, level = 0.95)
## 2.5 % 97.5 %
## (Intercept) -287.6783255 -154.5964660
## areaconst 0.6545573 0.8481562
## estrato 52.6061152 85.1305564
## habitaciones -0.3200344 19.5129055
## parqueaderos 9.6582491 32.4381611
## banios 11.0802326 35.9286066
Los intervalos de confianza al 95 % muestran que los coeficientes de área construida, estrato, parqueaderos y baños no incluyen el valor cero, lo cual es consistente con su significancia estadística. Por el contrario, el intervalo correspondiente a habitaciones se encuentra entre -0.32 y 19.51 e incluye el valor cero, lo que coincide con el resultado de la prueba t, donde esta variable presentó un p-value ligeramente superior a 0.05.
Una vez estimado el modelo de regresión lineal multiple, se validaran los supuestos con el fin de determinar si el modelo es confiable y si existen problremas que puedan afectar sus resultados.
Se evalua el supuesto de normalidad de los residuos mediante el gráfico Q-Q, el cual permite comparar la distribucion de los residuos del modelo con una distribucion nromal. Se espera que los residuos se acerquen a la linea recta, para confirmar su distribucion normal.
qqnorm(
residuals(modelo1),
main = "Gráfico Q-Q de los residuos"
)
qqline(
residuals(modelo1),
col = "red",
lwd = 2
)
En el gráfico Q-Q se observa que gran parte de los residuos ubicados en la zona central se encuentran sobre o cerca de la línea de referencia. Sin embargo, hacia los extremos se presentan desviaciones considerables, especialmente en la cola derecha. Por lo tanto, se puede decir que los residuos presentan un comportamiento cercano a la normalidad en la parte central, pero existen desviaciones importantes en las colas, por lo que el supuesto de normalidad no se cumple completamente.
A continuación, se evalúa el supuesto de homocedasticidad, con el fin de verificar si la variabilidad de los residuos se mantiene aproximadamente constante a lo largo de los valores estimados por el modelo. Para este supuesto se busca residuos distribuuidos alrededor de 0 con una dispersion mas o menso constante alrededor.
plot(
modelo1$fitted.values,
modelo1$residuals,
xlab = "Valores ajustados",
ylab = "Residuos",
main = "Residuos vs valores ajustados"
)
abline(h = 0, col = "red", lwd = 2)
En el gráfico de residuos vs. valores ajustados se observa que, para los valores ajustados más bajos, los residuos se encuentran relativamente concentrados alrededor de cero. Sin embargo, a medida que aumentan los valores ajustados, la dispersión de los residuos también aumenta, formando una especie de abanico. Esto indica que la variabilidad de los errores no se mantiene constante, por lo que el supuesto de homocedasticidad no se cumple completamente y se presenta evidencia de heterocedasticidad.
Para evaluar la indepenencia de los residuos utilizamos el estadistico Durbin-Watson. Se espera un valor de este cercano a 2 y un p value mayor que 0.05.
library(lmtest)
dwtest(modelo1)
##
## Durbin-Watson test
##
## data: modelo1
## DW = 1.6887, p-value = 6.016e-05
## alternative hypothesis: true autocorrelation is greater than 0
Para evaluar la independencia de los residuos se realizó la prueba de Durbin-Watson. Se obtuvo un estadístico DW de 1.6887 y un p-value de 6.016e-05. Debido a que el p-value es menor a 0.05, existe evidencia de autocorrelación positiva entre los residuos, por lo que el supuesto de independencia no se cumple.
Finalmente, se evalúa la posible presencia de multicolinealidad entre las variables predictoras mediante el Factor de Inflación de la Varianza (VIF). Este análisis permite determinar si alguna de las variables predictoras presenta una relación demasiado fuerte con las demás variables incluidas en el model. Se espera un valor menor o igual a 5.
library(car)
vif(modelo1)
## areaconst estrato habitaciones parqueaderos banios
## 1.758052 1.619879 1.921552 1.347995 2.173224
Los valores VIF obtenidos para todas las variables predictoras son inferiores a 5, encontrándose entre 1.35 y 2.17. Por lo tanto, no se evidencian problemas importantes de multicolinealidad en el modelo. Este resultado es consistente con la matriz de correlaciones, en la cual no se observaron correlaciones excesivamente altas entre las variables predictoras.
La linealidad sirve para comprobar que la relacion entre las variables predictoras y el precio de las viviendas puede representarse mediante una relacion lineal. Este supuesto es posbile evaluarlo con el grafico Residuals vs Fitted, evaluando que la linea roja sea mas o menos horizontal alrededor de 0
plot(modelo1, which = 1)
La línea de tendencia presenta una ligera curvatura, especialmente en
los valores ajustados intermedios, aunque no se observa un patrón curvo
fuertemente definido. Por lo tanto, se considera que el supuesto de
linealidad se cumple de manera aproximada, aunque se presentan pequeñas
desviaciones que indican que algunas relaciones podrían no ser
completamente lineales.
Adicionalmente, se realiza un análisis de observaciones extremas con el fin de identificar registros que puedan presentar comportamientos atípicos o tener una influencia importante sobre el modelo. Para esto se analizarán los residuos studentizados, el leverage y la distancia de Cook.
Esta sección responde a que viviendas tienen un error de prediccion muy grande. Para este análisis se consideran como posibles observaciones atípicas aquellas cuyos residuos studentizados presentan un valor absoluto superior a 3.
res_student <- rstudent(modelo1)
unname(which(abs(res_student) > 3))
## [1] 112 129 156 238 307 308 370 391 401 402 418 434 474 481 510 576
Se identificaron 16 observaciones con residuos studentizados superiores a 3 en valor absoluto. Estas representan aproximadamente el 2.7 % de las 594 viviendas analizadas, por lo que se consideran posibles observaciones atípicas que deben ser revisadas.
El leverage permite identificar observaciones que presentan combinaciones poco comunes en las variables predictoras, es decir, viviendas cuyas características de área, estrato, habitaciones, parqueaderos o baños se alejan considerablemente del comportamiento general de los datos..
leverage <- hatvalues(modelo1)
limite_leverage <- 2 * length(coef(modelo1)) / nrow(base1)
limite_leverage
## [1] 0.02020202
unname(which(leverage > limite_leverage))
## [1] 14 95 105 106 112 120 136 138 139 141 142 143 147 161 169 179 211 220 238
## [20] 246 251 268 306 308 311 317 321 333 334 339 349 356 358 370 373 391 392 401
## [39] 402 408 422 438 449 451 452 453 474 477 479 488 505 510 561 576 594
sum(leverage > limite_leverage)
## [1] 55
sum(leverage > limite_leverage) / nrow(base1) * 100
## [1] 9.259259
Para identificar observaciones con combinaciones poco comunes en las variables predictoras, se analiza el leverage. Como criterio de referencia se utiliza 2p/n, donde pcorresponde al número de parámetros del modelo y n al número de observaciones. Para este modelo se obtiene un límite de 0.0202; por lo tanto, las observaciones con leverage superior a este valor serán consideradas como observaciones de leverage potencialmente alto.Se identificaron 55 observaciones con valores de leverage superiores al límite de 0.0202, lo que representa aproximadamente el 9.26 % de las 594 viviendas analizadas. Estas observaciones presentan combinaciones de características predictoras que se alejan del comportamiento general de los datos, por lo que pueden considerarse observaciones con leverage potencialmente alto. Sin embargo, esto no significa necesariamente que tengan una influencia importante sobre el modelo.
Finalmente, se utiliza la distancia de Cook para identificar observaciones que pueden tener una influencia importante sobre el modelo. A diferencia del leverage, que identifica observaciones con características predictoras poco comunes, la distancia de Cook permite evaluar qué tanto puede influir una observación sobre los resultados del modelo. Se utiliza como referencia el criterio Di>4/n, por lo que Di>0.006
cook <- cooks.distance(modelo1)
limite_cook <- 4 / nrow(base1)
limite_cook
## [1] 0.006734007
unname(which(cook > limite_cook))
## [1] 60 61 62 105 112 129 131 138 156 161 190 232 238 306 307 308 311 349 370
## [20] 391 401 402 408 418 425 428 434 437 438 474 481 486 510 563 576 594
sum(cook > limite_cook)
## [1] 36
sum(cook > limite_cook) / nrow(base1) * 100
## [1] 6.060606
plot(
cook,
type = "h",
ylab = "Distancia de Cook",
main = "Distancia de Cook"
)
abline(
h = limite_cook,
col = "red",
lty = 2
)
Se identificaron 36 observaciones que superan este valor, equivalentes aproximadamente al 6.06 % de las 594 viviendas analizadas. Estas observaciones se consideran potencialmente influyentes, ya que pueden tener un efecto importante sobre el ajuste del modelo. Adicionalmente, en el gráfico se destaca una observación con una distancia de Cook considerablemente mayor que las demás (510), por lo que sería conveniente revisarla de manera particular.Misma observacion obtuvo un leverage y error de prediccion alto.
base1[510, ]
## # A tibble: 1 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 534 Zona N… 03 3 370 1440 1 4 10
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
res_student[510]
## 510
## -6.638335
leverage[510]
## 510
## 0.1476063
cook[510]
## 510
## 1.18504
Al revisar las observaciones identificadas, se destaca particularmente la observación 510, correspondiente a una vivienda con 1.440 m² de área construida, 10 habitaciones y un precio de 370 millones de pesos. Estas características se alejan considerablemente del comportamiento general de la base, especialmente por presentar un área construida muy elevada y un precio relativamente bajo para dicho tamaño. Esto ayuda a explicar la alta influencia que presenta esta observación sobre el modelo.
La validación de los supuestos muestra que el modelo presenta algunas limitaciones. La normalidad de los residuos se cumple principalmente en la parte central, pero presenta desviaciones en los extremos. También se observa heterocedasticidad y la prueba de Durbin-Watson muestra que existen problemas con la independencia de los residuos. Por otro lado, no se observan problemas importantes de multicolinealidad y la linealidad se cumple de manera aproximada.
Adicionalmente, al revisar las observaciones extremas se encontraron 16 registros con residuos studentizados altos (2.7 %), 55 con leverage alto (9.26 %) y 36 observaciones potencialmente influyentes según la distancia de Cook (6.06 %).
Como posibles mejoras se podría:
La solicitud para la vivienda 1 tiene las siguientes carcaterísticas - Área construida: 200 m² - Parqueaderos: 1 - Baños: 2 - Habitaciones: 4 - Estrato: 4 o 5 - Presupuesto: 350 millones
Como hay 2 posibilidades para estrato, se realizaran 2 predicciones: una suponiendo estrato 4 y otra estrato 5.
Creamos las caracteristicas de la vivienda 1, teniendo estrato 4 y estrato 5
vivienda1_pred <- data.frame(
areaconst = c(200, 200),
estrato = c(4, 5),
habitaciones = c(4, 4),
parqueaderos = c(1, 1),
banios = c(2, 2)
)
vivienda1_pred
## areaconst estrato habitaciones parqueaderos banios
## 1 200 4 4 1 2
## 2 200 5 4 1 2
predict(modelo1, newdata = vivienda1_pred)
## 1 2
## 311.0501 379.9184
Para una vivienda estrato 4 con las características solicitadas, el modelo estima un precio aproximado de 311.05 millones de pesos, valor que se encuentra dentro del crédito preaprobado de 350 millones. Para una vivienda estrato 5, el precio estimado aumenta aproximadamente a 379.92 millones de pesos, superando el presupuesto disponible. Por lo tanto, según las estimaciones del modelo, sería más viable encontrar una vivienda estrato 4 que cumpla con las características solicitadas y el presupuesto establecido.
Como estamos prediciendo el precio de una vivienda en particular, es posible obtener los intervalos de predicción para ambos estratos.
predict(
modelo1,
newdata = vivienda1_pred,
interval = "prediction",
level = 0.95
)
## fit lwr upr
## 1 311.0501 8.795091 613.3051
## 2 379.9184 76.916883 682.9199
Los intervalos de predicción obtenidos son bastante amplios. Para una vivienda estrato 4, aunque el precio estimado es de 311.05 millones de pesos, el intervalo al 95 % se encuentra entre 8.80 y 613.31 millones. Para estrato 5, el precio estimado es de 379.92 millones, con un intervalo entre 76.92 y 682.92 millones. Esta amplitud muestra que existe una alta incertidumbre al estimar el precio de una vivienda individual, por lo que las predicciones puntuales deben tomarse como valores de referencia y no como precios exactos.Por esta razón, para realizar la recomendación final también se tendrán en cuenta los precios reales y las características de las viviendas disponibles en la base de datos.
Teniendo en cuenta que el crédito preaprobado para la Vivienda 1 es de máximo 350 millones de pesos, se realiza una búsqueda dentro de la base 1 de viviendas que se ajusten al presupuesto y presenten características cercanas a las solicitadas. Se consideran viviendas de estrato 4 y 5, de acuerdo con los requerimientos del cliente, y se establece inicialmente un rango de área construida entre 180 y 220 m², tomando como referencia los 200 m² solicitados.
ofertas1 <- base1 |>
dplyr::filter(
estrato %in% c(4, 5),
preciom <= 350,
areaconst >= 180,
areaconst <= 220
)
nrow(ofertas1)
## [1] 15
ofertas1
## # A tibble: 15 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4511 Zona … <NA> 4 275 190 2 2 3
## 2 4210 Zona … 01 5 350 200 3 3 4
## 3 4267 Zona … 01 5 335 202 1 4 5
## 4 4313 Zona … 01 5 335 220 1 3 3
## 5 852 Zona … <NA> 5 340 208 2 6 4
## 6 1270 Zona … <NA> 5 350 203 2 2 5
## 7 1343 Zona … 02 5 320 200 2 4 4
## 8 1352 Zona … <NA> 5 350 190 1 3 3
## 9 1506 Zona … <NA> 5 340 180 2 4 4
## 10 1144 Zona … <NA> 4 320 200 2 4 4
## 11 1163 Zona … <NA> 5 350 216 2 2 4
## 12 515 Zona … <NA> 4 280 180 2 3 4
## 13 1151 Zona … <NA> 5 320 210 2 3 5
## 14 1887 Zona … 01 5 340 203 2 3 4
## 15 1914 Zona … 02 5 300 205 2 5 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
El resultado de este filtro arroja 15 registros que cumplen con el presupuesto máximo establecido, pertenecen a los estratos 4 o 5 y presentan un área construida entre 180 y 220 m². A partir de estas opciones se evaluarán las demás características solicitadas, como el número de habitaciones, baños y parqueaderos, con el fin de seleccionar las viviendas que mejor se ajusten a la solicitud.
De las 15 viviendas obtenidas inicialmente, se seleccionaron aquellas que cuentan con 4 habitaciones, al menos 2 baños y mínimo 1 parqueadero. Posteriormente, estas ofertas se ordenaron según qué tan cercana se encuentra su área construida a los 200 m² solicitados y, como criterio adicional, se tuvo en cuenta el menor precio. A partir de este procedimiento se seleccionaron las 5 ofertas que mejor se ajustan a las características requeridas.
mejores_ofertas <- ofertas1 |>
dplyr::filter(
habitaciones == 4,
banios >= 2,
parqueaderos >= 1
)
mejores_ofertas
## # A tibble: 8 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 4210 Zona N… 01 5 350 200 3 3 4
## 2 852 Zona N… <NA> 5 340 208 2 6 4
## 3 1343 Zona N… 02 5 320 200 2 4 4
## 4 1506 Zona N… <NA> 5 340 180 2 4 4
## 5 1144 Zona N… <NA> 4 320 200 2 4 4
## 6 1163 Zona N… <NA> 5 350 216 2 2 4
## 7 515 Zona N… <NA> 4 280 180 2 3 4
## 8 1887 Zona N… 01 5 340 203 2 3 4
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
mejores_ofertas <- mejores_ofertas |>
dplyr::mutate(
diferencia_area = abs(areaconst - 200)
) |>
dplyr::arrange(
diferencia_area,
preciom
)
mejores_ofertas
## # A tibble: 8 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1343 Zona N… 02 5 320 200 2 4 4
## 2 1144 Zona N… <NA> 4 320 200 2 4 4
## 3 4210 Zona N… 01 5 350 200 3 3 4
## 4 1887 Zona N… 01 5 340 203 2 3 4
## 5 852 Zona N… <NA> 5 340 208 2 6 4
## 6 1163 Zona N… <NA> 5 350 216 2 2 4
## 7 515 Zona N… <NA> 4 280 180 2 3 4
## 8 1506 Zona N… <NA> 5 340 180 2 4 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # diferencia_area <dbl>
top5_ofertas <- mejores_ofertas |>
dplyr::slice_head(n = 5)
top5_ofertas
## # A tibble: 5 × 14
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1343 Zona N… 02 5 320 200 2 4 4
## 2 1144 Zona N… <NA> 4 320 200 2 4 4
## 3 4210 Zona N… 01 5 350 200 3 3 4
## 4 1887 Zona N… 01 5 340 203 2 3 4
## 5 852 Zona N… <NA> 5 340 208 2 6 4
## # ℹ 5 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>,
## # diferencia_area <dbl>
Entre las cinco ofertas seleccionadas, las viviendas con ID 1343 y 1144 son las que mejor se ajustan a las características solicitadas. Ambas tienen un precio de 320 millones de pesos, 200 m² de área construida, 4 habitaciones, 4 baños y 2 parqueaderos. La principal diferencia se encuentra en el estrato, ya que la vivienda 1343 es estrato 5 y la 1144 es estrato 4. Adicionalmente, la vivienda 1343 registra piso 2, por lo que la elección entre ambas podría depender de las preferencias del cliente. En términos de las características solicitadas y del presupuesto, ambas representan buenas opciones.
El caso 2 corresponde a un apartamento en Zona Sur, 300 m², 3 parqueaderos, 3 baños, 5 habitaciones, estrato 5 o 6 y presupuesto máximo de 850 millones. Se sigue el mismo procedimiento utilizado en el caso 1, pero con las características solicitadas para este caso.
Se realiza el mismo filtro para caso 1, pero cambiamos la condiciones de tipo de vivienda: apartamento y la ubicacion en Zona Sur.
base2 <- vivienda_limpia %>%
filter(tipo == "Apartamento",
zona == "Zona Sur")
head(base2, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5098 Zona S… 05 4 290 96 1 2 3
## 2 698 Zona S… 02 3 78 40 1 1 2
## 3 8199 Zona S… <NA> 6 875 194 2 5 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
dim(base2)
## [1] 2787 13
Para base 2, correspondiente a viviendas tipo Apartamento ubicados en la Zona Sur de la ciudad, se obtienen 2787 registros y 13 variables. Como verificación del filtro realizado, se presentan los tres primeros registros de la base 2.
De manera similar que en el caso 1, se presenta la ubicación geográfica de las viviendas correspondientes a la base 2, utilizando las coordenadas de latitud y longitud disponibles en la base de datos. con este mapa es posible tener una visualización de como estan distribuidaslas ofertas de apartamentos registrados como pertenecientes a la Zona sur de Cali. Adicionalmente, se identificaron los valores mínimo y máximo de la variable latitud ( circulos rojos), ya que esta coordenada permite observar la variación de los puntos en dirección norte-sur. Estos dos puntos se resaltan en el mapa con el fin de observar qué tan alejadas se encuentran las viviendas ubicadas en los extremos de la base 2.
extremos_latitud <- base2 %>%
filter(
latitud == min(latitud, na.rm = TRUE) |
latitud == max(latitud, na.rm = TRUE)
)
extremos_latitud
## # A tibble: 2 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 221 Zona S… 04 5 360 99 1 3 2
## 2 5008 Zona S… 11 6 635 133 3 4 4
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
leaflet(base2) %>%
addTiles() %>%
# Todas las viviendas
addCircleMarkers(
lng = ~longitud,
lat = ~latitud,
radius = 2
) %>%
# Puntos con latitud mínima y máxima
addCircleMarkers(
data = extremos_latitud,
lng = ~longitud,
lat = ~latitud,
radius = 7,
color = "red",
fillColor = "red",
fillOpacity = 1,
popup = ~paste(
"Latitud:", latitud,
"<br>Longitud:", longitud
)
)
Como sucedio en el caso 1, gran cantidad de los puntos se encuentran localizados en la Zona Sur de Cali; sin embargo, también se observa una proporción considerable de puntos ubicados en otras zonas de la ciudad, a pesar de estar clasificados en la base como Zona Sur.Al comparar los extremos de la latitud , se tiene un valor máximo de 3.49684 y un mínimo de 3.33367, presentando una variación aproximada de 0.16°.Es evidente que esta diferencia se ve reflejada en la dispersión que presentan los los registros en el mapa. La presencia de puntos en otras zonas podría deberse a errores en las coordenadas de algunos registros o a una asignación incorrecta de la zona al momento de construir la base de datos. Por ello se procede a realizar un filtro a la base 2 para cumplir con el objetivo de la solicitud del caso 2.
Para realizar un correcto filtrado de la base, se procede a realizar un analisis de conglomerados con el fin de observar agrupaciones de vivienda de acuerdo a las variables longitud y latitud. Pirmeramente se realizara el metodo del codo con el fin de elegir correctamente la cantidad de clusters y posteriomente se procede a realizar el PCA.
fviz_nbclust(
base2[, c("longitud", "latitud")],
kmeans,
method = "wss",
k.max = 10
) +
labs(
title = "Método del codo",
x = "Número de conglomerados",
y = "Suma de cuadrados dentro de los grupos"
)
De manera similar a caso 1, el método del codo sugiere, en primera instancia, trabajar con 2 conglomerados. Esta selección resulta conveniente para el propósito del análisis, ya que se busca identificar un grupo que represente las viviendas realmente ubicadas en la Zona Sur y separar aquellas observaciones que, aunque se encuentran categorizadas como Zona Sur en la base de datos, presentan una ubicación geográfica diferente.
set.seed(123)
conglomerados2 <- kmeans(
base2[, c("longitud", "latitud")],
centers = 2
)
base2$conglomerado <- as.factor(conglomerados2$cluster)
table(base2$conglomerado)
##
## 1 2
## 1845 942
Representando los conglomerados obtenidos del PCA con los registros categorizados como apartamento en Zona Sur, se evidencia una clara separación entre los registros realmente ubicados en esta zona y aquellos que se encuentran en otras zonas de la ciudad. De esta manera, es posible identificar el conglomerado que debe conservarse ( para este caso el conlgomerado 1 en rojo) y excluir las observaciones que no cumplen con la ubicación requerida para el caso 2.
Por esta razón, se procede a excluir de la base 2 los registros pertenecientes al conglomerado que no corresponde geográficamente a la Zona Sur de Cali. De esta forma, la base queda conformada únicamente por las viviendas ubicadas en el sector de interés, y sobre esta nueva base se continuará con la revisión de calidad de los datos, el análisis exploratorio y la estimación del modelo de regresión.
## [1] 1845 13
Como se analizó al inicio del documento, se procederá a evaluar si existen valores faltantes (NA) en las variables de interés de la base 2, así como datos inconsistentes en el número de baños y habitaciones.
# Valores faltantes
sum(is.na(base2$preciom))
## [1] 0
sum(is.na(base2$areaconst))
## [1] 0
sum(is.na(base2$estrato))
## [1] 0
sum(is.na(base2$parqueaderos))
## [1] 239
sum(is.na(base2$banios))
## [1] 0
sum(is.na(base2$habitaciones))
## [1] 0
# Valores iguales a cero
sum(base2$parqueaderos == 0, na.rm = TRUE)
## [1] 0
sum(base2$banios == 0, na.rm = TRUE)
## [1] 3
sum(base2$habitaciones == 0, na.rm = TRUE)
## [1] 4
Al analizar las variables predictoras de la base 2, se identificaron algunos aspectos relacionados con la calidad de los datos:
Las demás variables predictoras analizadas no presentan valores faltantes. Estos resultados serán tenidos en cuenta para definir posteriormente el tratamiento más adecuado antes de estimar el modelo de regresión.
De manera similar que en el caso 1, para el tratamiento de los 239 valores faltantes de la variable parqueadero, se plantean estas opciones:
Para elegir entre la opción 1 y la opción 2, se procede a calcular la mediana del número de parqueaderos para cada estrato presente en la base 2 y, adicionalmente, la mediana general de toda la base. Esto con el fin de verificar qué tanto varía la mediana entre los diferentes estratos y determinar cuál de los dos métodos de imputación resulta más adecuado.
base2 %>%
group_by(estrato) %>%
summarise(
n = n(),
NA_parqueaderos = sum(is.na(parqueaderos)),
mediana_parqueaderos = median(parqueaderos, na.rm = TRUE)
)
## # A tibble: 4 × 4
## estrato n NA_parqueaderos mediana_parqueaderos
## <dbl> <int> <int> <dbl>
## 1 3 82 32 1
## 2 4 651 169 1
## 3 5 713 28 1
## 4 6 399 10 2
median(base2$parqueaderos, na.rm = TRUE)
## [1] 1
Al calcular la mediana del número de parqueaderos por estrato, se observa que para los estratos 3, 4 y 5 la mediana es de 1 parqueadero, mientras que para el estrato 6 es de 2. La mediana general de la base es de 1 parqueadero. Debido a que se presenta una diferencia para el estrato 6, se decide realizar la imputación teniendo en cuenta el estrato de cada vivienda.
base2 <- base2 %>%
mutate(
parqueaderos = case_when(
is.na(parqueaderos) & estrato %in% c(3, 4, 5) ~ 1,
is.na(parqueaderos) & estrato == 6 ~ 2,
TRUE ~ parqueaderos
)
)
sum(is.na(base2$parqueaderos))
## [1] 0
## id zona piso estrato preciom areaconst
## 0 0 417 0 0 0
## parqueaderos banios habitaciones tipo barrio longitud
## 0 0 0 0 0 0
## latitud
## 0
Después de realizar la imputación de la variable parqueaderos, se verificó nuevamente la presencia de valores faltantes. Se encontraron 417 valores NA correspondientes a la variable piso; sin embargo, debido a que esta variable no será utilizada como predictora en el modelo, estos valores no requieren ningún tratamiento adicional para el presente análisis.
Como se identifico anteriormente, existen registros con 0 baños y/o 0 habitaciones, valores que no son usuales para un apartamento. Para analizar estos casos, se revisará cuántos registros presentan 0 baños y 0 habitaciones al mismo tiempo, cuántos tienen 0 baños pero sí tienen habitaciones y cuántos tienen 0 habitaciones pero sí tienen baños.
# 0 baños y 0 habitaciones
sum(base2$banios == 0 & base2$habitaciones == 0)
## [1] 2
# 0 baños pero sí tiene habitaciones
sum(base2$banios == 0 & base2$habitaciones > 0)
## [1] 1
# 0 habitaciones pero sí tiene baños
sum(base2$habitaciones == 0 & base2$banios > 0)
## [1] 2
registros_cero1 <- sum(
base2$banios == 0 | base2$habitaciones == 0
)
registros_cero1
## [1] 5
porcentaje_cero1 <- registros_cero / nrow(base2) * 100
porcentaje_cero1
## [1] 0.6504065
De acuerdo con los resultados, se identificaron 5 registros con valores iguales a 0 en baños y/o habitaciones. Estos representan aproximadamente el 0.65 % de las 1045 observaciones de la base 2. Debido a que estos valores no son usuales para una vivienda y corresponden a una proporción pequeña de la base, se decide eliminar estos registros, considerando que su exclusión no representa una pérdida considerable de información.
base2 <- base2 %>%
filter(banios > 0,
habitaciones > 0)
sum(base2$banios == 0)
## [1] 0
sum(base2$habitaciones == 0)
## [1] 0
dim(base2)
## [1] 1840 13
summary(base2)
## id zona piso estrato
## Min. : 793 Length:1840 Length:1840 Min. :3.000
## 1st Qu.:2133 Class :character Class :character 1st Qu.:4.000
## Median :3566 Mode :character Mode :character Median :5.000
## Mean :3862 Mean :4.775
## 3rd Qu.:5449 3rd Qu.:5.000
## Max. :8228 Max. :6.000
## preciom areaconst parqueaderos banios
## Min. : 78.0 Min. : 40.00 Min. :1.000 Min. :1.000
## 1st Qu.: 190.0 1st Qu.: 65.00 1st Qu.:1.000 1st Qu.:2.000
## Median : 260.0 Median : 85.00 Median :1.000 Median :2.000
## Mean : 318.3 Mean : 98.26 Mean :1.395 Mean :2.564
## 3rd Qu.: 350.0 3rd Qu.:110.00 3rd Qu.:2.000 3rd Qu.:3.000
## Max. :1750.0 Max. :932.00 Max. :4.000 Max. :7.000
## habitaciones tipo barrio longitud
## Min. :1.000 Length:1840 Length:1840 Min. :-76.56
## 1st Qu.:3.000 Class :character Class :character 1st Qu.:-76.54
## Median :3.000 Mode :character Mode :character Median :-76.53
## Mean :2.949 Mean :-76.53
## 3rd Qu.:3.000 3rd Qu.:-76.52
## Max. :6.000 Max. :-76.50
## latitud
## Min. :3.334
## 1st Qu.:3.366
## Median :3.372
## Mean :3.373
## 3rd Qu.:3.383
## Max. :3.398
En esta sección se realizara un analisis exploratorio de la base 2 limpia, con el fin de observar la relación entre el precio de las viviendas y algunas de sus principales características, como el área construida, estrato, número de baños, habitaciones y parqueaderos.
Al igual que para el caso 1, se grafica un histograma de la variable objetivo precio con el fin de observar graficamente su distribución. Asi mismo, se realiza la prueba Shapiro-Wilk con el fin de evaluar si los datos se comportan como una distribucion normal. Se plantea las dos siguientes hipotesis: Ho: los datos siguen una distibucion normal H1: los datos no siguen una distribucion normal
plot_ly(
base2,
x = ~preciom,
type = "histogram"
)
shapiro.test(base2$preciom)
##
## Shapiro-Wilk normality test
##
## data: base2$preciom
## W = 0.76366, p-value < 2.2e-16
En la base 2 se observa un comportamiento similar al encontrado en el caso 1. La distribución del precio no presenta una forma de campana simétrica, sino que se encuentra desviada hacia la derecha, debido a la presencia de algunos apartamentos con precios considerablemente altos. Los precios varían entre 78 y 1750 millones de pesos, con una mediana de 260 millones y una media de 318.3 millones. Esta diferencia entre la media y la mediana también refleja la influencia de los precios más altos sobre la distribución. La prueba de Shapiro-Wilk obtuvo un p-value menor a 0.05, por lo que se rechaza H₀ y se concluye que la variable precio no presenta una distribución normal
Debido a que la prueba de Shapiro-Wilk indicó que la variable precio no presenta una distribución normal (p < 0.05), para el análisis de las relaciones entre las variables se utilizará el coeficiente de correlación de Kendall, siguiendo un enfoque no paramétrico.
Se analiza la relacion entre el precio de las viviendas y el area construida.
plot_ly(base2,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "markers")
cor(base2$areaconst, base2$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.7545093
Se observa una relación positiva fuerte entre el área construida y el precio de los apartamentos. El coeficiente de correlación de Kendall obtenido fue de 0.75, un valor considerablemente mayor al obtenido en el caso 1. Esto indica que, para los apartamentos de la Zona Sur, a medida que aumenta el área construida, el precio tiende a aumentar de manera bastante clara. En el gráfico también se observan algunos registros que se alejan del comportamiento general.
Debido a que la variable estrato es cualitativa ordinal, realizar un gráfico de dispersión no es lo más adecuado. Para observar si existe alguna relación entre el estrato y el precio, se realizarán diagramas de cajas para cada uno de los estratos, comparándolos con el precio de las viviendas.
plot_ly(base2,
x = ~factor(estrato),
y = ~preciom,
type = "box")
cor(base2$estrato, base2$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.6515308
Se observa una relación positiva relativamente fuerte entre el estrato y el precio. La correlación de Kendall fue de 0.65, siendo mayor que la obtenida en el caso 1. Además, en el gráfico se evidencia claramente que, a medida que aumenta el estrato, también aumenta el precio de los apartamentos. Esta tendencia es especialmente marcada en los estratos 5 y 6, aunque también se observan algunos valores que se alejan considerablemente del comportamiento general.
Debido a que el número de baños es una variable cuantitativa discreta, se realiza un diagrama de cajas para observar cómo cambia el precio de las viviendas según el número de baños.
plot_ly(base2,
x = ~factor(banios),
y = ~preciom,
type = "box")
cor(base2$banios, base2$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.6154826
table(base2$banios)
##
## 1 2 3 4 5 6 7
## 61 1060 453 162 95 8 1
Se observa una relación positiva relativamente fuerte entre el número de baños y el precio de los apartamentos, con una correlación de Kendall de 0.62. En el gráfico se evidencia claramente que, en general, a medida que aumenta el número de baños también aumenta el precio de las viviendas. Sin embargo, se observan algunos valores que se alejan del comportamiento general. En particular, la categoría de 7 baños presenta un único registro, por lo que este valor debe interpretarse con precaución y posteriormente podría revisarse como una posible observación atípica.
plot_ly(base2,
x = ~factor(habitaciones),
y = ~preciom,
type = "box")
cor(base2$habitaciones, base2$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.3579184
Se observa una relación positiva relativamente baja entre el número de habitaciones y el precio de los apartamentos, con una correlación de Kendall de 0.36, comportamiento similar al observado en el caso 1. A partir de las 2 habitaciones se aprecia una tendencia más clara de aumento del precio a medida que aumenta el número de habitaciones. Sin embargo, para los apartamentos de 1 habitación este comportamiento no sigue la misma tendencia. Además, se observa una cantidad considerable de valores por fuera de los rangos típicos de cada grupo, especialmente en los apartamentos de 2, 3 y 4 habitaciones, lo que muestra una alta variabilidad en los precios. Por esta razón, la relación entre habitaciones y precio no es tan clara como la observada con el estrato o el número de baños.
plot_ly(base2,
x = ~factor(parqueaderos),
y = ~preciom,
type = "box")
cor(base2$parqueaderos, base2$preciom,
method = "kendall",
use = "complete.obs")
## [1] 0.5894334
table(base2$parqueaderos)
##
## 1 2 3 4
## 1229 523 60 28
Se observa una relación positiva relativamente fuerte entre el número de parqueaderos y el precio de los apartamentos, con una correlación de Kendall de 0.59. En el gráfico se evidencia claramente que, a medida que aumenta el número de parqueaderos, también tiende a aumentar el precio de las viviendas. Esta relación es más clara que la observada en el caso 1, tanto gráficamente como en el valor del coeficiente de correlación. También se observa que la mayoría de los apartamentos cuentan con un solo parqueadero y que la cantidad de registros disminuye considerablemente a medida que aumenta el número de parqueaderos, siendo muy pocos los apartamentos con 3 o 4. Aunque existen algunos valores por fuera de los rangos típicos, la tendencia general entre ambas variables se encuentra bastante definida.
Antes de realizar el modelo de regresión lineal múltiple, se analizará la correlación entre las variables predictoras con el fin de identificar si existen relaciones fuertes entre ellas que puedan afectar el modelo.
library(corrplot)
cor_predictoras <- cor(
base2[, c(
"areaconst",
"estrato",
"habitaciones",
"parqueaderos",
"banios"
)],
method = "kendall",
use = "complete.obs"
)
corrplot(
cor_predictoras,
method = "color",
type = "upper",
addCoef.col = "black",
tl.col = "black",
tl.srt = 45,
diag = FALSE
)
En el caso 2 se observan correlaciones entre las variables predictoras
más altas que las encontradas en el caso 1. La relación más alta se
presenta entre el área construida y el número de baños, con un valor de
0.66, seguida por parqueaderos y baños con 0.60, y área construida y
parqueaderos con 0.58. En general, estas relaciones tienen sentido, ya
que apartamentos de mayor tamaño tienden a contar con más baños y
parqueaderos. Aunque algunas correlaciones son relativamente altas,
ninguna parece lo suficientemente fuerte como para considerar
inicialmente la eliminación de alguna variable. Posteriormente se
verificará esto mediante el VIF del modelo de regresión.
A continuación, se presenta el modelo de regresión lineal múltiple correspondiente a la base 2. Para evaluar los resultados del modelo se tendrá en cuenta la significancia individual de las variables mediante la prueba t y la significancia global del modelo mediante la prueba F.
Para la prueba t de cada coeficiente se plantean las siguientes hipótesis: H₀: βⱼ = 0: la variable no es estadísticamente significativa en el modelo. H₁: βⱼ ≠ 0: la variable es estadísticamente significativa en el modelo.
Para la prueba F global se plantean las siguientes hipótesis: H₀: β₁ = β₂ = β₃ = β₄ = β₅ = 0: ninguna de las variables predictoras es significativa en el modelo. H₁: al menos un βⱼ ≠ 0: al menos una de las variables predictoras es significativa en el modelo.
Para ambas pruebas se utilizará un nivel de significancia de 0.05. Cuando el p-value sea menor a 0.05 se rechazará la hipótesis nula.
modelo2 <- lm(
preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
data = base2
)
summary(modelo2)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos +
## banios, data = base2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1261.89 -36.60 -3.33 36.94 874.18
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -258.96922 16.51466 -15.68 < 2e-16 ***
## areaconst 1.49085 0.06261 23.81 < 2e-16 ***
## estrato 50.09751 3.44438 14.54 < 2e-16 ***
## habitaciones -20.72863 4.36435 -4.75 2.2e-06 ***
## parqueaderos 84.76186 5.14210 16.48 < 2e-16 ***
## banios 52.44016 3.90091 13.44 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 93.56 on 1834 degrees of freedom
## Multiple R-squared: 0.7956, Adjusted R-squared: 0.795
## F-statistic: 1427 on 5 and 1834 DF, p-value: < 2.2e-16
Los resultados muestran que todas las variables predictoras incluidas en el modelo son estadísticamente significativas, ya que presentan valores p menores a 0.05. Esto representa una diferencia frente al caso 1, donde la variable habitaciones no resultó significativa. Manteniendo constantes las demás variables, por cada metro cuadrado adicional de área construida, el precio estimado aumenta aproximadamente 1.49 millones de pesos. Por cada nivel adicional de estrato aumenta aproximadamente 50.10 millones, por cada parqueadero adicional aumenta 84.76 millones y por cada baño adicional aumenta 52.44 millones. En el caso de las habitaciones se obtiene un coeficiente negativo de -20.73 millones. Aunque inicialmente se observó una relación positiva entre habitaciones y precio, al considerar al mismo tiempo las demás variables del modelo la relación cambia. Esto indica que, manteniendo constantes el área, estrato, parqueaderos y baños, una habitación adicional se relaciona con una disminución aproximada de 20.73 millones en el precio estimado. El modelo presenta un R² de 0.7956, lo que indica que aproximadamente el 79.56 % de la variación observada en el precio de los apartamentos puede ser explicada por las variables incluidas. El R² ajustado es de 0.795, muy cercano al R². Finalmente, la prueba F global presenta un p-value menor a 0.05, por lo que el modelo en conjunto es estadísticamente significativo.
Para analizar un poco más la situación de habitaciones, calculemos el valor VIF y el modelo de regresión tomando unicamente la variable habitaciones:
library(car)
vif(modelo2)
## areaconst estrato habitaciones parqueaderos banios
## 2.315361 1.733426 1.412797 2.198524 2.754664
modelo_hab_solo <- lm(preciom ~ habitaciones, data = base2)
summary(modelo_hab_solo)
##
## Call:
## lm(formula = preciom ~ habitaciones, data = base2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -560.13 -105.03 -44.98 40.07 1425.02
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -65.178 22.632 -2.88 0.00402 **
## habitaciones 130.052 7.524 17.29 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 191.7 on 1838 degrees of freedom
## Multiple R-squared: 0.1398, Adjusted R-squared: 0.1394
## F-statistic: 298.8 on 1 and 1838 DF, p-value: < 2.2e-16
Aunque inicialmente se observó una relación positiva entre el número de habitaciones y el precio, al analizar únicamente esta variable se obtiene un coeficiente positivo cercano a 130 millones de pesos. Sin embargo, al incluirla junto con las demás variables en el modelo múltiple, su coeficiente cambia a -20.73 millones. Esto se debe a que el modelo múltiple evalúa el efecto de las habitaciones manteniendo constantes características como el área, estrato, baños y parqueaderos. Por ejemplo, entre dos apartamentos con la misma área y características similares, aquel que tenga un mayor número de habitaciones puede presentar una mayor división del espacio. Adicionalmente, los valores VIF son inferiores a 5, por lo que este cambio de signo no parece estar relacionado con un problema importante de multicolinealidad.
confint(modelo2, level = 0.95)
## 2.5 % 97.5 %
## (Intercept) -291.358733 -226.579715
## areaconst 1.368059 1.613646
## estrato 43.342194 56.852827
## habitaciones -29.288243 -12.169011
## parqueaderos 74.676870 94.846857
## banios 44.789474 60.090844
Los intervalos de confianza al 95 % muestran que ninguno de los coeficientes de las variables predictoras incluye el valor cero, lo cual coincide con los resultados de las pruebas de significancia, donde todas las variables presentaron un p-value menor a 0.05. En el caso particular de habitaciones, el intervalo se encuentra entre -29.29 y -12.17, confirmando que su efecto dentro del modelo múltiple es negativo y estadísticamente significativo.
Una vez estimado el modelo de regresión lineal multiple, se validaran los supuestos con el fin de determinar si el modelo es confiable y si existen problremas que puedan afectar sus resultados.
Se evalua el supuesto de normalidad de los residuos mediante el gráfico Q-Q, el cual permite comparar la distribucion de los residuos del modelo con una distribucion nromal. Se espera que los residuos se acerquen a la linea recta, para confirmar su distribucion normal.
qqnorm(
residuals(modelo2),
main = "Gráfico Q-Q de los residuos"
)
qqline(
residuals(modelo2),
col = "red",
lwd = 2
)
En el gráfico Q-Q se observa un comportamiento similar al presentado en el caso 1. Gran parte de los residuos ubicados en la zona central se encuentran sobre o cerca de la línea de referencia. Sin embargo, hacia los extremos se presentan desviaciones considerables, especialmente en la cola derecha. Por lo tanto, los residuos presentan un comportamiento cercano a la normalidad en la parte central, pero existen desviaciones importantes en las colas, por lo que el supuesto de normalidad no se cumple completamente.
A continuación, se evalúa el supuesto de homocedasticidad, con el fin de verificar si la variabilidad de los residuos se mantiene aproximadamente constante a lo largo de los valores estimados por el modelo. Para este supuesto se busca residuos distribuuidos alrededor de 0 con una dispersion mas o menso constante alrededor.
plot(
modelo2$fitted.values,
modelo2$residuals,
xlab = "Valores ajustados",
ylab = "Residuos",
main = "Residuos vs valores ajustados"
)
abline(h = 0, col = "red", lwd = 2)
En el gráfico de residuos vs. valores ajustados se observa un comportamiento similar al caso 1, aunque en este caso la dispersión parece ser menor. Para los valores ajustados más bajos, los residuos se encuentran relativamente concentrados alrededor de cero. Sin embargo, a medida que aumentan los valores ajustados, también aumenta la dispersión de los residuos, formando una especie de abanico. Además, se observan algunos valores extremos en la zona de precios más altos. Por lo tanto, la variabilidad de los errores no se mantiene constante y se presenta evidencia de heterocedasticidad.d.
Para evaluar la indepenencia de los residuos utilizamos el estadistico Durbin-Watson. Se espera un valor de este cercano a 2 y un p value mayor que 0.05.
library(lmtest)
dwtest(modelo2)
##
## Durbin-Watson test
##
## data: modelo2
## DW = 1.6561, p-value = 6.277e-14
## alternative hypothesis: true autocorrelation is greater than 0
Para evaluar la independencia de los residuos se realizó la prueba de Durbin-Watson. Se obtuvo un estadístico DW de 1.6561 y un p-value menor a 0.05. Por lo tanto, existe evidencia de autocorrelación positiva entre los residuos y el supuesto de independencia no se cumple completamente.
Finalmente, se evalúa la posible presencia de multicolinealidad entre las variables predictoras mediante el Factor de Inflación de la Varianza (VIF). Este análisis permite determinar si alguna de las variables predictoras presenta una relación demasiado fuerte con las demás variables incluidas en el model. Se espera un valor menor o igual a 5.
library(car)
vif(modelo2)
## areaconst estrato habitaciones parqueaderos banios
## 2.315361 1.733426 1.412797 2.198524 2.754664
Los valores VIF obtenidos para todas las variables predictoras son inferiores a 5, encontrándose entre 1.35 y 2.17. Por lo tanto, no se evidencian problemas importantes de multicolinealidad en el modelo. Este resultado confirma lo observado anteriormente en la matriz de correlaciones, donde tampoco se encontraron relaciones excesivamente altas entre las variables predictoras.
La linealidad sirve para comprobar que la relacion entre las variables predictoras y el precio de las viviendas puede representarse mediante una relacion lineal. Este supuesto es posbile evaluarlo con el grafico Residuals vs Fitted, evaluando que la linea roja sea mas o menos horizontal alrededor de 0
plot(modelo2, which = 1)
La línea de tendencia presenta una ligera curvatura, pero no se observa
un patrón curvo fuertemente definido. Por lo tanto, se considera que el
supuesto de linealidad se cumple de manera aproximada. Sin embargo, se
observan algunas desviaciones y valores atípicos que podrían estar
afectando el comportamiento del modelo.
Al igual que para el caso 1, se realiza un análisis de observaciones extremas con el fin de identificar registros que puedan presentar comportamientos atípicos o tener una influencia importante sobre el modelo. Para esto se analizarán los residuos studentizados, el leverage y la distancia de Cook.
Esta sección responde a que viviendas tienen un error de prediccion muy grande. Para este análisis se consideran como posibles observaciones atípicas aquellas cuyos residuos studentizados presentan un valor absoluto superior a 3.
res_student <- rstudent(modelo2)
unname(which(abs(res_student) > 3))
## [1] 106 107 196 197 247 280 301 303 357 495 522 596 784 821 852
## [16] 928 929 994 998 999 1021 1037 1519 1825
Se identificaron 24 observaciones con residuos studentizados superiores a 3 en valor absoluto. Estas representan aproximadamente el 1.3 % de las 1840 viviendas analizadas, por lo que se consideran posibles observaciones atípicas que deberían ser revisadas.
El leverage permite identificar observaciones que presentan combinaciones poco comunes en las variables predictoras, es decir, viviendas cuyas características de área, estrato, habitaciones, parqueaderos o baños se alejan considerablemente del comportamiento general de los datos..
leverage <- hatvalues(modelo2)
limite_leverage2 <- 2 * length(coef(modelo2)) / nrow(base2)
limite_leverage2
## [1] 0.006521739
unname(which(leverage > limite_leverage2))
## [1] 18 21 22 106 107 122 178 183 186 188 193 196 197 206 215
## [16] 219 220 254 272 280 286 287 288 290 291 301 302 303 317 326
## [31] 341 346 349 356 360 361 364 369 374 375 377 402 435 449 495
## [46] 522 527 533 545 548 560 575 585 596 618 623 631 635 714 742
## [61] 785 787 808 821 850 852 869 877 885 886 890 914 916 918 921
## [76] 927 928 929 930 932 933 941 944 948 954 961 973 980 981 987
## [91] 989 993 999 1001 1012 1014 1018 1019 1020 1021 1022 1023 1024 1028 1032
## [106] 1037 1042 1046 1047 1110 1327 1413 1519 1820 1825 1840
sum(leverage > limite_leverage2)
## [1] 116
sum(leverage > limite_leverage2) / nrow(base2) * 100
## [1] 6.304348
Para identificar observaciones con combinaciones poco comunes en las variables predictoras, se analiza el leverage. Como criterio de referencia se utiliza 2p/n. Para este modelo se obtiene un límite de 0.0065. Se identificaron 116 observaciones con valores de leverage superiores a este límite, lo que representa aproximadamente el 6.30 % de las 1840 viviendas analizadas. Estas observaciones presentan características que se alejan del comportamiento general de los datos, aunque esto no significa necesariamente que tengan una influencia importante sobre el modelo. Para ello, en la siguiente subsección analizaremos la distancia de cook, ya que permitirá evaluar cuales de esas observaciones tienene una influencia importante en el modelo.
Finalmente, se utiliza la distancia de Cook para identificar observaciones que pueden tener una influencia importante sobre el modelo. A diferencia del leverage, que identifica observaciones con características predictoras poco comunes, la distancia de Cook permite evaluar qué tanto puede influir una observación sobre los resultados del modelo. Se utiliza como referencia el criterio Di>4/n, por lo que Di>0.006
cook2 <- cooks.distance(modelo2)
limite_cook <- 4 / nrow(base2)
limite_cook
## [1] 0.002173913
unname(which(cook2 > limite_cook))
## [1] 18 22 36 57 106 107 122 178 183 196 197 206 211 219 247
## [16] 266 269 271 280 283 286 287 288 290 291 301 303 317 320 337
## [31] 338 339 342 349 356 357 361 362 369 370 493 494 495 515 522
## [46] 527 533 537 539 541 544 569 575 596 598 618 635 743 768 784
## [61] 821 852 885 886 889 897 899 921 927 928 929 932 933 946 952
## [76] 955 956 989 994 998 999 1012 1021 1023 1028 1037 1042 1047 1058 1519
## [91] 1825 1840
sum(cook2 > limite_cook)
## [1] 92
sum(cook2 > limite_cook) / nrow(base2) * 100
## [1] 5
plot(
cook2,
type = "h",
ylab = "Distancia de Cook",
main = "Distancia de Cook"
)
abline(
h = limite_cook,
col = "red",
lty = 2
)
Se identificaron 92 observaciones con una distancia de Cook superior al límite establecido de 0.00217, las cuales representan aproximadamente el 5 % de las 1840 viviendas analizadas. Estas observaciones pueden considerarse potencialmente influyentes sobre el modelo. Adicionalmente, en el gráfico se observa una observación que sobresale considerablemente respecto a las demás, ubicada alrededor del índice 1519, por lo que resulta conveniente revisarla de manera particular para determinar qué características presenta y por qué genera una influencia tan alta sobre el modelo.
A continuación revisaremos las caracteristicas de la vivienda con indice 1519.
base2[1519, ]
## # A tibble: 1 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6121 Zona S… 07 5 299 932 1 3 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
Al revisar la observación con mayor distancia de Cook, correspondiente al índice 1519 (ID 6121), se observa un apartamento con 932 m² de área construida, estrato 5, 3 habitaciones, 3 baños, 1 parqueadero y un precio de 299 millones de pesos. Este registro se aleja bastante del comportamiento general de los datos, principalmente porque presenta un área construida muy grande para un precio relativamente bajo. Esto puede explicar por qué esta observación tiene una influencia tan alta sobre el modelo.
La validación de los supuestos muestra que el modelo presenta algunas limitaciones. La normalidad de los residuos se cumple principalmente en la parte central, pero presenta desviaciones importantes en los extremos. También se observa heterocedasticidad y la prueba de Durbin-Watson indica problemas con la independencia de los residuos. Por otro lado, no se encontraron problemas importantes de multicolinealidad y la linealidad se cumple de manera aproximada. Adicionalmente, se identificaron algunas observaciones atípicas o que pueden tener una influencia importante sobre el modelo. En particular, la observación con índice 1519 (ID 6121) presenta una distancia de Cook considerablemente mayor que las demás, por lo que sería importante revisar este registro.
Como posibles mejoras se podría:
La solicitud para la vivienda 2 tiene las siguientes características - Tipo: Apartamento - Zona: Sur de Cali -Área construida: 300 m² - Parqueaderos: 3 - Baños: 3 - Habitaciones: 5 - Estrato: 5 o 6 - Presupuesto máximo: 850 millones de pesos
Como hay 2 posibilidades para estrato, se realizaran 2 predicciones: una suponiendo estrato 5 y otra estrato 6.
Creamos las caracteristicas de la vivienda 2, teniendo estrato 5 y estrato 6
vivienda2_pred <- data.frame(
areaconst = c(300, 300),
estrato = c(5, 6),
habitaciones = c(5, 5),
parqueaderos = c(3, 3),
banios = c(3, 3)
)
vivienda2_pred
## areaconst estrato habitaciones parqueaderos banios
## 1 300 5 5 3 3
## 2 300 6 5 3 3
Con el modelo 2, realizamos la prediccion del precio de la vivienda 2.
predict(modelo2, newdata = vivienda2_pred)
## 1 2
## 746.7370 796.8345
Para la vivienda 2 se realizaron dos predicciones, debido a que la solicitud permite viviendas de estrato 5 o 6. Manteniendo las demás características constantes, el modelo estima un precio de aproximadamente 746,74 millones de pesos para estrato 5 y 796,83 millones de pesos para estrato 6. Ambos valores se encuentran dentro del presupuesto máximo de 850 millones de pesos, por lo que, de acuerdo con el modelo, sería posible encontrar opciones que cumplan con las características solicitadas en ambos estratos. Además, entre las dos predicciones se presenta una diferencia aproximada de 50,10 millones de pesos, valor que coincide con el coeficiente estimado para la variable estrato en el modelo (50,10 millones por cada nivel adicional de estrato), mostrando coherencia entre los resultados de la predicción y el modelo obtenido.
Como estamos prediciendo el precio de una vivienda en particular, es posible obtener los intervalos de predicción para ambos estratos.
predict(
modelo2,
newdata = vivienda2_pred,
interval = "prediction",
level = 0.95
)
## fit lwr upr
## 1 746.7370 561.3633 932.1106
## 2 796.8345 611.5133 982.1557
Los intervalos de predicción obtenidos son más estrechos que los presentados en el caso 1. Para una vivienda de estrato 5, el precio estimado es de 746.73 millones de pesos, con un intervalo entre 561.36 y 932.11 millones. Para estrato 6, el precio estimado es de 796.83 millones, con un intervalo entre 611.51 y 982.16 millones. Esta menor amplitud puede estar relacionada con que el modelo 2 presentó un mejor ajuste y una menor dispersión de los residuos, por lo que existe menos incertidumbre en las predicciones. Sin embargo, los intervalos siguen siendo amplios y deben tomarse como valores de referencia y no como precios exactos. También es importante observar que, aunque los dos precios estimados se encuentran por debajo del presupuesto máximo de 850 millones, el límite superior de ambos intervalos supera este valor. Esto indica que en la realidad podrían encontrarse apartamentos con estas mismas características cuyo precio supere el presupuesto. Por esta razón, para la recomendación final también se revisarán los precios reales y las características de las viviendas disponibles en la base de datos.
Teniendo en cuenta que el crédito preaprobado para la Vivienda 2 es de máximo 850millones de pesos, se realiza una búsqueda dentro de la base 2 de viviendas que se ajusten al presupuesto y presenten características cercanas a las solicitadas. Se consideran viviendas de estrato 5 y 6, de acuerdo con los requerimientos del cliente, y se establece inicialmente un rango de área construida entre 280 y 310 m², tomando como referencia los 300 m² solicitados.
Como paso inicial realicemos un filtro de los apartamentos que se encuentran en base 2 que se encuentran entre 250 metros cuadrados y 350 metros cuadrados.
base2 %>%
filter(
estrato %in% c(5,6),
areaconst >= 250,
areaconst <= 350
) %>%
arrange(abs(areaconst - 300)) %>%
select(id, estrato, preciom, areaconst,
parqueaderos, banios, habitaciones)
## # A tibble: 26 × 7
## id estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 3785 6 1580 296 4 4 3
## 2 4706 6 980 306 4 5 3
## 3 3809 6 980 306 4 4 3
## 4 4870 6 980 306 4 4 3
## 5 6197 6 1700 290 3 4 3
## 6 6510 6 1600 290 3 5 4
## 7 6512 6 1750 290 3 4 3
## 8 5472 6 1590 310 3 4 3
## 9 5767 6 950 287 4 5 3
## 10 5306 5 650 275 2 5 5
## # ℹ 16 more rows
Ahora realicemos un filtro de las viviendas que cumplan con el filtro anterior y además cumplan con el presupuesto máximo planteado.
base2 %>%
filter(
estrato %in% c(5, 6),
areaconst >= 250,
areaconst <= 350,
preciom <= 850
) %>%
arrange(abs(areaconst - 300)) %>%
select(
id, estrato, preciom, areaconst,
parqueaderos, banios, habitaciones
)
## # A tibble: 4 × 7
## id estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5306 5 650 275 2 5 5
## 2 6175 5 350 270 3 3 4
## 3 6205 5 350 260 3 3 3
## 4 4266 6 700 250 2 4 5
Después de aplicar los filtros de área construida y presupuesto máximo, se obtienen cuatro viviendas como posibles opciones preliminares. Al evaluar sus principales características, se observa que ninguna cumple exactamente con todos los requerimientos de la Vivienda 2. Sin embargo, algunas presentan condiciones bastante cercanas, por lo que se comparan aspectos como el área, número de habitaciones, baños y parqueaderos para seleccionar la alternativa más conveniente. Entre las cuatro viviendas encontradas, se considera que la vivienda ID 6175 es la opción que mejor se ajusta a la solicitud. Corresponde a un apartamento de estrato 5, con 270 m², 3 parqueaderos, 3 baños y 4 habitaciones, y un precio de 350 millones de pesos, considerablemente por debajo del presupuesto máximo de 850 millones. Esta vivienda cumple con el número de parqueaderos y baños solicitados y presenta un área relativamente cercana a los 300 m² requeridos. Su principal diferencia es que cuenta con 4 habitaciones en lugar de las 5 solicitadas. Por esta razón, podría considerarse como la mejor alternativa si el cliente está dispuesto a aceptar una habitación menos.
El desarrollo de este trabajo fue retador, ya que fue necesario realizar una serie de pasos de manera ordenada para poder llegar a los resultados finales. Se partió de la revisión y limpieza de los datos, la identificación de las viviendas correspondientes a cada zona, el análisis exploratorio, la construcción de los modelos de regresión y la validación de sus supuestos, hasta llegar finalmente a la predicción de precios y selección de posibles viviendas para cada solicitud. Aunque el procedimiento aplicado en los dos casos fue prácticamente el mismo, los resultados obtenidos fueron diferentes. En el Caso 2 el modelo presentó un mejor ajuste y relaciones más claras entre varias de las características de las viviendas y su precio, lo que permitió realizar estimaciones con una menor incertidumbre en comparación con el Caso 1. Esto muestra que, aun utilizando la misma metodología, el comportamiento de los datos puede cambiar considerablemente dependiendo del tipo de vivienda y de la zona analizada.
Finalmente, el ejercicio permitió comprobar que el modelo de regresión es una herramienta útil para orientar la búsqueda y estimar precios, pero no debe utilizarse como único criterio para tomar una decisión. La selección final también debe considerar las características reales de las viviendas disponibles, el presupuesto y qué tan cerca se encuentran de las necesidades planteadas por el cliente.