Caso C&A

Maria comenzó como agente de bienes raíces en Cali hace 10 años. Después de laborar dos años para una empresa nacional, se traslado a Bogotá y trabajó para otra agencia de bienes raíces. Sus amigos y familiares la convencieron de que con su experiencia y conocimientos del negocio debía abrir su propia agencia. Terminó por adquirir la licencia de intermediario y al poco tiempo fundó su propia compañía, C&A (Casas y Apartamentos) en Cali. Santiago y Lina, dos vendedores de la empresa anterior aceptaron trabajar en la nueva compaña. En la actualidad ocho agentes de bienes raíces colaboran con ella en C&A.

Actualmente las ventas de bienes raíces en Cali se han visto disminuidas de manera significativa en lo corrido del año. Durante este periodo muchas instituciones bancarias de ahorro y vivienda están prestando grandes sumas de dinero para la industria y la construcción comercial y residencial. Cuando el efecto producto de las tensiones políticas y sociales disminuya, se espera que la actividad económica de este sector se reactive.

Hace dos días, María recibió una carta solicitando asesoría para la compra de dos viviendas por parte de una compañía internacional que desea ubicar a dos de sus empleados con sus familias en la ciudad. Las solicitudes incluyen las siguientes condiciones:

Características Vivienda 1 - Vivienda 2

Tipo Casa - Apartamento

área construida 200 - 300

parqueaderos 1 - 3

baños 2 - 3

habitaciones 4 - 5

estrato 4 o 5 - 5 o 6

zona Norte - Sur

crédito preaprobado 350 millones - 850 millones

Preparacion de Datos

-Carga de los datos y Caracteristicas de los datos.

# Ver las primeras filas del dataset
head(vivienda)
# Ver resumen de cada columna
summary(vivienda)
##        id           zona               piso              estrato     
##  Min.   :   1   Length:8322        Length:8322        Min.   :3.000  
##  1st Qu.:2080   Class :character   Class :character   1st Qu.:4.000  
##  Median :4160   Mode  :character   Mode  :character   Median :5.000  
##  Mean   :4160                                         Mean   :4.634  
##  3rd Qu.:6240                                         3rd Qu.:5.000  
##  Max.   :8319                                         Max.   :6.000  
##  NA's   :3                                            NA's   :3      
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  58.0   Min.   :  30.0   Min.   : 1.000   Min.   : 0.000  
##  1st Qu.: 220.0   1st Qu.:  80.0   1st Qu.: 1.000   1st Qu.: 2.000  
##  Median : 330.0   Median : 123.0   Median : 2.000   Median : 3.000  
##  Mean   : 433.9   Mean   : 174.9   Mean   : 1.835   Mean   : 3.111  
##  3rd Qu.: 540.0   3rd Qu.: 229.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1999.0   Max.   :1745.0   Max.   :10.000   Max.   :10.000  
##  NA's   :2        NA's   :3        NA's   :1605     NA's   :3       
##   habitaciones        tipo              barrio             longitud     
##  Min.   : 0.000   Length:8322        Length:8322        Min.   :-76.59  
##  1st Qu.: 3.000   Class :character   Class :character   1st Qu.:-76.54  
##  Median : 3.000   Mode  :character   Mode  :character   Median :-76.53  
##  Mean   : 3.605                                         Mean   :-76.53  
##  3rd Qu.: 4.000                                         3rd Qu.:-76.52  
##  Max.   :10.000                                         Max.   :-76.46  
##  NA's   :3                                              NA's   :3       
##     latitud     
##  Min.   :3.333  
##  1st Qu.:3.381  
##  Median :3.416  
##  Mean   :3.418  
##  3rd Qu.:3.452  
##  Max.   :3.498  
##  NA's   :3
# Ver estructura del dataset
str(vivienda)
## spc_tbl_ [8,322 × 13] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ id          : num [1:8322] 1147 1169 1350 5992 1212 ...
##  $ zona        : chr [1:8322] "Zona Oriente" "Zona Oriente" "Zona Oriente" "Zona Sur" ...
##  $ piso        : chr [1:8322] NA NA NA "02" ...
##  $ estrato     : num [1:8322] 3 3 3 4 5 5 4 5 5 5 ...
##  $ preciom     : num [1:8322] 250 320 350 400 260 240 220 310 320 780 ...
##  $ areaconst   : num [1:8322] 70 120 220 280 90 87 52 137 150 380 ...
##  $ parqueaderos: num [1:8322] 1 1 2 3 1 1 2 2 2 2 ...
##  $ banios      : num [1:8322] 3 2 2 5 2 3 2 3 4 3 ...
##  $ habitaciones: num [1:8322] 6 3 4 3 3 3 3 4 6 3 ...
##  $ tipo        : chr [1:8322] "Casa" "Casa" "Casa" "Casa" ...
##  $ barrio      : chr [1:8322] "20 de julio" "20 de julio" "20 de julio" "3 de julio" ...
##  $ longitud    : num [1:8322] -76.5 -76.5 -76.5 -76.5 -76.5 ...
##  $ latitud     : num [1:8322] 3.43 3.43 3.44 3.44 3.46 ...
##  - attr(*, "spec")=List of 3
##   ..$ cols   :List of 13
##   .. ..$ id          : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ zona        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ piso        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ estrato     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ preciom     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ areaconst   : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ parqueaderos: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ banios      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ habitaciones: list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ tipo        : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ barrio      : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_character" "collector"
##   .. ..$ longitud    : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   .. ..$ latitud     : list()
##   .. .. ..- attr(*, "class")= chr [1:2] "collector_double" "collector"
##   ..$ default: list()
##   .. ..- attr(*, "class")= chr [1:2] "collector_guess" "collector"
##   ..$ delim  : chr ";"
##   ..- attr(*, "class")= chr "col_spec"
##  - attr(*, "problems")=<externalptr>

-Tratamiento de NA.

# Ver la cantidad de valores NA en cada columna
colSums(is.na(vivienda))
##           id         zona         piso      estrato      preciom    areaconst 
##            3            3         2638            3            2            3 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1605            3            3            3            3            3 
##      latitud 
##            3
missing_rows_column <- which(is.na(vivienda$zona))
vivienda[missing_rows_column, ]
  • Se decide eliminar los NA que se repiten en todas las columnas.
viviendaR = vivienda ####se hace una copia de la base de datos
# 1. Eliminar filas con valores faltantes
viviendaR <- subset(viviendaR, !is.na(zona))
missing_rows_column <- which(is.na(viviendaR$zona))
viviendaR[missing_rows_column, ]
  • Resultado de la eliminacion de datos NA.
