Julian Andres Ospina Cuesta

Maestría en Ciencia de Datos - Universidad Javeriana Cali
Cali, Colombia
Código: 8984933 +57 3043912471
Autorizo que el documento sea publicado con el resto de mis compañeros de la Universidad Javeriana

1.Introducción

En la compañia de inmuebles C&A , Maria, su fundadora requiere apoyo de analisis sobre dos solicitudes de vivienda en la ciudad de Cali. se nos pide realizar 6 solicitudes a modo de informe en donde esten las recomendaciones de dichas viviendas y se debe adjuntar las estimaciones, validaciones y comparaciones de los modelos requeridos.

las solicitudes son las siguientes

Tabla Solicitudes de vivienda
Tipo area_construida parqueaderos banos habitaciones estrato1 estrato2 zona credito_preaprobado_mill
Casa 200 1 2 4 4 5 Zona Norte 350
Apartamento 300 3 3 5 5 6 Zona Sur 850

las consultas son las siguientes:

  1. Filtros sobre las zonas y visualización

  2. Correlación entre las variables

  3. Estimación de un modelo de regresión lineal multiple

  4. Validación del modelo planteado

  5. Predicción de las viviendas con base en lo buscado

  6. Realizar propuestas que se ajusten al presupuesto del pre aprobado

Se nos va ha compartir una base de datos con la información de 3 meses del sector, llamada vivienda

#data("vivienda")
ruta_del_archivo <- "/Users/Julian/Documents/Material Estudio/MDS Javeriana/Sem 2/[006]EstadTomaDeci/Act1/vivienda.xlsx"
data <- readxl::read_excel(ruta_del_archivo)

1.1 Entendimiento de los datos suministrados

A continuación realizaremos un entendimiento de los datos suministrados

