Importamos todo lo que necesitamos

Mostramos las primeras 10 filas de nuestro df, para verificar si debemos hacer algun proceso de limpieza

data(vivienda)
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   id = col_double(),
##   ..   zona = col_character(),
##   ..   piso = col_character(),
##   ..   estrato = col_double(),
##   ..   preciom = col_double(),
##   ..   areaconst = col_double(),
##   ..   parqueaderos = col_double(),
##   ..   banios = col_double(),
##   ..   habitaciones = col_double(),
##   ..   tipo = col_character(),
##   ..   barrio = col_character(),
##   ..   longitud = col_double(),
##   ..   latitud = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>
head(vivienda,10)
## # A tibble: 10 × 13
##       id zona   piso  estrato preciom areaconst parqueaderos banios habitaciones
##    <dbl> <chr>  <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
##  1  1147 Zona … <NA>        3     250        70            1      3            6
##  2  1169 Zona … <NA>        3     320       120            1      2            3
##  3  1350 Zona … <NA>        3     350       220            2      2            4
##  4  5992 Zona … 02          4     400       280            3      5            3
##  5  1212 Zona … 01          5     260        90            1      2            3
##  6  1724 Zona … 01          5     240        87            1      3            3
##  7  2326 Zona … 01          4     220        52            2      2            3
##  8  4386 Zona … 01          5     310       137            2      3            4
##  9  1209 Zona … 02          5     320       150            2      4            6
## 10  1592 Zona … 02          5     780       380            2      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
md.pattern(vivienda, rotate.names = TRUE)

##      preciom id zona estrato areaconst banios habitaciones tipo barrio longitud
## 4808       1  1    1       1         1      1            1    1      1        1
## 1909       1  1    1       1         1      1            1    1      1        1
## 876        1  1    1       1         1      1            1    1      1        1
## 726        1  1    1       1         1      1            1    1      1        1
## 1          1  0    0       0         0      0            0    0      0        0
## 2          0  0    0       0         0      0            0    0      0        0
##            2  3    3       3         3      3            3    3      3        3
##      latitud parqueaderos piso     
## 4808       1            1    1    0
## 1909       1            1    0    1
## 876        1            0    1    1
## 726        1            0    0    2
## 1          0            0    0   12
## 2          0            0    0   13
##            3         1605 2638 4275

Como nos damos cuenta que nuestros datos tiene NA, vamos a limpiarlos.

#Quitamos los que tienen valores faltantes en la variable ID
data <- vivienda[!is.na(vivienda$id), ]

# Dejamos solos los que tengan habitaciones y baños distintos de cero
data <- data %>%
  filter(habitaciones != 0 & banios != 0)

md.pattern(data, rotate.names = TRUE)

##      id zona estrato preciom areaconst banios habitaciones tipo barrio longitud
## 4787  1    1       1       1         1      1            1    1      1        1
## 1901  1    1       1       1         1      1            1    1      1        1
## 863   1    1       1       1         1      1            1    1      1        1
## 692   1    1       1       1         1      1            1    1      1        1
##       0    0       0       0         0      0            0    0      0        0
##      latitud parqueaderos piso     
## 4787       1            1    1    0
## 1901       1            1    0    1
## 863        1            0    1    1
## 692        1            0    0    2
##            0         1555 2593 4148

Llenaremos los registros NA faltantes asumiendo que son de 1 solo piso, y que no tienen parqueadero. Al final confirmaremos que no tenemos datos faltantes

data$piso <- ifelse(is.na(data$piso), 1, data$piso)
data$parqueaderos <- ifelse(is.na(data$parqueaderos), 0, data$parqueaderos)
md.pattern(data, rotate=TRUE)
##  /\     /\
## {  `---'  }
## {  O   O  }
## ==>  V <==  No need for mice. This data set is completely observed.
##  \  \|/  /
##   `-----'

##      id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## 8243  1    1    1       1       1         1            1      1            1
##       0    0    0       0       0         0            0      0            0
##      tipo barrio longitud latitud  
## 8243    1      1        1       1 0
##         0      0        0       0 0
head(data, 10)
## # A tibble: 10 × 13
##       id zona   piso  estrato preciom areaconst parqueaderos banios habitaciones
##    <dbl> <chr>  <chr>   <dbl>   <dbl>     <dbl>        <dbl>  <dbl>        <dbl>
##  1  1147 Zona … 1           3     250        70            1      3            6
##  2  1169 Zona … 1           3     320       120            1      2            3
##  3  1350 Zona … 1           3     350       220            2      2            4
##  4  5992 Zona … 02          4     400       280            3      5            3
##  5  1212 Zona … 01          5     260        90            1      2            3
##  6  1724 Zona … 01          5     240        87            1      3            3
##  7  2326 Zona … 01          4     220        52            2      2            3
##  8  4386 Zona … 01          5     310       137            2      3            4
##  9  1209 Zona … 02          5     320       150            2      4            6
## 10  1592 Zona … 02          5     780       380            2      3            3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>

