Problemática


María es agente de bienes raíces en Cali hace 10 años, 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:

Caracteristicas Vivienda1 Vivienda2
Tipo Casa Apartamento
area construida 200 300
parqueaderos 1 3
banos 2 3
habitaciones 4 5
estrato 4 o 5 5 o 6
zona Norte Sur
credito preaprobado 350 millones 850 millones


Análisis Vivienda 1

La base de datos original contiene 8322 registros, con el objetivo de realizar el análisis de la vivienda 1 se aplica un filtro a esta base de datos seleccionando solo las viviendas que en tipo tengan el valor “Casa” y en zona tengan “Zona Norte”, quedando una base de datos con 722 registros , y a continuación se presentan los tres (03) primeros:

library(paqueteMODELOS)
library(dplyr)
library(knitr)

data("vivienda")

viviendas1 <- vivienda %>%
  filter(tipo == "Casa", zona == "Zona Norte")


kable(head(viviendas1, 3))
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
1209 Zona Norte 02 5 320 150 2 4 6 Casa acopi -76.51341 3.47968
1592 Zona Norte 02 5 780 380 2 3 3 Casa acopi -76.51674 3.48721
4057 Zona Norte 02 6 750 445 NA 7 6 Casa acopi -76.52950 3.38527
dim(viviendas1)
## [1] 722  13


Luego de aplicado el filtro se presentan unas tablas que comprueban la consulta, es decir, que la nueva base solo contenga viviendas que el tipo sea “Casa” y estén ubicadas en la “Zona norte”, donde se puede visualizar que solo se presenta la información que tiene estas características y una frecuencia de 722:

library(kableExtra)

tabla <- as.data.frame(table(viviendas1$tipo))
names(tabla) <- c("Tipo de vivienda", "Frecuencia")
tabla
##   Tipo de vivienda Frecuencia
## 1             Casa        722
tabla1 <- as.data.frame(table(viviendas1$zona))
names(tabla1) <- c("Zona", "Frecuencia")
tabla1
##         Zona Frecuencia
## 1 Zona Norte        722


A continuación, se presenta el mapa interactivo de la ciudad de Santiago de Cali (Colombia) con los puntos de la base de datos tipo casa y ubicadas en la zona norte:

library(leaflet)

leaflet(viviendas1) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 4,
    color = "#7B02A8",
    popup = ~tipo
  )

En el anterior, se puede visualizar que hay una gran concentración de puntos en la parte superior del mapa, siendo esta la zona norte de la ciudad (latitudes más altas). Cabe destacar que, hay puntos que se ubican en la parte inferior del mapa, que no corresponderían a la zona norte del análisis, si no más a la zona sur. Lo que indicaría que hay errores en la clasificación de la zona de algunas casas, puesto que a pesar de que la latitud los ubica en la parte inferior, fueron registrados en zona como Zona Norte, también puede deberse a que la zona es una clasificación administrativa pero no siempre coincide con la zona de coordenadas reales.

Teniendo en cuenta que la solicitud del cliente indica que una de las condiciones es que la casa este ubicada en zona norte y como se evidencio anteriormente, no todas las zonas clasificadas como zona norte lo son realmente, se opta por realizar un análisis de conglomerados (clústeres) para dividir los puntos geográficos en zonas basadas en la concentración real de los datos y hacer el análisis posterior solo con los puntos que por conglomerado correspondan a las zonas con latitud más alta.

Al aplicar este análisis de conglomerados obtenemos dos clústeres, zona 1 y zona 2, cuya latitud promedio es 3.47 y 3.39 respectivamente, lo que indicaría que la zona 1 es la que contiene las casas ubicadas al norte de la ciudad, sobre las cuales se realizarían el análisis, quedando con 606 registros.

set.seed(123)  # Para reproducibilidad

# Solo latitud y longitud en matriz
coords <- viviendas1 %>% select(longitud, latitud)

# Aplicar k-means para 2 clusters
km <- kmeans(coords, centers = 2)

# Agregar cluster a la base como zona_real
viviendas1$zona_real <- factor(km$cluster, labels = c("Zona 1", "Zona 2"))

aggregate(latitud ~ zona_real, data = viviendas1, FUN = mean)
##   zona_real  latitud
## 1    Zona 1 3.472208
## 2    Zona 2 3.397372
table(viviendas1$zona_real)
## 
## Zona 1 Zona 2 
##    606    116


Al realizar nuevamente el mapa interactivo de la ciudad Santiago Cali luego de la división por conglomerados y tomando la zona 1 se puede visualizar que los puntos se agrupan solo en la parte superior del mapa, representando una mejor aproximación a las casas ubicadas en la zona norte que la original:

zona1 <- viviendas1 %>%
  filter(zona_real == "Zona 1")

leaflet(zona1) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 4,
    color = "#7B02A8",
    popup = ~tipo
  )


Continuando con el análisis, se revisan los datos faltantes en la base de datos resultante (zona 1), donde se evidencia que el 46% de la columna piso son datos faltantes, por lo cual, se toma la decisión de eliminar está columna. De igual manera, se procede a eliminar “id” puesto que es un indicador y no brinda información significativa en el análisis:

colSums(is.na(zona1))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          279            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          209            0            0            0            0            0 
##      latitud    zona_real 
##            0            0
zona1r <- zona1[, !(names(zona1) %in% c("piso", "id"))]

names(zona1r)
##  [1] "zona"         "estrato"      "preciom"      "areaconst"    "parqueaderos"
##  [6] "banios"       "habitaciones" "tipo"         "barrio"       "longitud"    
## [11] "latitud"      "zona_real"


Por otro lado, la variable parqueaderos presenta 209 datos faltantes, se observa que sus valores registrados van de 1 a 10, sin incluir el valor 0. Esto indica que en la base de datos no se representa explícitamente las viviendas que no cuentan con parqueadero y es común encontrar viviendas con esta característica:

table(zona1r$parqueaderos, useNA = "always")
## 
##    1    2    3    4    5    6    7    8    9   10 <NA> 
##  149  145   42   35   11    7    5    1    1    1  209


Asimismo, se evidencia que las viviendas con NA en parqueaderos tienen un precio promedio mucho menor que aquellas que, si tienen un valor registrado en esta variable, lo que podría sugerir que ese NA no es aleatorio y se asocia a 0 parqueaderos. Por lo cual, se procede a reemplazar NA de parqueaderos con cero:

aggregate(preciom ~ is.na(parqueaderos), data = zona1r, mean)
##   is.na(parqueaderos)  preciom
## 1               FALSE 479.3678
## 2                TRUE 334.4545
# Se reemplazan los NA en parquederos con cero