kable(do.call(data.frame, head(data)),
      format = "markdown",  
      caption = "**Tabla de los primeros registros base vivienda**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla de los primeros registros base vivienda
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

Observamos que tenemos 12 campos que constituyen los campos de la base, alguno de ellos categoricos y otros númericos.

resumen_data <- list(
  num_filas = nrow(data),
  num_col= ncol(data),
  vacios_totales = sum(is.na(data)),
  porc_vacios = sprintf('%.2f%%',sum(is.na(data))/(nrow(data)*ncol(data))*100),
  filas_con_mas_un_vacio = sum(apply(is.na(data), 1, any)),
  filas_all_vacios = sum(apply(is.na(data), 1, all)),
  col_all_vacios = sum(colSums(is.na(data)) == nrow(data)),
  filas_duplicadas= nrow(data[duplicated(data), ])
)

kable(do.call(data.frame, resumen_data),
      format = "markdown", 
      caption = "**Tabla Resumen de dataset antes de tratamientos**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla Resumen de dataset antes de tratamientos
num_filas num_col vacios_totales porc_vacios filas_con_mas_un_vacio filas_all_vacios col_all_vacios filas_duplicadas
8322 13 4275 3.95% 3514 2 0 1

Se ha identificado la existencia de dos filas vacías y nueve registros duplicados en el conjunto de datos. Estos elementos, por varias razones, no parecen ser esenciales para los objetivos de este estudio:

  1. En relación con el total de 8,322 observaciones, la presencia de estos 1 registros duplicados parece no ser representativa ni influyente.
  2. Se tiene 2 registros con todas las filas vacias.
  3. Tenemos un dataset con 3.95% de datos faltantes distribuidos entre 4275 registros y con 3514 registros con mas de 1 faltante.

Procedemos a observar un analisis enfocado a los valores faltantes

grafico_vacios <-md.pattern(data, rotate.names = TRUE)

Se identifica que los registros vacios a eliminar no tienen valor en el campo ‘id’

Iniciamos tratamiento de los registros que no vamos a requerir

#Se remueve los registros llenos de campos sin datos
data <- data[rowSums(is.na(data)) < ncol(data), ]
#Se remueve los duplicados
data <- data[!duplicated(data), ]
#Se remueve el campo que solo tiene el precio (se saca por id, es mas facil de identificar)
data <- data[!is.na(data$id), ]
#Removiendo columnas no requeridas
data <- select(data,-piso)

1.2 validacion de datos suministrados

Debido a que contamos con solo el campo de zonas y barrios para responder a las necesidades del cliente frente a sus solicitudes en terminos georeferencias (con latitud y longitud , identificar zonas de la ciudad es complejo), procedemos a analizar :

lista_graficos_cuali <- list()
for (col in c('barrio','zona')) {
 frecuencia_palabras_temp = aggregate(data[[col]], list(data[[col]]), FUN=length)
 colnames(frecuencia_palabras_temp)=c("Palabra","Frecuencia")
 lista_graficos_cuali[[col]] <- as.data.frame(frecuencia_palabras_temp)
}
frecuencia_palabras_temp = lista_graficos_cuali[['barrio']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Se puede observar que hay barrios con una falta de estandarización en los datos. Ejemplos de esto incluyen el uso inconsistente de mayúsculas, abreviaciones, puntuaciones y símbolos, lo que indica problemas en el manejo de la normalización de la información.

se plantea la siguiente función para normalizar los datos

reemplazar_caracteres <- function(texto) {
  #Función empleada para hacer limpieza de los caracteres encontrados en la base de datos
  #**NOTA**: No se trata de una función genérica, sino de una función personalizada 
  #construida específicamente para estos datos.
  #
  #@texto(string): el texto que vamos a procesar
  #@return(string): el texto previamente tratado
  
  caractares_remplazar <- c('Á','É','Í','Ó','Ú','√©','√∫')
  caracteres_remplazado <- c('A','E','I','O','U','E','U')
  texto <- toupper(texto)
  for (i in 1:length(caractares_remplazar)){
    texto <- gsub(caractares_remplazar[i],caracteres_remplazado[i],texto)
  }
  return (texto)
}
frecuencia_palabras_temp = lista_graficos_cuali[['zona']]
wordcloud(words = frecuencia_palabras_temp$Palabra,
                     freq = frecuencia_palabras_temp$Frecuencia,
                     min.freq = 2,
                     max.words = 60,
                     colors =  c("#515354","#50AC05","#0576FF", "#FF0576"),
                     random.order = F,
                     random.color = F,
                     scale = c(4 ,0.5),
                     rot.per = 0.25)

Se observa que el nombre de las zonas estas estandarizado

1.2.1 Comparación de nombres de barrios con el listado de barrios por comunas de las zonas de interes Norte - Sur

Con base en el listado oficial de barrios de la gobernaciona de Cali-Planeación, se realiza la siguiente depuración de datos .

nota: Por motivos practicos de informe, solo se muestran los primeros 10 registros en el reporte:

knitr::kable(head(datos_barrio_zona_depurados, 10),
      format = "markdown", 
      caption = "**Tabla Asociación de nombres entre las zonas de los datos y el mapa de planeación (10 registros)**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla Asociación de nombres entre las zonas de los datos y el mapa de planeación (10 registros)
zona barrio zona_revisada
Zona Norte acopi Zona Norte
Zona Sur acopi Zona Norte
Zona Norte alameda del río Zona Norte
Zona Norte alameda del rio Zona Norte
Zona Norte alamos Zona Norte
Zona Norte alcazares Zona Norte
Zona Norte altos de menga Zona Norte
Zona Sur arboleda Zona Norte
Zona Norte barranquilla Zona Norte
Zona Norte berlin Zona Norte

La idea es comparar con la asociació entre zona y barrio y cruzarlo con esta depuración de los datos que se realizó manualmente. permitiendo tener una nueva zona sugerida frente a barrios que pertenecen a la zona norte y sur de la ciudad.

1.2.2 Comparacion de barrios y comunas de las zonas de interes Norte - Sur

Con base en el mapa de Cali desde la gobernaciona de Cali-Planeación. se realiza la siguiente asociación entre zonas y comunas con base en poligonos (cuadrados)de coordenadas observadas en el mapa

df_asociacion <- data.frame(
  'Zonas Planeacion' = c('Oriente', 'Sur', 'Norte', 'Occidente', 'Centro geografico y Centro historico Comercial'),
  'Zona C&C' = c('Zona Oriente', 'Zona Sur', 'Zona Norte', 'Zona Oeste', 'Zona Centro')
)

knitr::kable(df_asociacion,
      format = "markdown", 
      caption = "**Tabla Asoacion de nombres entre los datos y el mapa de planeación**",
      align = "c", escape = FALSE,
      row.names = FALSE,
      booktabs = TRUE)
Tabla Asoacion de nombres entre los datos y el mapa de planeación
Zonas.Planeacion Zona.C.C
Oriente Zona Oriente
Sur Zona Sur
Norte Zona Norte
Occidente Zona Oeste
Centro geografico y Centro historico Comercial Zona Centro

A partir de esta asociación y las coordenadas en grados observados en el mapa, podemos determinar unos poligonos (cuadrados) para determinar si dada una coordenada aproximadamente pueda estar dentro de la zona en mención. A continuación se enseña coordenadas (las esquinas superior-izquierda e inferior-derecha de los cuadrados de área) que permitirán tener una aproximación:

coordenadas_norte = list(
  c(c(3.454564, -76.558411),c( 3.446252, -76.543127)),#comunda 2
  c(c(3.462423, -76.543127),c( 3.448916, -76.537159)),#comunda 2
  c(c(3.495136, -76.537159),c( 3.452614, -76.534739)),#comunda 2
  c(c(3.495136, -76.534739),c( 3.455389, -76.524408)),#comunda 2
  c(c(3.495136, -76.524408),c(3.461866, -76.496936)), #comunda 2
  c(c(3.488201, -76.520113),c(3.454628, -76.495372)), #comunda 4
  c(c(3.485687, -76.505831),c(3.461366, -76.485033)), #comunda 5
  c(c(3.503089, -76.503122),c(3.466336, -76.476343))  #comunda 6
)
coordenadas_sur = list(
  c(c(3.411852, -76.548099),c(3.362371, -76.513402)), #comunda 17
  c(c(3.373027, -76.555002),c(3.209006, -76.519925))  #comunda 22
)

Se plantean las siguientes funciones auxiliares que permitirán identificar si los registros de un dataframe pertenecen a dichas áreas sugeridas .Nota Entendemos que son una aproximación de los datos, pero nos permitiran descartar viviendas en ubicaciones muy atipicas.

crear_cuadrado <- function(coordenadas) {
  #Función empleadas para convertir las cordenadas superior-izquierda e inferiro-derecha en un listado
  #de coordenadas 'x' y 'y' que representan las cuatro esquinas del cuadrado que contiene la comuna
  x1 <- coordenadas[1]
  y1 <- coordenadas[2]
  x2 <- coordenadas[3]
  y2 <- coordenadas[4]

  x <- c(x1, x1 , x2, x2)
  y <- c(y1, y2, y1 , y2)
  
  return(list(x = x, y = y))
}


esta_dentro_cuadrado <- function(coordenada, cuadrado) {
  #Función empleada para determinar si dada una coordenada se encuentra dentro de un cuadrado ('región de la comuna')
  #dado por 4 coordenadas
  x <- coordenada[1]
  y <- coordenada[2]
  
  cuadrado_x <- cuadrado$x
  cuadrado_y <- cuadrado$y
  
  dentro_x <- x >= min(cuadrado_x) & x <= max(cuadrado_x)
  dentro_y <- y >= min(cuadrado_y) & y <= max(cuadrado_y)
  
  return(dentro_x & dentro_y)
}

crear_lista_cuadrados <- function(coordenadas_norte) {
  #Función empleada para crear el listado de cuadrados que vamos a emplear con base en una lista de coordenadas
  lista_cuadrados <- list()
  for (coords in coordenadas_norte) {
    cuadrado <- crear_cuadrado(coords)
    lista_cuadrados <- c(lista_cuadrados, list(cuadrado))
  }
  return(lista_cuadrados)
}

verificar_puntos_en_cuadrados <- function(df, lista_cuadrados) {
  # Función para verificar si cada punto está dentro de al menos un cuadrado
  #me retorno todo un vector de respuestas boolean
  puntos_dentro <- logical(nrow(df))
  
  for (j in 1:nrow(df)) {
    for (i in 1:length(lista_cuadrados)) {
      cuadrado <- lista_cuadrados[[i]]
      coordenada <- c(df$latitud[j], df$longitud[j])
      
      puntos_dentro[j] <- puntos_dentro[j] | esta_dentro_cuadrado(coordenada, cuadrado)
    }
  }
  return(puntos_dentro)
}

2. Filtros sobre las zonas y visualización

2.1 Vivienda_1

Iniciamos análisis de la consulta 1 de vivienda

2.1.1 Filtrando datos y tratamientos geograficos

unique_values <- unique(data$zona)
la_zona = vivienda_1$zona
datos_filtrados <- subset(data, tipo == vivienda_1$Tipo & zona == la_zona)

#tratamiento por nombre de barrio-zona
datos_filtrados = merge(datos_filtrados, datos_barrio_zona_depurados, by = c("zona", "barrio"), all.x = TRUE)

#tratamiento por latitud y longitud
lista_cuadrados <- crear_lista_cuadrados(coordenadas_norte)
puntos_validados <- verificar_puntos_en_cuadrados(datos_filtrados, lista_cuadrados)

datos_filtrados$en_poligono_de_zona <- puntos_validados
datos_filtrados$zona_revisada[is.na(datos_filtrados$zona_revisada)] <- "Fuera de zonas"

#tratamiento en nombre de los barrios
datos_filtrados$barrio  <- sapply(datos_filtrados$barrio, reemplazar_caracteres)

knitr::kable(head(datos_filtrados, 3),
             format = "markdown", 
             caption = glue("**Tabla Resumen de dataset de la zona {la_zona} **"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla Resumen de dataset de la zona Zona Norte
zona barrio id estrato preciom areaconst parqueaderos banios habitaciones tipo longitud latitud zona_revisada en_poligono_de_zona
Zona Norte ACOPI 4460 4 625 355 3 5 5 Casa -76.53179 3.40590 Zona Norte FALSE
Zona Norte ACOPI 604 5 520 455 NA 5 4 Casa -76.49966 3.46284 Zona Norte TRUE
Zona Norte ACOPI 4057 6 750 445 NA 7 6 Casa -76.52950 3.38527 Zona Norte FALSE

2.1.2 Mapa de la zona

Iniciaremos con la comparación de ambos acercamientos de acercamientos de zonas de Cali-Norte

td <-reshape2::dcast(datos_filtrados, zona_revisada ~ en_poligono_de_zona, value.var = "preciom", fun.aggregate = length)

knitr::kable(td,
             format = "markdown", 
             caption = glue("**Tabla Resumen de comparación de acercamientos de zonas de Cali-Norte**"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla Resumen de comparación de acercamientos de zonas de Cali-Norte
zona_revisada FALSE TRUE
Fuera de zonas 23 7
Zona Norte 163 526
Zona Sur 2 1

Se realizan las siguientes conclusiones a partir de la tabla de comparación de la zona Norte:

  • Problemas en nombres de barrios con relación a la zonas que no corresponden a la realidad de la ciudad.

  • Problemas en coordenadas en latitud y longitud que no corresponden a la realidad de la ciudad

  • Se observan 526 registros que cumplen con el criterio de tratamiento tanto por nombre del barrio, como por longitud y latitud.

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$zona_revisada)

mapa_por_barrio<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(zona_revisada),
                   popup = ~paste("ZonaRevisada:", zona_revisada,'Barrio:',barrio))  %>%
  addLegend("bottomright", pal = colores, values = ~zona_revisada,
            title = "zona_revisada",
            opacity = 1)

mapa_por_barrio

En el anterior mapa, vemos que las viviendas que estas dentro de la categoria de ‘Zona Norte’ se encuentran muy dispersos a lo largo de la ciudad, y apoyados desde la revisión de Barrios, encontramos que son varios que continuan correspondiendoa zonas que no son las esperadas.

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$en_poligono_de_zona)
mapa_por_poligono<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(en_poligono_de_zona),
                   popup = ~paste('Barrio:',barrio, 'Latitud:',latitud, 'Longitud:',longitud ))  %>%
  addLegend("bottomright", pal = colores, values = ~en_poligono_de_zona,
            title = "en_poligono_de_zona",
            opacity = 1)

mapa_por_poligono

Con base en las coordenadas latitud y longitud, observamos que se logro tener un buen acercamiento a lo reflejado en la ralidad de las comunas a la ‘zona Norte’. no obstante, debemos tener encuenta el barrios especificado en el registro apra garantizar la calidad del dato.

datos_filtrados$la_zona <- ifelse(datos_filtrados$zona_revisada == 'Zona Norte' & datos_filtrados$en_poligono_de_zona == TRUE,
                                        'La Zona Norte', 'Otra Zona')

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$la_zona)
mapa_revisado<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(la_zona),
                   popup = ~paste('Barrio:',barrio))  %>%
  addLegend("bottomright", pal = colores, values = ~la_zona,
            title = "la_zona",
            opacity = 1)

mapa_revisado

Teniendo encuenta los tratamientos anteriormente planteados para la calidad del dato (por coordenada y por nombre del barrio). se observa que el mapa corresponde a cada punto en nombre de barrio y latitud y longitud de la ‘Zona norte’

df_norte <- subset(datos_filtrados, la_zona == 'La Zona Norte')
df_norte <- select(df_norte, -zona,-zona_revisada,-en_poligono_de_zona,-la_zona,-tipo)

2.2 Vivienda_2

Iniciamos análisis de la consulta 2 de vivienda

2.2.1 Filtrando datos y tratamientos geograficos

unique_values <- unique(data$zona)
la_zona = vivienda_2$zona
datos_filtrados <- subset(data, tipo == vivienda_2$Tipo & zona == la_zona)

#tratamiento por nombre de barrio-zona
datos_filtrados = merge(datos_filtrados, datos_barrio_zona_depurados, by = c("zona", "barrio"), all.x = TRUE)

#tratamiento por latitud y longitud
lista_cuadrados <- crear_lista_cuadrados(coordenadas_sur)
puntos_validados <- verificar_puntos_en_cuadrados(datos_filtrados, lista_cuadrados)

datos_filtrados$en_poligono_de_zona <- puntos_validados
datos_filtrados$zona_revisada[is.na(datos_filtrados$zona_revisada)] <- "Fuera de zonas"

#tratamiento en nombre de los barrios
datos_filtrados$barrio  <- sapply(datos_filtrados$barrio, reemplazar_caracteres)

knitr::kable(head(datos_filtrados, 3),
             format = "markdown", 
             caption = glue("**Tabla Resumen de dataset de la zona {la_zona} **"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla Resumen de dataset de la zona Zona Sur
zona barrio id estrato preciom areaconst parqueaderos banios habitaciones tipo longitud latitud zona_revisada en_poligono_de_zona
Zona Sur ACOPI 5098 4 290 96 1 2 3 Apartamento -76.53464 3.44987 Zona Norte FALSE
Zona Sur AGUABLANCA 698 3 78 40 1 1 2 Apartamento -76.50100 3.40000 Fuera de zonas FALSE
Zona Sur AGUACATAL 8199 6 875 194 2 5 3 Apartamento -76.55700 3.45900 Fuera de zonas FALSE

2.2.2 Mapa de la zona

Iniciaremos con la comparación de ambos acercamientos de acercamientos de zonas de Cali-Sur

td <-reshape2::dcast(datos_filtrados, zona_revisada ~ en_poligono_de_zona, value.var = "preciom", fun.aggregate = length)

knitr::kable(td,
             format = "markdown", 
             caption = glue("**Tabla Resumen de comparación de acercamientos de zonas de Cali-Sur**"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla Resumen de comparación de acercamientos de zonas de Cali-Sur
zona_revisada FALSE TRUE
Fuera de zonas 407 195
Zona Norte 14 0
Zona Sur 341 1830

Se realizan las siguientes conclusiones a partir de la tabla de comparación de la zona Sur:

  • Problemas en nombres de barrios con relación a la zonas que no corresponden a la realidad de la ciudad.

  • Problemas en coordenadas en latitud y longitud que no corresponden a la realidad de la ciudad

  • Se observan 1830 registros que cumplen con el criterio de tratamiento tanto por nombre del barrio, como por longitud y latitud.

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$zona_revisada)

mapa_por_barrio<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(zona_revisada),
                   popup = ~paste("ZonaRevisada:", zona_revisada,'Barrio:',barrio))  %>%
  addLegend("bottomright", pal = colores, values = ~zona_revisada,
            title = "zona_revisada",
            opacity = 1)

mapa_por_barrio

En el anterior mapa, vemos que las viviendas que estas dentro de la categoria de ‘Zona Sur’ se encuentran muy dispersos a lo largo de la ciudad, y apoyados desde la revisión de Barrios, encontramos que son varios que continuan correspondiendoa zonas que no son las esperadas.

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$en_poligono_de_zona)
mapa_por_poligono<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(en_poligono_de_zona),
                   popup = ~paste('Barrio:',barrio, 'Latitud:',latitud, 'Longitud:',longitud ))  %>%
  addLegend("bottomright", pal = colores, values = ~en_poligono_de_zona,
            title = "en_poligono_de_zona",
            opacity = 1)

mapa_por_poligono

Con base en las coordenadas latitud y longitud, observamos que se logro tener un buen acercamiento a lo reflejado en la ralidad de las comunas a la ‘zona Sur’. no obstante, debemos tener en cuenta el barrios especificado en el registro apra garantizar la calidad del dato.

datos_filtrados$la_zona <- ifelse(datos_filtrados$zona_revisada == 'Zona Sur' & datos_filtrados$en_poligono_de_zona == TRUE,
                                        'La Zona Sur', 'Otra Zona')

colores <- colorFactor(palette = "Set1", domain = datos_filtrados$la_zona)
mapa_revisado<-leaflet(datos_filtrados) %>%
  addTiles() %>%
  addCircleMarkers(lng = ~longitud, 
                   lat = ~latitud, 
                   radius = 3,
                   color = ~colores(la_zona),
                   popup = ~paste('Barrio:',barrio))  %>%
  addLegend("bottomright", pal = colores, values = ~la_zona,
            title = "la_zona",
            opacity = 1)

mapa_revisado

Teniendo encuenta los tratamientos anteriormente planteados para la calidad del dato (por coordenada y por nombre del barrio). se observa que el mapa corresponde a cada punto en nombre de barrio y latitud y longitud de la ‘Zona sur’

df_sur <- subset(datos_filtrados, la_zona == 'La Zona Sur')
df_sur <- select(df_sur, -zona,-zona_revisada,-en_poligono_de_zona,-la_zona,-tipo)

3. Correlación entre las variables

3.1 Vivienda_1

Antes de realizar la correlación entre variables, debemos normalizar llos datos para obtener una mejor correlación, para ello procedemos a osbervar al dispersión de dos campos que tienen la escala muy diferente al resto de los datos

plot(df_norte$preciom, df_norte$areaconst, 
     xlab = "preciom", ylab = "areaconst",
     main = "Gráfico de Dispersión de preciom vs areaconst")

La dispersión de los datos, nos muestra que uan normalización con log podria ser la mas adecuada.

Frente al campo parqueadero, requerimos tratar los campos vacios, y al tener filtrados los datos, es un buen momento de tratarlos con la moda

Mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

moda <- Mode(na.omit(df_norte$parqueaderos))

Realizamos la normalización de los datos

df_norte$parqueaderos[is.na(df_norte$parqueaderos)] <- moda
df_norte$preciomlog=log(df_norte$preciom)
df_norte$areaconstlog=log(df_norte$areaconst)

Realizamos la construcción de la matriz de correlación

datos_normalizados <-  select(df_norte,-barrio, -id,-longitud,-latitud, - areaconst, - preciom) %>%
  scale()

r<- cor(datos_normalizados) #datos_normalizados
p<-ggcorrplot(r,type="lower",
              title="Correlaciones",
              colors=c("red","yellow","blue"),
              outline.color="black", ggtheme = theme_test() + theme(text = element_text(size = 7)))
plotly::ggplotly(p)

Se observa una alta correlacion entre el precio y el área de contruicción en su transformación de logaritmo (0.84). le siguen la relación de los campos anteriormente mencionadoscon el campo estrato (0.7 y 0.6 respectivamente).

3.2 Vivienda_2

Repetimos la comparación entre los campos con mayor cambio de escala

plot(df_sur$preciom, df_sur$areaconst, 
     xlab = "preciom", ylab = "areaconst",
     main = "Gráfico de Dispersión de preciom vs areaconst")

La dispersión de los datos, nos muestra que uan normalización con log podria ser la mas adecuada.

Frente al campo parqueadero, requerimos tratar los campos vacios, y al tener filtrados los datos, es un buen momento de tratarlos con la moda

Mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

moda <- Mode(na.omit(df_sur$parqueaderos))

Realizamos la normalización de los datos

df_sur$parqueaderos[is.na(df_sur$parqueaderos)] <- moda
df_sur$preciomlog=log(df_sur$preciom)
df_sur$areaconstlog=log(df_sur$areaconst)

Realizamos la construcción de la matriz de correlación

datos_normalizados <-  select(df_sur,-barrio, -id,-longitud,-latitud, - areaconst, - preciom) %>%
  scale()

r<- cor(datos_normalizados) #datos_normalizados
p<-ggcorrplot(r,type="lower",
              title="Correlaciones",
              colors=c("red","yellow","blue"),
              outline.color="black", ggtheme = theme_test() + theme(text = element_text(size = 7)))
plotly::ggplotly(p)

Se observa una alta correlacion entre el precio y el área de contruicción en su transformación de logaritmo (0.88). le siguen la relación de los campos anteriormente mencionadoscon el campo estrato (0.76 y 0.65 respectivamente). tambien, vemos que tenemos una relación itneresante entre el precio, área de contrucción con el número de baños (0.76) y número de parqueaderos (0.71 y 0.69 respectivamente).

4. Estimación de un modelo de regresión lineal multiple

4.1 Vivienda_1

Primer modelo empleando todos los campos

modelo_norte <- lm(preciomlog ~  banios + parqueaderos + areaconstlog + estrato, data = df_norte)

# Mostrar un resumen del modelo
summary(modelo_norte)
## 
## Call:
## lm(formula = preciomlog ~ banios + parqueaderos + areaconstlog + 
##     estrato, data = df_norte)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.76287 -0.16354 -0.01841  0.14962  1.06938 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  2.150838   0.106096  20.273  < 2e-16 ***
## banios       0.053171   0.009733   5.463 7.26e-08 ***
## parqueaderos 0.029325   0.010111   2.900  0.00389 ** 
## areaconstlog 0.519273   0.025779  20.143  < 2e-16 ***
## estrato      0.169054   0.014945  11.312  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2597 on 521 degrees of freedom
## Multiple R-squared:  0.7872, Adjusted R-squared:  0.7855 
## F-statistic: 481.7 on 4 and 521 DF,  p-value: < 2.2e-16

Vemos que el modelo inicial reporta un buen \(R^2\) de 0.7855, en donde todos los campos y el intercepto tener un buen grado de significaciía con excepción de parqueaderos.

Se desea mejorar elmodelo, para ello, empleamos dummy en campos caegoricos, es por ello que el estrato debemos pasarlo a olumnas por todos los valores presentes en la base de datos

datos_con_ficticias_norte <- dummy_cols(df_norte, select_columns = c("estrato"))

modelo_nortev2 <- lm(preciomlog ~  banios + parqueaderos + areaconstlog + estrato_3+ estrato_4+ estrato_5+ estrato_6, data = datos_con_ficticias_norte)

summary(modelo_nortev2)
## 
## Call:
## lm(formula = preciomlog ~ banios + parqueaderos + areaconstlog + 
##     estrato_3 + estrato_4 + estrato_5 + estrato_6, data = datos_con_ficticias_norte)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.71911 -0.16442 -0.01321  0.14242  1.01722 
## 
## Coefficients: (1 not defined because of singularities)
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   3.204035   0.146692  21.842  < 2e-16 ***
## banios        0.051220   0.009746   5.256 2.16e-07 ***
## parqueaderos  0.030534   0.010059   3.036  0.00252 ** 
## areaconstlog  0.511552   0.025867  19.776  < 2e-16 ***
## estrato_3    -0.527045   0.059078  -8.921  < 2e-16 ***
## estrato_4    -0.269958   0.056072  -4.814 1.94e-06 ***
## estrato_5    -0.173277   0.052738  -3.286  0.00109 ** 
## estrato_6           NA         NA      NA       NA    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.2581 on 519 degrees of freedom
## Multiple R-squared:  0.7905, Adjusted R-squared:  0.7881 
## F-statistic: 326.5 on 6 and 519 DF,  p-value: < 2.2e-16

Vemos que el modelo paso a \(R^2\) del 0.7881, permitiendo tener un buen nivel de significacia en todas las variabls, cone xcepción del estrato 6 casi no rpesenta datos)y el campo parqueaderos. NOTA Recordemos que esta solicitud tiene filtrado el tipo de vivienda ‘Casa’

4.2 Vivienda_2

Primer modelo empleando todos los campos

modelo_sur <- lm(preciomlog ~  banios + parqueaderos + areaconstlog + estrato, data = df_sur)

# Mostrar un resumen del modelo
summary(modelo_sur)
## 
## Call:
## lm(formula = preciomlog ~ banios + parqueaderos + areaconstlog + 
##     estrato, data = df_sur)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.70240 -0.11287  0.00702  0.12686  0.72325 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.481810   0.071793  20.640   <2e-16 ***
## banios       0.066757   0.007583   8.803   <2e-16 ***
## parqueaderos 0.106548   0.009448  11.277   <2e-16 ***
## areaconstlog 0.661533   0.020478  32.305   <2e-16 ***
## estrato      0.175810   0.007722  22.768   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1908 on 1825 degrees of freedom
## Multiple R-squared:  0.8569, Adjusted R-squared:  0.8566 
## F-statistic:  2733 on 4 and 1825 DF,  p-value: < 2.2e-16

Vemos que el modelo inicial reporta un buen \(R^2\) de 0.8566, en donde todos los campos y el intercepto tener un buen grado de significaciía con excepción de parqueaderos.

datos_con_ficticias_sur <- dummy_cols(df_sur, select_columns = c("estrato"))

modelo_surv2 <- lm(preciomlog ~  banios + parqueaderos + areaconstlog + estrato_3+ estrato_4+ estrato_5+ estrato_6, data = datos_con_ficticias_sur)

summary(modelo_surv2)
## 
## Call:
## lm(formula = preciomlog ~ banios + parqueaderos + areaconstlog + 
##     estrato_3 + estrato_4 + estrato_5 + estrato_6, data = datos_con_ficticias_sur)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.69724 -0.11550  0.00884  0.12784  0.66446 
## 
## Coefficients: (1 not defined because of singularities)
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   2.570403   0.083019  30.962  < 2e-16 ***
## banios        0.059167   0.007626   7.759 1.42e-14 ***
## parqueaderos  0.096114   0.009499  10.118  < 2e-16 ***
## areaconstlog  0.670746   0.020450  32.800  < 2e-16 ***
## estrato_3    -0.486148   0.040178 -12.100  < 2e-16 ***
## estrato_4    -0.386312   0.016465 -23.463  < 2e-16 ***
## estrato_5    -0.248100   0.014013 -17.705  < 2e-16 ***
## estrato_6           NA         NA      NA       NA    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1888 on 1823 degrees of freedom
## Multiple R-squared:   0.86,  Adjusted R-squared:  0.8595 
## F-statistic:  1866 on 6 and 1823 DF,  p-value: < 2.2e-16

Se desea mejorar el modelo, para ello, empleamos dummy en campos caegoricos, es por ello que el estrato debemos pasarlo a olumnas por todos los valores presentes en la base de datos

5. Validación del modelo planteado

5.1 Vivienda_1

par(mfrow = c(2, 2))
plot(modelo_nortev2)

Se observa que el modelo tiene una buena calidad de ajuste al modelo y su distribución de datos refleja ser un buen modelo.

  • Se observa que los residuos vs ajustados los puntos estén distribuidos aleatoriamente alrededor de la línea horizontal en 0.

  • En el gráfico de cuantiles normales (Normal Q-Q). los residuos siguen una distribución normal, los puntos caen aproximadamente en una línea diagonal.

  • En el gráfico ‘scalet location’. Los puntos están distribuidos aleatoriamente y la línea de regresión es horizontal, indica que la varianza de los residuos es constante.

  • En el gráfico ‘residuales vs influencia’ se observan pocos datos con alto grado de influencia = ‘leverage’

5.2 Vivienda_2

par(mfrow = c(2, 2))
plot(modelo_surv2)

Se observa que el modelo tiene una buena calidad de ajuste al modelo y su distribución de datos refleja ser un buen modelo.

  • Se observa que los residuos vs ajustados los puntos estén distribuidos aleatoriamente alrededor de la línea horizontal en 0.

  • En el gráfico de cuantiles normales (Normal Q-Q). los residuos siguen una distribución normal, los puntos caen aproximadamente en una línea diagonal.

  • En el gráfico ‘scalet location’. Los puntos están distribuidos aleatoriamente y la línea de regresión es horizontal, indica que la varianza de los residuos es constante.

  • En el gráfico ‘residuales vs influencia’ se observan pocos datos con alto grado de influencia = ‘leverage’

6. Predicción de las viviendas con base en lo buscado

6.1 Vivienda_1

valor_pred_1 = predict(modelo_nortev2, data.frame(areaconstlog=log(vivienda_1$area_construida), estrato_3=0, estrato_4=1, estrato_5=0, estrato_6=0, parqueaderos=vivienda_1$parqueaderos, banios=vivienda_1$banos, habitaciones=vivienda_1$habitaciones),interval = "confidence")

knitr::kable(valor_pred_1,
             format = "markdown", 
             caption = glue("**Tabla de predicciones de la consulta 1 de vivienda, en estrato 4 **"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla de predicciones de la consulta 1 de vivienda, en estrato 4
fit lwr upr
5.777418 5.719815 5.835021
valor_pred_2 = predict(modelo_nortev2, data.frame(areaconstlog=log(vivienda_1$area_construida), estrato_3=0, estrato_4=0, estrato_5=1, estrato_6=0, parqueaderos=vivienda_1$parqueaderos, banios=vivienda_1$banos, habitaciones=vivienda_1$habitaciones),interval = "confidence")

knitr::kable(valor_pred_2,
             format = "markdown", 
             caption = glue("**Tabla de predicciones de la consulta 1 de vivienda, en estrato 5**"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla de predicciones de la consulta 1 de vivienda, en estrato 5
fit lwr upr
5.874099 5.82333 5.924868

Se observan en cada predicción 3 valores que simbolizan el mejor valor, el menor valor y el ajustado. En este caso, el precio es logaritmo, por lo que los valores ajustado darian: 322 millones y 346 millones en los estratos 4 y 5. valores que estan por debajo del credito maximo aprobado para el cliente.

6.2 Vivienda_2

valor_pred_1 = predict(modelo_surv2, data.frame(areaconstlog=log(vivienda_2$area_construida), estrato_3=0, estrato_4=0, estrato_5=1, estrato_6=0, parqueaderos=vivienda_2$parqueaderos, banios=vivienda_2$banos, habitaciones=vivienda_2$habitaciones),interval = "confidence")

knitr::kable(valor_pred_1,
             format = "markdown", 
             caption = glue("**Tabla de predicciones de la consulta 2 de vivienda, en estrato 5 **"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla de predicciones de la consulta 2 de vivienda, en estrato 5
fit lwr upr
6.613937 6.57082 6.657055
valor_pred_2 = predict(modelo_surv2, data.frame(areaconstlog=log(vivienda_2$area_construida), estrato_3=0, estrato_4=0, estrato_5=0, estrato_6=1, parqueaderos=vivienda_2$parqueaderos, banios=vivienda_2$banos, habitaciones=vivienda_2$habitaciones),interval = "confidence")


knitr::kable(valor_pred_2,
             format = "markdown", 
             caption = glue("**Tabla de predicciones de la consulta 1 de vivienda, en estrato 6**"),
             align = "c", escape = FALSE,
             row.names = FALSE,
             booktabs = TRUE)
Tabla de predicciones de la consulta 1 de vivienda, en estrato 6
fit lwr upr
6.862037 6.822633 6.901442

Se observan en cada predicción 3 valores que simbolizan el mejor valor, el menor valor y el ajustado. para este caso.En este caso, el precio es logaritmo, por lo que los valores ajustado darian: 745 millones y 955 millones en los estratos 5 y 6. valores que estan por para el primer caso accesible para el cliente , y para el estrato 6 casu 100 millones por debajo del maximo credito aprobado.

7. Realizar propuestas que se ajusten al presupuesto del pre aprobado

Para realizar una aproximacion a la vivienda ideal, vamos a realizar aproximación por distancia eucidiana y una suposición de pesos de variables

# Calcula la distancia ponderada euclidiana para cada fila
calcular_distancia_ponderada <- function(datos, objetivo, pesos) {
  # Calcular la distancia ponderada euclidiana para cada fila
  distancias_ponderadas <- apply(datos, 1, function(x) {
    distancia <- sqrt(sum(as.numeric(pesos) * (x - objetivo)^2))
    return(distancia)
  })
  return(distancias_ponderadas)
}

# Define los pesos para cada columna del modelo
pesos <- list(preciomlog=0,areaconstlog=0.6, estrato_3=0, estrato_4=0, estrato_5=0, estrato_6=0, parqueaderos=0.2, banios=0.1, habitaciones=0.3)

nota: observa que el precio es ponderado cero, porque debo filtrar por debajo de este precio, pero quiero resaltar las otras caracteristicas

7.1 Vivienda_1

df_norte_modelo = select(datos_con_ficticias_norte,preciomlog, areaconstlog, estrato_3, estrato_4, estrato_5, estrato_6, parqueaderos, banios, habitaciones)

objetivo <- c(preciomlog = log(vivienda_1$credito_preaprobado_mill),areaconstlog=log(vivienda_1$area_construida), estrato_3=0, estrato_4=1, estrato_5=0, estrato_6=0, parqueaderos=vivienda_1$parqueaderos, banios=vivienda_1$banos, habitaciones=vivienda_1$habitaciones)
#Revision estrato 4
distancias_ponderadas <- calcular_distancia_ponderada(subset(df_norte_modelo, estrato_4 == 1 &
                                                               preciomlog <= log(vivienda_1$credito_preaprobado_mill) &
                                                               areaconstlog >= log(vivienda_1$area_construida*0.9) &
                                                               parqueaderos>= vivienda_1$parqueaderos&
                                                               banios>= vivienda_1$banos &
                                                               habitaciones>=4), objetivo, pesos)


sugerencias <- subset(df_norte, estrato == 4 &
                        preciom <= vivienda_1$credito_preaprobado_mill &
                        areaconst >= vivienda_1$area_construida*0.9 &
                        parqueaderos>= vivienda_1$parqueaderos&
                        banios>= vivienda_1$banos &
                        habitaciones>=4)
# Agrega la distancia ponderada euclidiana como una nueva columna al DataFrame
sugerencias$distancia_ponderada <- distancias_ponderadas

# Ordena el DataFrame por la distancia ponderada
resp_norte <- head(sugerencias[order(distancias_ponderadas, decreasing = TRUE), ],2)
resp_norte <- rbind(resp_norte, head(sugerencias[order(distancias_ponderadas, decreasing = FALSE), ],2))
#Revision estrato 5
objetivo <- c(preciomlog = log(vivienda_1$credito_preaprobado_mill),areaconstlog=log(vivienda_1$area_construida), estrato_3=0, estrato_4=0, estrato_5=1, estrato_6=0, parqueaderos=vivienda_1$parqueaderos, banios=vivienda_1$banos, habitaciones=vivienda_1$habitaciones)
distancias_ponderadas <- calcular_distancia_ponderada(subset(df_norte_modelo, estrato_5 == 1 &
                                                               preciomlog <= log(vivienda_1$credito_preaprobado_mill) &
                                                               areaconstlog >= log(vivienda_1$area_construida*0.9) &
                                                               parqueaderos>= vivienda_1$parqueaderos&
                                                               banios>= vivienda_1$banos &
                                                               habitaciones>=as.numeric(vivienda_1$habitaciones)), objetivo, pesos)

sugerencias <- subset(df_norte, estrato == 5 &
                        preciom <= vivienda_1$credito_preaprobado_mill &
                        areaconst >= vivienda_1$area_construida*0.9 &
                        parqueaderos>= vivienda_1$parqueaderos&
                        banios>= vivienda_1$banos &
                        habitaciones>=as.numeric(vivienda_1$habitaciones))
# Agrega la distancia ponderada euclidiana como una nueva columna al DataFrame
sugerencias$distancia_ponderada <- distancias_ponderadas


# Ordena el DataFrame por la distancia ponderada
resp_norte <- rbind(resp_norte,head(sugerencias[order(distancias_ponderadas, decreasing = TRUE), ],2))
resp_norte <- rbind(resp_norte, head(sugerencias[order(distancias_ponderadas, decreasing = FALSE), ],2))
mapa_propuestas<-leaflet(resp_norte) %>%
 addTiles() %>%
 addCircleMarkers(lng=~longitud,lat=~latitud,radius=7,
                  popup = ~paste('Barrio:',barrio,'Area:',areaconst,'|Parqueaderos:',parqueaderos,'|baños:',banios, '|habitaciones:',habitaciones,'|estrato:',estrato,'|precio:',preciom ))
 

mapa_propuestas

En el mapa se plantean 8 lugares que representan 4 propuesta para cada estrato con perspectivas muy cercanas a la buscadas y una que se alejan pero maximizan el peso del peso por variable sugerido, es decir, pueden favorecer a ciertas caracteristicas. en este caso, el cliente siempre encontrará propuestas que se ajustan idelamente a su credito sin mayor problema.

7.2 Vivienda_2

df_sur_modelo = select(datos_con_ficticias_sur,preciomlog, areaconstlog, estrato_3, estrato_4, estrato_5, estrato_6, parqueaderos, banios, habitaciones)

objetivo <- c(preciomlog = log(vivienda_2$credito_preaprobado_mill),areaconstlog=log(vivienda_2$area_construida), estrato_3=0, estrato_4=0, estrato_5=1, estrato_6=0, parqueaderos=vivienda_2$parqueaderos, banios=vivienda_2$banos, habitaciones=vivienda_2$habitaciones)
sugerencias <- subset(df_sur, estrato == 5 &
                        preciom <= vivienda_2$credito_preaprobado_mill &
                        areaconst >= vivienda_2$area_construida*0.9 &
                        parqueaderos>= vivienda_2$parqueaderos&
                        banios>= vivienda_2$banos &
                        habitaciones>=as.numeric(vivienda_2$habitaciones))
print(nrow(sugerencias))
## [1] 0
sugerencias <- subset(df_sur, estrato == 5 &
                        preciom <= vivienda_2$credito_preaprobado_mill*1.1 &
                        areaconst >= vivienda_2$area_construida*0.9 &
                        parqueaderos>= vivienda_2$parqueaderos*(2/3)&
                        banios>= vivienda_2$banos*(2/3) &
                        habitaciones>=as.numeric(vivienda_2$habitaciones)*(4/5))
print(nrow(sugerencias))
## [1] 4

Se observa que para el estrato 6 , las caracteristicas buscadas con el precio sugerido, no tiene ninguna vivienda. este comportamiento era previsible, dado que los precios estimados eran muy separados al credito buscado. se realizan algunos ajustes a la propuesta frente al área de contrucción, número de habitación, parqueadeos, baños y área de contrucción (habitualmente se coloca 90% por debajo de lo buscado para garantizar la busqueda)

#Revision estrato 5
distancias_ponderadas <- calcular_distancia_ponderada(subset(df_sur_modelo, estrato_5 == 1 &
                                                               preciomlog <= log(vivienda_2$credito_preaprobado_mill) &
                                                               areaconstlog >= log(vivienda_2$area_construida*0.9) &
                                                               parqueaderos>= vivienda_2$parqueaderos*(2/3)&
                                                               banios>= vivienda_2$banos*(2/3) &
                                                               habitaciones>=as.numeric(vivienda_2$habitaciones)*(4/5)), objetivo, pesos)



# Agrega la distancia ponderada euclidiana como una nueva columna al DataFrame
sugerencias$distancia_ponderada <- distancias_ponderadas

# Ordena el DataFrame por la distancia ponderada
resp_sur <- head(sugerencias[order(distancias_ponderadas, decreasing = TRUE), ],2)
resp_sur <- rbind(resp_sur, head(sugerencias[order(distancias_ponderadas, decreasing = FALSE), ],2))
#Revision estrato 6
objetivo <- c(preciomlog = log(vivienda_2$credito_preaprobado_mill),areaconstlog=log(vivienda_2$area_construida), estrato_3=0, estrato_4=0, estrato_5=0, estrato_6=1, parqueaderos=vivienda_2$parqueaderos, banios=vivienda_2$banos, habitaciones=vivienda_2$habitaciones)
distancias_ponderadas <- calcular_distancia_ponderada(subset(df_sur_modelo, estrato_6 == 1 &
                                                               preciomlog <= log(vivienda_2$credito_preaprobado_mill) &
                                                               areaconstlog >= log(vivienda_2$area_construida*(200/300)) &
                                                               parqueaderos>= vivienda_2$parqueaderos*(2/3)&
                                                               banios>= vivienda_2$banos*(2/3) &
                                                               habitaciones>=as.numeric(vivienda_2$habitaciones)*(4/5)), objetivo, pesos)

sugerencias <- subset(df_sur, estrato == 6 &
                        preciom <= vivienda_2$credito_preaprobado_mill &
                        areaconst >= vivienda_2$area_construida*(200/300) &
                        parqueaderos>= vivienda_2$parqueaderos*(2/3)&
                        banios>= vivienda_2$banos*(2/3) &
                        habitaciones>=as.numeric(vivienda_2$habitaciones*(4/5)))
print(nrow(sugerencias))
## [1] 6
# Agrega la distancia ponderada euclidiana como una nueva columna al DataFrame
sugerencias$distancia_ponderada <- distancias_ponderadas


# Ordena el DataFrame por la distancia ponderada
resp_sur <- rbind(resp_sur,head(sugerencias[order(distancias_ponderadas, decreasing = TRUE), ],2))
resp_sur <- rbind(resp_sur, head(sugerencias[order(distancias_ponderadas, decreasing = FALSE), ],2))
mapa_propuestas_sur<-leaflet(resp_sur) %>%
 addTiles() %>%
 addCircleMarkers(lng=~longitud,lat=~latitud,radius=7,
                  popup = ~paste('Barrio:',barrio,'Area:',areaconst,'|Parqueaderos:',parqueaderos,'|baños:',banios, '|habitaciones:',habitaciones,'|estrato:',estrato,'|precio:',preciom ))
 

mapa_propuestas_sur

En el mapa se plantean 8 lugares que representan 4 propuesta para cada estrato con perspectivas muy cercanas a la buscadas y una que se alejan pero maximizan el peso del peso por variable sugerido, es decir, pueden favorecer a ciertas caracteristicas. en este caso, el cliente no encuentra ninguna propuesta que cumpla con la totalidad de los criterios sugeridos, pero encontrará propuestas que se acercan a lo buscado y que cumplan con el maximo credito que tiene disponible.