Ahora si podemos realizar el análisis solicitado

1. Realice un filtro a la base de datos e incluya solo las ofertas de :base1: casas, de la zona norte de la ciudad. Presente los primeros 3 registros de las bases y algunas tablas que comprueben la consulta. (Adicional un mapa con los puntos de las bases. Discutir si todos los puntos se ubican en la zona correspondiente o se presentan valores en otras zonas, por que?).

# Aplicamos el filtro
base1 = subset(data, tipo == "Casa" & zona == "Zona Norte")
# Mostramos los primeros 3 registros como se solicita
kable(head(base1, 3))
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
1209 Zona Norte 02 5 320 150 2 4 6 Casa acopi -76.51341 3.47968
1592 Zona Norte 02 5 780 380 2 3 3 Casa acopi -76.51674 3.48721
4057 Zona Norte 02 6 750 445 0 7 6 Casa acopi -76.52950 3.38527

Confirmamos entonces que el filtro se aplico correctamente y ahora solo tenemos en base1 casas de la zona norte.

Para mostrar el mapa usaremos la librería Leaflet

# Calculamos el centro de las coordenadas de la base 1
centro_lng <- mean(base1$longitud)
centro_lat <- mean(base1$latitud)

print(centro_lat)
## [1] 3.46123
print(centro_lng)
## [1] -76.5169
# Creamos un mapa con Leaflet
mapaBase1 <- leaflet() %>%
  addTiles() %>%
  setView(lng = centro_lng, lat = centro_lat, zoom = 12) %>%
  addCircleMarkers(data = base1, 
                   lng = ~longitud, 
                   lat = ~latitud, 
                   color = "green")

# Mostramos el mapa
mapaBase1

Como podemos observar, aunque la gran mayoría de las propiedades estan en zona norte, tenemos algunas distribuidas de forma atípica. Puede deberse a errores en los datos, como por ejemplo una mala digitación u obtención de los mismos.

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

Exploremos las variables en cuestión:

summary(base1[c("preciom", "zona", "areaconst", "estrato", "banios", "habitaciones")])
##     preciom           zona             areaconst         estrato     
##  Min.   :  89.0   Length:700         Min.   :  30.0   Min.   :3.000  
##  1st Qu.: 256.0   Class :character   1st Qu.: 140.0   1st Qu.:3.000  
##  Median : 390.0   Mode  :character   Median : 240.0   Median :4.000  
##  Mean   : 443.2                      Mean   : 262.1   Mean   :4.206  
##  3rd Qu.: 550.0                      3rd Qu.: 336.0   3rd Qu.:5.000  
##  Max.   :1940.0                      Max.   :1440.0   Max.   :6.000  
##      banios      habitaciones   
##  Min.   : 1.0   Min.   : 1.000  
##  1st Qu.: 2.0   1st Qu.: 3.000  
##  Median : 3.0   Median : 4.000  
##  Mean   : 3.6   Mean   : 4.633  
##  3rd Qu.: 4.0   3rd Qu.: 5.000  
##  Max.   :10.0   Max.   :10.000

Obtengamos la correlación preciom con cada variable solicitada

# Realizar la prueba de correlación entre 'areaconst' y 'preciom'
cor_areaconst <- cor.test(base1$areaconst, base1$preciom)

# Realizar la prueba de correlación entre 'estrato' y 'preciom'
cor_estrato <- cor.test(base1$estrato, base1$preciom)

# Realizar la prueba de correlación entre 'banios' y 'preciom'
cor_banios <- cor.test(base1$banios, base1$preciom)

# Realizar la prueba de correlación entre 'habitaciones' y 'preciom'
cor_habitaciones <- cor.test(base1$habitaciones, base1$preciom)

# Mostrar los resultados
cor_areaconst
## 
##  Pearson's product-moment correlation
## 
## data:  base1$areaconst and base1$preciom
## t = 28.258, df = 698, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.6939227 0.7632523
## sample estimates:
##       cor 
## 0.7304639
cor_estrato
## 
##  Pearson's product-moment correlation
## 
## data:  base1$estrato and base1$preciom
## t = 20.691, df = 698, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5684518 0.6605058
## sample estimates:
##       cor 
## 0.6165818
cor_banios
## 
##  Pearson's product-moment correlation
## 
## data:  base1$banios and base1$preciom
## t = 18.256, df = 698, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5161150 0.6166041
## sample estimates:
##       cor 
## 0.5684762
cor_habitaciones
## 
##  Pearson's product-moment correlation
## 
## data:  base1$habitaciones and base1$preciom
## t = 10.682, df = 698, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.3093290 0.4368094
## sample estimates:
##       cor 
## 0.3748397