zona1r$parqueaderos[is.na(zona1r$parqueaderos)] <-0 

sum(is.na(zona1r$parqueaderos))
## [1] 0


Análisis de correlación entre variables


En la revisión de los estadísticos descriptivos generales de las columnas numéricas de las variables solicitadas en el análisis (precio, área construida, parqueaderos, baños y habitaciones), se identifica que los valores de parqueaderos, baños y habitaciones oscilan entre 0 y 10; el precio presenta un mínimo de 89 y un máximo de 1940; y el área construida varía entre 30 y 1440:

zona1r %>%
  select(preciom, areaconst, parqueaderos, banios, habitaciones) %>%
  summary()
##     preciom         areaconst       parqueaderos        banios      
##  Min.   :  89.0   Min.   :  30.0   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.: 245.0   1st Qu.: 140.0   1st Qu.: 0.000   1st Qu.: 2.000  
##  Median : 380.0   Median : 240.0   Median : 1.000   Median : 3.000  
##  Mean   : 429.4   Mean   : 261.4   Mean   : 1.426   Mean   : 3.517  
##  3rd Qu.: 540.0   3rd Qu.: 336.0   3rd Qu.: 2.000   3rd Qu.: 4.000  
##  Max.   :1940.0   Max.   :1440.0   Max.   :10.000   Max.   :10.000  
##   habitaciones   
##  Min.   : 0.000  
##  1st Qu.: 3.000  
##  Median : 4.000  
##  Mean   : 4.584  
##  3rd Qu.: 5.000  
##  Max.   :10.000


Por otro lado, se calculó la matriz de correlaciones de Pearson entre las variables numéricas y su análisis gráfico, donde se identifica que todas las variables tienen una relación positiva con el precio de las viviendas, aunque con distinta intensidad. El área construida tiene una relación fuerte (0.74) con el precio, lo que indica que, a mayor superficie, el precio tiende a aumentar de forma consistente, aunque hay cierta variabilidad. Las variables parqueaderos (0.41), baños (0.55) y habitaciones (0.41) presentan correlaciones moderadas a bajas con el precio, reflejando que su relación es más débil y que los datos están más dispersos.