# Ver la cantidad de valores NA en cada columna
colSums(is.na(viviendaR))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0         2635            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##         1602            0            0            0            0            0 
##      latitud 
##            0
  • Validacion Valores unicos de Piso y Parqueaderos.
unique(viviendaR$piso)
##  [1] NA   "02" "01" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12"
unique(viviendaR$parqueaderos)
##  [1]  1  2  3 NA  4  7  5  8  6  9 10
  • Remplazo NA por la moda de cada atributo, se elije esta opcion porque no tenemos manera de obtener la verdadeara informacion.
# Función para calcular la moda sin considerar NA
get_mode <- function(x) {
  x <- na.omit(x)  # Elimina los NA
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

# Calcular la moda para el campo 'piso'
moda_piso <- get_mode(viviendaR$piso)

# Calcular la moda para el campo 'baños'
moda_parqueaderos <- get_mode(viviendaR$parqueaderos)

# Reemplazar los valores NA con la moda en 'piso'
viviendaR$piso <- ifelse(is.na(viviendaR$piso), moda_piso, viviendaR$piso)

# Reemplazar los valores NA con la moda en 'parqueaderos'
viviendaR$parqueaderos <- ifelse(is.na(viviendaR$parqueaderos), moda_parqueaderos, viviendaR$parqueaderos)


# Ver la cantidad de valores NA en cada columna
colSums(is.na(viviendaR))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0            0            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##            0            0            0            0            0            0 
##      latitud 
##            0

-Analisis de valores unicos para las demas columnas.

#analizamos los valores unicos para cada columna, validar ajustes extras antes de analisis completo
cat ("Los valores únicos en zona son:", unique(viviendaR$zona), "\n")
## Los valores únicos en zona son: Zona Oriente Zona Sur Zona Norte Zona Oeste Zona Centro
cat ("Los valores únicos en zona son:", unique(viviendaR$piso), "\n")
## Los valores únicos en zona son: 02 01 03 04 05 06 07 08 09 10 11 12
cat ("Los valores únicos en zona son:", unique(viviendaR$estrato), "\n")
## Los valores únicos en zona son: 3 4 5 6
cat ("Los valores únicos en zona son:", unique(viviendaR$parqueaderos), "\n")
## Los valores únicos en zona son: 1 2 3 4 7 5 8 6 9 10
cat ("Los valores únicos en zona son:", unique(viviendaR$banios), "\n")
## Los valores únicos en zona son: 3 2 5 4 7 6 1 0 8 10 9
cat ("Los valores únicos en zona son:", unique(viviendaR$habitaciones), "\n")
## Los valores únicos en zona son: 6 3 4 5 2 0 1 8 7 10 9
cat ("Los valores únicos en zona son:", unique(viviendaR$tipo), "\n")
## Los valores únicos en zona son: Casa Apartamento

-Convertimos las variables piso y estrato a numerico y categorico respectivamente.

# Convertir 'piso' a numérico
viviendaR$piso <- as.numeric(viviendaR$piso)

# Convierte el campo 'estrato' de numérico a categórico
viviendaR$estrato <- as.factor(viviendaR$estrato)

# Verifica la conversión
str(viviendaR$estrato)
##  Factor w/ 4 levels "3","4","5","6": 1 1 1 2 3 3 2 3 3 3 ...
# Verificar la conversión
summary(viviendaR$piso)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00    2.00    2.00    3.21    4.00   12.00
str(viviendaR$piso)
##  num [1:8319] 2 2 2 2 1 1 1 1 2 2 ...

-Ahora verificamos la columna longitud y latitud si todos los separadores son puntos o comas.

# Verifica si hay algún valor que contenga una coma
latitud_con_coma <- any(grepl(",", vivienda$latitud))
longitud_con_coma <- any(grepl(",", vivienda$longitud))

# Muestra los resultados
if (latitud_con_coma | longitud_con_coma) {
  cat("Existen valores en latitud y/o longitud con comas en lugar de puntos.\n")
} else {
  cat("Todos los valores en latitud y longitud están correctamente separados por puntos.\n")
}
## Todos los valores en latitud y longitud están correctamente separados por puntos.

Se realiza preparacion de la data, ahora estamos preparados para empezar a trabajar la base de datos ya arreglada.

Primer propuesta

-Se filtra la base de datos por la zona Norte y tipo casa.

# Filtra la base de datos para incluir solo casas en la zona norte
viviendaR1 <- subset(viviendaR, tipo == "Casa" & zona == "Zona Norte")

# Muestra los primeros 3 registros de la base filtrada
head(viviendaR1, 3)
# Tabla de frecuencia para 'tipo_vivienda'
table(viviendaR1$tipo)
## 
## Casa 
##  722
# Tabla de frecuencia para 'zona'
table(viviendaR1$zona)
## 
## Zona Norte 
##        722

Analisis de las variables contra precio de la vivienda

  • Se convierte la variable estrato a factor de visualizacion.
# Convertir la variable 'estrato' en factor para la visualización
viviendaR1$estrato <- as.factor(viviendaR1$estrato)
  • precio vs area construida.
fig1 <- plot_ly(viviendaR1, x = ~areaconst, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Área Construida:', areaconst)) %>%
  layout(title = "Precio vs Área Construida",
         xaxis = list(title = "Área Construida"),
         yaxis = list(title = "Precio"))
fig1

Se evidencia una concordancia en la teoria, porque a mayor area construida mas vale la vivienda, teniendo datos atipicos pero no necesariamente son erroneos, ya que son precios no establecidos por la misma persona.

  • Precio vs estrato
# Crear un gráfico interactivo para 'precio' vs 'estrato'
fig2 <- plot_ly(viviendaR1, x = ~estrato, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Estrato",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio"))
fig2

Aca se evidencia una distribución de mayor estrato mayor es el precio de la vivienda, y que se tienen datos acordes entre si.

  • Precio vs Numero de Baños
# Crear un gráfico interactivo para 'precio' vs 'número de baños'
fig3 <- plot_ly(viviendaR1, x = ~banios, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Baños:', banios)) %>%
  layout(title = "Precio vs Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio"))
fig3

En esta comparación si vemos que hay viviendas con muchos o pocos baños y el precio varia desde el mas economico hasta la vivienda mas costosa, no se evidencia una distribución que sea creciente lineal.

  • Precio vs Numero de Habitaciones
# Crear un gráfico interactivo para 'precio' vs 'número de habitaciones'
fig4 <- plot_ly(viviendaR1, x = ~habitaciones, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Habitaciones:', habitaciones)) %>%
  layout(title = "Precio vs Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio"))
fig4

Con el numero de habitaciones pasa algo parecido que con los baños, aunque se concentran la mayoria de los datos en una lectura clave y es que a menos numero de habitaciones la mayoria de viviendas son mas economicas que las que tienen mas habitaciones.

  • Precio vs zona
# Crear un gráfico interactivo para 'precio' vs 'zona' (si tienes la variable 'zona')
fig5 <- plot_ly(viviendaR1, x = ~zona, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Zona",
         xaxis = list(title = "Zona"),
         yaxis = list(title = "Precio"))
fig5

como es una sola zona pues podemos ver un diagrama de cajas que muestra la media del precio de 390, con datos atipicos mayores en precio a 950 millones.

  • Se grafica las viviendas de la “zona norte”, para verificar con latitud y longitud su ubicación en la ciudad.

Se hace un poligono que representa el norte de la ciudad de Cali.

library(ggplot2)
library(sf)
## Warning: package 'sf' was built under R version 4.3.3
## Linking to GEOS 3.11.2, GDAL 3.8.2, PROJ 9.3.1; sf_use_s2() is TRUE
# Crear un dataframe con los puntos clave
puntos_clave <- data.frame(
  punto = c("Noroeste", "Noreste", "Sureste", "Suroeste", "Noroeste"),
  latitud = c(3.482, 3.482, 3.448, 3.448, 3.482),
  longitud = c(-76.545, -76.501, -76.545, -76.501, -76.545)
)


# DataFrame con las coordenadas de los 6 puntos
puntos_area <- data.frame(
  punto = c(1, 2, 3, 4, 5, 6),
  longitud = c(-76.492267, -76.53279888, -76.54114877, -76.52125592, -76.51829294, -76.4662556),
  latitud = c(3.50586231, 3.50235088, 3.45104121, 3.46020761, 3.45328482, 3.47101177)
)
  # Dibujar el mapa con los puntos de vivienda y agregar el polígono
  ggplot(viviendaR1) +
    geom_point(aes(x = longitud, y = latitud), color = "blue", size = 2) +
    geom_polygon(data = puntos_area, aes(x = longitud, y = latitud), 
                 fill = "red", alpha = 0.3, color = "black", size = 1) +
    ggtitle("Ubicación de las casas y área específica en la zona norte de la ciudad") +
    theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Se hace un area que representa la zona norte de Cali y se proyectan los puntos dados por la data, sin embargo encontramos que estos puntos que dicen estar en la Zona norte no es verdad si miramos las coordenadas dadas, por eso debemos trabajar solo con los puntos que estan en esta zona norte marcada por nosotros.

por eso vamos a tomar la base inicial y vamos a dejar las casas que caen dentro de nuestro poligono sin importar que zona diga en la varieble ZONA.

# Crear un objeto sf con los puntos clave
puntos_clave_sf <- st_as_sf(puntos_area, coords = c("longitud", "latitud"), crs = 4326)
# Convertir los puntos clave en un polígono
poligono <- st_convex_hull(st_union(puntos_clave_sf))
# Filtra la base de datos para incluir solo casas
viviendaR1 <- subset(viviendaR, tipo == "Casa")

# Convertir las casas en un objeto sf
vivienda_sf <- st_as_sf(viviendaR1, coords = c("longitud", "latitud"), crs = 4326)

# Filtrar las casas que están dentro del polígono
ViviendaR1C_ <- vivienda_sf[st_within(vivienda_sf, poligono, sparse = FALSE), ]

# Supongamos que `viviendas_filtradas` tiene una columna llamada `ID`
ids_filtrados <- ViviendaR1C_$id

# Filtrar las viviendas en el data frame
viviendaR1C <- ViviendaR1C_ %>%
  filter(id %in% ids_filtrados)

# Mostrar los primeros registros de las casas dentro de la zona norte
head(viviendaR1C)
ggplot() +
  geom_sf(data = poligono, fill = NA, color = "red") +  # Dibujar el polígono
  geom_sf(data = viviendaR1C, color = "blue", size = 2) +  # Casas dentro del polígono
  ggtitle("Casas en la zona norte de Cali") +
  theme_minimal()

Con la data ahora si comprobada que son las casas de la zona norte, se repite el analisis de las variables

  • Precio vs Area construida
# Crear un gráfico interactivo para 'precio' vs 'área construida'
fig1 <- plot_ly(viviendaR1C, x = ~areaconst, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Área Construida:', areaconst)) %>%
  layout(title = "Precio vs Área Construida",
         xaxis = list(title = "Área Construida"),
         yaxis = list(title = "Precio"))
fig1

Se evidencia un cambio leve en la grafica pero mantiene la estructura mayor area mayor el precio de la vivienda.

  • Precio vs Estrato
# Crear un gráfico interactivo para 'precio' vs 'estrato'
fig2 <- plot_ly(viviendaR1C, x = ~estrato, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Estrato",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio"))
fig2
## Warning: 'box' objects don't have these attributes: 'mode'
## Valid attributes include:
## 'alignmentgroup', 'boxmean', 'boxpoints', 'customdata', 'customdatasrc', 'dx', 'dy', 'fillcolor', 'hoverinfo', 'hoverinfosrc', 'hoverlabel', 'hoveron', 'hovertemplate', 'hovertemplatesrc', 'hovertext', 'hovertextsrc', 'ids', 'idssrc', 'jitter', 'legendgroup', 'legendgrouptitle', 'legendrank', 'line', 'lowerfence', 'lowerfencesrc', 'marker', 'mean', 'meansrc', 'median', 'mediansrc', 'meta', 'metasrc', 'name', 'notched', 'notchspan', 'notchspansrc', 'notchwidth', 'offsetgroup', 'opacity', 'orientation', 'pointpos', 'q1', 'q1src', 'q3', 'q3src', 'quartilemethod', 'sd', 'sdsrc', 'selected', 'selectedpoints', 'showlegend', 'stream', 'text', 'textsrc', 'transforms', 'type', 'uid', 'uirevision', 'unselected', 'upperfence', 'upperfencesrc', 'visible', 'whiskerwidth', 'width', 'x', 'x0', 'xaxis', 'xcalendar', 'xhoverformat', 'xperiod', 'xperiod0', 'xperiodalignment', 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'yhoverformat', 'yperiod', 'yperiod0', 'yperiodalignment', 'ysrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Al igual que la anterior se evidencia un cambio, pero matiene la norma de mayor estrato mayor es el costo, sin embargo, evidenciamos que los estratos 3 y 5 tienen mass datos atipicos.

  • Precio vs Numero de Baños
# Crear un gráfico interactivo para 'precio' vs 'número de baños'
fig3 <- plot_ly(viviendaR1C, x = ~banios, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Baños:', banios)) %>%
  layout(title = "Precio vs Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio"))
fig3

ahora hay una grafica conjunta de los baños, se consolidan y se ve que no sigue una regla general ya que la vivienda mas costosa tiene 8 baños con un precio de 1940 millones pero encontramos una vivienda de 10 baños con valor de 1400 millones, no se evidencia con esta grafica una norma predictiva.

  • Precio vs Numero Habitaciones
# Crear un gráfico interactivo para 'precio' vs 'número de habitaciones'
fig4 <- plot_ly(viviendaR1C, x = ~habitaciones, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Habitaciones:', habitaciones)) %>%
  layout(title = "Precio vs Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio"))
fig4

Al igual que la grafica anterior no se evidencia una regla general, sin embargo en este caso la vivienda mas costosa tiene el mayor numero de habitaciones posibles entonces tiene logica, pero no es que evidenciemos una norma general.

  • Precio vs Zona
# Crear un gráfico interactivo para 'precio' vs 'zona' (si tienes la variable 'zona')
fig5 <- plot_ly(viviendaR1C, x = ~zona, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Zona",
         xaxis = list(title = "Zona"),
         yaxis = list(title = "Precio"))
fig5
## Warning: 'box' objects don't have these attributes: 'mode'
## Valid attributes include:
## 'alignmentgroup', 'boxmean', 'boxpoints', 'customdata', 'customdatasrc', 'dx', 'dy', 'fillcolor', 'hoverinfo', 'hoverinfosrc', 'hoverlabel', 'hoveron', 'hovertemplate', 'hovertemplatesrc', 'hovertext', 'hovertextsrc', 'ids', 'idssrc', 'jitter', 'legendgroup', 'legendgrouptitle', 'legendrank', 'line', 'lowerfence', 'lowerfencesrc', 'marker', 'mean', 'meansrc', 'median', 'mediansrc', 'meta', 'metasrc', 'name', 'notched', 'notchspan', 'notchspansrc', 'notchwidth', 'offsetgroup', 'opacity', 'orientation', 'pointpos', 'q1', 'q1src', 'q3', 'q3src', 'quartilemethod', 'sd', 'sdsrc', 'selected', 'selectedpoints', 'showlegend', 'stream', 'text', 'textsrc', 'transforms', 'type', 'uid', 'uirevision', 'unselected', 'upperfence', 'upperfencesrc', 'visible', 'whiskerwidth', 'width', 'x', 'x0', 'xaxis', 'xcalendar', 'xhoverformat', 'xperiod', 'xperiod0', 'xperiodalignment', 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'yhoverformat', 'yperiod', 'yperiod0', 'yperiodalignment', 'ysrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Aca encontramos algo interesante y es que si analizamos por la variable zona, vemos que dentro de nuestro poligono de la ciudad hay viviendas que en su variable zona tiene zona diferente al norte, evidenciamos viviendas que realmente estan en el norte de la ciudad pero tienen en su variable zona, una diferente.

Estimacion del modelo

  • Estimacion del modelo
# Seleccionar las variables a usar en el modelo, excluyendo 'zona'
viviendaR1CF <- viviendaR1C %>%
  select(-zona)  # Excluir la variable 'zona'
# Revisar las primeras filas para asegurarse de que las variables están presentes
head(viviendaR1CF, 3)
  • Se entrena el modelo
# Estimar el modelo de regresión lineal múltiple
modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = viviendaR1CF)