Obtengamos un resumen estadístico más detallado usando el paquete GGally:

cor_1 <-base1[,c("preciom","areaconst","estrato","banios","habitaciones")]
ggpairs(cor_1, title="GGally ") 

Grafiquemoslo usando plot_ly:

# Calcula la matriz de correlación
correlation_matrix <- cor(data[, c("preciom", "areaconst", "estrato", "banios", "habitaciones")])

# Grafica la matriz de correlación con Plotly
library(plotly)
plot <- plot_ly(z = correlation_matrix, colorscale = "Blues", type = "heatmap",
                x = colnames(correlation_matrix), y = colnames(correlation_matrix),
                text = round(correlation_matrix, 3), zmin = -1, zmax = 1) %>%
        layout(title = "Correlación entre precio de la casa y cada variable",
               xaxis = list(title = "Variables"),
               yaxis = list(title = "Variables"),
               annotations = list(
                 text = round(correlation_matrix, 3),
                 x = rep(colnames(correlation_matrix), each = ncol(correlation_matrix)),
                 y = rep(colnames(correlation_matrix), times = ncol(correlation_matrix)),
                 showarrow = FALSE
               ))

plot

Creemos graficos de dispersión para continuar con la exploración visual:

# Gráfico de correlación entre 'preciom' y 'areaconst'
ggplot(data = base1, aes(x = preciom, y = areaconst)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Precio de la vivienda", y = "Área de la vivienda", title = "Correlación entre Precio y Área de la vivienda")
## `geom_smooth()` using formula = 'y ~ x'

# Gráfico de correlación entre 'preciom' y 'estrato'
ggplot(data = base1, aes(x = preciom, y = estrato)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Precio de la vivienda", y = "Estrato", title = "Correlación entre Precio y Estrato")
## `geom_smooth()` using formula = 'y ~ x'

# Gráfico de correlación entre 'preciom' y 'banios'
ggplot(data = base1, aes(x = preciom, y = banios)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Precio de la vivienda", y = "Número de baños", title = "Correlación entre Precio y Número de baños")
## `geom_smooth()` using formula = 'y ~ x'

# Gráfico de correlación entre 'preciom' y 'habitaciones'
ggplot(data = base1, aes(x = preciom, y = habitaciones)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Precio de la vivienda", y = "Número de habitaciones", title = "Correlación entre Precio y Número de habitaciones")
## `geom_smooth()` using formula = 'y ~ x'

De lo anterior podemos interpretar que:

Con respecto a “areaconst”, se observa una correlación positiva significativa con “preciom” (0.73). A medida que aumenta el área construida, tiende a aumentar el precio de la vivienda.

Para “estrato”, también se observa una correlación positiva significativa con “preciom” (0.61). Las viviendas ubicadas en estratos socioeconómicos más altos tienden a tener precios más altos.

En cambio, para ‘banios’ se muestra una correlación positiva moderada con “preciom” (0.52). Esto indica que las viviendas con más baños tienden a tener precios más altos, sin embargo la relación no es tan fuerte como la de “areaconst” o “estrato”.

Por ultimo, con respecto a la variable “habitaciones”, aunque tenemos una correlación positiva con ‘preciom’, esta es más débil (0.32), lo que sugiere que si bien el número de habitaciones también puede influir en el precio de la vivienda, su efecto es menos relevante que el área construida, el estrato o el número de baños.

3. Estime un modelo de regresión lineal múltiple con las variables del punto anterior (precio = f(área construida, estrato, número de cuartos, número de parqueaderos, número de baños ) ) e interprete los coeficientes si son estadísticamente significativos. Las interpretaciones deber están contextualizadas y discutir si los resultados son lógicos. Adicionalmente interprete el coeficiente R2 y discuta el ajuste del modelo e implicaciones (que podrían hacer para mejorarlo).

modelBase1 = lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = base1)
summary(modelBase1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = base1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -926.97  -80.56  -16.71   51.84 1077.19 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -251.35629   32.37948  -7.763 2.98e-14 ***
## areaconst       0.78378    0.04642  16.883  < 2e-16 ***
## estrato        86.13688    7.64507  11.267  < 2e-16 ***
## habitaciones    4.11542    4.82613   0.853    0.394    
## parqueaderos   -0.82085    4.32974  -0.190    0.850    
## banios         30.23622    5.91360   5.113 4.11e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 158.1 on 694 degrees of freedom
## Multiple R-squared:  0.654,  Adjusted R-squared:  0.6515 
## F-statistic: 262.3 on 5 and 694 DF,  p-value: < 2.2e-16

habitaciones no parece ser importante para el modelo, así que lo eliminaremos.

modelBase1 <- lm(preciom ~ areaconst + estrato + parqueaderos + banios, data = base1)
summary(modelBase1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + parqueaderos + banios, 
##     data = base1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -921.67  -77.20  -17.94   50.86 1086.06 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -236.48092   27.27336  -8.671  < 2e-16 ***
## areaconst       0.79501    0.04451  17.862  < 2e-16 ***
## estrato        84.00085    7.22164  11.632  < 2e-16 ***
## parqueaderos   -0.51087    4.31361  -0.118    0.906    
## banios         32.96241    4.97394   6.627 6.87e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 158 on 695 degrees of freedom
## Multiple R-squared:  0.6536, Adjusted R-squared:  0.6516 
## F-statistic: 327.8 on 4 and 695 DF,  p-value: < 2.2e-16
coeficientesM = coef(modelBase1)
print(coeficientesM)
##  (Intercept)    areaconst      estrato parqueaderos       banios 
## -236.4809245    0.7950091   84.0008522   -0.5108652   32.9624140

El coeficiente de determinación (R-squared) del modelo es 0.6508, lo que significa que alrededor del 65.08% de la variabilidad en el precio de la vivienda se explica por las variables independientes incluidas en el modelo.

Obtenemos el modelo:

preciom =−236.4809245+ 0.7950091 × areaconst + 84.0008522 × estrato − 0.5108652 × parqueaderos + 32.9624140 × banios

4. Realice la validación de supuestos del modelo e interprete los resultados (no es necesario corregir en caso de presentar problemas, solo realizar sugerencias de que se podría hacer).

# Obtener los residuos del modelo
residuos <- residuals(modelBase1)

# Obtener los valores ajustados del modelo
valores_ajustados <- fitted(modelBase1)

# Gráficos de diagnóstico
par(mfrow=c(2, 2))  # Organizar los gráficos en una matriz 2x2

# Gráfico de residuos estandarizados vs valores ajustados
plot(valores_ajustados, residuos / sqrt(summary(modelBase1)$sigma^2), 
     xlab = "Valores ajustados", ylab = "Residuos estandarizados",
     main = "Linealidad y Homocedasticidad")

# Línea horizontal en 0 para detectar patrones de homocedasticidad
abline(h = 0, col = "red")

# Histograma de los residuos
hist(residuos, breaks = 20, main = "Histograma de Residuos", xlab = "Residuos")

# Prueba de normalidad (Shapiro-Wilk)
shapiro.test(residuos)
## 
##  Shapiro-Wilk normality test
## 
## data:  residuos
## W = 0.83612, p-value < 2.2e-16
# Gráfico de residuos vs valores ajustados
plot(valores_ajustados, residuos, 
     xlab = "Valores ajustados", ylab = "Residuos",
     main = "Independencia de Residuos")
abline(h = 0, col = "red")

# Gráfico de influencia (Cook's distance)
library(car)
## Warning: package 'car' was built under R version 4.3.2
## Loading required package: carData
## Warning: package 'carData' was built under R version 4.3.2
## 
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
## The following object is masked from 'package:purrr':
## 
##     some
## The following object is masked from 'package:boot':
## 
##     logit
influencePlot(modelBase1, id.n = 3, main = "Gráfico de Influencia (Cook's distance)")
## Warning in plot.window(...): "id.n" is not a graphical parameter
## Warning in plot.xy(xy, type, ...): "id.n" is not a graphical parameter
## Warning in axis(side = side, at = at, labels = labels, ...): "id.n" is not a
## graphical parameter

## Warning in axis(side = side, at = at, labels = labels, ...): "id.n" is not a
## graphical parameter
## Warning in box(...): "id.n" is not a graphical parameter
## Warning in title(...): "id.n" is not a graphical parameter
## Warning in plot.xy(xy.coords(x, y), type = type, ...): "id.n" is not a
## graphical parameter

##        StudRes        Hat        CookD
## 174 -4.4390486 0.05300486 2.148047e-01
## 194  7.1632926 0.01312578 1.272814e-01
## 384 -0.0301136 0.07083496 1.384634e-05
## 612 -6.4034862 0.12288908 1.086467e+00
# Prueba de Breusch-Pagan para homocedasticidad
library(lmtest)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
bptest(modelBase1)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelBase1
## BP = 130.69, df = 4, p-value < 2.2e-16

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

6. Con las predicciones del modelo sugiera potenciales ofertas que responda a la solicitud de la vivienda 1. Tenga encuentra que la empresa tiene crédito pre-aprobado de máximo 350 millones de pesos. Realice un análisis y presente en un mapa al menos 5 ofertas potenciales que debe discutir.

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