Regresión Lineal Múltiple

Pontificia Universidad Javeriana - Cali

John Jairo Bedoya Saenz y Liz Mary Gutiérrez Rendón

2023-03-21


1. Carga y depuración de base de datos

Los datos proporcionados incluyen información sobre las propiedades inmobiliarias de Cali en una determinada zona, incluyendo el piso en el que se encuentran, su estrato socioeconómico, el precio en millones de pesos, el área construida, el número de parqueaderos, baños, habitaciones, el tipo de propiedad, el barrio en el que se encuentra, y las coordenadas de longitud y latitud.

data("vivienda")
vivienda=as.data.frame(vivienda[,-1])
head(vivienda, 3)
##           zona piso estrato preciom areaconst parqueaderos banios habitaciones
## 1 Zona Oriente <NA>       3     250        70            1      3            6
## 2 Zona Oriente <NA>       3     320       120            1      2            3
## 3 Zona Oriente <NA>       3     350       220            2      2            4
##   tipo      barrio  longitud latitud
## 1 Casa 20 de julio -76.51168 3.43382
## 2 Casa 20 de julio -76.51237 3.43369
## 3 Casa 20 de julio -76.51537 3.43566
summary(vivienda)
##      zona               piso              estrato         preciom      
##  Length:8322        Length:8322        Min.   :3.000   Min.   :  58.0  
##  Class :character   Class :character   1st Qu.:4.000   1st Qu.: 220.0  
##  Mode  :character   Mode  :character   Median :5.000   Median : 330.0  
##                                        Mean   :4.634   Mean   : 433.9  
##                                        3rd Qu.:5.000   3rd Qu.: 540.0  
##                                        Max.   :6.000   Max.   :1999.0  
##                                        NA's   :3       NA's   :2       
##    areaconst       parqueaderos        banios        habitaciones   
##  Min.   :  30.0   Min.   : 1.000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.: 3.000  
##  Median : 123.0   Median : 2.000   Median : 3.000   Median : 3.000  
##  Mean   : 174.9   Mean   : 1.835   Mean   : 3.111   Mean   : 3.605  
##  3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000   3rd Qu.: 4.000  
##  Max.   :1745.0   Max.   :10.000   Max.   :10.000   Max.   :10.000  
##  NA's   :3        NA's   :1605     NA's   :3        NA's   :3       
##      tipo              barrio             longitud         latitud     
##  Length:8322        Length:8322        Min.   :-76.59   Min.   :3.333  
##  Class :character   Class :character   1st Qu.:-76.54   1st Qu.:3.381  
##  Mode  :character   Mode  :character   Median :-76.53   Median :3.416  
##                                        Mean   :-76.53   Mean   :3.418  
##                                        3rd Qu.:-76.52   3rd Qu.:3.452  
##                                        Max.   :-76.46   Max.   :3.498  
##                                        NA's   :3        NA's   :3

La tabla muestra que hay 8.322 propiedades en total en la base, 5.100 apartamentos, 3.219 casas y 3 NA, evidenciando que hay valores faltantes en algunas variables. Por ejemplo, hay 2638 observaciones faltantes para la variable “piso” y 1605 en la variable “parqueaderos”. Antes de eliminar las filas que tienen datos faltantes se tiene en cuenta que la variable “piso” no se usará en el modelo por lo tanto esta se excluye de la base de datos, ahora bien, con respecto a la variable “parqueaderos” se observa que su rango está entre 1 y 10, por lo que se toma la descisión de reemplazar los valores faltantes por cero, al hacer esto se asume que estas viviendas no tienen parqueadero, ya que es probable que alguien que venda una vivienda sin parqueadero, no lo incluya en la información proporcionada.

Sin embargo, es importante tener en cuenta que esta es una asunción y podría no ser completamente precisa. En algunos casos, los valores faltantes podrían deberse a errores en la recopilación de datos o a que la información simplemente no se proporcionó. Por lo tanto, es importante analizar los datos cuidadosamente y tener en cuenta cualquier limitación o posible error al realizar el análisis.

## Se excluye la variable piso ya que no se usará en el modelo y tiene una gran cantidad de NA
vivienda = vivienda[,-c(2)]

## Parqueaderos en NA son 0

vivienda$parqueaderos[is.na(vivienda$parqueaderos)]=0

Por ultimo se retiran de la base las filas restantes que cuentan con datos faltantes (NA)

## retirar NA restantes
viv2=na.omit(vivienda)

2. Viviendas Zona Norte

Para el mapeo de los puntos se usa un objeto shape de las comunas de la ciudad de Cali, primero se carga el archivo, luego se filtra la base de viviendas en la zona norte y con las cordenadas de esta base filtrada se crea un obejeto espacial de puntos. Finalmente mediante la función plot se grafica el mapa con las viviendas.

require(sp)
library(raster)
library(sf)
library(tmap)

## Mapa de Cali con comunas
comunas=shapefile("C:/Users/jjbedoya/Desktop/Cali/Comunas.shp")

## Filtro de viviendas en Zona Norte
df_casa_norte=viv2[viv2$zona == "Zona Norte" & viv2$tipo == "Casa",]
head(df_casa_norte,3)
##          zona estrato preciom areaconst parqueaderos banios habitaciones tipo
## 9  Zona Norte       5     320       150            2      4            6 Casa
## 10 Zona Norte       5     780       380            2      3            3 Casa
## 11 Zona Norte       6     750       445            0      7            6 Casa
##    barrio  longitud latitud
## 9   acopi -76.51341 3.47968
## 10  acopi -76.51674 3.48721
## 11  acopi -76.52950 3.38527
# tabla de todas las viviendas de cali
table(vivienda$zona, vivienda$tipo)
##               
##                Apartamento Casa
##   Zona Centro           24  100
##   Zona Norte          1198  722
##   Zona Oeste          1029  169
##   Zona Oriente          62  289
##   Zona Sur            2787 1939
# tabla de  las viviendas tipo casa ubicadas en la zona norte de cali
table(df_casa_norte$zona, df_casa_norte$tipo)
##             
##              Casa
##   Zona Norte  722

