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.