Modelos Estadísticos para la toma de decisiones,
Maestría en Ciencia de Datos - Javeriana Cali
Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.
Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.
Se realiza el cargue de la base de datos importada desde excel, debido a las dificultades que se presentaron posteriormente con el cargue desde Github.
# Importación y cargue de la base de datos Vivienda
library(readxl)
vivienda_ <- read_excel("C:/Users/User/Downloads/vivienda_.xlsx")
New names:
• `` -> `...1`
Se verifica la clase correspondiente a cada una de las variables que contiene el set de datos.
attach(vivienda_)
str(vivienda_)
tibble [8,322 × 14] (S3: tbl_df/tbl/data.frame)
$ ...1 : chr [1:8322] "1" "2" "3" "4" ...
$ id : num [1:8322] 1147 1169 1350 5992 1212 ...
$ zona : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
$ piso : chr [1:8322] NA NA NA "02" ...
$ estrato : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
$ preciom : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
$ areaconst : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
$ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
$ banios : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
$ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
$ tipo : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
$ barrio : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
$ longitud : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
$ latitud : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
# Se elimina la primera columna como numeración que se creo al cargar el set de datos desde Excel
vivienda_1 = subset(vivienda_, select = -...1)
vivienda_1
# A tibble: 8,322 × 13
id zona piso estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
<dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 1147 Zona… <NA> 3 250 70 1 3 6 Casa 20 de…
2 1169 Zona… <NA> 3 320 120 1 2 3 Casa 20 de…
3 1350 Zona… <NA> 3 350 220 2 2 4 Casa 20 de…
4 5992 Zona… 02 4 400 280 3 5 3 Casa 3 de …
5 1212 Zona… 01 5 260 90 1 2 3 Apar… acopi
6 1724 Zona… 01 5 240 87 1 3 3 Apar… acopi
7 2326 Zona… 01 4 220 52 2 2 3 Apar… acopi
8 4386 Zona… 01 5 310 137 2 3 4 Apar… acopi
9 1209 Zona… 02 5 320 150 2 4 6 Casa acopi
10 1592 Zona… 02 5 780 380 2 3 3 Casa acopi
# … with 8,312 more rows, 2 more variables: longitud <dbl>, latitud <dbl>, and
# abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
# Detección del número de datos NA por variable
data(vivienda_1)
Warning in data(vivienda_1): data set 'vivienda_1' not found
apply(X = is.na(vivienda_1), MARGIN = 2, FUN = sum)
id zona piso estrato preciom areaconst
3 3 2638 3 2 3
parqueaderos banios habitaciones tipo barrio longitud
1605 3 3 3 3 3
latitud
3
Las variables que mayor número de registros NA tienen son: Piso (2638) y Parqueaderos con (1605), procedemos a revisarlas para darles tratamiento.
En primera medida, la variable piso no resulta muy revelante para las solicitudes pues en ningún caso ni en las casas ni en los apartamentos, se solicitó un número determinado de pisos en casas o la ubicación en un piso en particular en apartamentos, por tanto se elimina del set de datos.
vivienda_1 = subset(vivienda_1, select = -piso)
head(vivienda_1,5)
# A tibble: 5 × 12
id zona estrato preciom areac…¹ parqu…² banios habit…³ tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 1147 Zona Oriente 3 250 70 1 3 6 Casa 20 de…
2 1169 Zona Oriente 3 320 120 1 2 3 Casa 20 de…
3 1350 Zona Oriente 3 350 220 2 2 4 Casa 20 de…
4 5992 Zona Sur 4 400 280 3 5 3 Casa 3 de …
5 1212 Zona Norte 5 260 90 1 2 3 Apar… acopi
# … with 2 more variables: longitud <dbl>, latitud <dbl>, and abbreviated
# variable names ¹areaconst, ²parqueaderos, ³habitaciones
Con respecto a la variable parqueaderos, se analizó la frecuencia de su dato para evaluar la posibilidad de imputar un dato a las variables NA.
summarytools::freq(vivienda_1$parqueaderos)
Frequencies
vivienda_1$parqueaderos
Type: Numeric
Freq % Valid % Valid Cum. % Total % Total Cum.
----------- ------ --------- -------------- --------- --------------
1 3155 46.970 46.970 37.912 37.912
2 2475 36.847 83.817 29.740 67.652
3 520 7.742 91.559 6.248 73.901
4 384 5.717 97.276 4.614 78.515
5 68 1.012 98.288 0.817 79.332
6 68 1.012 99.300 0.817 80.149
7 18 0.268 99.568 0.216 80.365
8 17 0.253 99.821 0.204 80.570
9 4 0.060 99.881 0.048 80.618
10 8 0.119 100.000 0.096 80.714
<NA> 1605 19.286 100.000
Total 8322 100.000 100.000 100.000 100.000
Con el ojetivo de aprovechar este conjunto de datos para la predicción de los modelos conforme a las solicitudes propuestas, en el caso de esta variable se le imputará el valor 0 a los registros de parqueaderos que aparecen en NA.
vivienda_1$parqueaderos = ifelse(is.na(vivienda_1$parqueaderos), 0, vivienda_1$parqueaderos)
summarytools::freq(vivienda_1$parqueaderos)
Frequencies
vivienda_1$parqueaderos
Type: Numeric
Freq % Valid % Valid Cum. % Total % Total Cum.
----------- ------ --------- -------------- --------- --------------
0 1605 19.286 19.286 19.286 19.286
1 3155 37.912 57.198 37.912 57.198
2 2475 29.740 86.938 29.740 86.938
3 520 6.248 93.187 6.248 93.187
4 384 4.614 97.801 4.614 97.801
5 68 0.817 98.618 0.817 98.618
6 68 0.817 99.435 0.817 99.435
7 18 0.216 99.652 0.216 99.652
8 17 0.204 99.856 0.204 99.856
9 4 0.048 99.904 0.048 99.904
10 8 0.096 100.000 0.096 100.000
<NA> 0 0.000 100.000
Total 8322 100.000 100.000 100.000 100.000
Una vez surtido el proceso anterior, se evalua si persiste aún la presencia de valores NA.
# Detección del número de datos NA por variable
data(vivienda_1)
Warning in data(vivienda_1): data set 'vivienda_1' not found
apply(X = is.na(vivienda_1), MARGIN = 2, FUN = sum)
id zona estrato preciom areaconst parqueaderos
3 3 3 2 3 0
banios habitaciones tipo barrio longitud latitud
3 3 3 3 3 3
Se eliminan los valores nulos restantes ya que son pocos y no limitaran la capacidad de predicción del conjunto de datos.
SNA_vivienda_1 = na.omit(vivienda_1)
apply(X= is.na(SNA_vivienda_1), MARGIN = 2, FUN = sum)
id zona estrato preciom areaconst parqueaderos
0 0 0 0 0 0
banios habitaciones tipo barrio longitud latitud
0 0 0 0 0 0
dim(SNA_vivienda_1)
[1] 8319 12
Se crea una nueva variable categórica para asignale a cada registro ya sea de casa o apartamento, la base correspondiente según la zona en la cual se encuentra ubicado.
SNA_vivienda_1$base <- ifelse(SNA_vivienda_1$zona =="Zona Norte" & SNA_vivienda_1$tipo =="Casa", "Base 1",
ifelse(SNA_vivienda_1$zona =="Zona Centro" & SNA_vivienda_1$tipo =="Casa", "Base 2",
ifelse(SNA_vivienda_1$zona =="Zona Oeste" & SNA_vivienda_1$tipo =="Casa", "Base 3",
ifelse(SNA_vivienda_1$zona =="Zona Oriente" & SNA_vivienda_1$tipo =="Casa", "Base 4",
ifelse(SNA_vivienda_1$zona == "Zona Sur" & SNA_vivienda_1$tipo == "Casa", "Base 5",
ifelse(SNA_vivienda_1$zona =="Zona Norte" & SNA_vivienda_1$tipo =="Apartamento", "Base 1",
ifelse(SNA_vivienda_1$zona =="Zona Centro" & SNA_vivienda_1$tipo =="Apartamento", "Base 2",
ifelse(SNA_vivienda_1$zona =="Zona Oeste" & SNA_vivienda_1$tipo =="Apartamento", "Base 3",
ifelse(SNA_vivienda_1$zona =="Zona Oriente" & SNA_vivienda_1$tipo =="Apartamento", "Base 4", ifelse(SNA_vivienda_1$zona == "Zona Sur" & SNA_vivienda_1$tipo == "Apartamento", "Base 5", "NA"
))))))))))
A continuación se construye el mapa con todos los registros de la Base de datos.
library(leaflet)
# Se filtra el total de viviendas por la categoría Casas
vivienda_casas <- subset(SNA_vivienda_1, tipo == "Casa")
# Se convierte la variable Base en un tipo de carácter
vivienda_casas$base <- as.character(vivienda_casas$base)
# Se le asigna un color específico a cada una de las categorías establecidas
colores <- colorFactor(palette = c("blue","red", "green","purple","orange"), domain = vivienda_casas$base)
# se crea el mapa con las Casas según su latitud y longitud según el color marcado en el paso anterior.
mapa <- leaflet(vivienda_casas) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = ~colores(base), radius = 4)
# Ver Mapa
mapa
Como se observa en el mapa anterior, la base de datos tiene una dificultad y es que muchas viviendas que indican su ubicación en una zona determinada, geográficamente se reflejan en otra, por ejemplo, las casas de la Zona Sur se encuentran representadas por el color naranja y en el mapa se observan muchos registros con este color en otras zonas de la ciudad.
Sobre esta nueva base de datos SNA_vivienda_1, se analizan los indicadores descriptivos de las variables cuantitativas.
library(summarytools)
summarytools::descr(SNA_vivienda_1[,4:8])
Descriptive Statistics
SNA_vivienda_1
N: 8319
areaconst banios habitaciones parqueaderos preciom
----------------- ----------- --------- -------------- -------------- ---------
Mean 174.93 3.11 3.61 1.48 433.90
Std.Dev 142.96 1.43 1.46 1.24 328.67
Min 30.00 0.00 0.00 0.00 58.00
Q1 80.00 2.00 3.00 1.00 220.00
Median 123.00 3.00 3.00 1.00 330.00
Q3 229.00 4.00 4.00 2.00 540.00
Max 1745.00 10.00 10.00 10.00 1999.00
MAD 84.51 1.48 1.48 1.48 207.56
IQR 149.00 2.00 1.00 1.00 320.00
CV 0.82 0.46 0.40 0.84 0.76
Skewness 2.69 0.93 1.63 1.65 1.85
SE.Skewness 0.03 0.03 0.03 0.03 0.03
Kurtosis 12.91 1.13 3.98 5.42 3.67
N.Valid 8319.00 8319.00 8319.00 8319.00 8319.00
Pct.Valid 100.00 100.00 100.00 100.00 100.00
Como se observa en los estadísticos descriptivos de la tabla anterior, las variables areaconst y preciom muestran altos niveles de desviación estándar y dentro de sus valores máximos frente a los valores registrados en el tercer cuartil (Q3) se aprecia la presencia de outliers.
library(ggplot2)
ggplot(SNA_vivienda_1, aes(x = areaconst, y = preciom))+ geom_point(color = "#4A708B")
El gráfico anterior muestra la relación que existe entre el precio de la vivienda y el area construida, sobre la cual se infiere una correlación positiva, pero ademas se observa un fenomeno particular, asi como existen vivienda con un area reducida y un precio elevado, tambien se evidencia lo opuesto, es decir, viviendas con un precio inferior a 1000 millones pero con areas superiores a 500 mts2.
A continuación se analiza la frecuencia de las variables Zona, Estrato Socioeconómico y Número de baños.
summarytools::freq(SNA_vivienda_1$zona, plain.ascii = FALSE, style = "rmarkdown")
### Frequencies
#### SNA_vivienda_1$zona
**Type:** Character
| | Freq | % Valid | % Valid Cum. | % Total | % Total Cum. |
|-----------------:|-----:|--------:|-------------:|--------:|-------------:|
| **Zona Centro** | 124 | 1.49 | 1.49 | 1.49 | 1.49 |
| **Zona Norte** | 1920 | 23.08 | 24.57 | 23.08 | 24.57 |
| **Zona Oeste** | 1198 | 14.40 | 38.97 | 14.40 | 38.97 |
| **Zona Oriente** | 351 | 4.22 | 43.19 | 4.22 | 43.19 |
| **Zona Sur** | 4726 | 56.81 | 100.00 | 56.81 | 100.00 |
| **\<NA\>** | 0 | | | 0.00 | 100.00 |
| **Total** | 8319 | 100.00 | 100.00 | 100.00 | 100.00 |
En cuanto a la distribución de viviendas por zona, la mayor concentración de estas se ubica en la zona Sur con 4726 con 56,81%, seguido de la zona Norte con 1920 que representan un 23,08% del total y la zona Oeste con el 14,40%.
summarytools::freq(SNA_vivienda_1$estrato, plain.ascii = FALSE, style = "rmarkdown")
### Frequencies
#### SNA_vivienda_1$estrato
**Type:** Numeric
| | Freq | % Valid | % Valid Cum. | % Total | % Total Cum. |
|-----------:|-----:|--------:|-------------:|--------:|-------------:|
| **3** | 1453 | 17.47 | 17.47 | 17.47 | 17.47 |
| **4** | 2129 | 25.59 | 43.06 | 25.59 | 43.06 |
| **5** | 2750 | 33.06 | 76.11 | 33.06 | 76.11 |
| **6** | 1987 | 23.89 | 100.00 | 23.89 | 100.00 |
| **\<NA\>** | 0 | | | 0.00 | 100.00 |
| **Total** | 8319 | 100.00 | 100.00 | 100.00 | 100.00 |
Frente al estrato socioeconómico de las viviendas de la base de datos, un 33,06% se ubica en estrato 5, seguido de un 25,59% de viviendas en estrato 4 y 23,89% en estrato 6.
summarytools::freq(SNA_vivienda_1$banios,
plain.ascii = FALSE, style = "rmarkdown")
### Frequencies
#### SNA_vivienda_1$banios
**Type:** Numeric
| | Freq | % Valid | % Valid Cum. | % Total | % Total Cum. |
|-----------:|-----:|--------:|-------------:|--------:|-------------:|
| **0** | 45 | 0.54 | 0.54 | 0.54 | 0.54 |
| **1** | 496 | 5.96 | 6.50 | 5.96 | 6.50 |
| **2** | 2946 | 35.41 | 41.92 | 35.41 | 41.92 |
| **3** | 1993 | 23.96 | 65.87 | 23.96 | 65.87 |
| **4** | 1456 | 17.50 | 83.38 | 17.50 | 83.38 |
| **5** | 890 | 10.70 | 94.07 | 10.70 | 94.07 |
| **6** | 314 | 3.77 | 97.85 | 3.77 | 97.85 |
| **7** | 107 | 1.29 | 99.13 | 1.29 | 99.13 |
| **8** | 48 | 0.58 | 99.71 | 0.58 | 99.71 |
| **9** | 15 | 0.18 | 99.89 | 0.18 | 99.89 |
| **10** | 9 | 0.11 | 100.00 | 0.11 | 100.00 |
| **\<NA\>** | 0 | | | 0.00 | 100.00 |
| **Total** | 8319 | 100.00 | 100.00 | 100.00 | 100.00 |
Con respecto al número de baños con los que cuentan las viviendas en mención, un 35,41% tienen 2 baños, seguido de un 23,96% con 3 baños y un 17,50% de las viviendas con 4 baños.
Se construye una base de datos de Casas para cada una de las zonas y se presenta la base1, con los elementos de la primera consulta.
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
# A tibble: 3 × 13
id zona estrato preciom areaconst parqu…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 1209 Zona Norte 5 320 150 2 4 6 Casa acopi
2 1592 Zona Norte 5 780 380 2 3 3 Casa acopi
3 4057 Zona Norte 6 750 445 0 7 6 Casa acopi
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se construye el mapa de casas correpondientes a la zona norte de Cali.
library(leaflet)
# Filtrar solo las viviendas de tipo Casa
vivienda_casasNorte <- subset(SNA_vivienda_1, tipo == "Casa" & zona == "Zona Norte")
# Convertir la variable "base" en una variable de tipo carácter
vivienda_casasNorte$base <- as.character(vivienda_casasNorte$base)
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa <- leaflet(vivienda_casasNorte) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "blue", radius = 4)
# Mostrar el mapa
mapa
str(base1)
tibble [722 × 13] (S3: tbl_df/tbl/data.frame)
$ id : num [1:722] 1209 1592 4057 4460 6081 ...
$ zona : chr [1:722] "Zona Norte" "Zona Norte" "Zona Norte" "Zona Norte" ...
$ estrato : num [1:722] 5 5 6 4 5 4 5 5 3 3 ...
$ preciom : num [1:722] 320 780 750 625 750 600 420 490 230 190 ...
$ areaconst : num [1:722] 150 380 445 355 237 160 200 118 160 435 ...
$ parqueaderos: num [1:722] 2 2 0 3 2 1 4 2 0 0 ...
$ banios : num [1:722] 4 3 7 5 6 4 4 4 2 0 ...
$ habitaciones: num [1:722] 6 3 6 5 6 5 5 4 3 0 ...
$ tipo : chr [1:722] "Casa" "Casa" "Casa" "Casa" ...
$ barrio : chr [1:722] "acopi" "acopi" "acopi" "acopi" ...
$ longitud : num [1:722] -76.5 -76.5 -76.5 -76.5 -76.5 ...
$ latitud : num [1:722] 3.48 3.49 3.39 3.41 3.37 ...
$ base : chr [1:722] "Base 1" "Base 1" "Base 1" "Base 1" ...
- attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
La base de datos para la estimación del primer modelo cuenta con un total de 722 registros.
library(summarytools)
summarytools::descr(base1[,4:8])
Descriptive Statistics
base1
N: 722
areaconst banios habitaciones parqueaderos preciom
----------------- ----------- -------- -------------- -------------- ---------
Mean 264.85 3.56 4.51 1.31 445.91
Std.Dev 167.17 1.52 1.83 1.53 268.36
Min 30.00 0.00 0.00 0.00 89.00
Q1 140.00 2.00 3.00 0.00 260.00
Median 240.00 3.00 4.00 1.00 390.00
Q3 337.00 4.00 5.00 2.00 550.00
Max 1440.00 10.00 10.00 10.00 1940.00
MAD 146.78 1.48 1.48 1.48 220.17
IQR 196.75 2.00 2.00 2.00 288.75
CV 0.63 0.43 0.41 1.16 0.60
Skewness 1.85 0.67 0.64 1.60 1.76
SE.Skewness 0.09 0.09 0.09 0.09 0.09
Kurtosis 6.24 1.00 1.18 3.55 4.65
N.Valid 722.00 722.00 722.00 722.00 722.00
Pct.Valid 100.00 100.00 100.00 100.00 100.00
Como se observa en la tabla anterior, para el caso del areaconst se registra una desviación estándar de 167,17, con un area promedio de casas de 264,85 donde un 50% de las viviendas tiene un area entre 140 y 337 mts2, su valor máximo es de 1440.
Para el caso del preciom se presenta una desviación estándar de 268,36, con un valor promedio de casas de 445,91 millones donde un 50% de las viviendas tiene un area entre 260 y 550 millones, su valor máximo es de 1940.
Attaching package: 'plotly'
The following object is masked from 'package:ggplot2':
last_plot
The following object is masked from 'package:stats':
filter
The following object is masked from 'package:graphics':
layout
plot_ly(base1, x = ~ preciom, type = "histogram")
base1 %>%
plot_ly(y= ~ preciom, x = "freq") %>%
add_boxplot()
plot_ly(base1, x = ~ areaconst, type = "histogram")
base1 %>%
plot_ly(y= ~ areaconst, x = "freq") %>%
add_boxplot()
plot_ly(base1, x = ~ banios, type = "histogram")
base1 %>%
plot_ly(y= ~ banios, x = "freq") %>%
add_boxplot()
plot_ly(base1, x = ~ habitaciones, type = "histogram")
base1 %>%
plot_ly(y= ~ habitaciones, x = "freq") %>%
add_boxplot()
library(GGally)
Registered S3 method overwritten by 'GGally':
method from
+.gg ggplot2
corr = base1[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")]
ggpairs(corr, title="GGally")
En cuanto a la correlación de las variables con respecto al precio de la vivienda, en todos los casos esta es positiva pero se destaca su mayor fortaleza en area construida 0,731, Estrato 0,612 y Baños 0,523.
modelo_casas = lm(preciom ~ areaconst + estrato + parqueaderos + banios + habitaciones, data = base1)
summary(modelo_casas)
Call:
lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios +
habitaciones, data = base1)
Residuals:
Min 1Q Median 3Q Max
-964.04 -80.10 -17.08 50.06 1069.45
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -236.47551 30.36582 -7.788 2.40e-14 ***
areaconst 0.82677 0.04368 18.926 < 2e-16 ***
estrato 86.42579 7.39747 11.683 < 2e-16 ***
parqueaderos -1.67672 4.31505 -0.389 0.698
banios 26.97978 5.34384 5.049 5.65e-07 ***
habitaciones 1.44443 4.16411 0.347 0.729
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 159.1 on 716 degrees of freedom
Multiple R-squared: 0.6508, Adjusted R-squared: 0.6484
F-statistic: 266.9 on 5 and 716 DF, p-value: < 2.2e-16
En esta primera estimación se alcanza un coeficiente de determinación de Rˆ2 de 65,08%, lo que se traduce en que un 73,65% de las variaciones del precio son explicadas por las variables del modelo. Con respecto a los estimadores de los parametros se destaca que el intercepto y todas las variables excepto parqueaderos y habitaciones son significativas para el modelo.
COmo propuesta se sugiere eliminarlas para una estimación más precisa y adicionalmente, considerar aplicar un log a la variable precios para suavizar la serie que tiene variables atipicas.
par(mfrow= c(2,2))
plot(modelo_casas)
e = modelo_casas$residuals
# Ho: Errores tienen una distribución normal
shapiro.test(e)
Shapiro-Wilk normality test
data: e
W = 0.83823, p-value < 2.2e-16
Como el p-valor obtiene un valor de 2.2e-16 inferior al nivel de significancia (0,05), por tanto se rechaza la hipótesis nula indicando que los residuos del modelo no tienen una distribución normal, comportamiento que podría estar relacionado por la misma distribución de la variable Precio de las casas.
Loading required package: zoo
Attaching package: 'zoo'
The following objects are masked from 'package:base':
as.Date, as.Date.numeric
Durbin-Watson test
data: modelo_casas
DW = 1.633, p-value = 3.068e-07
alternative hypothesis: true autocorrelation is greater than 0
Según el test de Durbin - Watson, como el p-valor es inferior al nivel de significancia se rechaza la hipótesis nula indicando que los errores no son independientes.
# Ho: La varianza de los errores es constante
gqtest(modelo_casas)
Goldfeld-Quandt test
data: modelo_casas
GQ = 1.1432, df1 = 355, df2 = 355, p-value = 0.104
alternative hypothesis: variance increases from segment 1 to 2
A partir del test Goldfeld-Quandt se obtiene un p-valor de 0.104 el cual es superior al nivel de significancia (0,05), por tanto, se acepta la hipótesis nula indicando que la varianza de los errores es constante.
Se exponen los requerimientos del cliente para la primera consulta.
requerimientos_1 = data.frame(
estrato=c(4,5),
areaconst=c(200,200),
parqueaderos=c(1,1),
banios=c(2,2),
habitaciones=c(4,4)
)
requerimientos_1
estrato areaconst parqueaderos banios habitaciones
1 4 200 1 2 4
2 5 200 1 2 4
predict(modelo_casas,requerimientos_1)
1 2
332.6430 419.0688
Los resultados de la estimación determinan que para las características de casa que el cliente solicita el precio promedio en el estrato 4 es de 332,64 millones y en el estrato 5 este valor asciende a 419,07.
Se construye una base de datos para determinar las potenciales casas dentro el estrato 4 que cumplen con las condiciones exigidas.
base1_casas_e4 = base1 %>%
dplyr::filter(estrato==4,areaconst>=200,parqueaderos>=1,banios>=2,habitaciones==4,preciom<=350)
base1_casas_e4
# A tibble: 7 × 13
id zona estrato preciom areaconst parqu…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 4458 Zona Norte 4 315 270 2 4 4 Casa el bo…
2 3352 Zona Norte 4 335 300 3 4 4 Casa el bo…
3 937 Zona Norte 4 350 280 2 3 4 Casa la me…
4 1108 Zona Norte 4 330 260 1 3 4 Casa la me…
5 1144 Zona Norte 4 320 200 2 4 4 Casa la me…
6 2544 Zona Norte 4 340 264. 2 4 4 Casa vipasa
7 1822 Zona Norte 4 340 295 2 2 4 Casa vipasa
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se depura sobre aquellas que ofrezcan la mayor area construida.
base1_casas_e4_opciones = base1_casas_e4 %>%
dplyr::filter(id %in% c(3352,1822, 4458, 2544, 937))
head(base1_casas_e4_opciones)
# A tibble: 5 × 13
id zona estrato preciom areaconst parqu…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 4458 Zona Norte 4 315 270 2 4 4 Casa el bo…
2 3352 Zona Norte 4 335 300 3 4 4 Casa el bo…
3 937 Zona Norte 4 350 280 2 3 4 Casa la me…
4 2544 Zona Norte 4 340 264. 2 4 4 Casa vipasa
5 1822 Zona Norte 4 340 295 2 2 4 Casa vipasa
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se construye una base de datos para determinar las potenciales casas dentro el estrato 5 que cumplen con las condiciones exigidas.
base1_casas_e5 = base1 %>%
dplyr::filter(estrato==5,areaconst>=200,parqueaderos>=1,banios>=2,habitaciones==4,preciom<=350)
base1_casas_e5
# A tibble: 10 × 13
id zona estrato preciom areaco…¹ parqu…² banios habit…³ tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 4210 Zona Norte 5 350 200 3 3 4 Casa el bo…
2 4800 Zona Norte 5 340 250 2 4 4 Casa el bo…
3 819 Zona Norte 5 350 264 2 3 4 Casa la fl…
4 1343 Zona Norte 5 320 200 2 4 4 Casa la fl…
5 3053 Zona Norte 5 320 230 2 4 4 Casa la fl…
6 1163 Zona Norte 5 350 216 2 2 4 Casa la me…
7 1849 Zona Norte 5 330 246 2 4 4 Casa prado…
8 1887 Zona Norte 5 340 203 2 3 4 Casa vipasa
9 1842 Zona Norte 5 350 240 2 3 4 Casa vipasa
10 1943 Zona Norte 5 350 346 1 2 4 Casa vipasa
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
Se depura sobre aquellas que ofrezcan la mayor area construida.
base1_casas_e5_opciones = base1_casas_e5 %>%
dplyr::filter(id %in% c(1943, 819, 4800, 1849, 1842))
head(base1_casas_e4_opciones)
# A tibble: 5 × 13
id zona estrato preciom areaconst parqu…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 4458 Zona Norte 4 315 270 2 4 4 Casa el bo…
2 3352 Zona Norte 4 335 300 3 4 4 Casa el bo…
3 937 Zona Norte 4 350 280 2 3 4 Casa la me…
4 2544 Zona Norte 4 340 264. 2 4 4 Casa vipasa
5 1822 Zona Norte 4 340 295 2 2 4 Casa vipasa
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Posteriormente, se integran las dos bases para comparar las mejores alternativas dentro del parametro de mayor area frente al precio.
base_opciones_caso1<- rbind(base1_casas_e4_opciones,base1_casas_e5_opciones)
head(base_opciones_caso1, 10)
# A tibble: 10 × 13
id zona estrato preciom areaco…¹ parqu…² banios habit…³ tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 4458 Zona Norte 4 315 270 2 4 4 Casa el bo…
2 3352 Zona Norte 4 335 300 3 4 4 Casa el bo…
3 937 Zona Norte 4 350 280 2 3 4 Casa la me…
4 2544 Zona Norte 4 340 264. 2 4 4 Casa vipasa
5 1822 Zona Norte 4 340 295 2 2 4 Casa vipasa
6 4800 Zona Norte 5 340 250 2 4 4 Casa el bo…
7 819 Zona Norte 5 350 264 2 3 4 Casa la fl…
8 1849 Zona Norte 5 330 246 2 4 4 Casa prado…
9 1842 Zona Norte 5 350 240 2 3 4 Casa vipasa
10 1943 Zona Norte 5 350 346 1 2 4 Casa vipasa
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹areaconst, ²parqueaderos, ³habitaciones
Se seleccionan las 5 mejores alternativas para presentar al cliente.
base_opciones_caso1_final = base_opciones_caso1 %>%
dplyr::filter(id %in% c(1943, 3352, 1822, 937, 4458))
Se elabora el mapa para la presentación al cliente.
library(leaflet)
mapa_casas <- leaflet(base_opciones_caso1_final) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "blue", radius = 4)
# Mostrar el mapa
mapa_casas
Se construye una base de datos de Apartamentos para las zonas norte, centro y sur, y se presenta la Abase3 con los elementos de la segunda consulta.
# Base de datos para otras zonas
library(dplyr)
Abase1 = SNA_vivienda_1 %>% filter(tipo =="Apartamento", zona == "Zona Norte")
Abase2 = SNA_vivienda_1 %>% filter(tipo =="Apartamento", zona == "Zona Centro")
Abase3 = SNA_vivienda_1 %>% filter(tipo =="Apartamento", zona == "Zona Sur")
head(Abase3, 3)
# A tibble: 3 × 13
id zona estrato preciom areaconst parquea…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 5098 Zona Sur 4 290 96 1 2 3 Apar… acopi
2 698 Zona Sur 3 78 40 1 1 2 Apar… aguab…
3 8199 Zona Sur 6 875 194 2 5 3 Apar… aguac…
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se construye el mapa de apartamentos correpondientes a la zona sur de Cali.
library(leaflet)
# Filtrar solo las viviendas de tipo Casa
vivienda_Apto_Sur <- subset(SNA_vivienda_1, tipo == "Apartamento" & zona == "Zona Sur")
# Convertir la variable "base" en una variable de tipo carácter
vivienda_Apto_Sur$base <- as.character(vivienda_Apto_Sur$base)
# Crear un mapa con las viviendas marcadas por su latitud y longitud y con el color dictado por la variable "base"
mapa_Apto <- leaflet(vivienda_Apto_Sur) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "red", radius = 4)
# Mostrar el mapa
mapa_Apto
str(Abase3)
tibble [2,787 × 13] (S3: tbl_df/tbl/data.frame)
$ id : num [1:2787] 5098 698 8199 1241 5370 ...
$ zona : chr [1:2787] "Zona Sur" "Zona Sur" "Zona Sur" "Zona Sur" ...
$ estrato : num [1:2787] 4 3 6 3 3 4 3 3 3 4 ...
$ preciom : num [1:2787] 290 78 875 135 135 220 210 105 115 220 ...
$ areaconst : num [1:2787] 96 40 194 117 78 75 72 68 58 84 ...
$ parqueaderos: num [1:2787] 1 1 2 0 0 1 2 0 1 0 ...
$ banios : num [1:2787] 2 1 5 2 1 2 2 2 2 2 ...
$ habitaciones: num [1:2787] 3 2 3 3 3 3 3 3 2 3 ...
$ tipo : chr [1:2787] "Apartamento" "Apartamento" "Apartamento" "Apartamento" ...
$ barrio : chr [1:2787] "acopi" "aguablanca" "aguacatal" "alameda" ...
$ longitud : num [1:2787] -76.5 -76.5 -76.6 -76.5 -76.5 ...
$ latitud : num [1:2787] 3.45 3.4 3.46 3.44 3.44 ...
$ base : chr [1:2787] "Base 5" "Base 5" "Base 5" "Base 5" ...
- attr(*, "na.action")= 'omit' Named int [1:3] 8320 8321 8322
..- attr(*, "names")= chr [1:3] "8320" "8321" "8322"
La base de datos para la estimación del segundo modelo cuenta con un total de 2787 registros.
library(summarytools)
summarytools::descr(Abase3[,4:8])
Descriptive Statistics
Abase3
N: 2787
areaconst banios habitaciones parqueaderos preciom
----------------- ----------- --------- -------------- -------------- ---------
Mean 97.47 2.49 2.97 1.21 297.29
Std.Dev 52.57 0.93 0.63 0.79 191.55
Min 40.00 0.00 0.00 0.00 75.00
Q1 65.00 2.00 3.00 1.00 175.00
Median 85.00 2.00 3.00 1.00 245.00
Q3 110.00 3.00 3.00 2.00 335.00
Max 932.00 8.00 6.00 10.00 1750.00
MAD 31.13 0.00 0.00 0.00 114.16
IQR 45.00 1.00 0.00 1.00 160.00
CV 0.54 0.38 0.21 0.66 0.64
Skewness 4.33 1.18 -0.03 1.51 2.63
SE.Skewness 0.05 0.05 0.05 0.05 0.05
Kurtosis 38.13 1.77 2.74 11.20 10.44
N.Valid 2787.00 2787.00 2787.00 2787.00 2787.00
Pct.Valid 100.00 100.00 100.00 100.00 100.00
Como se observa en la tabla anterior, para el caso del areaconst se registra una desviación estándar de 52,57, con un area promedio de apartamentos de 97,47 donde un 50% de las viviendas tiene un area entre 65 y 110 mts2, su valor máximo es de 932.
Para el caso del preciom se presenta una desviación estándar de 191,55, con un valor promedio de apartamentos de 297,29 millonrd donde un 50% de las viviendas tiene un area entre 175 y 335 millones, su valor máximo es de 1750.
library(plotly)
plot_ly(Abase3, x = ~ preciom, type = "histogram")
Abase3 %>%
plot_ly(y= ~ preciom, x = "freq") %>%
add_boxplot()
plot_ly(Abase3, x = ~ areaconst, type = "histogram")
Abase3 %>%
plot_ly(y= ~ areaconst, x = "freq") %>%
add_boxplot()
plot_ly(Abase3, x = ~ banios, type = "histogram")
Abase3 %>%
plot_ly(y= ~ banios, x = "freq") %>%
add_boxplot()
plot_ly(Abase3, x = ~ habitaciones, type = "histogram")
Abase3 %>%
plot_ly(y= ~ habitaciones, x = "freq") %>%
add_boxplot()
library(GGally)
corr = Abase3[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")]
ggpairs(corr, title="GGally")
En cuanto a la correlación de las variables con respecto al precio de los apartamentos, en todos los casos esta es positiva pero se destaca su mayor fortaleza en area construida 0,758, Baños 0,720 y Estrato 0,673 .
modelo_aptos = lm(preciom ~ areaconst + estrato + parqueaderos + banios + habitaciones, data = Abase3)
summary(modelo_aptos)
Call:
lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios +
habitaciones, data = Abase3)
Residuals:
Min 1Q Median 3Q Max
-1252.31 -42.15 -2.06 36.32 934.06
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -221.04614 13.47771 -16.401 < 2e-16 ***
areaconst 1.46061 0.04876 29.956 < 2e-16 ***
estrato 57.00608 2.79648 20.385 < 2e-16 ***
parqueaderos 48.36353 3.02343 15.996 < 2e-16 ***
banios 48.60871 3.04050 15.987 < 2e-16 ***
habitaciones -22.71789 3.39549 -6.691 2.68e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 95.17 on 2781 degrees of freedom
Multiple R-squared: 0.7536, Adjusted R-squared: 0.7531
F-statistic: 1701 on 5 and 2781 DF, p-value: < 2.2e-16
En esta primera estimación se alcanza un coeficiente de determinación de Rˆ2 de 75,36%, lo que se traduce en que un 75,36% de las variaciones del precio son explicadas por las variables del modelo. Con respecto a los estimadores de los parametros se destaca que el intercepto y todas las variables son significativas para el modelo.
COmo propuesta para una estimación más precisa, es aplicar un log a la variable precios para suavizar la serie que tiene variables atipicas.
par(mfrow= c(2,2))
plot(modelo_aptos)
e = modelo_aptos$residuals
# Ho: Errores tienen una distribución normal
shapiro.test(e)
Shapiro-Wilk normality test
data: e
W = 0.78157, p-value < 2.2e-16
Como el p-valor obtiene un valor de 2.2e-16 inferior al nivel de significancia (0,05), por tanto se rechaza la hipótesis nula indicando que los residuos del modelo no tienen una distribución normal, comportamiento que podría estar relacionado por la misma distribución de la variable Precio de los apartamentos.
library(lmtest)
# Ho: Los errores son independientes (no estan relacionados)
dwtest(modelo_aptos)
Durbin-Watson test
data: modelo_aptos
DW = 1.5041, p-value < 2.2e-16
alternative hypothesis: true autocorrelation is greater than 0
Según el test de Durbin - Watson, como el p-valor es inferior al nivel de significancia se rechaza la hipótesis nula indicando que los errores no son independientes.
# Ho: La varianza de los errores es constante
gqtest(modelo_aptos)
Goldfeld-Quandt test
data: modelo_aptos
GQ = 0.93375, df1 = 1388, df2 = 1387, p-value = 0.8991
alternative hypothesis: variance increases from segment 1 to 2
A partir del test Goldfeld-Quandt se obtiene un p-valor de 0.8991 el cual es superior al nivel de significancia (0,05), por tanto, se acepta la hipótesis nula indicando que la varianza de los errores es constante.
Se exponen los requerimientos del cliente para la segunda consulta.
requerimientos_2 = data.frame(
estrato=c(5,6),
areaconst=c(300,300),
parqueaderos=c(3,3),
banios=c(3,3),
habitaciones=c(5,5)
)
requerimientos_2
estrato areaconst parqueaderos banios habitaciones
1 5 300 3 3 5
2 6 300 3 3 5
predict(modelo_aptos,requerimientos_2)
1 2
679.4951 736.5012
Los resultados de la estimación determinan que para las características de apartamento que el cliente solicita el precio promedio en el estrato 5 es de 679,50 millones y en el estrato 6 este valor asciende a 736,50.
Se construye una base de datos para determinar las potenciales apartamentos dentro el estrato 5 que cumplen con las condiciones exigidas.
Abase3_aptos_e5 = Abase3 %>%
dplyr::filter(estrato==5,areaconst>=300,parqueaderos>=3,banios>=3,habitaciones==5,preciom<=850)
Abase3_aptos_e5
# A tibble: 1 × 13
id zona estrato preciom areaconst parquea…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 7182 Zona Sur 5 730 573 3 8 5 Apar… guada…
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se construye una base de datos para determinar las potenciales apartamentos dentro el estrato 6 que cumplen con las condiciones exigidas.
Abase3_aptos_e6 = Abase3 %>%
dplyr::filter(estrato==6,areaconst>=300,parqueaderos>=3,banios>=3,habitaciones==5,preciom<=850)
Abase3_aptos_e6
# A tibble: 0 × 13
# … with 13 variables: id <dbl>, zona <chr>, estrato <dbl>, preciom <dbl>,
# areaconst <dbl>, parqueaderos <dbl>, banios <dbl>, habitaciones <dbl>,
# tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>, base <chr>
Posteriormente, se integran los resultados de cada base que para este caso solo cumple un registro del estrato 5.
base_opciones_caso2<- rbind(Abase3_aptos_e5,Abase3_aptos_e6)
head(base_opciones_caso2)
# A tibble: 1 × 13
id zona estrato preciom areaconst parquea…¹ banios habit…² tipo barrio
<dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 7182 Zona Sur 5 730 573 3 8 5 Apar… guada…
# … with 3 more variables: longitud <dbl>, latitud <dbl>, base <chr>, and
# abbreviated variable names ¹parqueaderos, ²habitaciones
Se elabora el mapa para la presentación al cliente, con la mejor alternativa según sus requerimientos.
library(leaflet)
mapa_aptos_ <- leaflet(base_opciones_caso2) %>%
addTiles() %>%
addCircleMarkers(lng = ~longitud, lat = ~latitud, color = "red", radius = 4)
# Mostrar el mapa
mapa_aptos_