# Resumen del modelo
summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = viviendaR1CF)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -892.74  -71.31  -20.68   34.69 1115.24 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   26.73179   21.83884   1.224 0.221405    
## areaconst      0.79127    0.05261  15.041  < 2e-16 ***
## estrato4      74.17903   21.25435   3.490 0.000518 ***
## estrato5     106.64668   20.06362   5.315 1.49e-07 ***
## estrato6     395.21762   33.03857  11.962  < 2e-16 ***
## habitaciones  -3.48051    5.49291  -0.634 0.526554    
## parqueaderos  29.13257    5.88291   4.952 9.51e-07 ***
## banios        25.56520    7.03195   3.636 0.000301 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 176.4 on 612 degrees of freedom
## Multiple R-squared:  0.6599, Adjusted R-squared:  0.656 
## F-statistic: 169.7 on 7 and 612 DF,  p-value: < 2.2e-16

Con este modelo, se ven resultados como que por cada metro cuadrado de mas son 791.270 pesos de mas en el costo de la vivienda, se ve cuanto aumenta el costo de una vivienda si aumenta el estrato, un estrato 4 vale 74 millones mas que un estrato 3 y ahi se evidencia para cada estrato vs el estrato 3 si tienen las mismas condiciones. ahora en la variable habitaciones evidenciamos que una habitacion mas en la vivienda reduce el costo de la vivienda pero esto es un resultado poco intuitivo suponiendo desde ya que el modelo lineal no es muy representativo a la relacion de las variables con el precio, las habitaciones y los baños aumentan el precio en 29 y 25 millones respectivamente.

