La Firma C&A ha sido contratada para brindar una consultoría y asesorar a una empresa para la adquisición de dos inmuebles en la ciudad de Cali, el cliente presentó unos requerimientos específicos sobre las características de las propiedades que proyecta adquirir. La Firma debe analizar la oferta inmobiliaria disponible e identificar los inmuebles que se ajusten a los requerimientos del cliente y recomendar cuales son las mejores opciones ajustadas a las peticiones del cliente.
El informe presenta un análisis detallado del mercado inmobiliario en la Ciudad de Cali, específicamente en las unidades de vivienda tipo casas y apartamentos ubicados en las Zonas Norte y Sur de la Ciudad. Se realizó un análisis exhaustivos de los precios y de las características de las viviendas en términos de estratos socio económico, área construidas, número de baños, número de habitaciones, número de parqueaderos, entre otras con el objetivo de estimar los precios e identificar las mejores ofertas de acuerdo con los requerimientos del cliente.
Para llevar a cabo este informe, se analizaron 521 casas ubicadas en la zona norte y 2.777 apartamentos ubicados en la zona sur. Identificamos que el precio promedio de las casas es de 433 Millones con un rango que oscila entre 58 Millones hasta los 2.000 Millones, y un área promedio 174 metros cuadrados con áreas desde los 30 hasta los 1745 metros cuadrados, en esta zona de la ciudad predomina el estrato 4 y 5.En lo que respecta a los apartamentos, identificamos que el precio promedio es de 695 Millones, el área promedio es de 123 metros cuadrados, que oscilan entre los 30 hasta los 229 metros cuadrados, en esta zona de la ciudad predomina el estrato 4.
En cuanto a la evaluación de precios, en general los precios de los apartamentos son más altos que el de las casas; sin embargo, los precios de las casas presentan mayor variabilidad mientras que los precios de los apartamentos son mas estables, se evidenció también, que en estas zonas hay inmuebles con precios significativamente altos en comparación con la media del sector.
De acuerdo con la restricción presupuestas sujeta al crédito pre aprobado por el Entidad Bancaria y, teniendo presente los requerimientos iniciales del cliente, se definió presentar las mejores ofertas basados en los criterios de la relación precio metros cuadrados, así mismo, los inmuebles disponibles que más se ajusta y se propone para la adquisición de la casa es una unidad de vivienda ubicada en el barrio San Vicente, con un área de 355 metros cuadrados, dos parqueaderos, cinco baños y ocho habitaciones en el estrato 5, además su precio es de 340 Millones. Para la adquisición del apartamento la mejor oferta disponible, es un inmueble ubicado en el barrio El Ingenio, estrato cinco, área de 600 metros cuadrados, dos parqueaderos, cuatro baños, cinco habitaciones y precio de 650 millones.
El análisis del mercado inmobiliario permite concluir que la Zona Norte es ideal para quienes buscan propiedades con mayor valor agregado, mientras que la Zona Sur ofrece oportunidades a precios más asequibles con buena proyección de valorización. Para el presupuesto del cliente, se identificaron las mejores opciones de casas y apartamentos en cada sector, optimizando la inversión en función de las necesidades y expectativas.
La metodología adoptada para el estudio inmobiliario de realizó desarrollando los siguientes pasos consecutivos y sistemáticos.
Implementación de la metodología, resultados de la moderación , validación y comparación de los modelos
library(paqueteMODELOS)
library(dplyr)
library(DT)
library(knitr)
library(pander)
library(tidyr)
data("vivienda")
Numero de filas: 8322
Numero de columnas: 13
| id | zona | piso | estrato | preciom | areaconst | parqueaderos | banios | habitaciones | tipo | barrio | longitud | latitud |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1147 | Zona Oriente | NA | 3 | 250 | 70 | 1 | 3 | 6 | Casa | 20 de julio | -76.51168 | 3.43382 |
| 1169 | Zona Oriente | NA | 3 | 320 | 120 | 1 | 2 | 3 | Casa | 20 de julio | -76.51237 | 3.43369 |
| 1350 | Zona Oriente | NA | 3 | 350 | 220 | 2 | 2 | 4 | Casa | 20 de julio | -76.51537 | 3.43566 |
| 5992 | Zona Sur | 02 | 4 | 400 | 280 | 3 | 5 | 3 | Casa | 3 de julio | -76.54000 | 3.43500 |
| 1212 | Zona Norte | 01 | 5 | 260 | 90 | 1 | 2 | 3 | Apartamento | acopi | -76.51350 | 3.45891 |
| 1724 | Zona Norte | 01 | 5 | 240 | 87 | 1 | 3 | 3 | Apartamento | acopi | -76.51700 | 3.36971 |
library(DT)
diccionario <- data.frame(
Variable = c("id", "barrio", "longitud", "latitud", "zona", "piso",
"estrato", "preciom", "areaconst", "parqueaderos", "banios",
"habitaciones", "tipo"),
Descripcion = c("Identificador único del inmueble",
"Nombre del barrio donde se encuentra la vivienda",
"Coordenada de longitud geográfica del inmueble",
"Coordenada de latitud geográfica del inmueble",
"Ubicación geográfica de la vivienda (ejemplo: Norte, Sur, Centro)",
"Número de piso en el que se ubica la vivienda",
"Estrato socioeconómico asignado a la vivienda",
"Precio del inmuble en millones de pesos ",
"Área construida de la vivienda en metros cuadrados",
"Cantidad de parqueaderos disponibles",
"Número de baños en la vivienda",
"Número de habitaciones en la vivienda",
"Tipo de inmueble (ejemplo: Casa, Apartamento)"),
stringsAsFactors = FALSE
)
datatable(diccionario, options = list(scrollX = TRUE))
estructura_vivienda <- tibble(
Variable = names(vivienda),
Tipo = sapply(vivienda, \(x) class(x)[1])
) %>% arrange(Tipo)
datatable(estructura_vivienda, options = list(
dom = 't',
pageLength = nrow(estructura_vivienda),
scrollX = TRUE
))
En una primera aproximación para conocer el contenido de la base de datos, observamos que es un base pequeña, que contiene 8.322 registros que corresponden al número total de inmuebles y 13 columnas que describen los atributos de cada registro, así mismo, se visualiza cuatro variables tipo texto y nueve tipo numérico, cabe resaltar que, sin bien estrato está definida como tipo número, esta variable es categórica; la variables tipo numérico contienen información referente al identificador de inmueble, el estrato socieconomico, precio del inmueble,área construida, número de parqueaderos, número de baños,número de habitaciones, longitud y latitud del inmueble; por otro lado, las variables tipo texto contienen información relacionada con el nombre de la zona del inmueble, tipo de inmueble (casa o apartamento), nombre del barrio, número de piso en donde se ubica el que se encuentra el inmueble.
| id | estrato | preciom | areaconst |
|---|---|---|---|
| Min. : 1 | Min. :3.000 | Min. : 58.0 | Min. : 30.0 |
| 1st Qu.:2080 | 1st Qu.:4.000 | 1st Qu.: 220.0 | 1st Qu.: 80.0 |
| Median :4160 | Median :5.000 | Median : 330.0 | Median : 123.0 |
| Mean :4160 | Mean :4.634 | Mean : 433.9 | Mean : 174.9 |
| 3rd Qu.:6240 | 3rd Qu.:5.000 | 3rd Qu.: 540.0 | 3rd Qu.: 229.0 |
| Max. :8319 | Max. :6.000 | Max. :1999.0 | Max. :1745.0 |
| NA’s :3 | NA’s :3 | NA’s :2 | NA’s :3 |
| parqueaderos | banios | habitaciones | longitud | latitud |
|---|---|---|---|---|
| Min. : 1.000 | Min. : 0.000 | Min. : 0.000 | Min. :-76.59 | Min. :3.333 |
| 1st Qu.: 1.000 | 1st Qu.: 2.000 | 1st Qu.: 3.000 | 1st Qu.:-76.54 | 1st Qu.:3.381 |
| Median : 2.000 | Median : 3.000 | Median : 3.000 | Median :-76.53 | Median :3.416 |
| Mean : 1.835 | Mean : 3.111 | Mean : 3.605 | Mean :-76.53 | Mean :3.418 |
| 3rd Qu.: 2.000 | 3rd Qu.: 4.000 | 3rd Qu.: 4.000 | 3rd Qu.:-76.52 | 3rd Qu.:3.452 |
| Max. :10.000 | Max. :10.000 | Max. :10.000 | Max. :-76.46 | Max. :3.498 |
| NA’s :1605 | NA’s :3 | NA’s :3 | NA’s :3 | NA’s :3 |
pander(
vivienda %>%
select(where(~ is.character(.) | is.factor(.))) %>%
summarise(across(everything(), ~ paste(names(table(.))[table(.) == max(table(.), na.rm = TRUE)], collapse = ", "))),
caption = "Moda de las Variables Categóricas"
)
| zona | piso | tipo | barrio |
|---|---|---|---|
| Zona Sur | 02 | Apartamento | valle del lili |
library(plotly)
library(reshape2)
library(htmltools)
vivienda_melt <- melt(vivienda[, sapply(vivienda, is.numeric)])
grupos <- list(c("id", "preciom", "areaconst"), c("estrato", "parqueaderos", "banios", "habitaciones"), c("longitud", "latitud"))
browsable(tagList(lapply(grupos, \(g) subplot(lapply(g, \(v)
plot_ly(vivienda_melt[vivienda_melt$variable == v, ], y = ~value, type = "box", name = v)),
nrows = 1, shareX = TRUE, titleX = TRUE))))
vars_categoricas <- names(vivienda)[sapply(vivienda, is.character)]
print(vars_categoricas)
## [1] "zona" "piso" "tipo" "barrio"
vivienda$estrato <- as.factor(vivienda$estrato)
library(plotly)
library(dplyr)
library(purrr)
vars_categoricas <- names(vivienda)[sapply(vivienda, is.character) | sapply(vivienda, is.factor)]
plots <- map(vars_categoricas, function(var) {
data_cat <- vivienda %>%
group_by(!!as.name(var)) %>%
summarise(count = n(), .groups = "drop")
plot_ly(data_cat, x = ~get(var), y = ~count, type = "bar", name = var) %>%
layout(title = paste("Distribución de", var),
xaxis = list(title = var),
yaxis = list(title = "Cantidad"))
})
plot_ly(vivienda, x = ~zona, y = ~preciom, type = "box", color = ~zona) %>%
layout(title = "Distribución del Precio por Zona")
plot_ly(vivienda, x = ~tipo, y = ~preciom, type = "box", color = ~tipo) %>%
layout(title = "Distribución del Precio por Tipo de Vivienda")
vivienda$estrato <- as.factor(vivienda$estrato)
plot_ly(vivienda, x = ~estrato, y = ~preciom, type = "box", color = ~estrato) %>%
layout(title = "Distribución del Precio de Inmuebles por Estrato",
xaxis = list(title = "Estrato"),
yaxis = list(title = "Precio (millones de pesos)"))
plot_ly(vivienda, x = ~zona, y = ~preciom, type = "bar", color = ~tipo) %>%
layout(title = "Distribución de Tipos de Vivienda por Zona", barmode = "stack")
Una vez finalizado el análisis exploratorio, observamos que la mayoría de los inmuebles se encuentran ubicados en el estrato 4 y 5, los precios de los inmuebles oscilan entre 58 M hasta 1999 M, con un valor promedio de 434 M, encontramos inmuebles con áreas desde 30 hasta 1745 metros cuadrados, así mismo, el promedio de parqueaderos por inmueble es de 2, baños 3 y habitaciones 3; Por otro lado, se evidencia que predominan los apartamentos sobre las casas y la mayoría de las propiedades se ubican en el barrio Valle de Lili en la zona Sur de Cali. Continuando con el análisis identificamos valores atípicos en la variable precios, área de construcción, parqueaderos y habitaciones, también, se presentan datos incoherentes como inmuebles sin baño y sin habitaciones; de igual forma, valores nulos en la variable estrato, precio, área de construcción, parqueadero, baños y habitaciones.
Observamos que los precios de las Casas son más altos en comparación con los apartamentos; en la Zona Oeste y Norte se encuentran los inmuebles más costosos, en la Zona Centro y Oriente los precios más bajos y en la Zona Sur los precios son intermedios respecto de las demás zonas, respectivamente observamos que, entre más alto el estrato más costosos los inmuebles.
Finalizando, se concluye que para los datos atípicos, nulos e incoherentes, es necesario implementar una estrategia para su adecuado tratamiento porque pueden reducir la precisión del modelo, por otro lado, preliminarmente se puede direccionar los esfuerzos localizando inmuebles en la Zona Oeste y Sur de la ciudad para cumplir con los requerimientos del cliente
vivienda_limpia <- vivienda
vivienda_limpia <- vivienda_limpia %>% select(-id)
if (!"id" %in% names(vivienda_limpia)) {
print("La columna ID hn sido eliminada correctamente.")
} else {
print("Error: La columna ID sigue presente en los datos.")
}
## [1] "La columna ID hn sido eliminada correctamente."
cantidad_filas_duplicadas <- sum(duplicated(vivienda_limpia))
print(paste("Cantidad de filas duplicadas:", cantidad_filas_duplicadas))
## [1] "Cantidad de filas duplicadas: 58"
vivienda_clean <- vivienda_limpia %>% distinct()
print("Filas duplicadas eliminadas correctamente.")
## [1] "Filas duplicadas eliminadas correctamente."
library(dplyr)
library(knitr)
find_issues <- function(df) {
total_filas <- nrow(df)
if (total_filas == 0) return(
tibble(Variable = character(),
Valores_Nulos = integer(), Porcentaje_Nulos = numeric(),
Caracteres_Extraños = integer(), Porcentaje_Caracteres_Extraños = numeric(),
Campos_Vacios = integer(), Porcentaje_Campos_Vacios = numeric())
)
valores_nulos <- sapply(df, function(x) sum(is.na(x)))
caracteres_extranos <- sapply(df, function(x) sum(grepl("[^ -~]", as.character(na.omit(x)), perl = TRUE)))
campos_vacios <- sapply(df, function(x) sum(nchar(trimws(as.character(na.omit(x)))) == 0))
tibble(
Variable = names(df),
Valores_Nulos = valores_nulos,
Porcentaje_Nulos = round((valores_nulos / total_filas) * 100, 2),
Caracteres_Extraños = caracteres_extranos,
Porcentaje_Caracteres_Extraños = round((caracteres_extranos / total_filas) * 100, 2),
Campos_Vacios = campos_vacios,
Porcentaje_Campos_Vacios = round((campos_vacios / total_filas) * 100, 2)
) %>%
arrange(desc(Valores_Nulos))
}
issues_df <- find_issues(vivienda_limpia)
kable(issues_df, format = "markdown", align = "c", caption = "Resumen de Problemas en los Datos")
| Variable | Valores_Nulos | Porcentaje_Nulos | Caracteres_Extraños | Porcentaje_Caracteres_Extraños | Campos_Vacios | Porcentaje_Campos_Vacios |
|---|---|---|---|---|---|---|
| piso | 2638 | 31.70 | 0 | 0.00 | 0 | 0 |
| parqueaderos | 1605 | 19.29 | 0 | 0.00 | 0 | 0 |
| zona | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| estrato | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| areaconst | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| banios | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| habitaciones | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| tipo | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| barrio | 3 | 0.04 | 1267 | 15.22 | 0 | 0 |
| longitud | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| latitud | 3 | 0.04 | 0 | 0.00 | 0 | 0 |
| preciom | 2 | 0.02 | 0 | 0.00 | 0 | 0 |
sin_banio <- vivienda_limpia %>%
filter(banios == 0) %>%
group_by(tipo) %>%
summarise(Sin_Banio = n(), .groups = "drop")
sin_habitaciones <- vivienda_limpia %>%
filter(habitaciones == 0) %>%
group_by(tipo) %>%
summarise(Sin_Habitaciones = n(), .groups = "drop")
resultado <- full_join(sin_banio, sin_habitaciones, by = "tipo") %>%
replace_na(list(Sin_Banio = 0, Sin_Habitaciones = 0))
resultado <- resultado %>%
bind_rows(
tibble(
tipo = "Total",
Sin_Banio = sum(resultado$Sin_Banio, na.rm = TRUE),
Sin_Habitaciones = sum(resultado$Sin_Habitaciones, na.rm = TRUE)
)
)
print("Cantidad de apartamentos y casas sin baño y sin habitaciones (incluyendo total):")
## [1] "Cantidad de apartamentos y casas sin baño y sin habitaciones (incluyendo total):"
print(resultado)
## # A tibble: 3 × 3
## tipo Sin_Banio Sin_Habitaciones
## <chr> <int> <int>
## 1 Apartamento 14 21
## 2 Casa 31 45
## 3 Total 45 66
vivienda_limpia <- vivienda_limpia %>%
filter(banios > 0 & habitaciones > 0)
print(paste("Cantidad de registros después de la limpieza:", nrow(vivienda_limpia)))
## [1] "Cantidad de registros después de la limpieza: 8243"
print("Cantidad de apartamentos y casas sin baño y sin habitaciones (incluyendo total):")
## [1] "Cantidad de apartamentos y casas sin baño y sin habitaciones (incluyendo total):"
print(resultado)
## # A tibble: 3 × 3
## tipo Sin_Banio Sin_Habitaciones
## <chr> <int> <int>
## 1 Apartamento 14 21
## 2 Casa 31 45
## 3 Total 45 66
library(dplyr)
contar_na <- function(df, vars) {
bind_rows(lapply(vars, function(v) {
df %>% group_by(tipo) %>% summarise(Cantidad_NA = sum(is.na(.data[[v]])), .groups = "drop") %>%
mutate(Variable = v)
})) %>%
mutate(tipo = replace_na(tipo, "Desconocido")) %>%
as.data.frame() %>%
print(row.names = FALSE)
}
contar_na(vivienda_limpia, c("piso", "parqueaderos"))
## tipo Cantidad_NA Variable
## Apartamento 1374 piso
## Casa 1219 piso
## Apartamento 857 parqueaderos
## Casa 698 parqueaderos
library(dplyr)
vivienda_limpia <- vivienda_limpia %>%
mutate(
piso = as.numeric(piso),
piso = if_else(is.na(piso), if_else(tipo == "Casa", 1, median(piso, na.rm = TRUE)), piso),
parqueaderos = replace_na(parqueaderos, 0)
) %>%
drop_na(zona, estrato, areaconst, banios, habitaciones, tipo, barrio, preciom)
colSums(is.na(vivienda_limpia))
## zona piso estrato preciom areaconst parqueaderos
## 0 0 0 0 0 0
## banios habitaciones tipo barrio longitud latitud
## 0 0 0 0 0 0
vivienda_limpia <- vivienda_limpia %>%
mutate(across(c(estrato, piso), as.factor))
variables_numericas <- names(select(vivienda_limpia, where(is.numeric)))
variables_categoricas <- names(select(vivienda_limpia, where(is.factor) | where(is.character)))
list(Numéricas = variables_numericas, Categóricas = variables_categoricas)
## $Numéricas
## [1] "preciom" "areaconst" "parqueaderos" "banios" "habitaciones"
## [6] "longitud" "latitud"
##
## $Categóricas
## [1] "piso" "estrato" "zona" "tipo" "barrio"
En la etapa de de limpieza, se realizaron ajustes para mejorar la calidad de los datos, inicialmente se detectaron filas duplicadas que fueron eliminadas, la variable id se excluye de la base de datos porque no aportan información relevante para el análisis; así mismo, generamos un resumen para detectar campos nulos, caracteres extraños y campos vacíos, identificamos valores nulos en todas las variables, optamos por eliminar los registros con valores nulos en las variables: zona, estrato, área construida, baños,habitaciones,tipo,barrio y precio, debido a que representan menos del 1% de los registros de la base de datos y, por lo tanto no presentan un riesgo en termino de perdida de datos, identificamos ademas, que la variable barrio contiene 1.267 caracteres extraños, sin embargo, es natural por tratarse de una variable tipo texto que contiene los nombres de los barrios, por lo tanto, no requirió tratamiento alguno, por último no se detectaron campos vacíos.
En cuanto a las variables piso y parqueadero, detectamos valores nulos con una participación del 38% y 19% en la base de datos respectivamente, dada importante participación en la base de datos se define una estrategia para su tratamiento para no eliminarlas. Analizando la variable piso observamos que de los 2.638 nulos, 1.254 corresponden a casa y los imputamos con el valor 1 porque tiene más sentido que las casas estén ubicadas en la primera planta, referente a los tipos apartamento, optamos por la estrategia de imputarlos con la moda de la zona a la que pertenecen.
Anteriormente, en la etapa de exploración de los datos, identificamos datos incoherentes como inmuebles tipo casa y apartamentos sin habitaciones, validando detectamos 45 inmuebles sin baño y 66 sin habitaciones, por tratarse de menos del 2% de los datos totales optamos por eliminarlos.
Por último definimos las variables categóricas y numéricas.
# Filtrar las casas ubicadas en la zona norte
casas_norte <- vivienda_limpia %>%
filter(tipo == "Casa" & zona == "Zona Norte")
# Mostrar la tabla con los resultados
library(DT)
datatable(casas_norte, options = list(scrollX = TRUE))
# Mostrar el total de casas
print(paste("Total de casas en la Zona Norte:", nrow(casas_norte)))
## [1] "Total de casas en la Zona Norte: 700"
# Filtrar las casas ubicadas en la zona norte y mostrar los tres primeros registros
casas_norte <- vivienda_limpia %>%
filter(tipo == "Casa" & zona == "Zona Norte") %>%
head(3)
# Mostrar la tabla con los resultados
library(DT)
datatable(casas_norte, options = list(scrollX = TRUE))
Enfocaremos nuestro análisis en identificar las casas de la Zona Norte de la Ciudad, de acuerdo con el requerimiento de nuestro Cliente, en esta paso filtramos la base de datos por las viviendas tipo Casa ubicadas en el Norte de la Ciudad y obtenemos un total de 700 casas en esta zona.
library(leaflet)
# Definir la paleta de colores
paleta_colores <- colorFactor(
palette = c("blue", "green", "purple", "red", "orange"),
domain = vivienda_limpia$zona
)
# Generar el mapa
leaflet(vivienda_limpia) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
color = ~paleta_colores(zona), fillOpacity = 0.7, radius =0.5,
label = ~paste("Barrio:", barrio),
popup = ~paste("Zona:", zona, "<br>",
"Precio:", preciom, "M", "<br>",
"Tipo:", tipo,"<br>",
"Área:", areaconst, "m²")
) %>%
addLegend(
"topright", pal = paleta_colores, values = vivienda_limpia$zona,
title = "Zonas de la ciudad"
)
Para obtener un visual gráfica generamos un mapa con la ubicación geográfica de las casas en Cali, identificando con un color según la zona asignada con las coordenadas de la base de datos, se evidencia asignaciones incorrectas,como por ejemplo viviendas con etiquetas de zonas de zona norte; sin embargo sus coordenadas son de la zona centro, sur oriente, esta misma casuística se presenta para el resto de las zonas, posiblemente este error se deriva de fallas en los procesos de captura de información o deficiencias en los programas para realizar las asignaciones de los registros, para evitar un error en la propuesta al Cliente se debe realizar la corrección del etiquetado de las Zonas, evitando ofrecer inmuebles que según su etiqueta figure como una casa en la zona de Cali, pero sus coordenadas apunten a otro sector de la Ciudad.
Abordamos esta corrección ubicando fuentes oficiales en donde podamos observar la limitación por zonas en la Ciudad de Cali, esta observación la obtenemos del repositorio de información de la Alcaldía de Cali y la utilizamos para definir las fronteras entre zonas.
library(dplyr)
library(geosphere)
library(leaflet)
# Definir las coordenadas promedio aproximadas de cada zona con los ajustes requeridos
zonas_coords <- data.frame(
zona = c("Zona Norte", "Zona Oriente", "Zona Oeste", "Zona Sur", "Zona Centro"),
latitud = c(3.4700, 3.4500, 3.4400, 3.4000, 3.4372),
longitud = c(-76.5000, -76.4800, -76.5600, -76.5200, -76.5225)
)
# Función para calcular la zona más cercana
asignar_zona <- function(lat, lon, zonas_coords) {
distancias <- distHaversine(c(lon, lat), zonas_coords[, c("longitud", "latitud")])
zona_cercana <- zonas_coords$zona[which.min(distancias)]
return(zona_cercana)
}
# Asignar la zona a cada registro en vivienda_limpia
vivienda_limpia <- vivienda_limpia %>%
rowwise() %>%
mutate(zona_corr = asignar_zona(latitud, longitud, zonas_coords))
# Crear una paleta de colores para las zonas
paleta_colores <- colorFactor(
palette = c("blue", "green", "purple", "red", "orange"),
domain = vivienda_limpia$zona_corr
)
# Generar el mapa con la nueva asignación de zonas
leaflet(vivienda_limpia) %>%
addTiles() %>%
addCircleMarkers(
~longitud, ~latitud,
color = ~paleta_colores(zona_corr), fillOpacity = 0.7, radius =0.5,
label = ~paste("Barrio:", barrio),
popup = ~paste("Zona:", zona_corr, "<br>",
"Precio:", preciom, "M", "<br>",
"Tipo:", tipo,"<br>",
"Área:", areaconst, "m²")
) %>%
addLegend(
"topright", pal = paleta_colores, values = vivienda_limpia$zona_corr,
title = "Zonas de la ciudad"
)
# Filtrar solo las casas ubicadas en la Zona Norte
casas_norte <- vivienda_limpia %>%
filter(tipo == "Casa" & zona_corr == "Zona Norte")
# Contar el número total de casas en la Zona Norte
total_casas_norte <- nrow(casas_norte)
# Crear un data frame con el resultado
tabla_casas_norte <- data.frame(
Zona = "Zona Norte",
Total_Casas = total_casas_norte
)
# Mostrar la tabla
print(tabla_casas_norte)
## Zona Total_Casas
## 1 Zona Norte 521
# Opcional: Mostrar la tabla de forma interactiva
library(DT)
datatable(tabla_casas_norte, options = list(scrollX = TRUE))
Observamos que con la resignación de las Zonas, la oferta de casas en la Zona Norte, disminuye en 179 unidades, pasando de 700 a 521.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Casas)
vivienda_filtrada <- vivienda_limpia %>%
filter(tipo == "Casa") %>% # Filtrar solo casas
select(preciom, zona) %>%
na.omit() # Eliminar valores faltantes
# Definir los colores personalizados según la imagen
colores_zonas <- c("Zona Centro" = "#76c6b8", # Verde menta
"Zona Norte" = "#e4805e", # Naranja
"Zona Oeste" = "#597dbd", # Azul
"Zona Oriente" = "#c779b1", # Rosa
"Zona Sur" = "#7db66f") # Verde claro
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_zona <- plot_ly(vivienda_filtrada,
x = ~factor(zona, levels = c("Zona Centro", "Zona Norte", "Zona Oeste", "Zona Oriente", "Zona Sur")),
y = ~preciom,
type = "box",
color = ~factor(zona),
colors = colores_zonas) %>%
layout(title = "Distribución del Precio por Zona (Solo Casas)",
xaxis = list(title = "Zona", categoryorder = "array"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_zona
Analizando el comportamiento de los precios según la ubicación de las casas en las distintas zonas, observamos que las casas más costosas se ubican en la zona oeste, seguido por la zona sur y, los precios más bajos se ubican en la zona norte, centro y oriente respectivamente, preliminarmente podríamos inferir que en la zona oeste se ubican las casas con el metro cuadrado más costoso y posiblemente porque son zonas exclusivas de la ciudad de estratos altos.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Norte y solo Casas)
vivienda_norte <- vivienda_limpia %>%
filter(zona == "Zona Norte", tipo == "Casa") %>% # Filtrar solo casas en la Zona Norte
select(preciom, areaconst) %>% # Seleccionar Precio y Área Construida
na.omit() # Eliminar valores faltantes
# Calcular la regresión lineal
modelo <- lm(preciom ~ areaconst, data = vivienda_norte)
# Crear datos de predicción para la línea de tendencia
predicciones <- data.frame(
areaconst = seq(min(vivienda_norte$areaconst), max(vivienda_norte$areaconst), length.out = 100)
)
predicciones$preciom <- predict(modelo, newdata = predicciones)
# Crear el gráfico de dispersión con la línea de tendencia
grafico_area_norte <- plot_ly() %>%
# Agregar puntos de dispersión
add_trace(data = vivienda_norte,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "markers",
marker = list(size = 5, opacity = 0.5, color = 'blue'),
name = "Casas en Zona Norte") %>%
# Agregar la línea de tendencia
add_trace(data = predicciones,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "lines",
line = list(color = 'red', width = 2),
name = "Línea de Tendencia") %>%
# Configuración del gráfico
layout(title = "Área Construida vs. Precio (Solo Casas en Zona Norte)",
xaxis = list(title = "Área Construida (m²)"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_area_norte
Observamos que se presenta una relación positiva entre la variable precio y área construida, preliminarmente de concluye que a medida que el área construida incrementa el precio aumenta, y tiene sentido,en el mercado inmobiliario las viviendas son mas costosas según su espacio.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Norte y solo Casas)
vivienda_norte_casas <- vivienda_limpia %>%
filter(zona == "Zona Norte", tipo == "Casa") %>% # Filtrar solo casas en la Zona Norte
select(preciom, estrato) %>% # Seleccionar solo Estrato y Precio
na.omit() # Eliminar valores faltantes
# Definir colores personalizados según las imágenes anteriores
colores_estrato <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_estrato_norte_casas <- plot_ly(vivienda_norte_casas,
x = ~factor(estrato, levels = sort(unique(vivienda_norte_casas$estrato))),
y = ~preciom,
type = "box",
color = ~factor(estrato),
colors = colores_estrato) %>%
layout(title = "Distribución del Precio por Estrato (Solo Casas en Zona Norte)",
xaxis = list(title = "Estrato", categoryorder = "category ascending"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_estrato_norte_casas
En cuanto al estrato, observamos también una relación positiva con el precio, a medida que el estrato aumenta el precio se incrementa, también tiene sentido, usualmente las zonas más exclusivas de la ciudad se encuentran ubicados en los estratos más altos y el metro cuadrado en estas ubicaciones privilegiadas suelen ser más costosos que el promedio en otros estratos.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Norte y solo Casas)
vivienda_norte_casas <- vivienda_limpia %>%
filter(zona == "Zona Norte", tipo == "Casa") %>% # Filtrar solo casas en la Zona Norte
select(preciom, banios) %>% # Seleccionar solo Número de Baños y Precio
na.omit() # Eliminar valores faltantes
# Ordenar los niveles del factor para que el número de baños más alto aparezca al final
niveles_ordenados <- sort(unique(vivienda_norte_casas$banios)) # Ordenar numéricamente
niveles_ordenados <- c(niveles_ordenados[niveles_ordenados != max(niveles_ordenados)], max(niveles_ordenados)) # Mover el mayor al final
# Definir colores personalizados para el número de baños
colores_banios <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f", "#ffcc00", "#8e44ad", "#ff5733", "#2a9d8f", "#8e44ad")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_banios_norte_casas <- plot_ly(vivienda_norte_casas,
x = ~factor(banios, levels = niveles_ordenados),
y = ~preciom,
type = "box",
color = ~factor(banios),
colors = colores_banios) %>%
layout(title = "Distribución del Precio por Número de Baños (Solo Casas en Zona Norte)",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_banios_norte_casas
Referente a la relación entre precio y número de baños, se aprecia una relación positiva, el precio es mayor en la medida que el inmueble tiene más baños, una de las razones de las variaciones del precio, es que si hay más baños la vivienda puede tener más metros cuadrados.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Norte y solo Casas)
vivienda_norte_casas <- vivienda_limpia %>%
filter(zona == "Zona Norte", tipo == "Casa") %>% # Filtrar solo casas en la Zona Norte
select(preciom, habitaciones) %>% # Seleccionar solo Número de Habitaciones y Precio
na.omit() # Eliminar valores faltantes
# Ordenar los niveles del factor para que el número de habitaciones más alto aparezca al final
niveles_ordenados <- sort(unique(vivienda_norte_casas$habitaciones)) # Ordenar numéricamente
niveles_ordenados <- c(niveles_ordenados[niveles_ordenados != max(niveles_ordenados)], max(niveles_ordenados)) # Mover el mayor al final
# Definir colores personalizados para el número de habitaciones
colores_habitaciones <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f", "#ffcc00", "#8e44ad", "#ff5733", "#2a9d8f", "#8e44ad")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_habitaciones_norte_casas <- plot_ly(vivienda_norte_casas,
x = ~factor(habitaciones, levels = niveles_ordenados),
y = ~preciom,
type = "box",
color = ~factor(habitaciones),
colors = colores_habitaciones) %>%
layout(title = "Distribución del Precio por Número de Habitaciones (Solo Casas en Zona Norte)",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_habitaciones_norte_casas
Por último, observamos que la relación entre el precio y el número de habitaciones también es positiva, entre mas habitaciones el precio es más alto, comportamiento similar al de los baños, se infiere que entre mas habitaciones tenga la vivienda el área es grande y por ende su precio.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar solo viviendas tipo Casa en todas las zonas y excluir longitud y latitud
vivienda_casas <- vivienda_limpia %>%
filter(tipo == "Casa") %>% # Filtrar solo casas
select_if(is.numeric) %>% # Seleccionar solo columnas numéricas
select(-longitud, -latitud) %>% # Excluir longitud y latitud
na.omit() # Eliminar valores faltantes
# Calcular la matriz de correlación
matriz_correlacion <- cor(vivienda_casas, use = "complete.obs")
# Crear etiquetas de texto con valores de correlación
texto_correlacion <- round(matriz_correlacion, 2) # Redondear a 2 decimales
# Crear el heatmap de correlación con escala de naranjas y valores dentro del gráfico
grafico_correlacion <- plot_ly(
z = matriz_correlacion,
x = colnames(matriz_correlacion),
y = colnames(matriz_correlacion),
type = "heatmap",
colorscale = "YlOrBr", # Escala de colores en tonos de naranja
text = texto_correlacion, # Agregar los valores de correlación como texto
texttemplate = "%{text}", # Mostrar los valores directamente en las celdas
hoverinfo = "text", # Mostrar los valores al pasar el mouse
showscale = TRUE # Mostrar la escala de colores
) %>%
layout(title = "Matriz de Correlación (Solo Casas en Todas las Zonas)",
xaxis = list(title = "Variables"),
yaxis = list(title = "Variables"))
# Mostrar el gráfico
grafico_correlacion
# Cargar librerías necesarias
library(GGally)
library(ggplot2)
library(dplyr)
# Filtrar solo viviendas tipo Casa en todas las zonas y excluir longitud y latitud
vivienda_casas <- vivienda_limpia %>%
filter(tipo == "Casa") %>% # Filtrar solo casas
select(preciom, areaconst, banios, habitaciones) %>% # Seleccionar variables relevantes
na.omit() # Eliminar valores faltantes
# Crear la matriz de correlación con ggpairs
ggpairs(vivienda_casas,
lower = list(continuous = ggally_points), # Gráficos de dispersión en la parte inferior
diag = list(continuous = ggally_densityDiag), # Histogramas de densidad en la diagonal
upper = list(continuous = ggally_cor)) + # Correlaciones en la parte superior
theme_minimal() # Estilo limpio
Observando las correlaciones respecto a la variables objetivo precio, identificamos una correlación fuerte positiva con área construida del 0.65. lo que sugiere que, dado su grado de correlación es la más influyente en el precio de las casas, seguida de parqueaderos con una correlación moderada positiva del 0.60 que puede ser un valor relevante en el precio, continuando con baños con una correlación moderada del 0.58,que influye en el precio pero en menor medida y, finalizando con habitaciones con un menor grado de correlación de 0.10, indicando que el número de habitaciones es irrelevante en la predicción del precio.
# Cargar librerías necesarias
library(dplyr)
# Filtrar solo viviendas tipo Casa y seleccionar variables relevantes
vivienda_modelo <- vivienda_limpia %>%
filter(tipo == "Casa") %>% # Filtrar solo casas
select(preciom, areaconst, estrato, banios, habitaciones, zona) %>% # Variables del modelo
na.omit() # Eliminar valores faltantes
# Convertir variables categóricas a factores para que el modelo las interprete correctamente
vivienda_modelo$zona <- as.factor(vivienda_modelo$zona)
vivienda_modelo$estrato <- as.factor(vivienda_modelo$estrato)
# Ajustar el modelo de regresión lineal múltiple
modelo <- lm(preciom~ areaconst + estrato + banios + habitaciones + zona, data = vivienda_modelo)
# Resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## zona, data = vivienda_modelo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1274.52 -93.49 -19.85 55.99 1176.58
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 63.36915 22.90276 2.767 0.005693 **
## areaconst 0.81947 0.02499 32.787 < 2e-16 ***
## estrato4 38.05227 12.41981 3.064 0.002204 **
## estrato5 115.32674 12.25892 9.408 < 2e-16 ***
## estrato6 425.17185 15.01324 28.320 < 2e-16 ***
## banios 46.30798 3.34225 13.855 < 2e-16 ***
## habitaciones -10.29233 2.68508 -3.833 0.000129 ***
## zonaZona Norte -38.41303 22.18943 -1.731 0.083524 .
## zonaZona Oeste 44.91279 26.50614 1.694 0.090282 .
## zonaZona Oriente -82.15296 23.12684 -3.552 0.000387 ***
## zonaZona Sur -6.59032 22.23330 -0.296 0.766931
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 196.5 on 3158 degrees of freedom
## Multiple R-squared: 0.7013, Adjusted R-squared: 0.7003
## F-statistic: 741.4 on 10 and 3158 DF, p-value: < 2.2e-16
El modelo se construye según los requerimientos del ejercicio planteado, en el que especifica que se deben incluir solo las variables predictoras: área construida, estrato, número de de baños, número de habitaciones, y zona de ubicación de la vivienda, para nuestro caso ubicación de las viviendas tipo casa, dado que nuestro objetivo es predecir en este punto la variable objetivo el precio de las casas, si bien, en el análisis de correlación observamos que la variable número de habitaciones no aportaba significativamente en la predicción, por requerimiento del ejercicio se incluye.
Los residuales indican desviaciones entre el precio real y el valor pronosticado, analizando los resultados de los residuos observamos que el valor de la mediana es bajo (-19.85), indicando que el modelo no se desvía significativamente del precio real evitando subestimar y sobrestimar los precios de las casas. continuando con el análisis, la mayoría de los errores se ubican entre -93.49 y 55.99, lo que significa que cuando el modelo se equivoca en la predicción el valor no es muy grande, es decir, que el valor no se desvía significativamente del precio real, por último, cuando revisamos el resultado de los valores los valores mínimos (-1274.52) y máximo (1176.58) de los errores, evidenciamos que los valores son altos, indicando que existen situaciones que el modelo no logra predecir con exactitud arrojando predicciones con errores muy altos, esto se puede atribuir al efecto de los datos atípicos.
Validando los resultados de p-valor de la prueba t-Student, que por regla nos indica que antes valores menores a 0.05 la variable contribuye a la predicción del modelo y, en caso contrario para valores mayores a 0.05 la variable no es significativa y no contribuye a la predicción del modelo, contrastando los resultados con esta regla, obtenemos que las variables que más aportan son área construida, estrato y número de baños, así mismo, validando observamos un dato con cierto grado de incoherencia, en el análisis de correlación observamos que el número de habitaciones, influye en un grado muy débil el precio de la vivienda y, cuando verificamos el p-valor ratificamos que influye en el precio; sin embargo, entre mas habitaciones el precio disminuye según los datos, pero en el mundo real entre más habitaciones tiene una vivienda su valor es más alto.
En cuanto al análisis de los coeficientes de variación (Betas) obtenemos los siguientes resultados:
De acuerdo con los resultados de los coeficientes podemos concluir que las variables estadísticamente significativas con p-valor menores a 0.05 son: área construida, estrato, número de baños y zona oriente,es decir que tienen un impacto importante en la predicción del precio de las casas, en caso contrario, las que más disminuyen el precio son: zona y número de habitaciones.
Analizando el error estándar de los coeficientes que nos indica que tan precisas son las estimaciones, obtenemos que las estimaciones con mayor grado de precisión son área construida y número de baños, y las menos confiables zona.
Evaluando los indicadores claves del modelo, podemos concluir que el modelo explica el 70.13% de la variabilidad en los precios de las casas , con un ajuste según el R² ajustado de 70.03%. El error estándar de los residuos (196.5) indica imprecisiones en las predicciones, atribuibles a factores no identificados. El estadístico F de 741.4 y el p-value menor a 2.2e-16 aseguran que el modelo es aceptable, sin embargo, aunque es confiable, se puede mejorar seleccionado variables con un valor más significativo y descartando las que menos le aporten al modelo como por ejemplo zona norte, zona oeste y zona sur.
# Ejecutar ANOVA en el modelo de regresión
anova_modelo <- anova(modelo)
# Mostrar resultados
print(anova_modelo)
## Analysis of Variance Table
##
## Response: preciom
## Df Sum Sq Mean Sq F value Pr(>F)
## areaconst 1 174724888 174724888 4524.364 < 2.2e-16 ***
## estrato 3 101219586 33739862 873.667 < 2.2e-16 ***
## banios 1 7875712 7875712 203.935 < 2.2e-16 ***
## habitaciones 1 674929 674929 17.477 2.987e-05 ***
## zona 4 1824834 456209 11.813 1.586e-09 ***
## Residuals 3158 121957748 38619
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Para confirmar los resultados de la etapa anterior, aplicamos un análisis ANOVA, para evaluar el grado de importancia de las variables en términos predictivos del modelo, analizando la variabilidad de cada uno de los predictores obtenemos que la suma de los cuadrados es 286.319.949 (SCR) y la suma de los cuadrados de los errores es 121.957.748 (SCE), indicando que la variación del precio de las casas es explicada en gran parte por los predictores (SCR); sin embargo, existe una variabilidad que no es explicada por los predictores, concluyendo como el SCR es mayor a SCR, gran parte de la variación de los precios es explicada por la variables elegidas y podemos afirmar que el modelo es aceptable.
# Cargar librerías necesarias
# Cargar librerías necesarias
library(dplyr)
library(knitr)
# Calcular resumen estadístico de los residuos
resumen_residuos <- data.frame(
Estadística = c("Mínimo", "Q1", "Mediana", "Media", "Q3", "Máximo", "Desviación Estándar", "Asimetría", "Curtosis"),
Valor = c(min(residuals(modelo)), quantile(residuals(modelo), c(0.25, 0.5, 0.75)), mean(residuals(modelo)),
max(residuals(modelo)), sd(residuals(modelo)),
mean((residuals(modelo) - mean(residuals(modelo)))^3) / sd(residuals(modelo))^3,
mean((residuals(modelo) - mean(residuals(modelo)))^4) / sd(residuals(modelo))^4 - 3)
)
# Mostrar tabla formateada
kable(resumen_residuos, format = "markdown", align = "c", caption = "Resumen Estadístico de los Residuos")
| Estadística | Valor |
|---|---|
| Mínimo | -1274.522029 |
| Q1 | -93.493137 |
| Mediana | -19.850542 |
| Media | 55.994491 |
| Q3 | 0.000000 |
| Máximo | 1176.578958 |
| Desviación Estándar | 196.205923 |
| Asimetría | 1.352024 |
| Curtosis | 6.218037 |
Supuesto de la mediana de los errores es siempre 0
# Calcular la mediana de los residuos del modelo
mediana_residuos <- median(residuals(modelo))
# Mostrar el resultado
print(paste("Mediana de los residuos:", round(mediana_residuos, 4)))
## [1] "Mediana de los residuos: -19.8505"
Interpretando los resultados, concluimos que el supuesto no se cumple, la mediana de los residuos es -19.8505, que es distinto de Cero; sin embargo, no es una desviación leve que nos indica que nuestro modelo presenta un pequeño sesgo subestimando el precio de las casas.
Supuesto de independencia de los errores
# Cargar la librería necesaria
library(lmtest)
# Realizar la prueba de Durbin-Watson
dw_test <- dwtest(modelo)
# Mostrar los resultados
print(dw_test)
##
## Durbin-Watson test
##
## data: modelo
## DW = 1.7384, p-value = 4.373e-14
## alternative hypothesis: true autocorrelation is greater than 0
Validando los resultados, obtuvimos que el supuesto no se cumple, aplicando el test, el estadístico DW = 1.74, cercano a 2 que nos indica que existe una leve correlación; sin embargo, contrastando con el resultado de p-valor = 4.373e-14, es muy cercano a 0, confirmando la hipótesis alternativa (existe correlación positiva en los residuos)
Supuesto de Varianza constante de los errores (Homocedasticidad)
# Cargar la librería necesaria
library(lmtest)
# Realizar la prueba de Breusch-Pagan
bp_test <- bptest(modelo)
# Mostrar los resultados
print(bp_test)
##
## studentized Breusch-Pagan test
##
## data: modelo
## BP = 476.73, df = 10, p-value < 2.2e-16
De acuerdo con los resultados obtenidos, este supuesto tampoco se cumple, el modelo presenta heterocedasticidad, revisando el p-valor < 2.2e-16 es muy cercano a 0 por lo que se rechaza la hipótesis nula (los errores tienen varianza constante), es decir, que los errores no presentan una varianza constante, lo que plantea dudas sobre precisión de los coeficientes estimados.
Supuesto de normalidad de los errores
# Gráfico Q-Q de los residuos
qqnorm(residuals(modelo))
qqline(residuals(modelo), col = "red")
# Prueba de Shapiro-Wilk para normalidad
shapiro_test <- shapiro.test(residuals(modelo))
# Mostrar los resultados
print(shapiro_test)
##
## Shapiro-Wilk normality test
##
## data: residuals(modelo)
## W = 0.86986, p-value < 2.2e-16
Para validar el cumplimiento de este supuesto, realizamos dos pruebas, Shapiro-Wilk y graficamos el Q-Q, el primer test nos arroja un p-valor muy cercano a 0; por lo tanto se rechaza la hipótesis nula (Los residuos siguen una distribución normal), para contrastar el resultado, observamos el comportamiento en el gráfico y verificamos que, si bien, en la parte central lo puntos se ajustan a la linea, sugiriendo que en esta zona los datos siguen un distribución aparentemente normal, en las colas se evidencia una desviación significativa a la linea de normalidad, por lo tanto, se concluye que no existe una distribución normal en los errores del modelo.
Supuesto de no colinealidad
# Cargar la librería necesaria
library(car)
# Calcular el VIF
vif_values <- vif(modelo)
# Mostrar los resultados
print(vif_values)
## GVIF Df GVIF^(1/(2*Df))
## areaconst 1.505755 1 1.227092
## estrato 2.609473 3 1.173344
## banios 2.137518 1 1.462025
## habitaciones 1.695554 1 1.302134
## zona 1.812101 4 1.077142
Validando este supuesto, el objetivo es verificar si se presenta colinealidad entre las variable predictoras, verificando los resultados del VF (factor de inflación de la varianza) de los errores, obtenemos que las cinco variables predicadoras presentan resultados menores a cinco, confirmando que no existe colinealidad entre las variables predictoras; por lo tanto, este es el único supuesto que se cumple.
Una vez evaluados los cinco supuestos y analizadas las métricas de desempeño, concluimos que, el modelo no presenta un buen desempeño en términos de normalidad, independencias y homocedasticidad de los errores, la mayoría de las métricas evaluadas no cumplen los criterios, para mejorar la precisión y predicción del modelo, es necesario aplicar estrategias de tratamiento a los valores atípicos e incluir otras variables predictoras que mejoren el desempeño y la confiabilidad de los resultados.
Estrato 4
# Crear un nuevo dataframe con las características de la vivienda a predecir
nueva_vivienda <- data.frame(
areaconst = 200, # Área construida
estrato = factor(4, levels = levels(vivienda_modelo$estrato)), # Estrato 4
banios = 2, # Número de baños
habitaciones = 4, # Número de habitaciones
zona = factor("Zona Norte", levels = levels(vivienda_modelo$zona)) # Zona Norte
)
# Realizar la predicción del precio de la vivienda
prediccion_precio <- predict(modelo, newdata = nueva_vivienda)
# Mostrar el resultado de la predicción
print(paste("El precio estimado de la vivienda es:", round(prediccion_precio, 2), "millones de pesos"))
## [1] "El precio estimado de la vivienda es: 278.35 millones de pesos"
Una vez ejecutamos el modelo, para una vivienda de acuerdo con estas características: Área construida: 200 m², estrato 4, con dos baños, cuatro habitaciones y ubicado en la zona norte, obtenemos que el precio estimado es aproximadamente de 278 Millones de pesos, cabe resaltar que la variable parqueaderos no se complementó en el modelo de acuerdo con los requerimientos del ejercicio , por lo tanto, la predicción incluye esa variable.
Estrato 5
# Crear un nuevo dataframe con las características de la vivienda a predecir
nueva_vivienda <- data.frame(
areaconst = 200, # Área construida
estrato = factor(5, levels = levels(vivienda_modelo$estrato)), # Estrato 4
banios = 2, # Número de baños
habitaciones = 4, # Número de habitaciones
zona = factor("Zona Norte", levels = levels(vivienda_modelo$zona)) # Zona Norte
)
# Realizar la predicción del precio de la vivienda
prediccion_precio <- predict(modelo, newdata = nueva_vivienda)
# Mostrar el resultado de la predicción
print(paste("El precio estimado de la vivienda es:", round(prediccion_precio, 2), "millones de pesos"))
## [1] "El precio estimado de la vivienda es: 355.62 millones de pesos"
En la segunda estimación, para una vivienda de acuerdo con estas características: Área construida: 200 m², estrato 5, con dos baños, cuatro habitaciones y ubicado en la zona norte, obtenemos que el precio estimado es aproximadamente de 356 Millones de pesos.
Comparando ambas estimaciones y contemplando la restricción presupuestaria indicando que el valor del inmueble debe tener un precio menor o igual a 350 Millones, basados en esa restricción la oferta que cumple con ese requisito es la casa en el estrato 4.
# Cargar librerías necesarias
library(dplyr)
library(leaflet)
# Definir el nuevo rango de precios entre 300M y 380M (para incluir más opciones)
precio_min <- 300
precio_max <- 350
# Filtrar solo casas en la Zona Norte con criterios más flexibles
casas_filtradas <- vivienda_limpia %>%
filter(
zona_corr == "Zona Norte",
tipo == "Casa",
areaconst >= 200, # Ampliar rango de área construida
estrato %in% c(4, 5), # Estrato 4 o 5
banios >= 2, # Al menos 2 baños
habitaciones >= 4, # Al menos 3 habitaciones
parqueaderos >= 1, # Incluir casas con o sin parqueaderos
preciom >= precio_min & preciom <= precio_max # Nuevo rango de precios más amplio
)
# Verificar si hay datos después del filtro
print(nrow(casas_filtradas)) # Mostrar la cantidad de casas que cumplen con los criterios
## [1] 27
# Definir iconos para las banderas de estrato 4 (rojo) y estrato 5 (verde)
iconos_bandera <- awesomeIcons(
icon = 'flag',
iconColor = 'white',
library = 'fa',
markerColor = ifelse(casas_filtradas$estrato == 4, "red", "green")
)
# Crear mapa interactivo con las casas filtradas
leaflet(casas_filtradas) %>%
addTiles() %>%
setView(lng = -76.5, lat = 3.45, zoom = 12) %>%
addAwesomeMarkers(
lng = ~longitud, lat = ~latitud,
icon = iconos_bandera,
popup = ~paste("Zona:", zona_corr, "<br>",
"Estrato:", estrato, "<br>",
"Precio:", preciom, "M", "<br>",
"Área:", areaconst, "m²", "<br>",
"Baños:", banios, "<br>",
"Habitaciones:", habitaciones, "<br>",
"Parqueaderos:", parqueaderos)
) %>%
addLegend(
position = "topright",
colors = c("red", "green"),
labels = c("Estrato 4", "Estrato 5"),
title = "Casas en Zona Norte (300M - 350M)"
)
# Cargar librerías necesarias
library(dplyr)
library(DT)
# Calcular el precio por metro cuadrado
casas_filtradas <- casas_filtradas %>%
mutate(precio_m2 = preciom / areaconst) %>%
arrange(precio_m2) # Ordenar de menor a mayor precio por metro cuadrado
# Verificar cuántas casas hay en el mapa
print(paste("Número total de casas identificadas:", nrow(casas_filtradas)))
## [1] "Número total de casas identificadas: 27"
# Mostrar todas las casas identificadas en la tabla interactiva
datatable(casas_filtradas,
options = list(scrollX = TRUE, pageLength = nrow(casas_filtradas)),
caption = "Todas las Casas Identificadas en la Zona Norte")
# Identificar las cinco mejores ofertas (menor precio por m²)
mejores_ofertas <- casas_filtradas %>%
head(5) # Tomar las 5 con menor precio por m²
# Mostrar las cinco mejores ofertas en una tabla interactiva
datatable(mejores_ofertas,
options = list(scrollX = TRUE, pageLength = 5),
caption = "Las 5 Mejores Ofertas en la Zona Norte (Basado en Precio por m²)")
De acuerdo con los requerimientos del cliente, realizamos una búsqueda exhaustiva de las ofertas disponibles de casas en el mercado inmobiliario de Cali, inicialmente aplicamos los siguientes criterios de búsqueda:
Como resultado obtuvimos una litado preliminar de 27 inmueble que cumplen esas condiciones, analizamos cada un de las propiedades y para realizar la oferta definimos criterios de valorización, ubicación y relación precio metro cuadrado, una vez analizados estos criterios se determina que las cinco ofertas más atractivas por su costo y valorización con las que se presentan en la tabla.
# Filtrar los apartamentos ubicados en la zona sur
apartamentos_sur <- vivienda_limpia %>%
filter(tipo == "Apartamento" & zona == "Zona Sur")
# Mostrar la tabla con los resultados
library(DT)
datatable(apartamentos_sur, options = list(scrollX = TRUE))
# Mostrar el total de apartamentos
print(paste("Total de apartamentos en la Zona Sur:", nrow(apartamentos_sur)))
## [1] "Total de apartamentos en la Zona Sur: 2777"
# Filtrar los apartamentos ubicados en la zona sur y mostrar los tres primeros registros
apartamentos_sur <- vivienda_limpia %>%
filter(tipo == "Apartamento" & zona == "Zona Sur") %>%
head(3)
# Mostrar la tabla con los resultados
library(DT)
datatable(apartamentos_sur, options = list(scrollX = TRUE))
# Filtrar solo los apartamentos ubicados en la Zona Sur
apartamentos_sur <- vivienda_limpia %>%
filter(tipo == "Apartamento" & zona_corr == "Zona Sur")
# Contar el número total de apartamentos en la Zona Sur
total_apartamentos_sur <- nrow(apartamentos_sur)
# Crear un data frame con el resultado
tabla_apartamentos_sur <- data.frame(
Zona = "Zona Sur",
Total_Apartamentos = total_apartamentos_sur
)
# Mostrar la tabla
print(tabla_apartamentos_sur)
## Zona Total_Apartamentos
## 1 Zona Sur 2416
# Opcional: Mostrar la tabla de forma interactiva
library(DT)
datatable(tabla_apartamentos_sur, options = list(scrollX = TRUE))
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Apartamentos)
vivienda_filtrada <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select(preciom, zona) %>%
na.omit() # Eliminar valores faltantes
# Definir los colores personalizados según la imagen
colores_zonas <- c("Zona Centro" = "#76c6b8", # Verde menta
"Zona Norte" = "#e4805e", # Naranja
"Zona Oeste" = "#597dbd", # Azul
"Zona Oriente" = "#c779b1", # Rosa
"Zona Sur" = "#7db66f") # Verde claro
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_zona <- plot_ly(vivienda_filtrada,
x = ~factor(zona, levels = c("Zona Centro", "Zona Norte", "Zona Oeste", "Zona Oriente", "Zona Sur")),
y = ~preciom,
type = "box",
color = ~factor(zona),
colors = colores_zonas) %>%
layout(title = "Distribución del Precio por Zona (Solo Apartamentos)",
xaxis = list(title = "Zona", categoryorder = "array"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_zona
Analizando el comportamiento de los precios según la ubicación de las apartamentos en las distintas zonas, observamos que las apartamentos más costosos se ubican en la zona oeste, seguido por la zona sur y norte, los precios más bajos se ubican en la zona centro y oriente respectivamente, preliminarmente podríamos inferir que en la zona oeste se ubican los apartamentos con el metro cuadrado más costoso y posiblemente porque son zonas exclusivas de la ciudad de estratos altos.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Sur y solo Apartamentos)
vivienda_sur <- vivienda_limpia %>%
filter(zona == "Zona Sur", tipo == "Apartamento") %>% # Filtrar solo apartamentos en la Zona Sur
select(preciom, areaconst) %>% # Seleccionar Precio y Área Construida
na.omit() # Eliminar valores faltantes
# Calcular la regresión lineal
modelo <- lm(preciom ~ areaconst, data = vivienda_sur)
# Crear datos de predicción para la línea de tendencia
predicciones <- data.frame(
areaconst = seq(min(vivienda_sur$areaconst), max(vivienda_sur$areaconst), length.out = 100)
)
predicciones$preciom <- predict(modelo, newdata = predicciones)
# Crear el gráfico de dispersión con la línea de tendencia
grafico_area_sur <- plot_ly() %>%
# Agregar puntos de dispersión
add_trace(data = vivienda_sur,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "markers",
marker = list(size = 5, opacity = 0.5, color = 'blue'),
name = "Apartamentos en Zona Sur") %>%
# Agregar la línea de tendencia
add_trace(data = predicciones,
x = ~areaconst,
y = ~preciom,
type = "scatter",
mode = "lines",
line = list(color = 'red', width = 2),
name = "Línea de Tendencia") %>%
# Configuración del gráfico
layout(title = "Área Construida vs. Precio (Solo Apartamentos en Zona Sur)",
xaxis = list(title = "Área Construida (m²)"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_area_sur
Observamos que se presenta una relación positiva entre la variable precio y área construida, preliminarmente de concluye que a medida que el área construida incrementa el precio aumenta, y tiene sentido,en el mercado inmobiliario las viviendas son mas costosas según su espacio.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Sur y solo Apartamentos)
vivienda_sur_apartamentos <- vivienda_limpia %>%
filter(zona == "Zona Sur", tipo == "Apartamento") %>% # Filtrar solo apartamentos en la Zona Sur
select(preciom, estrato) %>% # Seleccionar solo Estrato y Precio
na.omit() # Eliminar valores faltantes
# Definir colores personalizados según las imágenes anteriores
colores_estrato <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_estrato_sur_apartamentos <- plot_ly(vivienda_sur_apartamentos,
x = ~factor(estrato, levels = sort(unique(vivienda_sur_apartamentos$estrato))),
y = ~preciom,
type = "box",
color = ~factor(estrato),
colors = colores_estrato) %>%
layout(title = "Distribución del Precio por Estrato (Solo Apartamentos en Zona Sur)",
xaxis = list(title = "Estrato", categoryorder = "category ascending"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_estrato_sur_apartamentos
En cuanto al estrato, observamos también una relación positiva con el precio, a medida que el estrato aumenta el precio se incrementa, también tiene sentido, usualmente las zonas más exclusivas de la ciudad se encuentran ubicados en los estratos más altos y el metro cuadrado en estas ubicaciones privilegiadas suelen ser más costosos que el promedio en otros estratos.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Sur y solo Apartamentos)
vivienda_sur_apartamentos <- vivienda_limpia %>%
filter(zona == "Zona Sur", tipo == "Apartamento") %>% # Filtrar solo apartamentos en la Zona Sur
select(preciom, banios) %>% # Seleccionar solo Número de Baños y Precio
na.omit() # Eliminar valores faltantes
# Ordenar los niveles del factor para que el número de baños más alto aparezca al final
niveles_ordenados <- sort(unique(vivienda_sur_apartamentos$banios)) # Ordenar numéricamente
niveles_ordenados <- c(niveles_ordenados[niveles_ordenados != max(niveles_ordenados)], max(niveles_ordenados)) # Mover el mayor al final
# Definir colores personalizados para el número de baños
colores_banios <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f", "#ffcc00", "#8e44ad", "#ff5733", "#2a9d8f", "#8e44ad")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_banios_sur_apartamentos <- plot_ly(vivienda_sur_apartamentos,
x = ~factor(banios, levels = niveles_ordenados),
y = ~preciom,
type = "box",
color = ~factor(banios),
colors = colores_banios) %>%
layout(title = "Distribución del Precio por Número de Baños (Solo Apartamentos en Zona Sur)",
xaxis = list(title = "Número de Baños"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_banios_sur_apartamentos
Referente a la relación entre precio y número de baños, se aprecia una relación positiva, el precio es mayor en la medida que el inmueble tiene más baños, una de las razones de las variaciones del precio, es que si hay más baños la vivienda puede tener más metros cuadrados.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar y limpiar los datos (Solo Zona Sur y solo Apartamentos)
vivienda_sur_apartamentos <- vivienda_limpia %>%
filter(zona == "Zona Sur", tipo == "Apartamento") %>% # Filtrar solo apartamentos en la Zona Sur
select(preciom, habitaciones) %>% # Seleccionar solo Número de Habitaciones y Precio
na.omit() # Eliminar valores faltantes
# Ordenar los niveles del factor para que el número de habitaciones más alto aparezca al final
niveles_ordenados <- sort(unique(vivienda_sur_apartamentos$habitaciones)) # Ordenar numéricamente
niveles_ordenados <- c(niveles_ordenados[niveles_ordenados != max(niveles_ordenados)], max(niveles_ordenados)) # Mover el mayor al final
# Definir colores personalizados para el número de habitaciones
colores_habitaciones <- c("#76c6b8", "#e4805e", "#597dbd", "#c779b1", "#7db66f", "#ffcc00", "#8e44ad", "#ff5733", "#2a9d8f", "#8e44ad")
# Crear el gráfico de cajas (boxplot) con colores específicos
grafico_habitaciones_sur_apartamentos <- plot_ly(vivienda_sur_apartamentos,
x = ~factor(habitaciones, levels = niveles_ordenados),
y = ~preciom,
type = "box",
color = ~factor(habitaciones),
colors = colores_habitaciones) %>%
layout(title = "Distribución del Precio por Número de Habitaciones (Solo Apartamentos en Zona Sur)",
xaxis = list(title = "Número de Habitaciones"),
yaxis = list(title = "Precio (M)"))
# Mostrar el gráfico
grafico_habitaciones_sur_apartamentos
Por último, observamos que la relación entre el precio y el número de habitaciones también es positiva, entre mas habitaciones el precio es más alto, comportamiento similar al de los baños, se infiere que entre mas habitaciones tenga la vivienda el área es grande y por ende su precio también.
# Cargar librerías necesarias
library(dplyr)
library(plotly)
# Filtrar solo viviendas tipo Apartamento y excluir longitud y latitud
vivienda_apartamentos <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select_if(is.numeric) %>% # Seleccionar solo columnas numéricas
select(-longitud, -latitud) %>% # Excluir longitud y latitud
na.omit() # Eliminar valores faltantes
# Calcular la matriz de correlación
matriz_correlacion <- cor(vivienda_apartamentos, use = "complete.obs")
# Crear etiquetas de texto con valores de correlación
texto_correlacion <- round(matriz_correlacion, 2) # Redondear a 2 decimales
# Crear el heatmap de correlación con escala de naranjas y valores dentro del gráfico
grafico_correlacion <- plot_ly(
z = matriz_correlacion,
x = colnames(matriz_correlacion),
y = colnames(matriz_correlacion),
type = "heatmap",
colorscale = "YlOrBr", # Escala de colores en tonos de naranja
text = texto_correlacion, # Agregar los valores de correlación como texto
texttemplate = "%{text}", # Mostrar los valores directamente en las celdas
hoverinfo = "text", # Mostrar los valores al pasar el mouse
showscale = TRUE # Mostrar la escala de colores
) %>%
layout(title = "Matriz de Correlación (Solo Apartamentos)",
xaxis = list(title = "Variables"),
yaxis = list(title = "Variables"))
# Mostrar el gráfico
grafico_correlacion
# Cargar librerías necesarias
library(GGally)
library(ggplot2)
library(dplyr)
# Filtrar solo viviendas tipo Apartamento en todas las zonas y excluir longitud y latitud
vivienda_apartamentos <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select(preciom, areaconst, banios, habitaciones) %>% # Seleccionar variables relevantes
na.omit() # Eliminar valores faltantes
# Crear la matriz de correlación con ggpairs
ggpairs(vivienda_apartamentos,
lower = list(continuous = ggally_points), # Gráficos de dispersión en la parte inferior
diag = list(continuous = ggally_densityDiag), # Histogramas de densidad en la diagonal
upper = list(continuous = ggally_cor)) + # Correlaciones en la parte superior
theme_minimal() # Estilo limpio
Observando las correlaciones respecto a la variables objetivo precio,
identificamos una correlación fuerte positiva con área construida del
0.83 lo que sugiere que, dado su grado de correlación es la más
influyente en el precio de los apartamentos, seguida de baños con una
correlación fuerte positiva del 0.75 que puede ser un valor relevante en
el precio, continuando con parqueaderos con una correlación fuerte del
0.7 y, finalizando con habitaciones con un menor grado de correlación de
0.3, indicando que el número de habitaciones es irrelevante en la
predicción del precio.
# Cargar librerías necesarias
library(dplyr)
# Filtrar solo viviendas tipo Apartamento y seleccionar variables relevantes
vivienda_modelo <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select(preciom, areaconst, estrato, banios, habitaciones, zona) %>% # Variables del modelo
na.omit() # Eliminar valores faltantes
# Convertir variables categóricas a factores para que el modelo las interprete correctamente
vivienda_modelo$zona <- as.factor(vivienda_modelo$zona)
vivienda_modelo$estrato <- as.factor(vivienda_modelo$estrato)
# Ajustar el modelo de regresión lineal múltiple
modelo <- lm(preciom ~ areaconst + estrato + banios + habitaciones + zona, data = vivienda_modelo)
# Resumen del modelo
summary(modelo)
##
## Call:
## lm(formula = preciom ~ areaconst + estrato + banios + habitaciones +
## zona, data = vivienda_modelo)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1881.44 -48.72 1.96 44.21 950.88
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -50.83077 28.05010 -1.812 0.07002 .
## areaconst 2.23167 0.04257 52.422 < 2e-16 ***
## estrato4 18.85254 6.89473 2.734 0.00627 **
## estrato5 47.03699 6.85074 6.866 7.40e-12 ***
## estrato6 183.09264 8.69129 21.066 < 2e-16 ***
## banios 58.88372 3.01425 19.535 < 2e-16 ***
## habitaciones -37.08801 3.43837 -10.787 < 2e-16 ***
## zonaZona Norte 47.34843 26.87116 1.762 0.07812 .
## zonaZona Oeste 110.12558 27.21805 4.046 5.29e-05 ***
## zonaZona Oriente 6.71178 31.39613 0.214 0.83073
## zonaZona Sur 38.93694 26.83363 1.451 0.14683
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 129.9 on 5063 degrees of freedom
## Multiple R-squared: 0.7989, Adjusted R-squared: 0.7985
## F-statistic: 2011 on 10 and 5063 DF, p-value: < 2.2e-16
El modelo se construye según los requerimientos del ejercicio planteado, en el que especifica que se deben incluir solo las variables predictoras: área construida, estrato, número de de baños, número de habitaciones, y zona de ubicación de la vivienda, para nuestro caso ubicación de las viviendas tipo apartamento, dado que nuestro objetivo es predecir en este punto la variable objetivo el precio de las casas, si bien, en el análisis de correlación observamos que la variable número de habitaciones no aportaba significativamente en la predicción, por requerimiento del ejercicio se incluye.
Los residuales indican desviaciones entre el precio real y el valor pronosticado, analizando los resultados de los residuos observamos que el valor de la mediana es bajo (1.96), indicando que el modelo no se desvía significativamente del precio real evitando subestimar y sobrestimar los precios de los apartamentos. continuando con el análisis, la mayoría de los errores se ubican entre -48.72 y 44.21, lo que significa que cuando el modelo se equivoca en la predicción el valor no es muy grande, es decir, que el valor no se desvía significativamente del precio real, por último, cuando revisamos el resultado de los valores los valores mínimos (-1881.44) y máximo (950.88) de los errores, evidenciamos que los valores son altos, indicando que existen situaciones que el modelo no logra predecir con exactitud arrojando predicciones con errores muy altos, esto se puede atribuir al efecto de los datos atípicos.
Validando los resultados de p-valor de la prueba t-Student, que por regla nos indica que antes valores menores a 0.05 la variable contribuye a la predicción del modelo y, en caso contrario para valores mayores a 0.05 la variable no es significativa y no contribuye a la predicción del modelo, contrastando los resultados con esta regla, obtenemos que las variables que más aportan son área construida, estrato y número de baños, así mismo, validando observamos un dato con cierto grado de incoherencia, en el análisis de correlación observamos que el número de habitaciones, influye en el precio de la vivienda y, cuando verificamos el p-valor ratificamos que influye en el precio; sin embargo, entre mas habitaciones el precio disminuye según los datos, pero en el mundo real entre más habitaciones tiene una vivienda su valor es más alto.
En cuanto al análisis de los coeficientes de variación (Betas) obtenemos los siguientes resultados:
De acuerdo con los resultados de los coeficientes podemos concluir que las variables estadísticamente significativas con p-valor menores a 0.05 son: área construida, estrato, número de baños y zona oriente,es decir que tienen un impacto importante en la predicción del precio de los apartamentos, en caso contrario, la que más disminuye el precio es:número de habitaciones.
Analizando el error estándar de los coeficientes que nos indica que tan precisas son las estimaciones, obtenemos que las estimaciones con mayor grado de precisión son área construida y número de baños, y las menos confiables zona.
Evaluando los indicadores claves del modelo, podemos concluir que el modelo explica el 79.89 % de la variabilidad en los precios de las casas ,con un ajuste según el R² ajustado de 79.85%. El error estándar de los residuos (129.9) indica imprecisiones en las predicciones, atribuibles a factores no identificados. El estadístico F de 2011 y el p-value menor a < 2.2e-16 aseguran que el modelo es aceptable, sin embargo, aunque es confiable, se puede mejorar seleccionado variables con un valor más significativo y descartando las que menos le aporten al modelo.
# Ejecutar ANOVA en el modelo de regresión para apartamentos
anova_modelo_apartamentos <- anova(modelo)
# Mostrar resultados
print(anova_modelo_apartamentos)
## Analysis of Variance Table
##
## Response: preciom
## Df Sum Sq Mean Sq F value Pr(>F)
## areaconst 1 295692993 295692993 17514.658 < 2.2e-16 ***
## estrato 3 33887303 11295768 669.077 < 2.2e-16 ***
## banios 1 4386702 4386702 259.836 < 2.2e-16 ***
## habitaciones 1 2656627 2656627 157.359 < 2.2e-16 ***
## zona 4 2880671 720168 42.657 < 2.2e-16 ***
## Residuals 5063 85476613 16883
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Para confirmar los resultados de la etapa anterior, aplicamos un análisis ANOVA, para evaluar el grado de importancia de las variables en términos predictivos del modelo, analizando la variabilidad de cada uno de los predictores obtenemos que la suma de los cuadrados es 336.349.296 (SCR) y la suma de los cuadrados de los errores es 85.476.613 (SCE), indicando que la variación del precio de los apartamentos es explicada en gran parte por los predictores (SCR); sin embargo, existe una variabilidad que no es explicada por los predictores, concluyendo como el SCR es mayor a SCR, gran parte de la variación de los precios es explicada por la variables elegidas y podemos afirmar que el modelo es aceptable.
# Cargar librerías necesarias
library(dplyr)
library(knitr)
# Filtrar solo apartamentos y seleccionar variables relevantes
vivienda_modelo_apart <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select(preciom, areaconst, estrato, banios, habitaciones, zona) %>%
na.omit() # Eliminar valores faltantes
# Convertir variables categóricas a factores para el modelo
vivienda_modelo_apart$zona <- as.factor(vivienda_modelo_apart$zona)
vivienda_modelo_apart$estrato <- as.factor(vivienda_modelo_apart$estrato)
# Ajustar el modelo de regresión lineal múltiple para apartamentos
modelo_apart <- lm(preciom ~ areaconst + estrato + banios + habitaciones + zona, data = vivienda_modelo_apart)
# Calcular resumen estadístico de los residuos
resumen_residuos_apart <- data.frame(
Estadística = c("Mínimo", "Q1", "Mediana", "Media", "Q3", "Máximo",
"Desviación Estándar", "Asimetría", "Curtosis"),
Valor = c(
min(residuals(modelo_apart)),
quantile(residuals(modelo_apart), c(0.25, 0.5, 0.75)),
mean(residuals(modelo_apart)),
max(residuals(modelo_apart)),
sd(residuals(modelo_apart)),
mean((residuals(modelo_apart) - mean(residuals(modelo_apart)))^3) / sd(residuals(modelo_apart))^3,
mean((residuals(modelo_apart) - mean(residuals(modelo_apart)))^4) / sd(residuals(modelo_apart))^4 - 3
)
)
# Mostrar tabla formateada
kable(resumen_residuos_apart, format = "markdown", align = "c", caption = "Resumen Estadístico de los Residuos (Solo Apartamentos)")
| Estadística | Valor |
|---|---|
| Mínimo | -1881.443211 |
| Q1 | -48.720427 |
| Mediana | 1.964738 |
| Media | 44.211493 |
| Q3 | 0.000000 |
| Máximo | 950.876808 |
| Desviación Estándar | 129.804940 |
| Asimetría | 0.291357 |
| Curtosis | 18.885811 |
Supuesto de la mediana de los errores es siempre 0
# Calcular la mediana de los residuos del modelo ajustado para apartamentos
mediana_residuos_apart <- median(residuals(modelo_apart))
# Mostrar el resultado
print(paste("Mediana de los residuos (Solo Apartamentos):", round(mediana_residuos_apart, 4)))
## [1] "Mediana de los residuos (Solo Apartamentos): 1.9647"
Interpretando los resultados, concluimos que el supuesto no se cumple, la mediana de los residuos es 1.9647, que es distinto de Cero; sin embargo, no es una desviación mínima que nos indica que nuestro modelo presenta un pequeño sesgo subestimando el precio de los apartamentos.
Supuesto de independencia de los errores
# Cargar la librería necesaria
library(lmtest)
# Realizar la prueba de Durbin-Watson para el modelo de apartamentos
dw_test_apart <- dwtest(modelo_apart)
# Mostrar los resultados
print(dw_test_apart)
##
## Durbin-Watson test
##
## data: modelo_apart
## DW = 1.7647, p-value < 2.2e-16
## alternative hypothesis: true autocorrelation is greater than 0
Validando los resultados, obtuvimos que el supuesto no se cumple, aplicando el test, el estadístico DW = 1.76, cercano a 2 que nos indica que existe una leve correlación; sin embargo, contrastando con el resultado de p-valor < 2.2e-16, es muy cercano a 0, confirmando la hipótesis alternativa (existe correlación positiva en los residuos)
Supuesto de Varianza constante de los errores (Homocedasticidad)
# Cargar la librería necesaria
library(lmtest)
# Realizar la prueba de Breusch-Pagan para el modelo de apartamentos
bp_test_apart <- bptest(modelo_apart)
# Mostrar los resultados
print(bp_test_apart)
##
## studentized Breusch-Pagan test
##
## data: modelo_apart
## BP = 1413.1, df = 10, p-value < 2.2e-16
De acuerdo con los resultados obtenidos, este supuesto tampoco se cumple, el modelo presenta heterocedasticidad, revisando el p-valor < 2.2e-16 es muy cercano a 0 por lo que se rechaza la hipótesis nula (los errores tienen varianza constante), es decir, que los errores no presentan una varianza constante, lo que plantea dudas sobre precisión de los coeficientes estimados.
Supuesto de normalidad de los errores
# Cargar librerías necesarias
library(nortest) # Para Anderson-Darling test
library(ggplot2) # Para histogramas y gráficos
# Extraer residuos del modelo de apartamentos
residuos_apart <- residuals(modelo_apart)
# Gráfico Q-Q Plot
qqnorm(residuos_apart)
qqline(residuos_apart, col = "red")
# Histograma de los residuos
ggplot(data = data.frame(residuos_apart), aes(x = residuos_apart)) +
geom_histogram(aes(y=..density..), bins = 30, fill="blue", alpha=0.5) +
geom_density(color="red", size=1) +
ggtitle("Histograma de los Residuos - Apartamentos") +
xlab("Residuos") + ylab("Densidad")
# Prueba de Kolmogorov-Smirnov
ks_test_apart <- ks.test(residuos_apart, "pnorm", mean(residuos_apart), sd(residuos_apart))
print(ks_test_apart)
##
## Asymptotic one-sample Kolmogorov-Smirnov test
##
## data: residuos_apart
## D = 0.15227, p-value < 2.2e-16
## alternative hypothesis: two-sided
# Prueba de Anderson-Darling (Mejor para muestras grandes)
ad_test_apart <- ad.test(residuos_apart)
print(ad_test_apart)
##
## Anderson-Darling normality test
##
## data: residuos_apart
## A = 214.84, p-value < 2.2e-16
validando los resultados de las pruebas de normalidad Kolmogorov-Smirnov y Anderson-Darling muestran p-valores menores a 2.2e-16, lo que indica que los residuos no siguen una distribución normal. El estadístico D en KS (0.15227) y A en AD (214.84) refuerzan esta conclusión, señalando una desviación significativa respecto a la normalidad, por otro lado, observamos el comportamiento en el gráfico y verificamos que, si bien, en la parte central lo puntos se ajustan a la linea, sugiriendo que en esta zona los datos siguen un distribución aparentemente normal, en las colas se evidencia una desviación significativa a la linea de normalidad, por lo tanto, se concluye que no existe una distribución normal en los errores del modelo.
Supuesto de no colinealidad
# Cargar la librería necesaria
library(car)
# Calcular el VIF solo para apartamentos
vivienda_modelo_apart <- vivienda_limpia %>%
filter(tipo == "Apartamento") %>% # Filtrar solo apartamentos
select(preciom, areaconst, estrato, banios, habitaciones, zona) %>% # Seleccionar variables del modelo
na.omit() # Eliminar valores faltantes
# Convertir variables categóricas a factores
vivienda_modelo_apart$zona <- as.factor(vivienda_modelo_apart$zona)
vivienda_modelo_apart$estrato <- as.factor(vivienda_modelo_apart$estrato)
# Ajustar el modelo de regresión lineal múltiple para apartamentos
modelo_apart <- lm(preciom ~ areaconst + estrato + banios + habitaciones + zona, data = vivienda_modelo_apart)
# Calcular el VIF
vif_values_apart <- vif(modelo_apart)
# Mostrar los resultados
print(vif_values_apart)
## GVIF Df GVIF^(1/(2*Df))
## areaconst 2.568904 1 1.602780
## estrato 2.545711 3 1.168516
## banios 3.070394 1 1.752254
## habitaciones 1.495524 1 1.222916
## zona 1.748648 4 1.072353
Validando este supuesto, el objetivo es verificar si se presenta colinealidad entre las variable predictoras, verificando los resultados del VF (factor de inflación de la varianza) de los errores, obtenemos que las cinco variables predicadoras presentan resultados menores a cinco, confirmando que no existe colinealidad entre las variables predictoras; por lo tanto, este es el único supuesto que se cumple.
Una vez evaluados los cinco supuestos y analizadas las métricas de desempeño, concluimos que, el modelo presenta un desempeño aceptable; sin embargo, en términos de normalidad, independencias y homocedasticidad de los errores, la mayoría de las métricas evaluadas no cumplen los criterios, para mejorar la precisión y predicción del modelo, es necesario aplicar estrategias de tratamiento a los valores atípicos e incluir otras variables predictoras que mejoren el desempeño y la confiabilidad de los resultados.
Estrato 5
# Crear un nuevo dataframe con las características del apartamento a predecir
nueva_vivienda_apart <- data.frame(
areaconst = 300, # Área construida
estrato = factor(5, levels = levels(vivienda_modelo_apart$estrato)), # Estrato 5
banios = 3, # Número de baños
habitaciones = 5, # Número de habitaciones
zona = factor("Zona Sur", levels = levels(vivienda_modelo_apart$zona)) # Zona Sur
)
# Realizar la predicción del precio del apartamento
prediccion_precio_apart <- predict(modelo_apart, newdata = nueva_vivienda_apart)
# Mostrar el resultado de la predicción
print(paste("El precio estimado del apartamento es:", round(prediccion_precio_apart, 2), "millones de pesos"))
## [1] "El precio estimado del apartamento es: 695.85 millones de pesos"
Una vez ejecutamos el modelo, para una vivienda de acuerdo con estas características: Área construida: 300 m², estrato 5, con tres baños, cinco habitaciones y ubicado en la zona sur, obtenemos que el precio estimado es aproximadamente de 696 Millones de pesos, cabe resaltar que la variable parqueaderos no se complementó en el modelo de acuerdo con los requerimientos del ejercicio , por lo tanto, la predicción incluye esa variable.
Estrato 6
# Crear un nuevo dataframe con las características del apartamento a predecir
nueva_vivienda_apart <- data.frame(
areaconst = 300, # Área construida
estrato = factor(6, levels = levels(vivienda_modelo_apart$estrato)), # Estrato 5
banios = 3, # Número de baños
habitaciones = 5, # Número de habitaciones
zona = factor("Zona Sur", levels = levels(vivienda_modelo_apart$zona)) # Zona Sur
)
# Realizar la predicción del precio del apartamento
prediccion_precio_apart <- predict(modelo_apart, newdata = nueva_vivienda_apart)
# Mostrar el resultado de la predicción
print(paste("El precio estimado del apartamento es:", round(prediccion_precio_apart, 2), "millones de pesos"))
## [1] "El precio estimado del apartamento es: 831.91 millones de pesos"
Una vez ejecutamos el modelo, para una vivienda de acuerdo con estas características: Área construida: 300 m², estrato 6, con tres baños, cinco habitaciones y ubicado en la zona sur, obtenemos que el precio estimado es aproximadamente de 832 Millones de pesos, cabe resaltar que la variable parqueaderos no se complementó en el modelo de acuerdo con los requerimientos del ejercicio , por lo tanto, la predicción incluye esa variable
# Cargar librerías necesarias
library(dplyr)
library(leaflet)
# Definir el nuevo rango de precios entre 300M y 350M
precio_min <- 500
precio_max <- 1000
# Filtrar solo apartamentos en la Zona Sur con criterios específicos
apartamentos_filtrados <- vivienda_limpia %>%
filter(
zona_corr == "Zona Sur", # Filtrar solo Zona Sur
tipo == "Apartamento", # Solo apartamentos
areaconst >= 300, # Área construida mínima de 200 m²
estrato %in% c(5, 6), # Estrato 4 o 5
banios >= 3, # Al menos 2 baños
habitaciones >= 4, # Al menos 4 habitaciones
parqueaderos >= 2, # Al menos 1 parqueadero
preciom >= precio_min & preciom <= precio_max # Rango de precios de 300M a 350M
)
# Verificar si hay datos después del filtro
print(nrow(apartamentos_filtrados)) # Mostrar la cantidad de apartamentos que cumplen con los criterios
## [1] 7
# Definir iconos para las banderas de estrato 4 (rojo) y estrato 5 (verde)
iconos_bandera <- awesomeIcons(
icon = 'flag',
iconColor = 'white',
library = 'fa',
markerColor = ifelse(apartamentos_filtrados$estrato == 5, "red", "green")
)
# Crear mapa interactivo con los apartamentos filtrados
leaflet(apartamentos_filtrados) %>%
addTiles() %>%
setView(lng = -76.5, lat = 3.45, zoom = 12) %>%
addAwesomeMarkers(
lng = ~longitud, lat = ~latitud,
icon = iconos_bandera,
popup = ~paste("Zona:", zona_corr, "<br>",
"Estrato:", estrato, "<br>",
"Precio:", preciom, "M", "<br>",
"Área:", areaconst, "m²", "<br>",
"Baños:", banios, "<br>",
"Habitaciones:", habitaciones, "<br>",
"Parqueaderos:", parqueaderos)
) %>%
addLegend(
position = "topright",
colors = c("red", "green"),
labels = c("Estrato 5", "Estrato 6"),
title = "Apartamentos en Zona Sur (700M - 850M)"
)
# Cargar librerías necesarias
library(dplyr)
library(DT)
# Calcular el precio por metro cuadrado para apartamentos
apartamentos_filtrados <- apartamentos_filtrados %>%
mutate(precio_m2 = preciom / areaconst) %>%
arrange(precio_m2) # Ordenar de menor a mayor precio por metro cuadrado
# Verificar cuántos apartamentos hay en el mapa
print(paste("Número total de apartamentos identificados:", nrow(apartamentos_filtrados)))
## [1] "Número total de apartamentos identificados: 7"
# Mostrar todos los apartamentos identificados en la tabla interactiva
datatable(apartamentos_filtrados,
options = list(scrollX = TRUE, pageLength = nrow(apartamentos_filtrados)),
caption = "Todos los Apartamentos Identificados en la Zona Sur")
# Identificar las cinco mejores ofertas (menor precio por m²)
mejores_ofertas <- apartamentos_filtrados %>%
head(5) # Tomar los 5 con menor precio por m²
# Mostrar las cinco mejores ofertas en una tabla interactiva
datatable(mejores_ofertas,
options = list(scrollX = TRUE, pageLength = 5),
caption = "Las 5 Mejores Ofertas en la Zona Sur (Basado en Precio por m²)")
De acuerdo con los requerimientos del cliente, realizamos una búsqueda exhaustiva de las ofertas disponibles de apartamentos en el mercado inmobiliario de Cali, inicialmente aplicamos los siguientes criterios de búsqueda:
Como resultado obtuvimos una litado preliminar de 7 inmueble que cumplen algunas de las condiciones, analizamos cada un de las propiedades y para realizar la oferta definimos criterios de valorización, ubicación y relación precio metro cuadrado, una vez analizados estos criterios se determina que las cinco ofertas más atractivas por su costo y valorización con las que se presentan en la tabla.