Para la ubicación de los dos empleados de la compañia internacional se tienen condiciones para cada uno de los dos empleados. Un conjunto de requisitos con los que deben cumplir esas viviendas, entre estos, está el precio, el tipo de vivienda, el número de habitaciones y otras. El siguiente análisis se hará para cada vivienda.
Primero se hará una exploración de las viviendas que coinciden con el tipo de vivienda requerida y en la zona que se requiere.
base1 <- vivienda %>% filter(tipo == "Casa" &
zona == "Zona Norte")
head(base1, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1209 Zona N… 02 5 320 150 2 4 6
## 2 1592 Zona N… 02 5 780 380 2 3 3
## 3 4057 Zona N… 02 6 750 445 NA 7 6
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Distribución de los estratos en la zona norte
estrato_tabla <- table(base1$estrato)
# Agregar encabezados
estrato_tabla <- data.frame(Estrato = names(estrato_tabla), Frecuencia = as.numeric(estrato_tabla))
# Mostrar la tabla
estrato_tabla
## Estrato Frecuencia
## 1 3 235
## 2 4 161
## 3 5 271
## 4 6 55
# Resumen estadístico del precio de las casas en la zona norte
precio_summary <- summary(base1$preciom)
# Agregar encabezados
precio_summary <- data.frame(Estadística = names(precio_summary), Valor = round(as.numeric(precio_summary),2))
# Mostrar el resumen
precio_summary
## Estadística Valor
## 1 Min. 89.00
## 2 1st Qu. 261.25
## 3 Median 390.00
## 4 Mean 445.91
## 5 3rd Qu. 550.00
## 6 Max. 1940.00
ruta_archivo_gdb <- "C:/Users/danie/Documents/RStudio/Javeriana/divipola.gdb"
shp_data <- st_read(ruta_archivo_gdb, layer = "comunas")
## Reading layer `comunas' from data source
## `C:\Users\danie\Documents\RStudio\Javeriana\divipola.gdb' using driver `OpenFileGDB'
## Simple feature collection with 22 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 1054098 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
## Projected CRS: MAGNA-SIRGAS / Cali urban grid
puntos_sf <- st_as_sf(base1, coords = c("longitud", "latitud"), crs = 4326)
ggplot() +
geom_sf(data = shp_data) +
geom_sf(data = puntos_sf, size = 1) +
ggtitle("Distribución de viviendas según zona") +
theme_void()
Como se ve en el mapa, los puntos no se ubican realmente en la zona norte geográfica de la ciudad. Sin embargo, sabiendo que los datos provienen de información recolectada de una plataforma de venta, estos puntos en ocasiones vienen por defecto, o el usuario no lo ubica por cuestión de privacidada y seguridad. Mientras que la decisión de ubicarlo en una de las zonas de la ciudad sí es decisión del usuario cuando registra la vivienda, por lo que se deduce que entre las dos opciones, la más confiable es aquella que el mismo usuario decide seleccionar dentro de un listado, es decir, la zona, ya que la ubicación puede estar comprometida.
Se exploran los datos faltantes, para saber si influirá en el análisis que se ahrá para ofrecer la información necesaria.
grafico <- md.pattern(base1, rotate.names = TRUE)
base1$estrato <- factor(base1$estrato)
# Imputación de valores faltantes con valor definido
base1$parqueaderos[is.na(base1$parqueaderos)] <- 0
if ("piso" %in% colnames(base1)) {
base1 <- base1 %>% select(-piso)
}
base1 <- base1 %>% subset(banios != 0 & habitaciones != 0 & areaconst != 0)
Debido a que sólo presentan datos faltantes en parqueaderos y piso, ya que el piso no interesa para el modelo en cuestión, se omitirá esta información y los parqueaderos sin información se asumirán como 0 ya que se asume que si no se llenó esa información es porque carece del mismo. Igualmente se verifica que al ser casas tengan al menos un baño, una habitación y area construida.
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el área construida
scatter_precio_area <- plot_ly(data = base1, x = ~areaconst, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Área Construida:", areaconst, "m2"),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Área Construida",
xaxis = list(title = "Área Construida (m2)"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el estrato
scatter_precio_estrato <- plot_ly(data = base1, x = ~estrato, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Estrato:", estrato),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el número de baños
scatter_precio_baños <- plot_ly(data = base1, x = ~banios, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Número de Baños:", banios),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el número de habitaciones
scatter_precio_habitaciones <- plot_ly(data = base1, x = ~habitaciones, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Número de Habitaciones:", habitaciones),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (millones de pesos)"))
annotations = list(
list(
x = 0.2,
y = 1.0,
text = "Área Construida",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 1.0,
text = "Estrato",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.2,
y = 0.45,
text = "Número de baños",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 0.45,
text = "Número de habitaciones",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
))
# Mostrar los gráficos en una grilla
subplot(scatter_precio_area, scatter_precio_estrato, scatter_precio_baños,
scatter_precio_habitaciones, nrows = 2, shareY = TRUE, margin = 0.05) %>%
layout(title = list(text = "Relación entre precio de venta y características de la vivienda")) %>%
layout(annotations = annotations)
Como se observa en los gráficos, se observa una relación entre cada una de las variables y el precio de la vivienda, sin embargo, en la que puede ser más clara es respecto al área construida. Por lo que se procede a hacer una matriz de correlación:
# Seleccionar las columnas numéricas
numeric_columns <- c("areaconst", "banios", "habitaciones", "parqueaderos")
# Calcular las correlaciones entre "preciom" y otras variables
correlations <- cor(base1[, numeric_columns], base1$preciom)
# Crear un dataframe con las correlaciones
cor_df <- data.frame(variable = c("area construida", "baños",
"número de habitaciones", "número de parqueaderos"),
correlation = correlations)
# Visualizar las correlaciones en un gráfico de barras
plotly::plot_ly(data = cor_df, y = ~variable, x = ~correlation, type = "bar", orientation = 'h') %>%
layout(title = "Correlación entre precio de la vivienda y otras características",
xaxis = list(title = "Coeficiente de correlación"), yaxis = list(title = "", showticklabels = TRUE))
A partir de la gráfica es claro que existe una mayor correlación entre el precio de la vivienda por el área construída, siendo también importante el número de baños, mientras que la relación no es tan clara para el número de habitaciones ni parqueaderos. Igualmente es claro que todas son relaciones positivas, es decir, que a medida que aumenta cualquiera de ellas, significa un aumento del precio de la vivienda.
# Crear variables dummy
dummy_variables <- model.matrix(~ estrato - 1, data = base1)
# Concatenar las variables dummy al dataframe base1
base1 <- cbind(base1, dummy_variables)
# Ajustar el modelo de regresión lineal múltiple
modelo <- lm(preciom ~ areaconst + habitaciones + banios + parqueaderos +
estrato4 + estrato5 + estrato6, data = base1)
# Mostrar un resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + banios + parqueaderos +
## estrato4 + estrato5 + estrato6, data = base1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -934.95 -70.86 -16.08 44.21 1084.54
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 17.47094 18.94531 0.922 0.357
## areaconst 0.78277 0.04584 17.075 < 2e-16 ***
## habitaciones 4.05579 4.77813 0.849 0.396
## banios 29.13290 5.91459 4.926 1.05e-06 ***
## parqueaderos 3.20413 4.37719 0.732 0.464
## estrato4 73.87582 17.82902 4.144 3.84e-05 ***
## estrato5 136.46361 17.28472 7.895 1.14e-14 ***
## estrato6 324.38915 27.37675 11.849 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 156.1 on 692 degrees of freedom
## Multiple R-squared: 0.6636, Adjusted R-squared: 0.6602
## F-statistic: 195 on 7 and 692 DF, p-value: < 2.2e-16
El modelo construido con Minimos Cuadrados Ordinarios indica que el precio base de una casa con todas las demás variables predictoras en cero (lo cual no tendría sentido) es de aproximadamente 17.47 millones de pesos. Ahora, respecto a cada una de las variables predictoras: - Área Construida (areaconst): Por cada unidad adicional en el área construida, el precio de la casa aumenta en aproximadamente 0.7827 millones de pesos. - Estrato 3, 4, 5, 6: Estos coeficientes representan el efecto del estrato en el precio de la casa. Por ejemplo, el coeficiente para el estrato 4 indica que, en promedio, las casas en el estrato 4 experimentan aumento del valor en 73.87 millones de pesos. - Baños (banios): Por cada baño adicional, el precio de la casa aumenta en aproximadamente 29.13 millones de pesos. - Habitaciones (habitaciones): No es estadísticamente significativo, lo que sugiere que el número de habitaciones no tiene un efecto significativo en el precio de la casa en este modelo. Igual con el número de parqueaderos.
Ahora bien, lo más complicado es que muestra el modelo que tanto el numero de parqueaderos, como el numero de habitaciones no son significativas en el modelo.
Por otro lado, el coeficiente de determinación es 0.6636, lo que indica que alrededor del 66.33% de la variabilidad en el precio de las casas puede ser explicada por las variables incluidas en el modelo. Para mejorar ello sería posible transformar las variables, asumiendolas no líneales, así como evaluar interacciones entre las variables, o seleccionar las más adecuadas y evaluar si es posible obtener mejores resultados.
# Ajustar el modelo de regresión lineal múltiple
modelo_log_log <- lm(log(preciom) ~ log(areaconst) + habitaciones + banios + parqueaderos +
estrato4 + estrato5 + estrato6, data = base1)
# Mostrar un resumen del modelo
summary(modelo_log_log)
##
## Call:
## lm(formula = log(preciom) ~ log(areaconst) + habitaciones + banios +
## parqueaderos + estrato4 + estrato5 + estrato6, data = base1)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.73978 -0.17651 -0.01379 0.15584 1.09956
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.926358 0.103835 28.183 < 2e-16 ***
## log(areaconst) 0.455186 0.023297 19.538 < 2e-16 ***
## habitaciones 0.015198 0.008162 1.862 0.063 .
## banios 0.060127 0.010082 5.964 3.93e-09 ***
## parqueaderos 0.011100 0.007439 1.492 0.136
## estrato4 0.253669 0.031099 8.157 1.62e-15 ***
## estrato5 0.391682 0.030921 12.667 < 2e-16 ***
## estrato6 0.661616 0.047683 13.875 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2649 on 692 degrees of freedom
## Multiple R-squared: 0.7844, Adjusted R-squared: 0.7822
## F-statistic: 359.7 on 7 and 692 DF, p-value: < 2.2e-16
Así es posible alcalzar un 78% de explicación, que aún siendo bajo, es más adecuado. Sin embargo, para el ejercicio se continuará con el modelo inicial
# Crear una nueva área de trazado dividida en 1 fila y 2 columnas
par(mfrow = c(1, 2))
# Trazar el primer gráfico en la primera columna
qqnorm(residuals(modelo), main = "Probabilidad normal de los residuales") # Normalidad de los errores
qqline(residuals(modelo))
# Cambiar a la segunda columna y trazas el segundo gráfico
plot(modelo, which = 1, main = "Residuales versus valores ajustados") #Varianza constante de los errores
# Realizamos las pruebas y guardamos los resultados
shapiro_test <- shapiro.test(residuals(modelo)) # 1. Normalidad de los errores
mean_residuos <- mean(residuals(modelo)) # 2. Media cero de los errores
gq_test <- gqtest(modelo) # 3. Varianza constante de los errores
dw_test <- dwtest(modelo) # 4. Independencia de los errores
# Creamos un data frame con los resultados
resultados <- data.frame(
Prueba = c("Normalidad de los errores", "Media cero de los errores", "Varianza constante de los errores", "Independencia de los errores"),
Test = c("Shapiro-Wilk", "Media", "Goldfeld-Quandt", "Durbin-Watson"),
Indicador = c(shapiro_test$p.value, mean_residuos, gq_test$p.value, dw_test$statistic)
)
# Nombramos las columnas
colnames(resultados) <- c("Prueba", "Test", "Indicador (Valor p / Resultado)")
# Función para la interpretación
interpretar_resultado <- function(prueba, valor) {
if (prueba == "Shapiro-Wilk") {
if (valor > 0.05) {
return("No hay suficiente evidencia para rechazar la hipótesis nula de normalidad.")
} else {
return("La hipótesis nula de normalidad es rechazada.")
}
} else if (prueba == "Media") {
if (valor > -0.0005 && valor < 0.0005) {
return("La media de los residuos es cercana a cero.")
} else {
return("La media de los residuos no es cercana a cero.")
}
} else if (prueba == "Goldfeld-Quandt") {
if (valor > 0.05) {
return("No hay suficiente evidencia para rechazar la hipótesis nula de varianza constante.")
} else {
return("La hipótesis nula de varianza constante es rechazada.")
}
} else if (prueba == "Durbin-Watson") {
if (valor > 1.5 && valor < 2.5) {
return("No hay autocorrelación serial significativa en los residuos.")
} else {
return("Hay autocorrelación serial significativa en los residuos.")
}
} else {
return("Interpretación no disponible.")
}
}
# Añadimos la columna de interpretación usando mapply
resultados$Interpretación <- mapply(interpretar_resultado, resultados$Test, resultados$`Indicador (Valor p / Resultado)`)
# Mostramos la tabla
print(resultados)
## Prueba Test
## 1 Normalidad de los errores Shapiro-Wilk
## 2 Media cero de los errores Media
## 3 Varianza constante de los errores Goldfeld-Quandt
## 4 Independencia de los errores Durbin-Watson
## Indicador (Valor p / Resultado)
## 1 1.113334e-26
## 2 8.946251e-15
## 3 1.272089e-02
## 4 1.686310e+00
## Interpretación
## 1 La hipótesis nula de normalidad es rechazada.
## 2 La media de los residuos es cercana a cero.
## 3 La hipótesis nula de varianza constante es rechazada.
## 4 No hay autocorrelación serial significativa en los residuos.
Esto indica que el supuesto que no se cumple es el de la normalidad de los errores, así como la varianza constante. Lo que se podría solucionar realizando otras transformaciones, eliminando datos que se identifiquen como atipicos, o remuestrear los datos para obtener una normalidad.
# Características de la primera solicitud
area_const_primera_solicitud <- 200
estrato_primera_solicitud <- c(4,5)
banios_primera_solicitud <- 2
habitaciones_primera_solicitud <- 4
parqueaderos_primera_solicitud <- 1
# Crear un nuevo conjunto de datos con las características de la primera solicitud
nueva_solicitud <- data.frame(
areaconst = area_const_primera_solicitud,
estrato3 = ifelse(estrato_primera_solicitud == 3, 1, 0),
estrato4 = ifelse(estrato_primera_solicitud == 4, 1, 0),
estrato5 = ifelse(estrato_primera_solicitud == 5, 1, 0),
estrato6 = ifelse(estrato_primera_solicitud == 6, 1, 0),
parqueaderos = parqueaderos_primera_solicitud,
banios = banios_primera_solicitud,
habitaciones = habitaciones_primera_solicitud
)
# Realizar la predicción
precio_predicho <- predict(modelo, newdata = nueva_solicitud)
# Mostrar el precio predicho
print(precio_predicho)
## 1 2
## 325.5934 388.1812
Ahora bien, al predecir el valor de la vivienda con las caracteristicas buscadas, se encuentra que se obtiene un precio para estrato 4 y otro para estrato 5. Siendo estrato 5 por encima de lo que está presupuestado, eso quiere decir que con crédito que se tiene podría optar por una vivienda de estrato 4.
limite_credito <- 350
viviendas_potenciales <- base1 %>% filter(preciom <= precio_predicho[[1]] & banios >= 2 &
estrato %in% c(4,5) & habitaciones >= 4 &
areaconst >= 200 & parqueaderos >= 1)
puntos_sf <- st_as_sf(viviendas_potenciales, coords = c("longitud", "latitud"), crs = 4326)
mapa_interactivo <- ggplot() +
geom_sf(data = shp_data) +
geom_sf(data = puntos_sf, aes(color = preciom, size = areaconst,
text = paste("id:", id,
"<br>Precio: $", preciom,
"<br>Área construida:", areaconst,
"<br>Número de Baños:", banios,
"<br>Número de Habitaciones:", habitaciones,
"<br>Número de Parqueaderos:", parqueaderos,
"<br>Estrato:", estrato))) +
ggtitle("Distribución de viviendas potenciales") +
theme_void()
# Convertir el gráfico en interactivo con plotly
mapa_interactivo <- ggplotly(mapa_interactivo, tooltip = "text")
# Mostrar el mapa interactivo
mapa_interactivo
A partir de la información que se muestra, estas serían 10 opciones que se presentan para la persona, las cuales cumplen como mínimo con lo que tiene como restricciones, aún teniendo un valor por debajo del crédito, de acuerdo con el modelo, debería corresponder a un valor menor. En ese sentido, se presenta un punto que cumple y tiene un precio reducido, teniendo un área construida mayor (teniendo esto como el mayor determinante). Es decir, el id 1020.
Primero se hará una exploración de las viviendas que coinciden con el tipo de vivienda requerida y en la zona que se requiere.
base2 <- vivienda %>% filter(tipo == "Apartamento" &
zona == "Zona Sur")
head(base2, 3)
## # A tibble: 3 × 13
## id zona piso estrato preciom areaconst parqueaderos banios habitaciones
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 5098 Zona S… 05 4 290 96 1 2 3
## 2 698 Zona S… 02 3 78 40 1 1 2
## 3 8199 Zona S… <NA> 6 875 194 2 5 3
## # ℹ 4 more variables: tipo <chr>, barrio <chr>, longitud <dbl>, latitud <dbl>
# Distribución de los estratos en la zona norte
estrato_tabla <- table(base2$estrato)
# Agregar encabezados
estrato_tabla <- data.frame(Estrato = names(estrato_tabla), Frecuencia = as.numeric(estrato_tabla))
# Mostrar la tabla
estrato_tabla
## Estrato Frecuencia
## 1 3 201
## 2 4 1091
## 3 5 1033
## 4 6 462
# Resumen estadístico del precio de las casas en la zona norte
precio_summary <- summary(base2$preciom)
# Agregar encabezados
precio_summary <- data.frame(Estadística = names(precio_summary), Valor = round(as.numeric(precio_summary),2))
# Mostrar el resumen
precio_summary
## Estadística Valor
## 1 Min. 75.00
## 2 1st Qu. 175.00
## 3 Median 245.00
## 4 Mean 297.29
## 5 3rd Qu. 335.00
## 6 Max. 1750.00
ruta_archivo_gdb <- "C:/Users/danie/Documents/RStudio/Javeriana/divipola.gdb"
shp_data <- st_read(ruta_archivo_gdb, layer = "comunas")
## Reading layer `comunas' from data source
## `C:\Users\danie\Documents\RStudio\Javeriana\divipola.gdb' using driver `OpenFileGDB'
## Simple feature collection with 22 features and 4 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 1054098 ymin: 860192.1 xmax: 1068492 ymax: 879441.5
## Projected CRS: MAGNA-SIRGAS / Cali urban grid
puntos_sf <- st_as_sf(base2, coords = c("longitud", "latitud"), crs = 4326)
ggplot() +
geom_sf(data = shp_data) +
geom_sf(data = puntos_sf, size = 1) +
ggtitle("Distribución de viviendas según zona") +
theme_void()
Al igual que con las ubicaciones en el norte, los puntos no se ubican realmente en la zona geográfica de la ciudad. Sin embargo, sabiendo que los datos provienen de información recolectada de una plataforma de venta, estos puntos en ocasiones vienen por defecto, o el usuario no lo ubica por cuestión de privacidada y seguridad. Mientras que la decisión de ubicarlo en una de las zonas de la ciudad sí es decisión del usuario cuando registra la vivienda, por lo que se deduce que entre las dos opciones, la más confiable es aquella que el mismo usuario decide seleccionar dentro de un listado, es decir, la zona, ya que la ubicación puede estar comprometida.
Se exploran los datos faltantes, para saber si influirá en el análisis que se ahrá para ofrecer la información necesaria.
grafico <- md.pattern(base2, rotate.names = TRUE)
base2$estrato <- factor(base2$estrato)
# Imputación de valores faltantes con valor definido
base2$parqueaderos[is.na(base2$parqueaderos)] <- 0
if ("piso" %in% colnames(base2)) {
base2 <- base2 %>% select(-piso)
}
base2 <- base2 %>% subset(banios != 0 & habitaciones != 0 & areaconst != 0)
Debido a que sólo presentan datos faltantes en parqueaderos y piso, ya que el piso no interesa para el modelo en cuestión, se omitirá esta información y los parqueaderos sin información se asumirán como 0 ya que se asume que si no se llenó esa información es porque carece del mismo. Igualmente se verifica que al ser casas tengan al menos un baño, una habitación y area construida.
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el área construida
scatter_precio_area <- plot_ly(data = base2, x = ~areaconst, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Área Construida:", areaconst, "m2"),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Área Construida",
xaxis = list(title = "Área Construida (m2)"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el estrato
scatter_precio_estrato <- plot_ly(data = base2, x = ~estrato, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Estrato:", estrato),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el número de baños
scatter_precio_baños <- plot_ly(data = base2, x = ~banios, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Número de Baños:", banios),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Número de Baños",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (millones de pesos)"))
# Crear un gráfico de dispersión para visualizar la relación entre el precio y el número de habitaciones
scatter_precio_habitaciones <- plot_ly(data = base2, x = ~habitaciones, y = ~preciom, type = "scatter", mode = "markers",
marker = list(color = ~estrato, size = 10, colorscale = "Viridis"),
text = ~paste("Precio: $", preciom, "<br>Número de Habitaciones:", habitaciones),
hoverinfo = "text") %>%
layout(title = "Relación entre Precio y Número de Habitaciones",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (millones de pesos)"))
annotations = list(
list(
x = 0.2,
y = 1.0,
text = "Área Construida",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 1.0,
text = "Estrato",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.2,
y = 0.45,
text = "Número de baños",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
),
list(
x = 0.8,
y = 0.45,
text = "Número de habitaciones",
xref = "paper",
yref = "paper",
xanchor = "center",
yanchor = "bottom",
showarrow = FALSE
))
# Mostrar los gráficos en una grilla
subplot(scatter_precio_area, scatter_precio_estrato, scatter_precio_baños,
scatter_precio_habitaciones, nrows = 2, shareY = TRUE, margin = 0.05) %>%
layout(title = list(text = "Relación entre precio de venta y características de la vivienda")) %>%
layout(annotations = annotations)
El comportamiento es similar al de las casas en el norte de Cali. Se observa una relación entre cada una de las variables y el precio de la vivienda, sin embargo, en la que puede ser más clara es respecto al área construida. Por lo que se procede a hacer una matriz de correlación:
# Seleccionar las columnas numéricas
numeric_columns <- c("areaconst", "banios", "habitaciones", "parqueaderos")
# Calcular las correlaciones entre "preciom" y otras variables
correlations <- cor(base2[, numeric_columns], base2$preciom)
# Crear un dataframe con las correlaciones
cor_df <- data.frame(variable = c("area construida", "baños",
"número de habitaciones", "número de parqueaderos"),
correlation = correlations)
# Visualizar las correlaciones en un gráfico de barras
plotly::plot_ly(data = cor_df, y = ~variable, x = ~correlation, type = "bar", orientation = 'h') %>%
layout(title = "Correlación entre precio de la vivienda y otras características",
xaxis = list(title = "Coeficiente de correlación"), yaxis = list(title = "", showticklabels = TRUE))
A partir de la gráfica es claro que existe una mayor correlación entre el precio de la vivienda por el área construída, siendo también importante el número de baños, y llamativamente, el número de parqueaderos es igual de importante y correlacionado con el precio de la vivienda. Igualmente es claro que todas son relaciones positivas, es decir, que a medida que aumenta cualquiera de ellas, significa un aumento del precio de la vivienda.
# Crear variables dummy
dummy_variables <- model.matrix(~ estrato - 1, data = base2)
# Concatenar las variables dummy al dataframe base1
base2 <- cbind(base2, dummy_variables)
# Ajustar el modelo de regresión lineal múltiple
modelo <- lm(preciom ~ areaconst + habitaciones + banios + parqueaderos +
estrato4 + estrato5 + estrato6, data = base2)
# Mostrar un resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + habitaciones + banios + parqueaderos +
## estrato4 + estrato5 + estrato6, data = base2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1128.01 -36.37 0.19 32.96 902.12
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.03597 10.20047 -0.102 0.919
## areaconst 1.35976 0.04723 28.787 < 2e-16 ***
## habitaciones -15.86854 3.31972 -4.780 1.84e-06 ***
## banios 42.05035 2.97293 14.144 < 2e-16 ***
## parqueaderos 45.27180 2.93647 15.417 < 2e-16 ***
## estrato4 17.49104 6.95729 2.514 0.012 *
## estrato5 36.93566 7.27806 5.075 4.13e-07 ***
## estrato6 198.17289 9.18097 21.585 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 88.29 on 2769 degrees of freedom
## Multiple R-squared: 0.7866, Adjusted R-squared: 0.786
## F-statistic: 1458 on 7 and 2769 DF, p-value: < 2.2e-16
Respecto a las variables explicativas tenemos que:
areaconst: El coeficiente asociado es 1.35976 lo cual indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el área construida se asocia con un aumento de aproximadamente 1.36 millones en el precio de la vivienda. habitaciones: El coeficiente es -15.86854. Esto indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el número de habitaciones está asociado con una disminución de aproximadamente 15.87 millones en el precio de la vivienda. banios: El coeficiente es 42.05035. Esto indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el número de baños se asocia con un aumento de aproximadamente 42.05 millones en el precio de la vivienda. parqueaderos: El coeficiente es 45.27180. Esto indica que, manteniendo todas las demás variables constantes, un aumento de una unidad en el número de parqueaderos se asocia con un aumento de aproximadamente 45.27 millones en el precio de la vivienda. estrato4, estrato5 y estrato6: Estas variables categóricas representan el estrato de la vivienda. Los coeficientes indican el efecto que tienen en el precio de la vivienda en comparación con el estrato de referencia (estrato3). Por ejemplo, el coeficiente para estrato4 es 17.49104, lo que significa que, en promedio, las viviendas en el estrato 4 tienen un precio aproximadamente 17.49milones más alto que las viviendas en el estrato 3, manteniendo todas las demás variables constantes.
Es interesante que al contrario que con el modelo de las viviendas para la solicitud 1, el comportamiento de las habitaciones no aumenta el precio como se podría esperar en este caso. Y además, todas las variables tienen una participación significativa y el coeficiente de determinación múltiple (Multiple R-squared) es 0.7866, lo que indica que aproximadamente el 78.66% de la variabilidad en el precio de la vivienda puede ser explicada por las variables incluidas en el modelo.
# Ajustar el modelo de regresión lineal múltiple
modelo_log_log <- lm(log(preciom) ~ log(areaconst) + habitaciones + banios + parqueaderos +
estrato4 + estrato5 + estrato6, data = base2)
# Mostrar un resumen del modelo
summary(modelo_log_log)
##
## Call:
## lm(formula = log(preciom) ~ log(areaconst) + habitaciones + banios +
## parqueaderos + estrato4 + estrato5 + estrato6, data = base2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.65560 -0.12144 0.01071 0.13217 0.89766
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.165821 0.063906 33.891 < 2e-16 ***
## log(areaconst) 0.640228 0.017178 37.271 < 2e-16 ***
## habitaciones -0.036008 0.007520 -4.789 1.77e-06 ***
## banios 0.058325 0.006780 8.602 < 2e-16 ***
## parqueaderos 0.099436 0.006636 14.984 < 2e-16 ***
## estrato4 0.249534 0.015335 16.273 < 2e-16 ***
## estrato5 0.380700 0.016162 23.555 < 2e-16 ***
## estrato6 0.674613 0.020531 32.858 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.195 on 2769 degrees of freedom
## Multiple R-squared: 0.8547, Adjusted R-squared: 0.8544
## F-statistic: 2327 on 7 and 2769 DF, p-value: < 2.2e-16
Sin embargo, al igual que con el modelo de viviendas del norte, es posible mejorarlo al tener un modelo log-log con respecto al área construida. Alcanzando un porcentaje de 85% de explicación con este modelo. A pesar de esto, de nuevo, como en la primer solicitud, por motivos del ejercicio, se continua con el modelo lineal inicial.
# Crear una nueva área de trazado dividida en 1 fila y 2 columnas
par(mfrow = c(1, 2))
# Trazar el primer gráfico en la primera columna
qqnorm(residuals(modelo), main = "Probabilidad normal de los residuales") # Normalidad de los errores
qqline(residuals(modelo))
# Cambiar a la segunda columna y trazas el segundo gráfico
plot(modelo, which = 1, main = "Residuales versus valores ajustados") #Varianza constante de los errores
# Realizamos las pruebas y guardamos los resultados
shapiro_test <- shapiro.test(residuals(modelo)) # 1. Normalidad de los errores
mean_residuos <- mean(residuals(modelo)) # 2. Media cero de los errores
gq_test <- gqtest(modelo) # 3. Varianza constante de los errores
dw_test <- dwtest(modelo) # 4. Independencia de los errores
# Creamos un data frame con los resultados
resultados <- data.frame(
Prueba = c("Normalidad de los errores", "Media cero de los errores", "Varianza constante de los errores", "Independencia de los errores"),
Test = c("Shapiro-Wilk", "Media", "Goldfeld-Quandt", "Durbin-Watson"),
Indicador = c(shapiro_test$p.value, mean_residuos, gq_test$p.value, dw_test$statistic)
)
# Nombramos las columnas
colnames(resultados) <- c("Prueba", "Test", "Indicador (Valor p / Resultado)")
# Añadimos la columna de interpretación usando mapply
resultados$Interpretación <- mapply(interpretar_resultado, resultados$Test, resultados$`Indicador (Valor p / Resultado)`)
# Mostramos la tabla
print(resultados)
## Prueba Test
## 1 Normalidad de los errores Shapiro-Wilk
## 2 Media cero de los errores Media
## 3 Varianza constante de los errores Goldfeld-Quandt
## 4 Independencia de los errores Durbin-Watson
## Indicador (Valor p / Resultado)
## 1 1.628733e-52
## 2 1.071173e-14
## 3 9.818828e-01
## 4 1.710530e+00
## Interpretación
## 1 La hipótesis nula de normalidad es rechazada.
## 2 La media de los residuos es cercana a cero.
## 3 No hay suficiente evidencia para rechazar la hipótesis nula de varianza constante.
## 4 No hay autocorrelación serial significativa en los residuos.
Con este resultado se observa que sólo se incumple el supuesto de la normalidad de los errores. Esto se podría solucionar realizando otras transformaciones, eliminando datos que se identifiquen como atipicos, o remuestrear los datos para obtener una normalidad.
# Características de la primera solicitud
area_const_primera_solicitud <- 300
estrato_primera_solicitud <- c(5, 6)
banios_primera_solicitud <- 3
habitaciones_primera_solicitud <- 5
parqueaderos_primera_solicitud <- 3
# Crear un nuevo conjunto de datos con las características de la primera solicitud
nueva_solicitud <- data.frame(
areaconst = area_const_primera_solicitud,
estrato3 = ifelse(estrato_primera_solicitud == 3, 1, 0),
estrato4 = ifelse(estrato_primera_solicitud == 4, 1, 0),
estrato5 = ifelse(estrato_primera_solicitud == 5, 1, 0),
estrato6 = ifelse(estrato_primera_solicitud == 6, 1, 0),
parqueaderos = parqueaderos_primera_solicitud,
banios = banios_primera_solicitud,
habitaciones = habitaciones_primera_solicitud
)
# Realizar la predicción
precio_predicho <- predict(modelo, newdata = nueva_solicitud)
# Mostrar el precio predicho
print(precio_predicho)
## 1 2
## 626.4514 787.6886
Ahora bien, al predecir el valor de la vivienda con las caracteristicas buscadas, se encuentra que se obtiene un precio para estrato 5 y otro para estrato 6. Siendo ambas opciones por debajo del crédito adquirido, podría optar para cualquier vivienda, estimando precios por debajo de ese límite para estrato 6.
limite_credito <- 850
viviendas_potenciales <- base2 %>% filter(preciom <= precio_predicho[[2]] & banios >= 3 &
estrato %in% c(5, 6) & habitaciones >= 5 &
areaconst >= 300 & parqueaderos >= 3)
puntos_sf <- st_as_sf(viviendas_potenciales, coords = c("longitud", "latitud"), crs = 4326)
mapa_interactivo <- ggplot() +
geom_sf(data = shp_data) +
geom_sf(data = puntos_sf, aes(color = preciom, size = areaconst,
text = paste("id:", id,
"<br>Precio: $", preciom,
"<br>Área construida:", areaconst,
"<br>Número de Baños:", banios,
"<br>Número de Habitaciones:", habitaciones,
"<br>Número de Parqueaderos:", parqueaderos,
"<br>Estrato:", estrato))) +
ggtitle("Distribución de viviendas potenciales") +
theme_void()
# Convertir el gráfico en interactivo con plotly
mapa_interactivo <- ggplotly(mapa_interactivo, tooltip = "text")
# Mostrar el mapa interactivo
mapa_interactivo
Al respecto, de la base de datos disponible, sólo se obtienen dos opciones de vivienda posibles, sin embargo, al comparar ellas, tienen diferencias en el número de habitaciones y baños, así como el área de la vivienda y el precio claramente. Ya en este punto se tendría en cuenta la ponderación del cliente sobre qué es más importante, teniendo en cuenta que ambas viviendas cumplen con todos los requisitos que se tenian. En el caso de querer el menor precio posible, sería la vivienda con id 7512, sin embargo, si es más importante, por ejemplo, el área construida, sería la vivienda de id 7182.