Cuando analizamos el error estandar (Residual standar error) demuestra que el modelo no se ajusta correctamente a los datos.

El modelo explica un 66% de la variabilidad en los precios de las viviendas, aunque podemos ver que tiene muchas mejoras este modelo respecto a la data estudiada.

Se encontro que el modelo no tiene muy buena adaptabilidad a representar los datos, se recomienda probar una transformacion de variables o ajustar las predicciones finales de alguna manera, tambien se recomienda analizar un modelo mejor para los datos.

Validacion de supuestos

  • Linealidad
# Obtener los valores ajustados y los residuos
fitted_values1 <- fitted(modelo1)
residuals1 <- residuals(modelo1)

# Graficar los residuos versus los valores ajustados
plot(fitted_values1, residuals1,
     xlab = "Valores Ajustados",
     ylab = "Residuos",
     main = "Residuos vs Valores Ajustados")
abline(h = 0, col = "red")

Con el analisis de linealidad encontramos que los valores no son lineales, por ende este modelado no seria buen predictor para el objetivo final.

  • Independencia
dwtest(modelo1)
## 
##  Durbin-Watson test
## 
## data:  modelo1
## DW = 1.6804, p-value = 2.481e-05
## alternative hypothesis: true autocorrelation is greater than 0

Tenemos un valor de 1.6 es alto y cercano a 2 indicando que los residuos no estan autocorrelacionados.

  • Homosteasidad