library(kableExtra)
correlacionviviendas1 <- cor(zona1r[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones")], method = "pearson")

kable(correlacionviviendas1, caption = "Matriz de correlación de Pearson") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Matriz de correlación de Pearson
preciom areaconst parqueaderos banios habitaciones
preciom 1.0000000 0.7388374 0.4121477 0.5524624 0.4055618
areaconst 0.7388374 1.0000000 0.3565642 0.4991671 0.4374937
parqueaderos 0.4121477 0.3565642 1.0000000 0.3766356 0.2586741
banios 0.5524624 0.4991671 0.3766356 1.0000000 0.6246336
habitaciones 0.4055618 0.4374937 0.2586741 0.6246336 1.0000000
library(GGally)
ggpairs(zona1r[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones")], title="Matriz de dispersión y correlación de Pearson") 


Posteriormente, se hizo el análisis del precio de las viviendas con la variable categórica estrato, mediante el precio promedio y el diagrama de cajas, donde se evidencia que hay una tendencia positiva entre el estrato y el precio promedio de las viviendas, lo que indica que los precios aumentan a medida que aumenta el estrato:

library(dplyr)


tabla_resumen <- zona1r %>%
  group_by(estrato) %>%
  summarise(
    precio_prom = mean(preciom, na.rm = TRUE),
    n = n()
  )

tabla_resumen %>%
  kable(
    caption = "Precio promedio por estrato",
    digits = 0,            # número de decimales
    col.names = c("Estrato", "Precio Promedio", "Cantidad de Viviendas")
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Precio promedio por estrato
Estrato Precio Promedio Cantidad de Viviendas
3 241 210
4 434 129
5 547 233
6 767 34
library(plotly)

plot_ly(
  data = zona1r,
  x = ~factor(estrato),
  y = ~preciom,
  type = "box",
  jitter = 0.3,
  marker = list(color = "#7B02A8")
) %>%
  layout(
    title = "Distribución del precio por estrato",
    xaxis = list(title = "Estrato"),
    yaxis = list(title = "Precio")
  )


Asimismo, se realizó un ANOVA para revisar estadísticamente si existen diferencias significativas en los precios promedio de las viviendas entre los estratos, teniendo las siguientes hipótesis

H0: Las medias del precio son iguales en todos los estratos

H1: Al menos un estrato tiene media diferente

anova_model <- aov(preciom ~ factor(estrato), data = zona1r)
summary(anova_model)
##                  Df   Sum Sq Mean Sq F value Pr(>F)    
## factor(estrato)   3 14584322 4861441   109.4 <2e-16 ***
## Residuals       602 26759167   44450                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

El análisis ANOVA muestra que existen diferencias estadísticamente significativas en el precio de las viviendas entre los distintos estratos. Esto coincide con el gráfico de cajas y la tabla, donde se observa que los estratos más altos tienen precios promedio mayores.



Estimación del modelo de regresión lineal múltiple

Antes de la estimación del modelo de regresión lineal múltiple se hace un proceso de construccion de variables indicadoras (dummy) para la variable estrato, puesto que es categorica y no se puede asumir que hay un incremento lineal monotómico entre los estratos, donde el estrato 3 será la categoría base, es decir, cuando estrato 4, 5 y 6 tomen valor de cero simultaneamente, significa que la casa es estrato 3, a continuación se muestra el ejemplo de como quedaron los tres primeros registros con estas nuevas variables dummys:

library(fastDummies)

zona1r <- fastDummies::dummy_cols(zona1r, 
                                      select_columns = "estrato",
                                      remove_first_dummy = TRUE) # opcional para evitar multicolinealidad

head(zona1r[, c("estrato_4", "estrato_5", "estrato_6")], 3) %>%
  kable(
    caption = "Variables Dummy para Estratos (primeras 3 filas)",
    col.names = c("Estrato 4", "Estrato 5", "Estrato 6")
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Variables Dummy para Estratos (primeras 3 filas)
Estrato 4 Estrato 5 Estrato 6
0 1 0
0 1 0
0 0 0


Se ajusta el modelo de regresión lineal mútliple para explicar el precio de la vivienda en función de la el área construida, los parqueaderos, los baños, las habitaciones y el estrato:

# Dividir el dataset ( 70% entrenamiento y 30% prueba)
indices <- sample(1:nrow(zona1r), size = 0.7 * nrow(zona1r))

train_data1 <- zona1r[indices, ]
test_data1 <- zona1r[-indices, ]



modelo1 <- lm(preciom ~ areaconst + parqueaderos + banios + habitaciones + estrato_4 + estrato_5 + estrato_6, data = train_data1)

summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + banios + habitaciones + 
##     estrato_4 + estrato_5 + estrato_6, data = train_data1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -851.79  -70.94  -17.13   32.42 1115.04 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    9.4935    22.4998   0.422 0.673287    
## areaconst      0.6958     0.0566  12.294  < 2e-16 ***
## parqueaderos  13.0412     5.7466   2.269 0.023756 *  
## banios        27.5658     7.4200   3.715 0.000231 ***
## habitaciones   8.6981     5.5421   1.569 0.117302    
## estrato_4     73.2429    23.2990   3.144 0.001788 ** 
## estrato_5    128.0187    21.7180   5.895 7.77e-09 ***
## estrato_6    259.7367    41.3498   6.281 8.46e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 159 on 416 degrees of freedom
## Multiple R-squared:  0.6393, Adjusted R-squared:  0.6332 
## F-statistic: 105.3 on 7 and 416 DF,  p-value: < 2.2e-16
  • El intercepto en este modelo no tiene interpretación real, porque no es posible que todas las variables tengan valor de cero.

  • área construida (0.70): Por cada metro cuadrado adicional construido incrementa el precio en 0.70, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • parqueadero (13.04): Por cada parqueadero adicional en la vivienda aumenta su precio en 13.04, manteniendo todo lo demás constante. Es un beta es estadísticamente significativo (p < 0.05).

  • baño (27.56): Por cada baño adicional en la vivienda aumenta su precio en 27.56, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • Habitaciones (8.69): No es estadísticamente significativa (p = 0.11), es decir, no se puede afirmar que tiene efecto sobre el precio,manteniendo todo lo demás constante.

  • estrato 4 (73.24): Comparado con el estrato 3, la vivienda al pertenecer al estrato 4 aumenta el precio en 73.24 en promedio, manteniendo todo lo demás constante. Es un beta es estadísticamente significativo (p < 0.05).

  • estrato 5 (128): Comparado con el estrato de referencia (estrato 3) la vivienda al pertenecer al estrato 5 aumenta el precio en 128 en promedio, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • estrato 6 (259): Comparado con el estrato 3, la vivienda al pertenecer al estrato 6 aumenta el precio en 259 en promedio, manteniendo todo lo demás constante. Es un beta es estadísticamente significativo (p < 0.05).

Por otro lado, el R cuadrado ajustado nos indica que el modelo explica aproximadamente 63% de la variabilidad de los precios y es estadísticamente significativo (p-value: < 2.2e-16). En sintesis, el modelo de regresión lineal múltiple muestra que el precio de las viviendas aumenta significativamente con el aumento de la superficie construida, número de baños, parqueaderos y estrato socioeconómico, siendo resultados coherentes con la realidad, ya que mejores características en la vivienda tienden a aumentar su precio, dado que implican mayor diseño, materiales, trabajo y espacios adicionales, convirtiéndose así en propiedades de mayor valor o lujo.

Para mejorar el ajuste del modelo, se podrían considerar nuevas características de la vivienda, así como aspectos del sector, cercanía a servicios o amenidades adicionales. También sería útil agrupar la variable habitaciones en rangos, dado que no resultó significativa, para evaluar si su aporte mejora. Adicionalmente, se recomienda identificar y tratar valores atípicos (outliers) que influyan en los errores, probar transformaciones logarítmicas de las variables y, finalmente, crear un índice de barrio que capture la calidad del vecindario y su impacto en los precios.


Validación de los supuestos del modelo
par(mfrow=c(2,2))
plot(modelo1)

par(mfrow=c(2,2))

En el análisis gráfico se puede visualizar que los residuos presentan dispersión en valores grandes y puntos que se alejan de la línea, reflejando valores atípicos o influyentes. En el Normal Q-Q los puntos se alejan de la línea en los extremos superiores e inferiores, lo que indica que los residuos no siguen una distribución normal. En el Scale-Location hay una tendencia ascendente, lo que indica la presencia de heterocedasticidad, la dispersión de los residuos aumenta a medida que aumentan los valores predichos. En el Residuals vs Leverage se ve que los puntos 521, 136 y 588 tiene un residuo grande y es un punto influyente que puede estar afectando mucho el modelo.


Normalidad
Residual_modelo1 <- residuals(modelo1)
shapiro.test(Residual_modelo1)
## 
##  Shapiro-Wilk normality test
## 
## data:  Residual_modelo1
## W = 0.78229, p-value < 2.2e-16

En el resultado arrojado por el test de normalidad de Shapiro-Wilk se puede apreciar que el p-value es muy pequeño y menor que un nivel de significancia del 0.05, eso significa que hay fuerte evidencia para rechazar la hipótesis de normalidad de los errores.


Homocedasticidad
library(lmtest)
bptest(modelo1)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo1
## BP = 86.833, df = 7, p-value = 5.524e-16

En el resultado arrojado por el test de Breusch-Pagan se puede apreciar que el p-value es pequeño y menor que un nivel de significancia del 0.05, por tanto se concluye la presencia de heterocedasticidad con una significancia del 5%.


Independencia
# Test de Durbin-Watson
dwtest(modelo1)
## 
##  Durbin-Watson test
## 
## data:  modelo1
## DW = 1.9851, p-value = 0.4432
## alternative hypothesis: true autocorrelation is greater than 0
# Test de Breusch-Godfrey
bgtest(modelo1)
## 
##  Breusch-Godfrey test for serial correlation of order up to 1
## 
## data:  modelo1
## LM test = 0.021834, df = 1, p-value = 0.8825

Los p-value obtenidos en las test de Durbin-Watson y Breusch-Godfrey son muy pequeños, menores a 0.05, lo que indica que con un nivel de significancia del 5% se rechaza la hipótesis nula y los errores no son independientes, hay una autocorrelación positiva en los residuos del modelo.


Multicolinealidad
library(car)
vif(modelo1)
##    areaconst parqueaderos       banios habitaciones    estrato_4    estrato_5 
##     1.638839     1.355203     2.178484     1.797343     1.459848     1.900809 
##    estrato_6 
##     1.289356

Se evaluó la presencia de multicolinealidad mediante el Factor de Inflación de la Varianza (VIF). Los valores obtenidos estuvieron entre 1.36 y 2.12, valores menores al umbral de 5, por lo que, se deduce que no hay problemas de multicolinealidad entre las variables independientes del modelo.


En síntesis, la validación del modelo arrojo que, no hay normalidad en los residuos, posible existencia de Valores atípicos o influyentes, heterocedasticidad y no independencia de los errores, se sugiere que, se apliquen transformaciones (logaritmo) a la variable precio y las variables explicativas como por ejemplo logaritmo, realizar tratamiento de valores atípicos (suavizarlos o eliminarlos) o finalmente, elegir otro tipo de modelo.


Predicción

predicciones1 <- predict(modelo1, newdata = test_data1)
resultados1 <- data.frame(Real = test_data1$preciom,
                         Predicho = predicciones1)
head(resultados1, 5)
##   Real Predicho
## 1  320 430.4229
## 2  780 536.8064
## 3  230 202.0543
## 4  500 373.2039
## 5  395 397.3818

Al comparar los precios reales con los estimados por el modelo, se observa que algunas predicciones se acercan bastante al valor real (por ejemplo, fila 5: 395 vs 397), mientras que otras muestran diferencias mayores (fila 2: 780 vs 537). Esto indica que el modelo captura la tendencia general de los precios, pero tiende a subestimar las viviendas muy caras y sobreestimar las más económicas, por lo que puede ser útil ajustar o complementar el modelo para mejorar la precisión en los extremos.


Se realiza la predicción del precio de una vivienda tipo casa ubicada en la zona norte, con las siguientes características: área construida de 200, 1 parqueadero, 2 baños y 4 habitaciones. Para un estrato 4, el precio estimado es de 324.15, mientras que para un estrato 5, el precio estimado aumenta a 379.65.

#Predicción para estrato 4 
predict(modelo1, 
        newdata = data.frame(
        areaconst = 200,
        parqueaderos= 1,
        banios= 2,
        habitaciones=4,
        estrato_4=1,
        estrato_5=0,
        estrato_6=0))
##        1 
## 324.8702
#Predicción para estrato 5
predict(modelo1, 
        newdata = data.frame(
        areaconst = 200,
        parqueaderos= 1,
        banios= 2,
        habitaciones=4,
        estrato_4=0,
        estrato_5=1,
        estrato_6=0))
##       1 
## 379.646

Potenciales ofertas

zona1r$precio_pred <- predict(modelo1, newdata = zona1r)


viviendas1_disponibles <- subset(
  zona1r,
  preciom <= 350 &          
  areaconst >= 200 &
  habitaciones >= 4 &
  banios >= 2 &
  parqueaderos >= 1 &
  estrato_4 ==1
)
ofertas_potenciales <- head(viviendas1_disponibles[order(-viviendas1_disponibles$preciom, -viviendas1_disponibles$areaconst ), ], 5)

ofertas_potenciales[, c("areaconst","habitaciones","banios","parqueaderos","estrato","preciom","precio_pred")] %>%
  kable(
    caption = "Ofertas potenciales de vivienda dentro del presupuesto",
    col.names = c("Área construida", "Habitaciones", "Baños", "Parqueaderos", "Estrato", "Precio real", "Precio estimado"),
    digits = 2
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Ofertas potenciales de vivienda dentro del presupuesto
Área construida Habitaciones Baños Parqueaderos Estrato Precio real Precio estimado
350.0 5 4 1 4 350 493.08
280.0 4 3 2 4 350 421.14
295.0 4 2 2 4 340 404.02
264.5 4 4 2 4 340 437.92
275.0 5 3 2 4 330 426.36

Con base en las características solicitadas para la vivienda y considerando el crédito pre-aprobado máximo de 350 millones de pesos, se identificaron cinco viviendas potenciales que cumplen con los requisitos mínimos de área construida, número de habitaciones, baños, parqueaderos y estrato.

Las viviendas seleccionadas corresponden a aquellas que, dentro del presupuesto disponible, ofrecen las mejores características en términos de área construida y distribución, buscando maximizar el valor de la vivienda que el cliente puede adquirir con el crédito disponible:

• Oferta 1: vivienda de 350 m², con 5 habitaciones, 4 baños y 1 parqueadero, en estrato 4. Tiene un precio real de 350 millones.

• Oferta 2: vivienda de 280 m², con 4 habitaciones, 3 baños y 2 parqueaderos, también en estrato 4. Su precio real es de 350 millones.

• Oferta 3: vivienda de 295 m², con 4 habitaciones, 2 baños y 2 parqueaderos, con un precio real de 340 millones.

• Oferta 4: vivienda de 264.5 m², con 4 habitaciones, 4 baños y 2 parqueaderos, cuyo precio real es de 340 millones.

• Oferta 5: vivienda de 275 m², con 5 habitaciones, 3 baños y 2 parqueaderos, con precio real de 330 millones.

Es importante señalar que, las opciones seleccionadas presentan precios reales dentro del presupuesto, pero el modelo estima valores superiores, lo que sugiere que podrían representar oportunidades de compra atractivas en el mercado

A continuación, se presenta su ubicación geográfica:

library(leaflet)


icono_casa <-awesomeIcons(
  icon = "map-marker",
  iconColor = "white",
  markerColor = "purple",                                  
  library = "fa"
)


leaflet(ofertas_potenciales) %>%
  addTiles() %>%
  addAwesomeMarkers(
    lng = ~longitud,
    lat = ~latitud,
    icon = icono_casa,
    popup = ~paste(
      "<b>Precio:</b>", preciom,
      "<br><b>Área:</b>", areaconst,
      "<br><b>Habitaciones:</b>", habitaciones,
      "<br><b>Baños:</b>", banios
    )
  )




Análisis Vivienda 2

La base de datos original contiene 8322 registros, con el objetivo de realizar el análisis de la vivienda 2 se aplica un filtro a esta base de datos seleccionando solo las viviendas que en tipo tengan el valor “Apartamento” y en zona tengan “Zona Sur”, quedando una base de datos con 2787 registros , y a continuación se presentan los tres (03) primeros:

viviendas2 <- vivienda %>%
  filter(tipo == "Apartamento", zona == "Zona Sur")


kable(head(viviendas2, 3))
id zona piso estrato preciom areaconst parqueaderos banios habitaciones tipo barrio longitud latitud
5098 Zona Sur 05 4 290 96 1 2 3 Apartamento acopi -76.53464 3.44987
698 Zona Sur 02 3 78 40 1 1 2 Apartamento aguablanca -76.50100 3.40000
8199 Zona Sur NA 6 875 194 2 5 3 Apartamento aguacatal -76.55700 3.45900
dim(viviendas2)
## [1] 2787   13


Luego de aplicado el filtro se presentan unas tablas que comprueban la consulta, es decir, que la nueva base solo contenga viviendas que el tipo sea “Apartamento” y estén ubicadas en la “Zona Sur”, donde se puede visualizar que solo se presenta la información que tiene estas características y una frecuencia de 2787:

library(kableExtra)

tabla2 <- as.data.frame(table(viviendas2$tipo))
names(tabla2) <- c("Tipo de vivienda", "Frecuencia")
tabla2
##   Tipo de vivienda Frecuencia
## 1      Apartamento       2787
tabla3 <- as.data.frame(table(viviendas2$zona))
names(tabla3) <- c("Zona", "Frecuencia")
tabla3
##       Zona Frecuencia
## 1 Zona Sur       2787


A continuación, se presenta el mapa interactivo de la ciudad de Santiago de Cali (Colombia) con los puntos de la base de datos tipo Apartamentos y ubicados en la Zona Sur:

library(leaflet)

leaflet(viviendas2) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 4,
    color = "#7B02A8",
    popup = ~tipo
  )

En el anterior, se puede visualizar que hay una gran concentración de puntos en la parte inferior del mapa, siendo esta la zona sur de la ciudad (latitudes más bajas). Cabe destacar que, hay puntos distribuidos por todo el mapa de la ciudad y hay muchos puntos que se ubican en la parte superior del mapa, que no corresponderían a la zona sur del análisis, si no más a la zona norte. Lo que indicaría que hay errores en la clasificación de la zona de algunos apartamentos (algo similar a lo que sucedia con las casas de la zona norte), puesto que a pesar de que la latitud los ubica en la parte superior, fueron registrados en zona como Zona Sur, también puede deberse a que la zona es una clasificación administrativa pero no siempre coincide con la zona de coordenadas reales.

Teniendo en cuenta que la solicitud del cliente indica que una de las condiciones es que el apartamento este ubicado en zona sur y como se evidencio anteriormente, no todas las zonas clasificadas como zona sur lo son realmente, se opta por realizar un análisis de conglomerados (clústeres) para dividir los puntos geográficos en zonas basadas en la concentración real de los datos y hacer el análisis posterior solo con los puntos que por conglomerado correspondan a las zonas con latitud más baja.

Al aplicar este análisis de conglomerados obtenemos dos clústeres, zona 1 y zona 2, cuya latitud promedio es 3.42 y 3.37 respectivamente, lo que indicaría que la zona 2 es la que contiene los apartamentos ubicados más al sur de la ciudad, sobre las cuales se realizarían el análisis, quedando con 1845 registros.

set.seed(123)  # Para reproducibilidad

# Solo latitud y longitud en matriz
coords <- viviendas2 %>% select(longitud, latitud)

# Aplicar k-means para 2 clusters
km <- kmeans(coords, centers = 2)

# Agregar cluster a la base como zona_real
viviendas2$zona_real <- factor(km$cluster, labels = c("Zona 2", "Zona 1"))

aggregate(latitud ~ zona_real, data = viviendas2, FUN = mean)
##   zona_real  latitud
## 1    Zona 2 3.372778
## 2    Zona 1 3.422378
table(viviendas2$zona_real)
## 
## Zona 2 Zona 1 
##   1845    942


Al realizar nuevamente el mapa interactivo de la ciudad Santiago Cali luego de la división por conglomerados y tomando la zona 2 se puede visualizar que los puntos se agrupan solo en la parte inferior del mapa, representando una mejor aproximación a los apartamentos ubicadas en la zona sur que la original:

zona2 <- viviendas2 %>%
  filter(zona_real == "Zona 2")

leaflet(zona2) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 4,
    color = "#7B02A8",
    popup = ~tipo
  )


Continuando con el análisis, se revisan los datos faltantes en la base de datos resultante (zona 2), donde se evidencia que el 23% de la columna piso son datos faltantes, por lo cual, se toma la decisión de eliminar está columna, puesto que no es una caracteristica solicitada por el cliente. De igual manera, se procede a eliminar “id” puesto que es un indicador y no brinda información significativa en el análisis:

colSums(is.na(zona2))
##           id         zona         piso      estrato      preciom    areaconst 
##            0            0          417            0            0            0 
## parqueaderos       banios habitaciones         tipo       barrio     longitud 
##          239            0            0            0            0            0 
##      latitud    zona_real 
##            0            0
zona2r <- zona2[, !(names(zona2) %in% c("piso", "id"))]

names(zona2r)
##  [1] "zona"         "estrato"      "preciom"      "areaconst"    "parqueaderos"
##  [6] "banios"       "habitaciones" "tipo"         "barrio"       "longitud"    
## [11] "latitud"      "zona_real"


Por otro lado, la variable parqueaderos presenta 239 datos faltantes, aligual que en el caso de casas, se observa que sus valores registrados van de 1 a 10, sin incluir el valor 0. Esto indica que en la base de datos no se representa explícitamente las viviendas que no cuentan con parqueadero y es común encontrar viviendas con esta característica:

table(zona2r$parqueaderos, useNA = "always")
## 
##    1    2    3    4   10 <NA> 
## 1003  514   60   28    1  239


Asimismo, se evidencia que los apartamentos con NA en parqueaderos tienen un precio promedio mucho menor que aquellos que, si tienen un valor registrado en esta variable, lo que podría sugerir que ese NA no es aleatorio y se asocia a 0 parqueaderos. Por lo cual, se procede a reemplazar NA de parqueaderos con cero:

aggregate(preciom ~ is.na(parqueaderos), data = zona2r, mean)
##   is.na(parqueaderos)  preciom
## 1               FALSE 339.4707
## 2                TRUE 178.6318
# Se reemplazan los NA en parquederos con cero

zona2r$parqueaderos[is.na(zona2r$parqueaderos)] <-0 

sum(is.na(zona2r$parqueaderos))
## [1] 0


Análisis de correlación entre variables


En la revisión de los estadísticos descriptivos generales de las columnas numéricas de las variables solicitadas en el análisis (precio, área construida, parqueaderos, baños y habitaciones), se identifica que los valores de parqueadero oscila entre 0 y 1, baños y habitaciones fluctuan entre 0 y 7, 0 y 6 respectivamente ; el precio por apartamento presenta un mínimo de 78 y un máximo de 1750; y el área construida varía entre 30 y 1440:

zona2r %>%
  select(preciom, areaconst, parqueaderos, banios, habitaciones) %>%
  summary()
##     preciom         areaconst       parqueaderos        banios    
##  Min.   :  78.0   Min.   : 40.00   Min.   : 0.000   Min.   :0.00  
##  1st Qu.: 190.0   1st Qu.: 65.00   1st Qu.: 1.000   1st Qu.:2.00  
##  Median : 260.0   Median : 85.00   Median : 1.000   Median :2.00  
##  Mean   : 318.6   Mean   : 98.35   Mean   : 1.264   Mean   :2.56  
##  3rd Qu.: 350.0   3rd Qu.:110.00   3rd Qu.: 2.000   3rd Qu.:3.00  
##  Max.   :1750.0   Max.   :932.00   Max.   :10.000   Max.   :7.00  
##   habitaciones  
##  Min.   :0.000  
##  1st Qu.:3.000  
##  Median :3.000  
##  Mean   :2.943  
##  3rd Qu.:3.000  
##  Max.   :6.000


Por otro lado, se calculó la matriz de correlaciones de Pearson entre las variables numéricas y su análisis gráfico, donde se identifica que todas las variables tienen una relación positiva con el precio de los apartamentos, aunque con distinta intensidad. El área construida, baños y parqueaderos tiene una relación fuerte, 0.79, 0.75 y 0.71 respectivamente, lo que indica que mayor superficie, más baños y más parqueaderos, el precio tiende a aumentar de forma consistente, aunque hay cierta variabilidad. La variable habitaciones (0.35) presenta una correlación baja con el precio, reflejando que su relación es más débil y que los datos están más dispersos.

library(kableExtra)
correlacionviviendas2 <- cor(zona2r[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones")], method = "pearson")

kable(correlacionviviendas2, caption = "Matriz de correlación de Pearson") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Matriz de correlación de Pearson
preciom areaconst parqueaderos banios habitaciones
preciom 1.0000000 0.7983390 0.7137791 0.7510829 0.3560771
areaconst 0.7983390 1.0000000 0.6233564 0.6818235 0.4198300
parqueaderos 0.7137791 0.6233564 1.0000000 0.5924295 0.3216734
banios 0.7510829 0.6818235 0.5924295 1.0000000 0.5174674
habitaciones 0.3560771 0.4198300 0.3216734 0.5174674 1.0000000
library(GGally)
ggpairs(zona2r[, c("preciom", "areaconst", "parqueaderos", "banios", "habitaciones")], title="Matriz de dispersión y correlación de Pearson") 


Posteriormente, se hizo el análisis del precio de los apartamentos con la variable categórica estrato, mediante el precio promedio y el diagrama de cajas, donde se evidencia que hay una tendencia positiva entre el estrato y el precio promedio de los apartamentos, lo que indica que los precios aumentan a medida que aumenta el estrato:

library(dplyr)


tabla_resumen <- zona2r %>%
  group_by(estrato) %>%
  summarise(
    precio_prom = mean(preciom, na.rm = TRUE),
    n = n()
  )

tabla_resumen %>%
  kable(
    caption = "Precio promedio por estrato",
    digits = 0,            # número de decimales
    col.names = c("Estrato", "Precio Promedio", "Cantidad de apartamentos")
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Precio promedio por estrato
Estrato Precio Promedio Cantidad de apartamentos
3 140 82
4 200 651
5 295 713
6 592 399
library(plotly)

plot_ly(
  data = zona2r,
  x = ~factor(estrato),
  y = ~preciom,
  type = "box",
  jitter = 0.3,
  marker = list(color = "#7B02A8")
) %>%
  layout(
    title = "Distribución del precio por estrato",
    xaxis = list(title = "Estrato"),
    yaxis = list(title = "Precio")
  )


Asimismo, se realizó un ANOVA para revisar estadísticamente si existen diferencias significativas en los precios promedio de los apartamentos entre los estratos, teniendo las siguientes hipótesis

H0: Las medias del precio son iguales en todos los estratos

H1: Al menos un estrato tiene media diferente

anova_model <- aov(preciom ~ factor(estrato), data = zona2r)
summary(anova_model)
##                   Df   Sum Sq  Mean Sq F value Pr(>F)    
## factor(estrato)    3 41982302 13994101   695.1 <2e-16 ***
## Residuals       1841 37064321    20133                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

El análisis ANOVA muestra que existen diferencias estadísticamente significativas en el precio de los apartamentos entre los distintos estratos. Esto coincide con el gráfico de cajas y la tabla, donde se observa que los estratos más altos tienen precios promedio mayores.



Estimación del modelo de regresión lineal múltiple

Antes de la estimación del modelo de regresión lineal múltiple se hace un proceso de construccion de variables indicadoras (dummy) para la variable estrato, puesto que es categorica y no se puede asumir que hay un incremento lineal monotómico entre los estratos, donde el estrato 3 será la categoría base, es decir, cuando estrato 4, 5 y 6 tomen valor de cero simultaneamente, significa que la casa es estrato 3, a continuación se muestra el ejemplo de como quedaron los tres primeros registros con estas nuevas variables dummys:

library(fastDummies)

zona2r <- fastDummies::dummy_cols(zona2r, 
                                      select_columns = "estrato",
                                      remove_first_dummy = TRUE) # opcional para evitar multicolinealidad

head(zona2r[, c("estrato_4", "estrato_5", "estrato_6")], 3) %>%
  kable(
    caption = "Variables Dummy para Estratos (primeras 3 filas)",
    col.names = c("Estrato 4", "Estrato 5", "Estrato 6")
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Variables Dummy para Estratos (primeras 3 filas)
Estrato 4 Estrato 5 Estrato 6
1 0 0
0 0 0
1 0 0


Se ajusta el modelo de regresión lineal mútliple para explicar el precio de la vivienda en función de la el área construida, los parqueaderos, los baños, las habitaciones y el estrato:

indices2 <- sample(1:nrow(zona2r), size = 0.7 * nrow(zona2r))

train_data2 <- zona2r[indices2, ]
test_data2 <- zona2r[-indices2, ]


modelo2 <- lm(preciom ~ areaconst + parqueaderos + banios + habitaciones + estrato_4 + estrato_5 + estrato_6, data =train_data2)

summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + parqueaderos + banios + habitaciones + 
##     estrato_4 + estrato_5 + estrato_6, data = train_data2)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -711.02  -32.57    1.04   31.18  764.10 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -27.93200   15.68484  -1.781 0.075177 .  
## areaconst      2.03169    0.07492  27.119  < 2e-16 ***
## parqueaderos  40.04457    4.38709   9.128  < 2e-16 ***
## banios        37.35100    4.23309   8.824  < 2e-16 ***
## habitaciones -20.74933    4.73433  -4.383 1.27e-05 ***
## estrato_4     29.59628   11.66575   2.537 0.011298 *  
## estrato_5     44.19214   11.91001   3.711 0.000216 ***
## estrato_6    167.35753   14.04798  11.913  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 83 on 1283 degrees of freedom
## Multiple R-squared:  0.8319, Adjusted R-squared:  0.831 
## F-statistic: 906.9 on 7 and 1283 DF,  p-value: < 2.2e-16
  • El intercepto en este modelo no tiene interpretación real, porque no es posible que todas las variables tengan valor de cero.

  • área construida (2.03): Por cada metro cuadrado adicional construido incrementa el precio en 2.03 en los apartamentos, manteniendo las demás variables constantes. Es un beta estadísticamente significativo (p < 0.05).

  • parqueadero (40.04): Por cada parqueadero adicional en el apartamento aumenta su precio en 40.04, manteniendo todo lo demás constante. Es un beta estadísticamente significativo (p < 0.05).

  • baño ( 37.35): Por cada baño adicional en el apartamento aumenta su precio en 37.35, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • Habitaciones (-20.75): Por cada habitación adicional en el apartamento disminuye su precio en 20.75, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • Estrato 4 (29.60): Comparado con el estrato 3, el apartamento al pertenecer al estrato 4 aumenta el precio en 29.60 en promedio, manteniendo todo lo demás constante. Es un beta es estadísticamente significativo (p < 0.05).

  • estrato 5 (44.19): Comparado con el estrato de referencia (estrato 3) el apartamento al pertenecer al estrato 5 aumenta el precio en 44.19 en promedio, manteniendo las demás variables constantes. Es un beta es estadísticamente significativo (p < 0.05).

  • estrato 6 (167.37): Comparado con el estrato 3, el apartamento al pertenecer al estrato 6 aumenta el precio en 167.37 en promedio, manteniendo todo lo demás constante. Es un beta es estadísticamente significativo (p < 0.05).

Por otro lado, el R cuadrado ajustado nos indica que el modelo explica aproximadamente 83% de la variabilidad de los precios de los apartamentos y es estadísticamente significativo (p-value: < 2.2e-16). En síntesis, el modelo de regresión lineal múltiple muestra que el precio de los apartamentos aumenta significativamente con el aumento de la superficie construida, número de baños, parqueaderos y estrato socioeconómico, siendo resultados coherentes con la realidad, ya que mejores características en la vivienda tienden a aumentar su precio, dado que implican mayor diseño, materiales, trabajo y espacios adicionales, convirtiéndose así en propiedades de mayor valor o lujo.

Las habitaciones presenta una relación negativa, es decir, más habitaciones significan menores precios, aunque intuitivamente no es lógico, se puede deber a que, más habitaciones en un mismo espacio significa habitaciones más pequeñas y prefieren menos habitaciones, pero más amplias.

El modelo tiene muy buen nivel de ajuste y sus betas son significativos, explorar si hay otras variables que se pueda incluir que mejoren su poder explicativo, vamos a revisar si se cumplen los supuestos del modelo:


Validación de los supuestos del modelo
par(mfrow=c(2,2))
plot(modelo2)

par(mfrow=c(2,2))

En el análisis gráfico se puede visualizar que los residuos presentan dispersión en valores grandes y puntos que se alejan de la línea, reflejando valores atípicos o influyentes. En el Normal Q-Q los puntos se alejan de la línea en los extremos superiores e inferiores, lo que indica que los residuos no siguen una distribución normal. En el Scale-Location hay una tendencia ascendente, lo que indica la presencia de heterocedasticidad, la dispersión de los residuos aumenta a medida que aumentan los valores predichos. En el Residuals vs Leverage se ve que los puntos 1524, 598 y 304 tiene un residuo grande y es un punto influyente que puede estar afectando mucho el modelo.


Normalidad
Residual_modelo2 <- residuals(modelo2)
shapiro.test(Residual_modelo2)
## 
##  Shapiro-Wilk normality test
## 
## data:  Residual_modelo2
## W = 0.78017, p-value < 2.2e-16

En el resultado arrojado por el test de normalidad de Shapiro-Wilk se puede apreciar que el p-value es muy pequeño y menor que un nivel de significancia del 0.05, eso significa que hay fuerte evidencia para rechazar la hipótesis de normalidad de los errores.


Homocedasticidad
library(lmtest)
bptest(modelo2)
## 
##  studentized Breusch-Pagan test
## 
## data:  modelo2
## BP = 387.3, df = 7, p-value < 2.2e-16

En el resultado arrojado por el test de Breusch-Pagan se puede apreciar que el p-value es pequeño y menor que un nivel de significancia del 0.05, por tanto se concluye la presencia de heterocedasticidad con una significancia del 5%.


Independencia
# Test de Durbin-Watson
dwtest(modelo2)
Durbin-Watson test

data: modelo2 DW = 2.0104, p-value = 0.5743 alternative hypothesis: true autocorrelation is greater than 0

# Test de Breusch-Godfrey
bgtest(modelo2)
Breusch-Godfrey test for serial correlation of order up to 1

data: modelo2 LM test = 0.038176, df = 1, p-value = 0.8451

Los p-value obtenidos en las test de Durbin-Watson y Breusch-Godfrey son muy pequeños, menores a 0.05, lo que indica que con un nivel de significancia del 5% se rechaza la hipótesis nula y los errores no son independientes, hay una autocorrelación positiva en los residuos del modelo.


Multicolinealidad
library(car)
vif(modelo2)
##    areaconst parqueaderos       banios habitaciones    estrato_4    estrato_5 
##     2.597086     2.157472     2.870739     1.508443     5.905212     6.331508 
##    estrato_6 
##     5.914481

Se evaluó la presencia de multicolinealidad mediante el Factor de Inflación de la Varianza (VIF). Los valores obtenidos oscilaron entre 1.44 y 6.35. La mayoría de las variables presentan valores inferiores a 5, lo que indica que no existe un problema fuerte de multicolinealidad entre las variables explicativas. Sin embargo, las variables asociadas a los estratos (estrato 4, 5 y 6) presentan valores ligeramente superiores a este umbral, lo cual puede deberse a la relación existente entre las variables dummy que representan las categorías del estrato. Aun así, estos valores se mantienen por debajo de 10, por lo que no se considera un problema grave para la estimación del modelo


En síntesis, la validación del modelo arrojo que, no hay normalidad en los residuos, posible existencia de Valores atípicos o influyentes, heterocedasticidad y no independencia de los errores, se sugiere que, se apliquen transformaciones (logaritmo) a la variable precio y las variables explicativas como por ejemplo logaritmo, realizar tratamiento de valores atípicos (suavizarlos o eliminarlos) o finalmente, elegir otro tipo de modelo.


Predicción

predicciones2 <- predict(modelo2, newdata = test_data2)
resultados2 <- data.frame(Real = test_data2$preciom,
                         Predicho = predicciones2)
head(resultados2, 5)
##   Real  Predicho
## 1  220 184.77999
## 2  165 136.01950
## 3  125  79.23066
## 4  600 627.19367
## 5  680 622.51948

Al comparar los precios reales con los estimados por el modelo se evidencia que logra aproximarse bastante a los precios reales de las viviendas, aunque tiende a subestimar algunas propiedades de menor valor y sobrestimar algunas de mayor valor. Esto indica que, aunque el modelo capta la tendencia general de los precios, existen desviaciones puntuales que podrían mejorar ajustando variables o revisando valores atípicos.


Se realiza la predicción del precio de una vivienda tipo casa ubicada en la zona norte, con las siguientes características: área construida de 300, 3 parqueadero, 3 baños y 5 habitaciones. Para un estrato 5, el precio estimado es de 687, mientras que para un estrato 6, el precio estimado aumenta a 815.

#Predicción para estrato 5 
predict(modelo2, 
        newdata = data.frame(
        areaconst = 300,
        parqueaderos= 3,
        banios= 3,
        habitaciones=5,
        estrato_4=0,
        estrato_5=1,
        estrato_6=0))
##        1 
## 754.2063
#Predicción para estrato 6
predict(modelo2, 
        newdata = data.frame(
        areaconst = 300,
        parqueaderos= 3,
        banios= 3,
        habitaciones=5,
        estrato_4=0,
        estrato_5=0,
        estrato_6=1))
##        1 
## 877.3717

Potenciales ofertas

zona2r$precio_pred <- predict(modelo2, newdata = zona2r)


viviendas2_disponibles <- subset(
  zona2r,
  preciom <= 850 &          
  areaconst >= 300 &
  habitaciones >= 4 &
  banios >= 3 &
  parqueaderos >= 2 &
  estrato_5 ==1
)
# Convertir a data.frame para evitar problemas

ofertas_potenciales1 <- head(viviendas2_disponibles[order(-viviendas2_disponibles$preciom, -viviendas2_disponibles$areaconst ), ], 5)

ofertas_potenciales1[, c("areaconst","habitaciones","banios","parqueaderos","estrato","preciom","precio_pred")] %>%
  kable(
    caption = "Ofertas potenciales de vivienda dentro del presupuesto reduciendo número de habitaciones y parqueadero",
    col.names = c("Area construida", "Habitaciones", "Banos", "Parqueaderos", "Estrato", "Precio real", "Precio estimado"),
    digits = 2
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Ofertas potenciales de vivienda dentro del presupuesto reduciendo número de habitaciones y parqueadero
Area construida Habitaciones Banos Parqueaderos Estrato Precio real Precio estimado
486 4 4 2 5 690 1150.16
600 5 4 2 5 650 1361.02

Con base en las características solicitadas para la vivienda y considerando el crédito pre-aprobado máximo de 850 millones de pesos,no se encontró ningun apartamento en la zona sur que con ese presupuesto cumpliera con todas las características, por lo cual, se proponen dos alternativas derivadas de reducir el número de habitaciones y parqueaderos, que estaría dentro del rango estimado:

• Oferta 1: vivienda de 486 m², con 4 habitaciones, 4 baños y 2 parqueadero, en estrato 5. Tiene un precio real de 690 millones.

• Oferta 2: vivienda de 600 m², con 5 habitaciones, 4 baños y 2 parqueaderos, también en estrato 5. Su precio real es de 650 millones.

Es importante señalar que, las opciones seleccionadas presentan características muy cercanas a las solicitadas y estan dentro del rango de precio, tambien se propone si desean aumentar el valor del credito pre aprobado les permitira acceder a todas las características deseadas.

A continuación, se presenta su ubicación geográfica:

library(leaflet)


icono_casa <-awesomeIcons(
  icon = "map-marker",
  iconColor = "white",
  markerColor = "purple",                                  
  library = "fa"
)


leaflet(ofertas_potenciales1) %>%
  addTiles() %>%
  addAwesomeMarkers(
    lng = ~longitud,
    lat = ~latitud,
    icon = icono_casa,
    popup = ~paste(
      "<b>Precio:</b>", preciom,
      "<br><b>Área:</b>", areaconst,
      "<br><b>Habitaciones:</b>", habitaciones,
      "<br><b>Baños:</b>", banios
    )
  )



Conclusiones


La estimación del precio de viviendas, tanto casas como apartamentos, es un fenómeno complejo que depende de múltiples factores y características específicas de cada inmueble. La utilización de modelos de regresión lineal para estas estimaciones presenta desafíos importantes, dado que los datos suelen tener propiedades que dificultan el cumplimiento de los supuestos del modelo.

En ambos modelos, el precio de la vivienda aumenta con el área construida, número de baños, parqueaderos y estrato socioeconómico, reflejando que propiedades más amplias y mejor ubicadas valen más. En los apartamentos, se observó que más habitaciones reducen ligeramente el precio, probablemente por la reducción de tamaño por habitación.

El modelo para apartamentos mostró un R ajustado superior (83%) indicado mejor capacidad de predicción que el modelo para casas (explico el 63%), además de que hay mayor homogeneidad apartamentos zona sur y mayor heterogeneidad casa zona norte. Para las casas, el estrato socioeconómico demostró tener un efecto más marcado sobre el valor.

En el análisis se identificó que ninguno de los modelos construidos cumple completamente con los supuestos de normalidad, homocedasticidad e independencia de los residuos. Además, la presencia de valores atípicos o influyentes impactan significativamente la estimación, lo que implica que las predicciones pueden ser precisas para la mayoría de las viviendas, pero pueden subestimar propiedades muy caras o sobrestimar las más económicas.

Los modelos ayudan a recomendar las mejores opciones de compra dentro de su presupuesto. Por ejemplo, para un crédito de 350 millones, se pueden priorizar casas con mayor área construida o distribución óptima de habitaciones y baños. En los requisititos de los apartamentos, el presupuesto no alcanza para todas las características deseadas, por lo que se propusieron ajustes en habitaciones o parqueaderos, o aumento en el crédito para acceder a la vivienda ideal

Para los siguientes modelos se recomienda: • Explorar transformaciones logarítmicas a las variables. • Investigar la inclusión de variables adicionales. • Realizar una segmentación o agrupamiento de variables (por ejemplo, habitaciones en rangos) cuando algunas variables individuales no sean significativas. • Aplicar métodos robustos para manejar la heterocedasticidad. • Realizar un análisis más exhaustivo de valores atípicos y puntos influyentes que podrían distorsionar la estimación.