De acuerdo con el filtro realizado, se obseva que para la zona norte y tipo de vivienda casa, se cuenta con 722 datos que serán georeferenciados a continuación.

## longitud en formato -xx.xxxxx
#longitud_c = df_casa_norte$longitud
#re = "(\\d{2})(\\d{2})"
#longitud_c = gsub(re, "\\1.\\2", longitud_c)

## latitud en formato xx.xxxxx
#latitud_c = df_casa_norte$latitud
#re = "(\\d{1,1})(\\d{1,4})"
#latitud_c = gsub(re, "\\1.\\2", latitud_c)

#df_casa_norte$longitud = as.double(longitud_c)
#df_casa_norte$latitud = as.double(latitud_c)

## se convierte en un objeto espacial
puntos = SpatialPointsDataFrame(coords = df_casa_norte[,c("longitud","latitud")],
                                 data = df_casa_norte,
                                 proj4string = CRS(proj4string(comunas)))
## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html
plot(comunas)
plot(puntos, add=T, pch=1, col="red")

Se encontró que algunos puntos estaban ubicados en zonas que no correspondían a la zona norte. Para clasificar los puntos mal ubicados, se realizón un cruce de las viviendas con el mapa de cumunas teniendo en cuenta la información suministrada por la IDESC del Departamento Administrativo de Planeación - Cali que las comunas de la zona norte son las comunas 6, 5, 4 y 2 (fuente: https://www.cali.gov.co/planeacion/publicaciones/169423/zonas_geograficas_idesc/).

## Hay muchos puntos que no se encuentran al norte de la ciudad
## Para validar se cruzan los puntos con las comunas
## Se considera que las comunas de zona norte son la 6, 5, 4 y 2
## con esto se clasifican los puntos que estarian bien ubicados y los que estan mal

require(dplyr)
## Loading required package: dplyr
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:rgeos':
## 
##     intersect, setdiff, union
## The following objects are masked from 'package:raster':
## 
##     intersect, select, union
## The following object is masked from 'package:kableExtra':
## 
##     group_rows
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
result = over(puntos, comunas)

df_casa_norte$comuna = result$comuna

df_casa_norte$clasificacion = ifelse(df_casa_norte$comuna%in%c(6,5,4,2),"bien","mal")

df_casa_norte_ok = df_casa_norte[df_casa_norte$clasificacion == "bien",] 

head(df_casa_norte_ok, 10)
##          zona estrato preciom areaconst parqueaderos banios habitaciones tipo
## 9  Zona Norte       5     320       150            2      4            6 Casa
## 10 Zona Norte       5     780       380            2      3            3 Casa
## 31 Zona Norte       3     180       120            0      3            3 Casa
## 35 Zona Norte       5     520       455            0      5            4 Casa
## 43 Zona Norte       3     380       300            0      5            8 Casa
## 59 Zona Norte       5     395       165            0      4            4 Casa
## 82 Zona Norte       5     460       319            0      5            4 Casa
## 86 Zona Norte       5     390       357            0      3            6 Casa
## 87 Zona Norte       5     780       380            0      3            3 Casa
## 90 Zona Norte       4     420       265            0      6            7 Casa
##    barrio  longitud latitud comuna clasificacion
## 9   acopi -76.51341 3.47968      2          bien
## 10  acopi -76.51674 3.48721      2          bien
## 31  acopi -76.49768 3.47060      5          bien
## 35  acopi -76.49966 3.46284      5          bien
## 43  acopi -76.50743 3.46566      4          bien
## 59  acopi -76.51797 3.47651      2          bien
## 82  acopi -76.52190 3.47317      2          bien
## 86  acopi -76.52255 3.48133      2          bien
## 87  acopi -76.52293 3.48427      2          bien
## 90  acopi -76.52419 3.48054      2          bien
require(table1)
## Loading required package: table1
## 
## Attaching package: 'table1'
## The following objects are masked from 'package:base':
## 
##     units, units<-
y <- table1::table1(~barrio|zona, data = df_casa_norte_ok)
y
Zona Norte
(N=527)
Overall
(N=527)
barrio
acopi 11 (2.1%) 11 (2.1%)
alameda del río 1 (0.2%) 1 (0.2%)
alamos 2 (0.4%) 2 (0.4%)
barranquilla 3 (0.6%) 3 (0.6%)
barrio tranquilo y 1 (0.2%) 1 (0.2%)
brisas de los 16 (3.0%) 16 (3.0%)
brisas del guabito 1 (0.2%) 1 (0.2%)
calima 5 (0.9%) 5 (0.9%)
calimio norte 2 (0.4%) 2 (0.4%)
centenario 2 (0.4%) 2 (0.4%)
chipichape 5 (0.9%) 5 (0.9%)
ciudad los álamos 11 (2.1%) 11 (2.1%)
el bosque 32 (6.1%) 32 (6.1%)
el guabito 1 (0.2%) 1 (0.2%)
el sena 1 (0.2%) 1 (0.2%)
flora industrial 4 (0.8%) 4 (0.8%)
floralia 3 (0.6%) 3 (0.6%)
gaitan 1 (0.2%) 1 (0.2%)
granada 9 (1.7%) 9 (1.7%)
jorge eliecer gaitán 1 (0.2%) 1 (0.2%)
juanamb√∫ 8 (1.5%) 8 (1.5%)
la campiña 3 (0.6%) 3 (0.6%)
la esmeralda 1 (0.2%) 1 (0.2%)
la flora 82 (15.6%) 82 (15.6%)
La Flora 1 (0.2%) 1 (0.2%)
la merced 23 (4.4%) 23 (4.4%)
la rivera 8 (1.5%) 8 (1.5%)
la rivera i 1 (0.2%) 1 (0.2%)
la rivera ii 2 (0.4%) 2 (0.4%)
la riviera 1 (0.2%) 1 (0.2%)
la villa del 1 (0.2%) 1 (0.2%)
las delicias 2 (0.4%) 2 (0.4%)
los andes 10 (1.9%) 10 (1.9%)
los guaduales 8 (1.5%) 8 (1.5%)
los guayacanes 2 (0.4%) 2 (0.4%)
manzanares 1 (0.2%) 1 (0.2%)
menga 2 (0.4%) 2 (0.4%)
metropolitano del norte 1 (0.2%) 1 (0.2%)
oasis de comfandi 1 (0.2%) 1 (0.2%)
occidente 1 (0.2%) 1 (0.2%)
pacara 2 (0.4%) 2 (0.4%)
parque residencial el 1 (0.2%) 1 (0.2%)
paseo de los 2 (0.4%) 2 (0.4%)
paso del comercio 2 (0.4%) 2 (0.4%)
popular 2 (0.4%) 2 (0.4%)
portada de comfandi 1 (0.2%) 1 (0.2%)
portales de comfandi 1 (0.2%) 1 (0.2%)
porvenir 2 (0.4%) 2 (0.4%)
prados del norte 23 (4.4%) 23 (4.4%)
quintas de salomia 1 (0.2%) 1 (0.2%)
salomia 15 (2.8%) 15 (2.8%)
san luis 2 (0.4%) 2 (0.4%)
san vicente 23 (4.4%) 23 (4.4%)
santa bárbara 1 (0.2%) 1 (0.2%)
santa monica 14 (2.7%) 14 (2.7%)
Santa Monica 1 (0.2%) 1 (0.2%)
santa mónica 1 (0.2%) 1 (0.2%)
santa monica norte 1 (0.2%) 1 (0.2%)
santa monica residencial 5 (0.9%) 5 (0.9%)
santa mónica residencial 14 (2.7%) 14 (2.7%)
santander 1 (0.2%) 1 (0.2%)
torres de comfandi 2 (0.4%) 2 (0.4%)
urbanización barranquilla 1 (0.2%) 1 (0.2%)
urbanización la flora 20 (3.8%) 20 (3.8%)
urbanización la merced 4 (0.8%) 4 (0.8%)
versalles 14 (2.7%) 14 (2.7%)
villa de veracruz 3 (0.6%) 3 (0.6%)
villa del prado 33 (6.3%) 33 (6.3%)
Villa Del Prado 1 (0.2%) 1 (0.2%)
villa del sol 12 (2.3%) 12 (2.3%)
villas de veracruz 6 (1.1%) 6 (1.1%)
Villas De Veracruz 1 (0.2%) 1 (0.2%)
vipasa 27 (5.1%) 27 (5.1%)
zona norte 17 (3.2%) 17 (3.2%)

Al limpiar la base de datos, excluyendo los clasificdos como “mal” ubicadas se reduce a 527 datos correctos, esto puede estar dándose debido a: Algunos del barrio Acopi tienen direcciones que corresponden al municipio de Yumbo, pero en el proceso de geocodificación se puede estar buscando la dirección en la ciudad de Cali, lo que genera un error en la ubicación de estas viviendas. También se pueden ver algunos errores en cuanto al barrio siendo que uno de ellos es “zona norte” y este barrio no existe. Para lal depuración de la base de datos, se tiene como referncia la longitud y latitud para tener certeza de la ubicación de los predios.

## Georreferenciamos nuevamente las viviendas tipo casa de la zona norte 

puntos1 = SpatialPointsDataFrame(coords = df_casa_norte_ok[,c("longitud","latitud")],
                                 data = df_casa_norte_ok,
                                 proj4string = CRS(proj4string(comunas)))
## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html
plot(comunas)
plot(puntos1, add=T, pch=1, col="purple")

3. Relación entre variables

# Matriz de correlación
require(graphics)
require(plotly)

#viv2$estrato = as.numeric(viv2$estrato)
cor_mat = cor(viv2[,c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones", "estrato")])
cor_mat
##                preciom areaconst parqueaderos    banios habitaciones
## preciom      1.0000000 0.6873520    0.6397779 0.6691456   0.26409121
## areaconst    0.6873520 1.0000000    0.4823840 0.6484165   0.51691292
## parqueaderos 0.6397779 0.4823840    1.0000000 0.5231299   0.19875551
## banios       0.6691456 0.6484165    0.5231299 1.0000000   0.58990641
## habitaciones 0.2640912 0.5169129    0.1987555 0.5899064   1.00000000
## estrato      0.6098066 0.2743233    0.5127889 0.4203218  -0.07137615
##                  estrato
## preciom       0.60980664
## areaconst     0.27432332
## parqueaderos  0.51278892
## banios        0.42032178
## habitaciones -0.07137615
## estrato       1.00000000
plot_ly(z = cor_mat, x = colnames(cor_mat), y = colnames(cor_mat), type = "heatmap", colorscale = "Plasma",zmin = -1, zmax = 1, reversescale = TRUE) %>% layout(title = "Matriz de correlación", xaxis = list(title = ""), yaxis = list(title = ""))

Se puede ver que hay una correlación positiva moderada a fuerte entre el precio de la propiedad y el área construida, los parqueaderos, los baños y el estrato. La correlación entre el precio y el número de habitaciones es baja. El estrato tiene una correlación moderada positiva con los parqueaderos y una correlación negativa débil con el número de habitaciones.

## Boxplot de zonas

plot_ly(data = viv2, x = ~zona, y = ~preciom, type = "box")

4. Modelo de regresión multiple

Ahora se realizará el modelo de regresión lineal múltiple para predecir el precio de las casas ubicadas en la zona norte de Cali, utilizando las variables de Área construida, número de parqueaderos, estrato, número de baños y número de habitaciones. Luego se ajusta el modelo de regresión lineal múltiple, para finalmente mostrar un resumen de los resultados del modelo.

df_casa_norte_ok$estrato = as.numeric(df_casa_norte_ok$estrato)

mod1=lm(preciom~ areaconst + parqueaderos + estrato + banios + habitaciones, df_casa_norte_ok)

summary(mod1)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = df_casa_norte_ok)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -924.18  -66.95  -16.60   35.06 1107.53 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -170.84010   35.64048  -4.793 2.14e-06 ***
## areaconst       0.77882    0.05318  14.645  < 2e-16 ***
## parqueaderos   12.58900    4.77655   2.636  0.00865 ** 
## estrato        71.49706    8.78042   8.143 2.87e-15 ***
## banios         14.28141    6.53460   2.186  0.02930 *  
## habitaciones    5.93136    5.12237   1.158  0.24742    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 149.1 on 521 degrees of freedom
## Multiple R-squared:  0.6413, Adjusted R-squared:  0.6379 
## F-statistic: 186.3 on 5 and 521 DF,  p-value: < 2.2e-16

El modelo ajustado a los datos muestra los siguientes coeficientes:

  • El coeficiente del intercepto es -170.84. Esto indica que el valor esperado del precio de una vivienda con todas las variables explicativas en cero (es decir, cuando el apartamento no tiene área construida, parqueaderos, estrato, baños o habitaciones) es de -170.84 millones de pesos. Sin embargo, este valor no tiene una interpretación práctica, ya que es poco probable que se encuentre una vivienda sin ninguna de estas características.

  • El coeficiente de área construida es 0.778. Esto indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el área construida de un apartamento se asocia, en promedio, con un aumento de 0.778 millones de pesos en el precio del apartamento.

  • El coeficiente de parqueaderos indica que, manteniendo todas las otras variables constantes, un incremento de 1 en la cantidad de parqueaderos se asocia con un aumento en el precio promedio de la vivienda de 12.58 millones de pesos.

  • El coeficiente de Estrato indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el estrato se asocia con un incremento en el precio promedio de la vivienda de 71.49 millones de pesos.

  • El coeficiente de Baños indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el número de baños se asocia con un incremento en el precio promedio de la vivienda de 14.28 millones de pesos.

  • El coeficiente de Habitaciones indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el número de habitaciones se asocia con un incremento en el precio promedio de la vivienda de 5.93 millones de pesos. Sin embargo, el valor p es grande (0.247), lo que indica que no hay suficiente evidencia para rechazar la hipótesis nula de que el coeficiente es cero.

Se puede ver que el R-cuadrado es de 0.6413. Esto significa que alrededor del 64.13% de la variabilidad en el precio de las casas en la región norte se explica por las variables independientes incluidas en el modelo

Para mejorar el modelo se pude optar por las siguientes opciones:

  • Incluir variables adicionales: puede ser útil incluir variables adicionales que puedan estar relacionadas con el precio de la vivienda, como la ubicación, la antigüedad de la propiedad, la calidad de los materiales de construcción, etc.

  • Eliminar variables no significativas: es posible que algunas de las variables incluidas en el modelo no sean significativas y estén afectando negativamente la calidad del modelo. En este caso, se podrían eliminar estas variables para mejorar la precisión del modelo.

  • Transformar variables: en algunos casos, la relación entre la variable independiente y la variable dependiente no es lineal. En estos casos, se pueden utilizar transformaciones como la raíz cuadrada, el logaritmo, entre otras.

5. Validacion de supuestos

En esta sección se validan los supuestos del modelo.

5.1. Normalidad

## Validacion de supuestos

res=mod1$residuals
## Normalidad de residuales
shapiro.test(res)
## 
##  Shapiro-Wilk normality test
## 
## data:  res
## W = 0.78594, p-value < 2.2e-16

H0: los errores tienen una distribución normal

Se rechaza H0 si el p-valor de la prueba es <= a 0.05

En este caso, se utilizó la prueba de Shapiro-Wilk para determinar si los residuos se distribuyen normalmente. El resultado muestra que el valor de W es 0.78594 y el p-value es aproximadamente 0, lo que indica que los residuos no se distribuyen normalmente.

5.2. Homocedasticidad (Supuesto de varianza constante)

## Homocedasticidad

library(lmtest)

bptest(mod1)
## 
##  studentized Breusch-Pagan test
## 
## data:  mod1
## BP = 108.46, df = 5, p-value < 2.2e-16

H0: la varianza de los erroes es constante

En este caso, se utiliza la prueba de Breusch-Pagan para determinar si los residuos son homocedásticos. El resultado muestra que el valor de BP es 108.46 y el p-value es aproximadamente 0, lo que indica que los residuos no son homocedásticos.

5.3. Indenpendencia

## Independecia

dwtest(mod1)
## 
##  Durbin-Watson test
## 
## data:  mod1
## DW = 1.7833, p-value = 0.005495
## alternative hypothesis: true autocorrelation is greater than 0

H0: no hay autocorrelación

En este caso, se utiliza la prueba de Durbin-Watson para determinar si los residuos son independientes. El resultado muestra que el valor de DW es 1.7833 y el p-value es 0.005495, lo que indica que los residuos no son independientes y están correlacionados entre sí.

6. Predicción valor de la vivienda

Se requiere predecir el valor de una vivienda con las siguientes caracteristicas.

Caracteristicas Vivienda 1
Tipo Casa
área construida 200
parqueaderos 1
baños 2
habitaciones 4
estrato 4 o 5
zona Norte
crédito preaprobado 350 millones

Mediante la función predict se realiza la predicción, primero declarando un dataframe con los datos sobre la vivienda que se quiere vender.

## Prediccion

new_dat = data.frame(
  areaconst = 200,
  parqueaderos = 1,
  estrato = c(4, 5),
  banios = 2,
  habitaciones = 4
)
new_dat
##   areaconst parqueaderos estrato banios habitaciones
## 1       200            1       4      2            4
## 2       200            1       5      2            4
predict(mod1,new_dat)
##        1        2 
## 335.7888 407.2858

El resultado indica que una casa con estas caracteristicas de estrato 4 valdria en promedio 335.78 millones de pesos y si es estrato 5 valdría 407.28 millones de pesos.

7. Potenciales ofertas

Teniendo en cuenta el credito preaprobado de 350 millones, a cada casa de la base de datos de zona norte se le incluye la predicción del modelo y el residual asociado. Esto con el fin de filtrar casas de estrato 4 ó 5 que tengan una predicción menor a 350 millones cuyo residual sea negativo, con esto nos aseguramos que estas casas tendran un precio por debajo del credito aprobado y son potenciales ofertas ya que su predicción indica un precio mayor del que valen realmente.

## se incluyen predicciones y residuales en la base

df_casa_norte_ok$res<-res
df_casa_norte_ok$pred<-mod1$fitted.values


df<-head(df_casa_norte_ok[(df_casa_norte_ok$pred<=350 & df_casa_norte_ok$habitaciones>=4 & df_casa_norte_ok$banios>=2 & df_casa_norte_ok$parqueaderos>=1 & df_casa_norte_ok$res<=0 &  df_casa_norte_ok$estrato %in% c(4, 5)),]%>% arrange(desc(pred)))

knitr::kable(df[,-c(9,10,11,12,13,14)],"pipe")
zona estrato preciom areaconst parqueaderos banios habitaciones tipo pred
Zona Norte 4 280 130 2 4 4 Casa 322.4234
Zona Norte 4 265 162 1 3 4 Casa 320.4751
Zona Norte 4 253 140 2 3 4 Casa 315.9302
Zona Norte 4 280 147 1 3 4 Casa 308.7929
Zona Norte 4 240 82 1 3 4 Casa 258.1698
Zona Norte 4 223 73 1 3 4 Casa 251.1604

En el resultado se puede observar que se presentan 6 viviendas opcionadas pero no con las especificaciones exactas del cleinte.

## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html

Otra opción para analizar las mejores ofertas es revisar las casas cuyo precio sea menor a 350 millones y el residual en la predicción sea negativo, ya que segun el modelo estas casas por sus caracteristicas podrian valer más del precio que tienen actualmente en la base.

df2=head(df_casa_norte_ok[df_casa_norte_ok$preciom<350 & df_casa_norte_ok$res<=0,]%>% arrange(desc(preciom)),5)

knitr::kable(df2[,-c(9,10,11,14)],"pipe")
zona estrato preciom areaconst parqueaderos banios habitaciones tipo comuna clasificacion pred
Zona Norte 5 343 170 3 4 4 Casa 2 bien 437.6622
Zona Norte 5 342 250 1 4 6 Casa 2 bien 486.6522
Zona Norte 5 340 250 2 4 4 Casa 2 bien 487.3785
Zona Norte 5 340 208 0 6 4 Casa 4 bien 458.0530
Zona Norte 5 340 240 2 5 6 Casa 2 bien 505.7345

Aquí ya se presentan 2 ofertas potenciales de casas, en la fila 2 y en la fila 3.

## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html

8. Modelo Zona sur para apartamentos

Se siguen los mismos pasos que en el modelo anterior inicialmente se realiza la validación en cuanto a las coordenadas de los apartamentos.

## Filtro de apartamentos en Zona sur

df_sur_apar = viv2[(viv2$zona=="Zona Sur" & viv2$tipo=="Apartamento"),]

## se convierte en un objeto espacial
puntos4 <- SpatialPointsDataFrame(coords = df_sur_apar[,c("longitud","latitud")],
                                 data = df_sur_apar,
                                 proj4string = CRS(proj4string(comunas)))
## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html
plot(comunas)
plot(puntos4, add=T, pch=20, col="blue")

Al igual que en el caso anterior hay puntos mal ubicados, realizando la validación por comunas se obtiene el siguiente resultado. Asumiendo que las comunas de la zona sur son las 10, 16,17,18,19 y 22 de acuerdo con la infrmación suministrada por la IDESC del DAP-Cali.

## comunas 10,16,17,18,19,22

result1 = over(puntos4, comunas)

df_sur_apar$comuna = result1$comuna

df_sur_apar$clasificacion = ifelse(df_sur_apar$comuna%in%c(10,16,17,18,19,22),"bien","mal")

df_sur_apar_ok = df_sur_apar[df_sur_apar$clasificacion == "bien",] 

head(df_sur_apar_ok, 10)
##         zona estrato preciom areaconst parqueaderos banios habitaciones
## 312 Zona Sur       4     220        75            1      2            3
## 315 Zona Sur       3     115        58            1      2            2
## 317 Zona Sur       4     220        84            0      2            3
## 341 Zona Sur       3     230        63            1      2            2
## 344 Zona Sur       5     344       107            2      2            3
## 444 Zona Sur       5     310       166            2      4            3
## 446 Zona Sur       5     175        64            1      2            2
## 447 Zona Sur       5     285       120            2      4            3
## 449 Zona Sur       6     305       125            2      3            3
## 451 Zona Sur       5     185        78            1      2            3
##            tipo             barrio  longitud latitud comuna clasificacion
## 312 Apartamento      alfv©rez real -76.54627 3.39109     18          bien
## 315 Apartamento      alfv©rez real -76.54924 3.39121     18          bien
## 317 Apartamento       alferez real -76.54600 3.39000     18          bien
## 341 Apartamento        alto jordán -76.55300 3.37200     18          bien
## 344 Apartamento altos de guadalupe -76.55500 3.41000     19          bien
## 444 Apartamento        bella suiza -76.56400 3.41000     19          bien
## 446 Apartamento        bella suiza -76.56493 3.40916     19          bien
## 447 Apartamento        bella suiza -76.56400 3.41000     19          bien
## 449 Apartamento        bella suiza -76.56500 3.40800     19          bien
## 451 Apartamento        bella suiza -76.56475 3.41022     19          bien
require(table1)
y <- table1::table1(~barrio|zona, data = df_sur_apar_ok)
y
Zona Sur
(N=2461)
Overall
(N=2461)
barrio
alferez real 1 (0.0%) 1 (0.0%)
alférez real 2 (0.1%) 2 (0.1%)
alto jordán 1 (0.0%) 1 (0.0%)
altos de guadalupe 1 (0.0%) 1 (0.0%)
bella suiza 5 (0.2%) 5 (0.2%)
bochalema 18 (0.7%) 18 (0.7%)
bosques del limonar 11 (0.4%) 11 (0.4%)
brisas del limonar 1 (0.0%) 1 (0.0%)
buenos aires 3 (0.1%) 3 (0.1%)
caldas 1 (0.0%) 1 (0.0%)
Cali 2 (0.1%) 2 (0.1%)
calicanto 2 (0.1%) 2 (0.1%)
cambulos 2 (0.1%) 2 (0.1%)
camino real 13 (0.5%) 13 (0.5%)
Camino Real 1 (0.0%) 1 (0.0%)
campestre 1 (0.0%) 1 (0.0%)
caney 55 (2.2%) 55 (2.2%)
caney especial 1 (0.0%) 1 (0.0%)
cañasgordas 5 (0.2%) 5 (0.2%)
cañaveralejo 8 (0.3%) 8 (0.3%)
cañaverales 17 (0.7%) 17 (0.7%)
cañaverales los samanes 1 (0.0%) 1 (0.0%)
capri 38 (1.5%) 38 (1.5%)
cataya real 1 (0.0%) 1 (0.0%)
cerro cristales 1 (0.0%) 1 (0.0%)
champagnat 1 (0.0%) 1 (0.0%)
ciudad 2000 19 (0.8%) 19 (0.8%)
ciudad bochalema 35 (1.4%) 35 (1.4%)
ciudad capri 8 (0.3%) 8 (0.3%)
ciudad jardin 9 (0.4%) 9 (0.4%)
ciudad jardín 202 (8.2%) 202 (8.2%)
ciudad jardin pance 1 (0.0%) 1 (0.0%)
ciudad melendez 1 (0.0%) 1 (0.0%)
ciudad meléndez 1 (0.0%) 1 (0.0%)
ciudad pacifica 1 (0.0%) 1 (0.0%)
Ciudad Pacifica 1 (0.0%) 1 (0.0%)
ciudadela comfandi 1 (0.0%) 1 (0.0%)
ciudadela melendez 1 (0.0%) 1 (0.0%)
ciudadela pasoancho 3 (0.1%) 3 (0.1%)
colinas del sur 2 (0.1%) 2 (0.1%)
colseguros 20 (0.8%) 20 (0.8%)
colseguros andes 1 (0.0%) 1 (0.0%)
cristales 1 (0.0%) 1 (0.0%)
cristobal colón 2 (0.1%) 2 (0.1%)
cuarto de legua 29 (1.2%) 29 (1.2%)
departamental 14 (0.6%) 14 (0.6%)
el caney 112 (4.6%) 112 (4.6%)
El Caney 1 (0.0%) 1 (0.0%)
el dorado 6 (0.2%) 6 (0.2%)
el gran limonar 3 (0.1%) 3 (0.1%)
el guabal 4 (0.2%) 4 (0.2%)
el ingenio 120 (4.9%) 120 (4.9%)
el ingenio 3 1 (0.0%) 1 (0.0%)
el ingenio i 12 (0.5%) 12 (0.5%)
el ingenio ii 9 (0.4%) 9 (0.4%)
el ingenio iii 9 (0.4%) 9 (0.4%)
el jordán 1 (0.0%) 1 (0.0%)
el lido 29 (1.2%) 29 (1.2%)
el limonar 52 (2.1%) 52 (2.1%)
el refugio 70 (2.8%) 70 (2.8%)
gran limonar 8 (0.3%) 8 (0.3%)
guadalupe 8 (0.3%) 8 (0.3%)
ingenio 1 (0.0%) 1 (0.0%)
ingenio ii 1 (0.0%) 1 (0.0%)
la alborada 4 (0.2%) 4 (0.2%)
la cascada 2 (0.1%) 2 (0.1%)
la hacienda 99 (4.0%) 99 (4.0%)
La Hacienda 1 (0.0%) 1 (0.0%)
la luisa 1 (0.0%) 1 (0.0%)
la selva 6 (0.2%) 6 (0.2%)
las acacias 1 (0.0%) 1 (0.0%)
las camelias 1 (0.0%) 1 (0.0%)
las granjas 6 (0.2%) 6 (0.2%)
las vegas de 1 (0.0%) 1 (0.0%)
los cambulos 18 (0.7%) 18 (0.7%)
los cámbulos 3 (0.1%) 3 (0.1%)
los farallones 2 (0.1%) 2 (0.1%)
mayapan las vegas 29 (1.2%) 29 (1.2%)
melendez 35 (1.4%) 35 (1.4%)
meléndez 17 (0.7%) 17 (0.7%)
miraflores 1 (0.0%) 1 (0.0%)
multicentro 21 (0.9%) 21 (0.9%)
napoles 1 (0.0%) 1 (0.0%)
nápoles 10 (0.4%) 10 (0.4%)
nueva tequendama 33 (1.3%) 33 (1.3%)
oasis de pasoancho 1 (0.0%) 1 (0.0%)
pampa linda 12 (0.5%) 12 (0.5%)
pampalinda 3 (0.1%) 3 (0.1%)
panamericano 2 (0.1%) 2 (0.1%)
pance 177 (7.2%) 177 (7.2%)
parcelaciones pance 17 (0.7%) 17 (0.7%)
pasoancho 5 (0.2%) 5 (0.2%)
ponce 1 (0.0%) 1 (0.0%)
prados del limonar 2 (0.1%) 2 (0.1%)
Prados Del Limonar 1 (0.0%) 1 (0.0%)
primero de mayo 22 (0.9%) 22 (0.9%)
quintas de don 56 (2.3%) 56 (2.3%)
refugio 1 (0.0%) 1 (0.0%)
samanes 1 (0.0%) 1 (0.0%)
samanes de guadalupe 1 (0.0%) 1 (0.0%)
san fernando 16 (0.7%) 16 (0.7%)
san fernando nuevo 3 (0.1%) 3 (0.1%)
san fernando viejo 4 (0.2%) 4 (0.2%)
san joaquin 1 (0.0%) 1 (0.0%)
santa anita 34 (1.4%) 34 (1.4%)
Santa Anita 1 (0.0%) 1 (0.0%)
santa elena 1 (0.0%) 1 (0.0%)
santa isabel 7 (0.3%) 7 (0.3%)
santa teresita 1 (0.0%) 1 (0.0%)
santo domingo 1 (0.0%) 1 (0.0%)
sector cañaveralejo guadalupe 2 (0.1%) 2 (0.1%)
seminario 19 (0.8%) 19 (0.8%)
templete 1 (0.0%) 1 (0.0%)
tequendama 14 (0.6%) 14 (0.6%)
unicentro cali 1 (0.0%) 1 (0.0%)
urbanización colseguros 2 (0.1%) 2 (0.1%)
urbanizacion gratamira 1 (0.0%) 1 (0.0%)
urbanización nueva granada 1 (0.0%) 1 (0.0%)
urbanización río lili 2 (0.1%) 2 (0.1%)
urbanización tequendama 2 (0.1%) 2 (0.1%)
valle de lili 1 (0.0%) 1 (0.0%)
valle del lili 727 (29.5%) 727 (29.5%)
Valle Del Lili 1 (0.0%) 1 (0.0%)
zona sur 32 (1.3%) 32 (1.3%)

Al limpiar la base de datos, excluyendo los clasificdos como “mal” ubicadas se reduce a 2.461 datos correctos, estos posibles errores se deben a: por ejemplo, el barrio bochalema debido a que al ser una zona nueva en la ciudad el proceso de geolocalización no ubica bien las direcciones de estos apartamentos.

puntos4s <- SpatialPointsDataFrame(coords = df_sur_apar_ok[,c("longitud","latitud")],
                                 data = df_sur_apar_ok,
                                 proj4string = CRS(proj4string(comunas)))
## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html
plot(comunas)
plot(puntos4s, add=T, pch=20, col="darkgreen")

8.1 Modelo de regresión multiple

Se ajusta el modelo de regresión multiple para los apartamentos de zona sur

## Modelo
mod2=lm(preciom~ areaconst + parqueaderos + estrato + banios + habitaciones, df_sur_apar_ok)
summary(mod2)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + estrato + banios + 
##     habitaciones, data = df_sur_apar_ok)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1164.91   -43.94    -2.35    36.48   941.89 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -212.43832   14.62644 -14.524  < 2e-16 ***
## areaconst       1.35982    0.05258  25.862  < 2e-16 ***
## parqueaderos   56.21782    3.39733  16.548  < 2e-16 ***
## estrato        54.79524    3.01705  18.162  < 2e-16 ***
## banios         49.98059    3.31463  15.079  < 2e-16 ***
## habitaciones  -23.71470    3.66809  -6.465 1.22e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 96.58 on 2455 degrees of freedom
## Multiple R-squared:  0.7487, Adjusted R-squared:  0.7482 
## F-statistic:  1463 on 5 and 2455 DF,  p-value: < 2.2e-16

El modelo ajustado cuenta con los siguientes coeficientes:

  • El coeficiente del intercepto es -212.43, esto indica que el valor esperado del precio de una vivienda con todas las variables explicativas en cero (es decir, cuando el apartamento no tiene área construida, parqueaderos, estrato, baños o habitaciones) es de -212.43 millones de pesos. Sin embargo, este valor no tiene una interpretación práctica, ya que es poco probable que se encuentre una vivienda sin ninguna de estas características.

  • El coeficiente de área construida es 1.35. Esto indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el área construida de un apartamento se asocia, en promedio, con un aumento de 1.35 millones de pesos en el precio del apartamento.

  • El coeficiente de parqueaderos indica que, manteniendo todas las otras variables constantes, un incremento de 1 en la cantidad de parqueaderos se asocia con una disminución en el precio promedio de la vivienda de 56.21 millones de pesos.

  • El coeficiente de Estrato indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el estrato se asocia con un incremento en el precio promedio de la vivienda de 54.79 millones de pesos.

  • El coeficiente de Baños indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el número de baños se asocia con un incremento en el precio promedio de la vivienda de 49.98 millones de pesos.

  • El coeficiente de Habitaciones indica que, manteniendo todas las otras variables constantes, un incremento de 1 en el número de habitaciones se asocia con una disminución en el precio promedio de la vivienda de 23.71 millones de pesos.

se puede ver que el R-cuadrado es de 0.7487. Esto significa que alrededor del 74.87% de la variabilidad en el precio de las casas en la región norte se explica por las variables independientes incluidas en el modelo.

plot_ly(data = df_sur_apar_ok, x = ~habitaciones, y = ~preciom, type = "box")

En este caso puede que resulte un poco extraño el coeficiente de habitaciones, ya que este es negativo y no se espera que a mayor cantidad de habitaciones el precio de la vivienda sea menor, sin embargo obervando un grafico de cajas de el precio según el número de habitaciones, se puede ver que hay muchos apartamentos de 3 y 4 habitaciones que tienen precios de más de mil millones de pesos, mientras que los que tienen 6 habitaciones sólo uno supera este valor. Por lo tanto el coeficiente puede tener sentido

8.2 Validación de supuestos

Validacion de supuestos

En esta sección se validan los supuestos del modelo.

8.2.1. Normalidad

## Validacion de supuestos

res2<-mod2$residuals

## Normalidad de residuales
shapiro.test(res2)
## 
##  Shapiro-Wilk normality test
## 
## data:  res2
## W = 0.78088, p-value < 2.2e-16

En este caso, se utiliza la prueba de Shapiro-Wilk para determinar si los residuos se distribuyen normalmente. El resultado muestra que el valor de W es 0.78088 y el p-value es aproximadamente 0, lo que indica que los residuos no se distribuyen normalmente.

8.2.2. Homocedasticidad

## Homocedasticidad
bptest(mod2)
## 
##  studentized Breusch-Pagan test
## 
## data:  mod2
## BP = 848.53, df = 5, p-value < 2.2e-16

En este caso, se utiliza la prueba de Breusch-Pagan para determinar si los residuos son homocedásticos. El resultado muestra que el valor de BP es 848.53 y el p-value es aproximadamente 0, lo que indica que los residuos no son homocedásticos.

8.2.3. Indenpendencia

## Independecia
dwtest(mod2)
## 
##  Durbin-Watson test
## 
## data:  mod2
## DW = 1.4939, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0

En este caso, se utiliza la prueba de Durbin-Watson para determinar si los residuos son independientes. El resultado muestra que el valor de DW es 1.4939 y el p-value es aproximadamente 0, lo que indica que los residuos no son independientes y están correlacionados entre sí.

8.3. Predicción valor de la vivienda

Se requiere predecir el valor de una vivienda con las siguientes caracteristicas.

Caracteristicas Vivienda 2
Tipo Apartamento
área construida 300
parqueaderos 3
baños 3
habitaciones 5
estrato 5 o 6
zona Sur
crédito preaprobado 850 millones

Mediante la función predict se realiza la predicción, primero declarando un dataframe con los datos sobre la vivienda que se quiere vender.

## Prediccion

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


predict(mod2,new_dat2)
##        1        2 
## 669.5054 724.3007

El resultado indica que un apartamento con estas caracteristicas de estrato 5 valdria en promedio 669.5 millones de pesos y si es estrato 6 valdría 724.3 millones de pesos.

8.4. Potenciales ofertas

Siguiendo las mismas metodologias del modelo anterior se buscan ofertas potenciales para la vivienda solicitada.

## se incluyen predicciones y residuales en la base
df_sur_apar_ok$res = res2

df_sur_apar_ok$pred = mod2$fitted.values

df3 = head(df_sur_apar_ok[(df_sur_apar_ok$pred<=850 & df_sur_apar_ok$res<=0 &  df_sur_apar_ok$estrato %in% c(5, 6)),]%>% arrange(desc(pred)),5)

knitr::kable(df3[,-c(9,10,11,12,13,14)],"pipe")
zona estrato preciom areaconst parqueaderos banios habitaciones tipo pred
Zona Sur 6 660 210 4 5 3 Apartamento 805.5253
Zona Sur 6 650 223 3 5 3 Apartamento 766.9852
Zona Sur 5 670 300 3 5 6 Apartamento 745.7519
Zona Sur 6 699 164 4 5 3 Apartamento 742.9736
Zona Sur 5 500 330 2 4 4 Apartamento 727.7775

De esta manera la mejor oferta es el apartemento de la fila 3 ya que es el unico que cumple parcialmente con las especificaciones dadas y tiene un precio de 670 millones.

## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html

Resultados con el otro metodo:

df4=head(df_sur_apar_ok[df_sur_apar_ok$preciom<=850 & df_sur_apar_ok$res<=0,]%>% arrange(desc(preciom)),5)

knitr::kable(df4[,-c(9,10,11,12,13,14)],"pipe")
zona estrato preciom areaconst parqueaderos banios habitaciones tipo pred
Zona Sur 6 850 352 4 3 3 Apartamento 898.6585
Zona Sur 5 730 573 3 8 5 Apartamento 1290.6391
Zona Sur 6 699 164 4 5 3 Apartamento 742.9736
Zona Sur 5 690 486 2 4 4 Apartamento 939.9093
Zona Sur 5 670 300 3 5 6 Apartamento 745.7519

En este caso se pude ver que el apartamento anterior también se encuentra, solo que en la fila 5, con este metodo también se halla otra potencial oferta, que sería el apartamento de la fila 2, el cual cuenta con una mayor area construida y mayor cantidad de baños con un precio de 730 millones.

## Warning in proj4string(comunas): CRS object has comment, which is lost in output; in tests, see
## https://cran.r-project.org/web/packages/sp/vignettes/CRS_warnings.html