# Graficar los residuos versus los valores ajustados
plot(fitted_values1, residuals1,
     xlab = "Valores Ajustados",
     ylab = "Residuos",
     main = "Residuos vs Valores Ajustados")
abline(h = 0, col = "red")

#Homosteasidad, se verifica con el grafico de dispersion de linealidad Si la dispersión de los residuos es constante a lo largo de los valores ajustados, se cumple el supuesto de homoscedasticidad.
#Si los residuos muestran una dispersión creciente o decreciente, podría haber problemas de heteroscedasticidad.

Analizando el grafico se encuentra un resultado heterocedastico de los residuos, no cumple el supuesto de homoscedasticidad. Indica que los datos no son lineales.

  • Normalidad
#Normalidad
# Histograma de los residuos
hist(residuals1, main = "Histograma de los Residuos", xlab = "Residuos", breaks = 30)

# Q-Q plot
qqnorm(residuals1)
qqline(residuals1, col = "red")

#Sugerencias si se encuentran problemas:
#Transformación de Variables: Considera transformar variables si observas problemas con la linealidad.
#Modelos Alternativos: Si hay heteroscedasticidad, considera modelos como la regresión robusta.
#Variables de Control: Agrega variables adicionales o interacciones si los residuos muestran patrones no explicados.

En el grafico vemos una distribucion en forma de campana indicando unos residuos normales. pero en el segundo grafico encontramos que hay demasiados residuos que no tienen esta normalidad, por eso en el histograma encontramos una campana pero con los bordes muy alargados.

Se sugiere que se realice un modelado diferente, pueden utilizar disferentes modelos y encontrar uno que se adapte mejor a los datos.

Particion del modelo 70%-30%

library(caret)
## Warning: package 'caret' was built under R version 4.3.3
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following object is masked from 'package:boot':
## 
##     melanoma
# Establecer una semilla para reproducibilidad

set.seed(123)  # Puedes cambiar el número si deseas resultados diferentes

# Crear un índice aleatorio para dividir los datos
training_indices1 <- createDataPartition(viviendaR1CF$preciom, p = 0.7, list = FALSE)

# Crear los conjuntos de entrenamiento y prueba
train_data1 <- viviendaR1CF[training_indices1, ]
test_data1 <- viviendaR1CF[-training_indices1, ]
  • Entrenamiento del modelo
# Entrenar el modelo en el conjunto de entrenamiento
modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data1)

# Resumen del modelo entrenado
summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = train_data1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -841.84  -70.44  -22.78   35.96 1014.47 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   60.47313   24.94996   2.424 0.015774 *  
## areaconst      0.78063    0.05638  13.845  < 2e-16 ***
## estrato4      84.57951   23.98948   3.526 0.000468 ***
## estrato5     128.83342   22.24684   5.791 1.36e-08 ***
## estrato6     331.66215   37.24457   8.905  < 2e-16 ***
## habitaciones  -8.06161    6.24304  -1.291 0.197298    
## parqueaderos  28.10474    6.50063   4.323 1.91e-05 ***
## banios        19.94320    8.18446   2.437 0.015228 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 166.9 on 428 degrees of freedom
## Multiple R-squared:  0.6459, Adjusted R-squared:  0.6401 
## F-statistic: 111.5 on 7 and 428 DF,  p-value: < 2.2e-16

Ahora realizamos el modelado pero partiendo los datos en un 70% destinados a entrenamiento y otro 30% para testeo, no tenemos resultados significativamente diferentes ya que tenemos que cada metro cuadrado aumenta en 780.630 pesos, que el costo de las viviendas aumenta por el aumento de estrato un poco mayor al anterior. En la variable habitacion tenemos la misma contrariedad que el anterior.

Cuando analizamos el error estandar (Residual standar error) demuestra que el modelo no se ajusta correctamente a los datos.

El modelo explica un 64% de la variabilidad en los precios de las viviendas, aunque podemos ver que tiene muchas mejoras este modelo respecto a la data estudiada.

Se sigue encontrando que el modelo no tiene muy buena adaptabilidad a representar los datos, se recomienda probar una transformacion de variables o ajustar las predicciones finales de alguna manera, tambien se recomienda analizar un modelo mejor para los datos.

Prediccion del precio de vivienda para la primera solicitud

Se crea un dataframe como filtro

# Crear un data frame con las características de la vivienda
nueva_vivienda1 <- data.frame(
  areaconst = 200,
  estrato = factor(c(4, 5), levels = c(4, 5)),  # Estratos 4 y 5
  habitaciones = 4,
  parqueaderos = 1,
  banios = 2
)

Prediccion del precio de la vivienda

# Hacer la predicción usando el modelo
precio_predicho1 <- predict(modelo1, nueva_vivienda1)

# Mostrar el precio predicho
precio_predicho1
##        1        2 
## 336.9228 381.1767
###segun la prediccion si es posible cumplir los pedidos del clientes con el preaprobado segun el modelo planteado

Evaluacion del modelo

# Hacer predicciones en el conjunto de prueba
predicciones1 <- predict(modelo1, newdata = test_data1)

# Comparar las predicciones con los valores reales
resultados1 <- data.frame(Real = test_data1$preciom, Prediccion = predicciones1)

# Mostrar los primeros resultados
head(resultados1)
# Calcular el error medio absoluto (MAE)
mae1 <- mean(abs(resultados1$Real - resultados1$Prediccion))
print(paste("Error Medio Absoluto (MAE):", mae1))
## [1] "Error Medio Absoluto (MAE): 115.982712351239"
# También podrías calcular el Root Mean Square Error (RMSE) si es necesario
rmse1 <- sqrt(mean((resultados1$Real - resultados1$Prediccion)^2))
print(paste("Root Mean Square Error (RMSE):", rmse1))
## [1] "Root Mean Square Error (RMSE): 204.287701009325"
# Calcular el R² (Coeficiente de Determinación)
R21 <- 1 - sum((predicciones1 - test_data1$preciom)^2) / sum((mean(test_data1$preciom) - test_data1$preciom)^2)
print(paste("R^2 :", R21))
## [1] "R^2 : 0.653897045035854"

Interpretando los resultados, se encuentra un MAE de 115.98 esto significa que en promedio las predicciones del modelo se desvian 115.98 millones de pesos dando un resultado alejado del real.

Se tiene un RMSE de 204.29 tenemos una magnitud de error de 204.29 millones de pesos, penalizando los errores mas grandes.

Analizando un coeficiente de 0.6539, quiere decir que el modelo es util pero tenemos un 35% de variablidad que no esta siendo explicada. Esto nos alerta a que el modelo no sea la unica fuente de datos para la toma de decisiones.

segunda propuesta

Como ya se vio que la variable zona esta errada, pasaremos directamente a graficar y filtrar por la ubicacion del poligono de la zona sur de la ciudad.

Analisis de las variables contra precio de la vivienda

  • Se hace un poligono que representa el sur de la ciudad.
library(ggplot2)
library(sf)

# DataFrame con las coordenadas de los 6 puntos
puntos_area2 <- data.frame(
  punto = c(1, 2, 3, 4, 5, 6, 7),
  longitud = c(-76.49156207, -76.50980486, -76.52266282, -76.54846102, -76.54268384, -76.55132043, -76.55113485),
  latitud = c(3.31719806, 3.38924985, 3.40522812, 3.41472793, 3.37528857, 3.36238751, 3.31869774)
)
  # Dibujar el mapa con los puntos de vivienda y agregar el polígono
  ggplot(viviendaR) +
    geom_point(aes(x = longitud, y = latitud), color = "blue", size = 2) +
    geom_polygon(data = puntos_area2, aes(x = longitud, y = latitud), 
                 fill = "red", alpha = 0.3, color = "black", size = 1) +
    ggtitle("Ubicación de los apartamentos y área específica en la zona sur de la ciudad") +
    theme_minimal()

Se hace un area que representa la zona sur de Cali y se proyectan los puntos dados por la data, sin embargo encontramos que estos puntos que dicen estar en la Zona SUR no es verdad si miramos las coordenadas dadas, por eso debemos trabajar solo con los puntos que estan en esta zona sur marcada por nosotros.

por eso vamos a tomar la base inicial y vamos a dejar los apartamentos que caen dentro de nuestro poligono sin importar que zona diga en la varieble ZONA.

# Crear un objeto sf con los puntos clave
puntos_clave_sf2 <- st_as_sf(puntos_area2, coords = c("longitud", "latitud"), crs = 4326)
# Convertir los puntos clave en un polígono
poligono2 <- st_convex_hull(st_union(puntos_clave_sf2))
# Filtra la base de datos para incluir solo apartamentos
viviendaR2 <- subset(viviendaR, tipo == "Apartamento")

# Convertir las casas en un objeto sf
vivienda_sf2 <- st_as_sf(viviendaR2, coords = c("longitud", "latitud"), crs = 4326)

# Filtrar las casas que están dentro del polígono
ViviendaR2A_ <- vivienda_sf2[st_within(vivienda_sf2, poligono2, sparse = FALSE), ]

# Supongamos que `viviendas_filtradas` tiene una columna llamada `ID`
ids_filtrados2 <- ViviendaR2A_$id

# Filtrar las viviendas en el data frame
viviendaR2A <- ViviendaR2A_ %>%
  filter(id %in% ids_filtrados2)

# Mostrar los primeros registros de las casas dentro de la zona sur
head(viviendaR2A)
ggplot() +
  geom_sf(data = poligono2, fill = NA, color = "red") +  # Dibujar el polígono
  geom_sf(data = viviendaR2A, color = "blue", size = 2) +  # apartamentos dentro del polígono
  ggtitle("Apartamentos en la zona sur de Cali") +
  theme_minimal()

Con la data ahora si comprobada que son los apartamentos de la zona sur, se realiza analisis de las variables.

  • Precio vs Area construida
# Crear un gráfico interactivo para 'precio' vs 'área construida'
fig1 <- plot_ly(viviendaR2A, x = ~areaconst, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Área Construida:', areaconst)) %>%
  layout(title = "Precio vs Área Construida",
         xaxis = list(title = "Área Construida"),
         yaxis = list(title = "Precio"))
fig1

Se evidencia que mayor area mayor el precio de la vivienda.

  • Precio vs Estrato
# Crear un gráfico interactivo para 'precio' vs 'estrato'
fig2 <- plot_ly(viviendaR2A, x = ~estrato, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Estrato",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio"))
fig2
## Warning: 'box' objects don't have these attributes: 'mode'
## Valid attributes include:
## 'alignmentgroup', 'boxmean', 'boxpoints', 'customdata', 'customdatasrc', 'dx', 'dy', 'fillcolor', 'hoverinfo', 'hoverinfosrc', 'hoverlabel', 'hoveron', 'hovertemplate', 'hovertemplatesrc', 'hovertext', 'hovertextsrc', 'ids', 'idssrc', 'jitter', 'legendgroup', 'legendgrouptitle', 'legendrank', 'line', 'lowerfence', 'lowerfencesrc', 'marker', 'mean', 'meansrc', 'median', 'mediansrc', 'meta', 'metasrc', 'name', 'notched', 'notchspan', 'notchspansrc', 'notchwidth', 'offsetgroup', 'opacity', 'orientation', 'pointpos', 'q1', 'q1src', 'q3', 'q3src', 'quartilemethod', 'sd', 'sdsrc', 'selected', 'selectedpoints', 'showlegend', 'stream', 'text', 'textsrc', 'transforms', 'type', 'uid', 'uirevision', 'unselected', 'upperfence', 'upperfencesrc', 'visible', 'whiskerwidth', 'width', 'x', 'x0', 'xaxis', 'xcalendar', 'xhoverformat', 'xperiod', 'xperiod0', 'xperiodalignment', 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'yhoverformat', 'yperiod', 'yperiod0', 'yperiodalignment', 'ysrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Al igual que la anterior mantiene la norma de mayor estrato mayor es el costo, sin embargo, evidenciamos que los estratos 5 y 6 tienen mas datos atipicos.

  • Precio vs Numero de Baños
# Crear un gráfico interactivo para 'precio' vs 'número de baños'
fig3 <- plot_ly(viviendaR2A, x = ~banios, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Baños:', banios)) %>%
  layout(title = "Precio vs Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio"))
fig3

ahora vemos una grafica conjunta de los baños, se consolidan y vemos que no sigue una regla general ya que las viviendas mas costosas tiene una de 4 y la otra de 5 baños pero con el mismo precio.

  • Precio vs Numero Habitaciones
# Crear un gráfico interactivo para 'precio' vs 'número de habitaciones'
fig4 <- plot_ly(viviendaR2A, x = ~habitaciones, y = ~preciom, type = 'scatter', mode = 'markers',
                marker = list(color = 'rgba(255, 182, 193, .9)', size = 10),
                text = ~paste('Precio:', preciom, '<br>Habitaciones:', habitaciones)) %>%
  layout(title = "Precio vs Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio"))
fig4

Al igual que la grafica anterior no se evidencia una regla general.

  • Precio vs Zona
# Crear un gráfico interactivo para 'precio' vs 'zona' (si tienes la variable 'zona')
fig5 <- plot_ly(viviendaR2A, x = ~zona, y = ~preciom, type = 'box',
                boxmean = "sd") %>%
  layout(title = "Precio por Zona",
         xaxis = list(title = "Zona"),
         yaxis = list(title = "Precio"))
fig5
## Warning: 'box' objects don't have these attributes: 'mode'
## Valid attributes include:
## 'alignmentgroup', 'boxmean', 'boxpoints', 'customdata', 'customdatasrc', 'dx', 'dy', 'fillcolor', 'hoverinfo', 'hoverinfosrc', 'hoverlabel', 'hoveron', 'hovertemplate', 'hovertemplatesrc', 'hovertext', 'hovertextsrc', 'ids', 'idssrc', 'jitter', 'legendgroup', 'legendgrouptitle', 'legendrank', 'line', 'lowerfence', 'lowerfencesrc', 'marker', 'mean', 'meansrc', 'median', 'mediansrc', 'meta', 'metasrc', 'name', 'notched', 'notchspan', 'notchspansrc', 'notchwidth', 'offsetgroup', 'opacity', 'orientation', 'pointpos', 'q1', 'q1src', 'q3', 'q3src', 'quartilemethod', 'sd', 'sdsrc', 'selected', 'selectedpoints', 'showlegend', 'stream', 'text', 'textsrc', 'transforms', 'type', 'uid', 'uirevision', 'unselected', 'upperfence', 'upperfencesrc', 'visible', 'whiskerwidth', 'width', 'x', 'x0', 'xaxis', 'xcalendar', 'xhoverformat', 'xperiod', 'xperiod0', 'xperiodalignment', 'xsrc', 'y', 'y0', 'yaxis', 'ycalendar', 'yhoverformat', 'yperiod', 'yperiod0', 'yperiodalignment', 'ysrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

aca encontramos algo interesante y es que si analizamos por la variable zona, vemos que dentro de nuestro poligono de la ciudad hay viviendas que en su variable zona tiene zona diferente al sur, evidenciamos viviendas que realmente estan en el sur de la ciudad pero tienen en su variable zona, una diferente.

Estimacion del modelo

  • Estimacion del modelo
# Seleccionar las variables a usar en el modelo, excluyendo 'zona'
viviendaR2AF <- viviendaR2A %>%
  select(-zona)  # Excluir la variable 'zona'
# Revisar las primeras filas para asegurarse de que las variables están presentes
head(viviendaR2AF, 3)
  • Se entrena el modelo
# Estimar el modelo de regresión lineal múltiple
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = viviendaR2AF)

# Resumen del modelo
summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = viviendaR2AF)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1024.36   -38.03    -2.77    35.94   891.07 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -68.29534   13.67125  -4.996 6.35e-07 ***
## areaconst      1.24325    0.05323  23.356  < 2e-16 ***
## estrato4      30.02654    9.87977   3.039   0.0024 ** 
## estrato5      51.21655    9.99574   5.124 3.27e-07 ***
## estrato6     189.74830   11.79980  16.081  < 2e-16 ***
## habitaciones  -9.57630    3.89246  -2.460   0.0140 *  
## parqueaderos  75.03333    4.31017  17.408  < 2e-16 ***
## banios        45.14030    3.48295  12.960  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 92.96 on 2110 degrees of freedom
## Multiple R-squared:  0.7912, Adjusted R-squared:  0.7906 
## F-statistic:  1142 on 7 and 2110 DF,  p-value: < 2.2e-16

Con este modelo, se ven resultados como que por cada metro cuadrado de mas son 1.243 millones de pesos mas en el costo de la vivienda, se ve cuanto aumenta el costo de una vivienda si aumenta el estrato, un estrato 4 vale 30 millones mas que un estrato 3 y ahi se evidencia para cada estrato vs el estrato 3 si tienen las mismas condiciones. ahora en la variable habitaciones evidenciamos que una habitacion mas en la vivienda reduce el costo de la vivienda pero esto es un resultado poco intuitivo suponiendo desde ya que el modelo lineal no es muy representativo a la relacion de las variables con el precio, las habitaciones y los baños aumentan el precio en 75 y 45 millones respectivamente.

Cuando analizamos el error estandar (Residual standar error) demuestra que el modelo no se ajusta correctamente a los datos.

El modelo explica un 79% de la variabilidad en los precios de las viviendas, aunque podemos ver que tiene muchas mejoras este modelo respecto a la data estudiada.

Al igual que el anterior, se encontro que el modelo no tiene muy buena adaptabilidad a representar los datos, se recomienda probar una transformacion de variables o ajustar las predicciones finales de alguna manera, tambien se recomienda analizar un modelo mejor para los datos.

Validacion de supuestos

  • Linealidad
# Obtener los valores ajustados y los residuos
fitted_values2 <- fitted(modelo2)
residuals2 <- residuals(modelo2)

# Graficar los residuos versus los valores ajustados
plot(fitted_values2, residuals2,
     xlab = "Valores Ajustados",
     ylab = "Residuos",
     main = "Residuos vs Valores Ajustados")
abline(h = 0, col = "red")

No tiene una buena linealidad los datos, entendemos que el modelo no es el mejor para representar los datos y las variables que se estan manejando.

  • Independencia
install.packages("zoo")
## Warning: package 'zoo' is in use and will not be installed
library(lmtest)
dwtest(modelo2)
## 
##  Durbin-Watson test
## 
## data:  modelo2
## DW = 1.711, p-value = 1.053e-11
## alternative hypothesis: true autocorrelation is greater than 0

Tenemos un valor aun mayor en el DW, no tienen correlacion los residuos.

  • Homosteasidad
# Graficar los residuos versus los valores ajustados
plot(fitted_values2, residuals2,
     xlab = "Valores Ajustados",
     ylab = "Residuos",
     main = "Residuos vs Valores Ajustados")
abline(h = 0, col = "red")

#Homosteasidad, se verifica con el grafico de dispersion de linealidad Si la dispersión de los residuos es constante a lo largo de los valores ajustados, se cumple el supuesto de homoscedasticidad.
#Si los residuos muestran una dispersión creciente o decreciente, podría haber problemas de heteroscedasticidad.

Analizando el grafico se encuentra un resultado heterocedastico de los residuos, no cumple el supuesto de homoscedasticidad. Indica que los datos no son lineales.

-Normalidad

#Normalidad
# Histograma de los residuos
hist(residuals2, main = "Histograma de los Residuos", xlab = "Residuos", breaks = 30)

# Q-Q plot
qqnorm(residuals2)
qqline(residuals2, col = "red")

#Sugerencias si se encuentran problemas:
#Transformación de Variables: Considera transformar variables si observas problemas con la linealidad.
#Modelos Alternativos: Si hay heteroscedasticidad, considera modelos como la regresión robusta.
#Variables de Control: Agrega variables adicionales o interacciones si los residuos muestran patrones no explicados.

Si se analiza el grafico demuestra un poco mas de normalidad en los residuos que el anterior, pero no es del todo representativo.

Se sugiere que se realice un modelado diferente, pueden utilizar disferentes modelos y encontrar uno que se adapte mejor a los datos.

Particion del modelo 70%-30%

library(caret)
# Establecer una semilla para reproducibilidad

set.seed(123)  # Puedes cambiar el número si deseas resultados diferentes

# Crear un índice aleatorio para dividir los datos
training_indices2 <- createDataPartition(viviendaR2AF$preciom, p = 0.7, list = FALSE)

# Crear los conjuntos de entrenamiento y prueba
train_data2 <- viviendaR2AF[training_indices2, ]
test_data2 <- viviendaR2AF[-training_indices2, ]
  • Entrenamiento del modelo
# Entrenar el modelo en el conjunto de entrenamiento
modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios, data = train_data2)

# Resumen del modelo entrenado
summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = train_data2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -888.11  -37.01   -2.26   36.47  921.87 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -62.72178   16.44152  -3.815 0.000142 ***
## areaconst      1.07972    0.06101  17.697  < 2e-16 ***
## estrato4      30.19596   11.55892   2.612 0.009084 ** 
## estrato5      55.55203   11.66150   4.764 2.09e-06 ***
## estrato6     187.07123   13.85225  13.505  < 2e-16 ***
## habitaciones  -8.75138    4.59084  -1.906 0.056809 .  
## parqueaderos  78.76207    4.94994  15.912  < 2e-16 ***
## banios        45.15705    4.02833  11.210  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 91.06 on 1475 degrees of freedom
## Multiple R-squared:  0.7837, Adjusted R-squared:  0.7826 
## F-statistic: 763.3 on 7 and 1475 DF,  p-value: < 2.2e-16

Ahora realizamos el modelado pero partiendo los datos en un 70% destinados a entrenamiento y otro 30% para testeo, no tenemos resultados significativamente diferentes ya que tenemos que cada metro cuadrado aumenta en 1.079 millones de pesos, que el costo de las viviendas aumenta por el aumento de estrato un poco mayor al anterior. En la variable habitacion tenemos la misma contrariedad que el anterior.

Cuando analizamos el error estandar (Residual standar error) demuestra que el modelo no se ajusta correctamente a los datos.

El modelo explica un 78% de la variabilidad en los precios de las viviendas, aunque podemos ver que tiene muchas mejoras este modelo respecto a la data estudiada.

Se sigue encontrando que el modelo no tiene muy buena adaptabilidad a representar los datos, se recomienda probar una transformacion de variables o ajustar las predicciones finales de alguna manera, tambien se recomienda analizar un modelo mejor para los datos.

Prediccion del precio de vivienda para la primera solicitud

  • Se crea un dataframe como filtro
# Crear un data frame con las características de la vivienda
nueva_vivienda2 <- data.frame(
  areaconst = 300,
  estrato = factor(c(5, 6), levels = c(5, 6)),  # Estratos 4 y 5
  habitaciones = 5,
  parqueaderos = 3,
  banios = 3
)
  • Prediccion del precio de la vivienda
# Hacer la predicción usando el modelo
precio_predicho2 <- predict(modelo2, nueva_vivienda2)

# Mostrar el precio predicho
precio_predicho2
##        1        2 
## 644.7462 776.2654
###segun la prediccion si es posible cumplir los pedidos del clientes con el preaprobado segun el modelo planteado

Evaluacion del modelo

# Hacer predicciones en el conjunto de prueba
predicciones2 <- predict(modelo2, newdata = test_data2)

# Comparar las predicciones con los valores reales
resultados2 <- data.frame(Real = test_data2$preciom, Prediccion = predicciones2)

# Mostrar los primeros resultados
head(resultados2)
# Calcular el error medio absoluto (MAE)
mae2 <- mean(abs(resultados2$Real - resultados2$Prediccion))
print(paste("Error Medio Absoluto (MAE):", mae2))
## [1] "Error Medio Absoluto (MAE): 58.8590443061943"
# También podrías calcular el Root Mean Square Error (RMSE) si es necesario
rmse2 <- sqrt(mean((resultados2$Real - resultados2$Prediccion)^2))
print(paste("Root Mean Square Error (RMSE):", rmse2))
## [1] "Root Mean Square Error (RMSE): 98.5103203154061"
# Calcular el R² (Coeficiente de Determinación)
R22 <- 1 - sum((predicciones2 - test_data2$preciom)^2) / sum((mean(test_data2$preciom) - test_data2$preciom)^2)
print(paste("R^2 :", R22))
## [1] "R^2 : 0.799712349557379"

Interpretando los resultados, se encuentra un MAE de 58.85 esto significa que en promedio las predicciones del modelo se desvian 58.85 millones de pesos dando un resultado alejado del real.

Se tiene un RMSE de 98.51 tenemos una magnitud de error de 98.51 millones de pesos, penalizando los errores mas grandes.

Analizando un coeficiente de 0.7997, quiere decir que el modelo es util pero tenemos un 20% de variablidad que no esta siendo explicada. Esto nos alerta a que el modelo no sea la unica fuente de datos para la toma de decisiones.

CONCLUSIONES

Luego de realizar el modelado y la evaluacion de resultados tenemos observaciones como que los modelos representan mas del 64% de los datos (ambos modelos), pero no es recomendable como unica fuente de informacion para la toma de deciciones ya que se encontro una variabilidad de resultados alto y que no es muy acertada en los valores de los precios, respecto al valor real con la data recibida.

Se recomienda que se realice el ejercicio de modelado, probando 3 opciones, la priemra una transformacion de variables, la segunda probar un modelado logaritmico y la tercera probar modelos como arbol de deciciones u otro que se considere pertinente.

tener encuenta que los poligonos utilizados estan basados en la informacion encontrada en la pagina de la alcaldia de Cali, no es 100% real pero si tiene mas del 95% de parecido a la distribucion encontrada en la pagina de la alcaldia.