1 Introducción

Este informe tiene como objetivo apoyar a María, propietaria de la agencia inmobiliaria C&A (Casas y Apartamentos) en Cali, para dar respuesta a la solicitud de una compañía internacional que desea adquirir dos viviendas para ubicar a sus empleados en la ciudad.

Para ello se emplean técnicas de regresión lineal múltiple, que permiten estimar el precio de las viviendas a partir de características observables como el área construida, el estrato, el número de habitaciones, baños y parqueaderos.

Las dos solicitudes son:

Característica Vivienda 1 (Casa) Vivienda 2 (Apartamento)
Tipo Casa Apartamento
Área construida 200 m² 300 m²
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

2 Carga de datos y paquetes

# Instalación (solo la primera vez — comentar después de instalar)
# install.packages("devtools")
# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)
# install.packages(c("plotly", "leaflet", "dplyr", "ggplot2",
#                     "car", "lmtest", "nortest", "corrplot", # "kableExtra"))

library(paqueteMODELOS)
library(dplyr)
library(ggplot2)
library(plotly)
library(leaflet)
library(car)
library(lmtest)
library(nortest)
library(kableExtra)
library(corrplot)

data("vivienda")

3 PARTE 1 – Vivienda 1: Casa en la Zona Norte

3.1 Filtro y depuración de datos

Se filtra la base de datos para conservar únicamente las casas ubicadas en la Zona Norte de Cali.

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

# Dimensiones de la base filtrada
cat("Registros totales en la base filtrada:", nrow(base1), "\n")
## Registros totales en la base filtrada: 722
cat("Variables disponibles:", ncol(base1), "\n")
## Variables disponibles: 13

3.1.1 Primeros 3 registros

base1 %>%
  head(3) %>%
  kable(caption = "Primeros 3 registros – Casas Zona Norte") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
Primeros 3 registros – Casas Zona Norte
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

3.1.2 Verificación del filtro

# Distribución por tipo
table(base1$tipo) %>%
  kable(col.names = c("Tipo", "Frecuencia"),
        caption = "Distribución por tipo de vivienda") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Distribución por tipo de vivienda
Tipo Frecuencia
Casa 722
# Distribución por zona
table(base1$zona) %>%
  kable(col.names = c("Zona", "Frecuencia"),
        caption = "Distribución por zona") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Distribución por zona
Zona Frecuencia
Zona Norte 722

3.1.3 Mapa de las casas en Zona Norte

leaflet(base1) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 5,
    color = "#2196F3",
    fillOpacity = 0.7,
    popup = ~paste0(
      "<b>Barrio:</b> ", barrio, "<br>",
      "<b>Estrato:</b> ", estrato, "<br>",
      "<b>Precio:</b> $", preciom, " M<br>",
      "<b>Área:</b> ", areaconst, " m²"
    )
  ) %>%
  addLegend("bottomright",
            colors = "#2196F3",
            labels = "Casas – Zona Norte",
            title = "C&A Cali")

Observación: Al revisar el mapa, puede notarse que algunos puntos se ubican ligeramente fuera del perímetro típicamente asociado a la Zona Norte de Cali. Esto puede deberse a inconsistencias en la clasificación de zonas dentro de la base de datos, a la presencia de barrios limítrofes que comparten características de más de una zona administrativa, o a errores en las coordenadas geográficas registradas. Se recomienda validar estos registros con la nomenclatura oficial del municipio antes de utilizarlos en la estimación.


3.2 Análisis Exploratorio de Datos (EDA)

3.2.1 Estadísticas descriptivas

base1 %>%
  select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
  summary() %>%
  kable(caption = "Estadísticas descriptivas – Variables numéricas") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Estadísticas descriptivas – Variables numéricas
preciom areaconst estrato banios habitaciones parqueaderos
Min. : 89.0 Min. : 30.0 Min. :3.000 Min. : 0.000 Min. : 0.000 Min. : 1.000
1st Qu.: 261.2 1st Qu.: 140.0 1st Qu.:3.000 1st Qu.: 2.000 1st Qu.: 3.000 1st Qu.: 1.000
Median : 390.0 Median : 240.0 Median :4.000 Median : 3.000 Median : 4.000 Median : 2.000
Mean : 445.9 Mean : 264.9 Mean :4.202 Mean : 3.555 Mean : 4.507 Mean : 2.182
3rd Qu.: 550.0 3rd Qu.: 336.8 3rd Qu.:5.000 3rd Qu.: 4.000 3rd Qu.: 5.000 3rd Qu.: 3.000
Max. :1940.0 Max. :1440.0 Max. :6.000 Max. :10.000 Max. :10.000 Max. :10.000
NA NA NA NA NA NA’s :287

3.2.2 Distribución del precio

p1 <- plot_ly(base1, x = ~preciom, type = "histogram",
              marker = list(color = "#1976D2", line = list(color = "white", width = 0.5)),
              nbinsx = 25) %>%
  layout(title = "Distribución del Precio – Casas Zona Norte",
         xaxis = list(title = "Precio (millones COP)"),
         yaxis = list(title = "Frecuencia"))
p1

La distribución del precio presenta una asimetría positiva, lo que indica la presencia de viviendas con precios muy elevados que sesgan la media hacia la derecha. La mayoría de las casas se concentra en rangos de precio más accesibles.

3.2.3 Precio vs Área construida

p2 <- plot_ly(base1, x = ~areaconst, y = ~preciom,
              type = "scatter", mode = "markers",
              color = ~factor(estrato),
              marker = list(size = 8, opacity = 0.7),
              text = ~paste("Barrio:", barrio, "<br>Precio: $", preciom, "M<br>Área:", areaconst, "m²")) %>%
  layout(title = "Precio vs Área Construida (color = Estrato)",
         xaxis = list(title = "Área Construida (m²)"),
         yaxis = list(title = "Precio (millones COP)"),
         legend = list(title = list(text = "Estrato")))
p2

Se observa una relación positiva entre el área construida y el precio: a mayor área, mayor precio. Adicionalmente, el estrato actúa como un diferenciador importante, pues las viviendas de estratos más altos tienden a ubicarse en los segmentos de precio más elevado para una misma área.

3.2.4 Precio vs Estrato

p3 <- plot_ly(base1, x = ~factor(estrato), y = ~preciom,
              type = "box",
              color = ~factor(estrato)) %>%
  layout(title = "Precio por Estrato – Casas Zona Norte",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio (millones COP)"))
p3

El precio promedio aumenta consistentemente con el estrato, lo cual es coherente con la realidad del mercado inmobiliario colombiano. Los estratos 5 y 6 presentan mayor dispersión, reflejando mayor heterogeneidad en las características de las viviendas más costosas.

3.2.5 Precio vs Número de Baños

p4 <- plot_ly(base1, x = ~banios, y = ~preciom,
              type = "scatter", mode = "markers",
              marker = list(color = "#43A047", size = 8, opacity = 0.6),
              text = ~paste("Baños:", banios, "<br>Precio: $", preciom, "M")) %>%
  layout(title = "Precio vs Número de Baños",
         xaxis = list(title = "Número de Baños"),
         yaxis = list(title = "Precio (millones COP)"))
p4

3.2.6 Precio vs Habitaciones

p5 <- plot_ly(base1, x = ~habitaciones, y = ~preciom,
              type = "scatter", mode = "markers",
              marker = list(color = "#FB8C00", size = 8, opacity = 0.6),
              text = ~paste("Habitaciones:", habitaciones, "<br>Precio: $", preciom, "M")) %>%
  layout(title = "Precio vs Número de Habitaciones",
         xaxis = list(title = "Número de Habitaciones"),
         yaxis = list(title = "Precio (millones COP)"))
p5

3.2.7 Matriz de correlación

vars_cor <- base1 %>%
  select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
  na.omit()

cor_matrix <- cor(vars_cor)

corrplot(cor_matrix,
         method = "color",
         type = "upper",
         addCoef.col = "black",
         tl.cex = 0.9,
         number.cex = 0.8,
         title = "Matriz de Correlación – Variables Numéricas",
         mar = c(0, 0, 2, 0))

La variable areaconst presenta la correlación más alta con preciom, seguida de estrato. Las variables banios y habitaciones también muestran correlaciones positivas moderadas. Se observa cierta correlación entre las variables explicativas (p. ej., areaconst y banios), lo que podría sugerir problemas de multicolinealidad a considerar en el modelo.


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

3.3.1 Modelo estimado

modelo1 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
              data = base1)

summary(modelo1)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = base1)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -784.29  -77.56  -16.03   47.67  978.61 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -238.17090   44.40551  -5.364 1.34e-07 ***
## areaconst       0.67673    0.05281  12.814  < 2e-16 ***
## estrato        80.63495    9.82632   8.206 2.70e-15 ***
## habitaciones    7.64511    5.65873   1.351    0.177    
## parqueaderos   24.00598    5.86889   4.090 5.14e-05 ***
## banios         18.89938    7.48800   2.524    0.012 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 155.1 on 429 degrees of freedom
##   (287 observations deleted due to missingness)
## Multiple R-squared:  0.6041, Adjusted R-squared:  0.5995 
## F-statistic: 130.9 on 5 and 429 DF,  p-value: < 2.2e-16

3.3.2 Interpretación de coeficientes

coef_df <- as.data.frame(summary(modelo1)$coefficients)
coef_df$Variable <- rownames(coef_df)
coef_df <- coef_df[, c("Variable", "Estimate", "Std. Error", "t value", "Pr(>|t|)")]
names(coef_df) <- c("Variable", "Estimado", "Error Estándar", "t", "p-valor")

coef_df %>%
  kable(digits = 4, caption = "Coeficientes del Modelo de Regresión – Base 1") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(which(coef_df$`p-valor` < 0.05), background = "#E8F5E9")
Coeficientes del Modelo de Regresión – Base 1
Variable Estimado Error Estándar t p-valor
(Intercept) (Intercept) -238.1709 44.4055 -5.3635 0.0000
areaconst areaconst 0.6767 0.0528 12.8140 0.0000
estrato estrato 80.6349 9.8263 8.2060 0.0000
habitaciones habitaciones 7.6451 5.6587 1.3510 0.1774
parqueaderos parqueaderos 24.0060 5.8689 4.0904 0.0001
banios banios 18.8994 7.4880 2.5240 0.0120

Interpretación de los coeficientes estadísticamente significativos (p < 0.05):

  • Intercepto: Representa el precio base estimado cuando todas las variables son cero. Su interpretación directa no es relevante en el contexto del negocio.
  • areaconst: Por cada metro cuadrado adicional en el área construida, el precio de la casa aumenta en promedio X millones de pesos, manteniendo lo demás constante. Esto es coherente: casas más grandes tienen mayor valor comercial.
  • estrato: El precio aumenta en promedio X millones por cada nivel adicional de estrato. Refleja el mayor valor de las zonas de estrato alto en el mercado inmobiliario de Cali.
  • parqueaderos: Cada parqueadero adicional eleva el precio promedio de la vivienda. Tiene sentido dado que los parqueaderos representan un activo valorado en el mercado colombiano.
  • Variables no significativas: Aquellas con p-valor > 0.05 no muestran evidencia estadística suficiente de influir en el precio dentro de este subconjunto de datos. Se podría explorar eliminarlas o transformarlas.

Coeficiente R²: El modelo explica aproximadamente el 60.4% de la variabilidad en el precio de las casas de la Zona Norte. Este nivel de ajuste es aceptable para datos inmobiliarios reales, donde existen muchas variables cualitativas difíciles de capturar (estado de la construcción, acabados, vista, etc.). Para mejorar el ajuste se podría incluir variables adicionales como el barrio (como variable categórica), el piso, o transformaciones logarítmicas del precio y el área.


3.4 Validación de supuestos

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

par(mfrow = c(1, 1))

3.4.1 1. Linealidad

plot(fitted(modelo1), residuals(modelo1),
     main = "Residuales vs Valores Ajustados",
     xlab = "Valores ajustados", ylab = "Residuales",
     pch = 20, col = "#1565C0")
abline(h = 0, col = "red", lwd = 2)

Se inspecciona si los residuales se distribuyen aleatoriamente alrededor de cero. La presencia de patrones sistemáticos indicaría no linealidad en la relación.

3.4.2 2. Normalidad de residuales

# Test de Shapiro-Wilk
shapiro_test <- shapiro.test(residuals(modelo1))
cat("Test de Shapiro-Wilk:\n")
## Test de Shapiro-Wilk:
cat("  Estadístico W:", round(shapiro_test$statistic, 4), "\n")
##   Estadístico W: 0.8525
cat("  p-valor:", round(shapiro_test$p.value, 4), "\n\n")
##   p-valor: 0
# Q-Q Plot interactivo
residuales <- residuals(modelo1)
qq_data <- qqnorm(residuales, plot.it = FALSE)
p_qq <- plot_ly() %>%
  add_markers(x = qq_data$x, y = qq_data$y,
              name = "Residuales",
              marker = list(color = "#1976D2", size = 6)) %>%
  add_lines(x = range(qq_data$x),
            y = range(qq_data$x) * sd(residuales) + mean(residuales),
            name = "Línea teórica",
            line = list(color = "red")) %>%
  layout(title = "Q-Q Plot – Normalidad de Residuales",
         xaxis = list(title = "Cuantiles teóricos"),
         yaxis = list(title = "Cuantiles de la muestra"))
p_qq

El test de Shapiro-Wilk rechaza la hipótesis de normalidad (p < 0.05). Esto puede deberse a la presencia de valores atípicos o a la distribución sesgada del precio. Una posible solución es aplicar una transformación logarítmica a la variable respuesta.

3.4.3 3. Homocedasticidad

bp_test <- bptest(modelo1)
cat("Test de Breusch-Pagan:\n")
## Test de Breusch-Pagan:
cat("  Estadístico:", round(bp_test$statistic, 4), "\n")
##   Estadístico: 80.2808
cat("  p-valor:", round(bp_test$p.value, 4), "\n")
##   p-valor: 0

Se rechaza el supuesto de homocedasticidad (p < 0.05), lo que indica varianza no constante en los errores (heterocedasticidad). Para corregirlo se podrían usar errores estándar robustos o transformar la variable respuesta.

3.4.4 4. Independencia de errores

dw_test <- dwtest(modelo1)
cat("Test de Durbin-Watson:\n")
## Test de Durbin-Watson:
cat("  Estadístico DW:", round(dw_test$statistic, 4), "\n")
##   Estadístico DW: 1.7615
cat("  p-valor:", round(dw_test$p.value, 4), "\n")
##   p-valor: 0.0055

Se detecta autocorrelación en los residuales. Dado que los datos son de corte transversal (no de series de tiempo), este resultado podría deberse a agrupaciones espaciales en los datos. Se sugiere explorar modelos espaciales.

3.4.5 5. Multicolinealidad

vif_vals <- vif(modelo1)
vif_df <- data.frame(Variable = names(vif_vals), VIF = round(vif_vals, 3))
vif_df %>%
  kable(caption = "Factor de Inflación de Varianza (VIF)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(which(vif_df$VIF > 5), background = "#FFEBEE")
Factor de Inflación de Varianza (VIF)
Variable VIF
areaconst areaconst 1.461
estrato estrato 1.308
habitaciones habitaciones 1.721
parqueaderos parqueaderos 1.226
banios banios 1.967

Variables con VIF > 5 indicarían multicolinealidad moderada; VIF > 10 indicaría multicolinealidad severa. En este caso, todos los valores son menores a 5, por lo que no hay evidencia de multicolinealidad problemática.


3.5 Predicción – Vivienda 1

Se predice el precio de una casa en la Zona Norte con las siguientes características: - Área construida: 200 m²
- Parqueaderos: 1
- Baños: 2
- Habitaciones: 4
- Estrato: 4 (se evalúan estrato 4 y 5)

# Nueva observación - Estrato 4
nueva_casa_e4 <- data.frame(
  areaconst    = 200,
  estrato      = 4,
  habitaciones = 4,
  parqueaderos = 1,
  banios       = 2
)

# Nueva observación - Estrato 5
nueva_casa_e5 <- data.frame(
  areaconst    = 200,
  estrato      = 5,
  habitaciones = 4,
  parqueaderos = 1,
  banios       = 2
)

pred_e4 <- predict(modelo1, newdata = nueva_casa_e4, interval = "prediction", level = 0.95)
pred_e5 <- predict(modelo1, newdata = nueva_casa_e5, interval = "prediction", level = 0.95)

# Tabla resumen
pred_tabla <- data.frame(
  Escenario = c("Estrato 4", "Estrato 5"),
  `Precio estimado` = round(c(pred_e4[1], pred_e5[1]), 1),
  `Límite inferior 95%` = round(c(pred_e4[2], pred_e5[2]), 1),
  `Límite superior 95%` = round(c(pred_e4[3], pred_e5[3]), 1)
)

pred_tabla %>%
  kable(caption = "Predicción del precio – Vivienda 1 (millones COP)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Predicción del precio – Vivienda 1 (millones COP)
Escenario Precio.estimado Límite.inferior.95. Límite.superior.95.
Estrato 4 312.1 6.2 618.0
Estrato 5 392.7 86.2 699.3

Interpretación: El modelo estima que una casa con las características solicitadas tendría un precio aproximado de 312.1 millones (estrato 4) o 392.7 millones (estrato 5). El crédito preaprobado de $350 millones resulta suficiente para el escenario de estrato 4.


3.6 Ofertas potenciales – Vivienda 1

Se filtran las casas de la Zona Norte con precio ≤ 350 millones que más se asemejan a la solicitud:

ofertas1 <- base1 %>%
  filter(preciom <= 350,
         estrato %in% c(4, 5),
         habitaciones >= 3,
         banios >= 2) %>%
  arrange(abs(areaconst - 200)) %>%
  head(10)

ofertas1 %>%
  select(barrio, zona, estrato, preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  kable(caption = "Potenciales ofertas – Vivienda 1 (≤ $350 M, Zona Norte)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Potenciales ofertas – Vivienda 1 (≤ $350 M, Zona Norte)
barrio zona estrato preciom areaconst habitaciones banios parqueaderos
el bosque Zona Norte 5 350 200 4 3 3
la flora Zona Norte 5 320 200 4 4 2
la merced Zona Norte 4 320 200 4 4 2
el bosque Zona Norte 5 335 202 5 4 1
el bosque Zona Norte 5 350 203 5 2 2
vipasa Zona Norte 5 340 203 4 3 2
vipasa Zona Norte 5 300 205 6 5 2
el bosque Zona Norte 4 340 207 4 4 NA
el bosque Zona Norte 5 340 208 4 6 NA
acopi Zona Norte 4 275 190 3 2 NA

3.6.1 Mapa de ofertas potenciales

top_ofertas1 <- ofertas1 %>% head(5)

leaflet(top_ofertas1) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 8,
    color = "#E53935",
    fillColor = "#EF9A9A",
    fillOpacity = 0.8,
    weight = 2,
    popup = ~paste0(
      "<b>Barrio:</b> ", barrio, "<br>",
      "<b>Precio:</b> $", preciom, " M<br>",
      "<b>Área:</b> ", areaconst, " m²<br>",
      "<b>Estrato:</b> ", estrato, "<br>",
      "<b>Habitaciones:</b> ", habitaciones, "<br>",
      "<b>Baños:</b> ", banios
    ),
    label = ~paste0("$", preciom, "M – ", barrio)
  ) %>%
  addLegend("bottomright",
            colors = "#E53935",
            labels = "Ofertas potenciales ≤ $350M",
            title = "Vivienda 1")

Análisis de ofertas: Se presentan las 5 viviendas de la base de datos que mejor satisfacen las condiciones de la solicitud: casas en la Zona Norte con estrato 4 o 5, al menos 3 habitaciones, 2 baños y precio igual o inferior a $350 millones. Se recomienda a María visitar personalmente estas propiedades para verificar acabados y estado de conservación, factores que no están incluidos en el modelo pero tienen un impacto significativo en el valor comercial final.


4 PARTE 2 – Vivienda 2: Apartamento en la Zona Sur

4.1 Filtro y depuración de datos

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

cat("Registros en base2 (Apartamentos – Zona Sur):", nrow(base2), "\n")
## Registros en base2 (Apartamentos – Zona Sur): 2787

4.1.1 Primeros 3 registros

base2 %>%
  head(3) %>%
  kable(caption = "Primeros 3 registros – Apartamentos Zona Sur") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Primeros 3 registros – Apartamentos Zona Sur
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

4.1.2 Verificación del filtro

table(base2$tipo) %>%
  kable(col.names = c("Tipo", "Frecuencia"),
        caption = "Distribución por tipo") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Distribución por tipo
Tipo Frecuencia
Apartamento 2787
table(base2$zona) %>%
  kable(col.names = c("Zona", "Frecuencia"),
        caption = "Distribución por zona") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Distribución por zona
Zona Frecuencia
Zona Sur 2787

4.1.3 Mapa de los apartamentos en Zona Sur

leaflet(base2) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 5,
    color = "#7B1FA2",
    fillOpacity = 0.7,
    popup = ~paste0(
      "<b>Barrio:</b> ", barrio, "<br>",
      "<b>Estrato:</b> ", estrato, "<br>",
      "<b>Precio:</b> $", preciom, " M<br>",
      "<b>Área:</b> ", areaconst, " m²"
    )
  ) %>%
  addLegend("bottomright",
            colors = "#7B1FA2",
            labels = "Apartamentos – Zona Sur",
            title = "C&A Cali")

Observación: Al igual que en la base 1, pueden presentarse puntos fuera del límite estricto de la Zona Sur, posiblemente por las mismas razones mencionadas anteriormente: barrios limítrofes, errores en coordenadas o clasificaciones ambiguas en la fuente de datos.


4.2 Análisis Exploratorio de Datos (EDA) – Base 2

4.2.1 Estadísticas descriptivas

base2 %>%
  select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
  summary() %>%
  kable(caption = "Estadísticas descriptivas – Apartamentos Zona Sur") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Estadísticas descriptivas – Apartamentos Zona Sur
preciom areaconst estrato banios habitaciones parqueaderos
Min. : 75.0 Min. : 40.00 Min. :3.00 Min. :0.000 Min. :0.000 Min. : 1.000
1st Qu.: 175.0 1st Qu.: 65.00 1st Qu.:4.00 1st Qu.:2.000 1st Qu.:3.000 1st Qu.: 1.000
Median : 245.0 Median : 85.00 Median :5.00 Median :2.000 Median :3.000 Median : 1.000
Mean : 297.3 Mean : 97.47 Mean :4.63 Mean :2.488 Mean :2.966 Mean : 1.415
3rd Qu.: 335.0 3rd Qu.:110.00 3rd Qu.:5.00 3rd Qu.:3.000 3rd Qu.:3.000 3rd Qu.: 2.000
Max. :1750.0 Max. :932.00 Max. :6.00 Max. :8.000 Max. :6.000 Max. :10.000
NA NA NA NA NA NA’s :406

4.2.2 Precio vs Área construida

p6 <- plot_ly(base2, x = ~areaconst, y = ~preciom,
              type = "scatter", mode = "markers",
              color = ~factor(estrato),
              marker = list(size = 8, opacity = 0.7),
              text = ~paste("Barrio:", barrio, "<br>Precio: $", preciom, "M<br>Área:", areaconst, "m²")) %>%
  layout(title = "Precio vs Área Construida – Apartamentos Zona Sur",
         xaxis = list(title = "Área Construida (m²)"),
         yaxis = list(title = "Precio (millones COP)"),
         legend = list(title = list(text = "Estrato")))
p6

4.2.3 Precio por estrato

p7 <- plot_ly(base2, x = ~factor(estrato), y = ~preciom,
              type = "box", color = ~factor(estrato)) %>%
  layout(title = "Precio por Estrato – Apartamentos Zona Sur",
         xaxis = list(title = "Estrato"),
         yaxis = list(title = "Precio (millones COP)"))
p7

4.2.4 Matriz de correlación

vars_cor2 <- base2 %>%
  select(preciom, areaconst, estrato, banios, habitaciones, parqueaderos) %>%
  na.omit()

cor_matrix2 <- cor(vars_cor2)

corrplot(cor_matrix2,
         method = "color",
         type = "upper",
         addCoef.col = "black",
         tl.cex = 0.9,
         number.cex = 0.8,
         title = "Matriz de Correlación – Apartamentos Zona Sur",
         mar = c(0, 0, 2, 0))


4.3 Modelo de regresión – Apartamentos Zona Sur

modelo2 <- lm(preciom ~ areaconst + estrato + habitaciones + parqueaderos + banios,
              data = base2)

summary(modelo2)
## 
## Call:
## lm(formula = preciom ~ areaconst + estrato + habitaciones + parqueaderos + 
##     banios, data = base2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1092.02   -42.28    -1.33    40.58   926.56 
## 
## Coefficients:
##                Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -261.62501   15.63220 -16.736  < 2e-16 ***
## areaconst       1.28505    0.05403  23.785  < 2e-16 ***
## estrato        60.89709    3.08408  19.746  < 2e-16 ***
## habitaciones  -24.83693    3.89229  -6.381 2.11e-10 ***
## parqueaderos   72.91468    3.95797  18.422  < 2e-16 ***
## banios         50.69675    3.39637  14.927  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 98.02 on 2375 degrees of freedom
##   (406 observations deleted due to missingness)
## Multiple R-squared:  0.7485, Adjusted R-squared:  0.748 
## F-statistic:  1414 on 5 and 2375 DF,  p-value: < 2.2e-16

4.3.1 Coeficientes

coef_df2 <- as.data.frame(summary(modelo2)$coefficients)
coef_df2$Variable <- rownames(coef_df2)
coef_df2 <- coef_df2[, c("Variable", "Estimate", "Std. Error", "t value", "Pr(>|t|)")]
names(coef_df2) <- c("Variable", "Estimado", "Error Estándar", "t", "p-valor")

coef_df2 %>%
  kable(digits = 4, caption = "Coeficientes del Modelo – Apartamentos Zona Sur") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(which(coef_df2$`p-valor` < 0.05), background = "#EDE7F6")
Coeficientes del Modelo – Apartamentos Zona Sur
Variable Estimado Error Estándar t p-valor
(Intercept) (Intercept) -261.6250 15.6322 -16.7363 0
areaconst areaconst 1.2850 0.0540 23.7853 0
estrato estrato 60.8971 3.0841 19.7457 0
habitaciones habitaciones -24.8369 3.8923 -6.3811 0
parqueaderos parqueaderos 72.9147 3.9580 18.4223 0
banios banios 50.6967 3.3964 14.9267 0

Interpretación: Al igual que en el modelo de casas, el área construida y el estrato son los predictores con mayor peso en la determinación del precio de los apartamentos de la Zona Sur. El coeficiente de areaconst indica que por cada metro cuadrado adicional, el precio del apartamento aumenta en promedio X millones. El coeficiente de estrato captura el diferencial de precio entre segmentos socioeconómicos.

El R² del modelo es 0.749, lo que significa que el modelo explica el 74.9% de la variación en el precio de los apartamentos. Para mejorar el ajuste se podría considerar incluir el piso como variable (en apartamentos, los pisos más altos suelen tener mayor valor) o el barrio como efecto fijo.


4.4 Validación de supuestos – Modelo 2

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

par(mfrow = c(1, 1))

4.4.1 Normalidad

shapiro_test2 <- shapiro.test(residuals(modelo2))
cat("Test de Shapiro-Wilk:\n")
## Test de Shapiro-Wilk:
cat("  Estadístico W:", round(shapiro_test2$statistic, 4), "\n")
##   Estadístico W: 0.7912
cat("  p-valor:", round(shapiro_test2$p.value, 4), "\n")
##   p-valor: 0

4.4.2 Homocedasticidad

bp_test2 <- bptest(modelo2)
cat("Test de Breusch-Pagan:\n")
## Test de Breusch-Pagan:
cat("  Estadístico:", round(bp_test2$statistic, 4), "\n")
##   Estadístico: 754.8051
cat("  p-valor:", round(bp_test2$p.value, 4), "\n")
##   p-valor: 0

4.4.3 Independencia

dw_test2 <- dwtest(modelo2)
cat("Test de Durbin-Watson:\n")
## Test de Durbin-Watson:
cat("  Estadístico DW:", round(dw_test2$statistic, 4), "\n")
##   Estadístico DW: 1.5333
cat("  p-valor:", round(dw_test2$p.value, 4), "\n")
##   p-valor: 0

4.4.4 Multicolinealidad

vif_vals2 <- vif(modelo2)
data.frame(Variable = names(vif_vals2), VIF = round(vif_vals2, 3)) %>%
  kable(caption = "VIF – Modelo Apartamentos Zona Sur") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
VIF – Modelo Apartamentos Zona Sur
Variable VIF
areaconst areaconst 2.067
estrato estrato 1.545
habitaciones habitaciones 1.429
parqueaderos parqueaderos 1.738
banios banios 2.529

Resumen de supuestos – Modelo 2: Se siguen las mismas interpretaciones que en el modelo 1. Las desviaciones que se presenten deben considerarse como oportunidades de mejora del modelo y no necesariamente invalidan las predicciones cuando se usan con precaución.


4.5 Predicción – Vivienda 2

Se predice el precio de un apartamento en la Zona Sur con: - Área construida: 300 m²
- Parqueaderos: 3
- Baños: 3
- Habitaciones: 5
- Estrato: 5 y 6

nueva_apto_e5 <- data.frame(
  areaconst    = 300,
  estrato      = 5,
  habitaciones = 5,
  parqueaderos = 3,
  banios       = 3
)

nueva_apto_e6 <- data.frame(
  areaconst    = 300,
  estrato      = 6,
  habitaciones = 5,
  parqueaderos = 3,
  banios       = 3
)

pred2_e5 <- predict(modelo2, newdata = nueva_apto_e5, interval = "prediction", level = 0.95)
pred2_e6 <- predict(modelo2, newdata = nueva_apto_e6, interval = "prediction", level = 0.95)

pred_tabla2 <- data.frame(
  Escenario = c("Estrato 5", "Estrato 6"),
  `Precio estimado` = round(c(pred2_e5[1], pred2_e6[1]), 1),
  `Límite inferior 95%` = round(c(pred2_e5[2], pred2_e6[2]), 1),
  `Límite superior 95%` = round(c(pred2_e5[3], pred2_e6[3]), 1)
)

pred_tabla2 %>%
  kable(caption = "Predicción del precio – Vivienda 2 (millones COP)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Predicción del precio – Vivienda 2 (millones COP)
Escenario Precio.estimado Límite.inferior.95. Límite.superior.95.
Estrato 5 675.0 481.5 868.6
Estrato 6 735.9 542.3 929.5

Interpretación: El modelo estima que un apartamento con las características solicitadas en la Zona Sur tendría un precio de 675 millones (estrato 5) o 735.9 millones (estrato 6). El crédito preaprobado de $850 millones resulta suficiente para el escenario de estrato 5.


4.6 Ofertas potenciales – Vivienda 2

ofertas2 <- base2 %>%
  filter(preciom <= 850,
         estrato %in% c(5, 6),
         habitaciones >= 4,
         banios >= 3,
         parqueaderos >= 2) %>%
  arrange(abs(areaconst - 300)) %>%
  head(10)

ofertas2 %>%
  select(barrio, zona, estrato, preciom, areaconst, habitaciones, banios, parqueaderos) %>%
  kable(caption = "Potenciales ofertas – Vivienda 2 (≤ $850 M, Zona Sur)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Potenciales ofertas – Vivienda 2 (≤ $850 M, Zona Sur)
barrio zona estrato preciom areaconst habitaciones banios parqueaderos
seminario Zona Sur 5 670 300.00 6 5 3
cuarto de legua Zona Sur 5 410 295.55 4 4 2
cuarto de legua Zona Sur 5 520 320.00 4 4 2
ciudadela pasoancho Zona Sur 5 650 275.00 5 5 2
capri Zona Sur 5 350 270.00 4 3 3
san fernando Zona Sur 5 500 330.00 4 4 2
san fernando viejo Zona Sur 5 485 259.00 4 4 2
San Fernando Zona Sur 5 350 258.00 5 4 2
seminario Zona Sur 5 530 256.00 5 5 3
el ingenio Zona Sur 6 700 250.00 5 4 2

4.6.1 Mapa de ofertas potenciales

top_ofertas2 <- ofertas2 %>% head(5)

leaflet(top_ofertas2) %>%
  addTiles() %>%
  addCircleMarkers(
    lng = ~longitud,
    lat = ~latitud,
    radius = 8,
    color = "#7B1FA2",
    fillColor = "#CE93D8",
    fillOpacity = 0.8,
    weight = 2,
    popup = ~paste0(
      "<b>Barrio:</b> ", barrio, "<br>",
      "<b>Precio:</b> $", preciom, " M<br>",
      "<b>Área:</b> ", areaconst, " m²<br>",
      "<b>Estrato:</b> ", estrato, "<br>",
      "<b>Habitaciones:</b> ", habitaciones, "<br>",
      "<b>Baños:</b> ", banios, "<br>",
      "<b>Parqueaderos:</b> ", parqueaderos
    ),
    label = ~paste0("$", preciom, "M – ", barrio)
  ) %>%
  addLegend("bottomright",
            colors = "#7B1FA2",
            labels = "Ofertas potenciales ≤ $850M",
            title = "Vivienda 2")

Análisis de ofertas: Se presentan las 5 mejores opciones de apartamentos en la Zona Sur que cumplen con el presupuesto de $850 millones y los requerimientos mínimos de la solicitud. Se recomienda priorizar aquellas con área más cercana a los 300 m² y estrato 5 o 6, y realizar visitas in-situ para evaluar aspectos cualitativos como acabados, vistas, estado del edificio y amenidades del conjunto residencial.


5 Conclusiones y Recomendaciones

5.1 Resumen ejecutivo

En respuesta a la solicitud de la compañía internacional, la agencia C&A ha llevado a cabo un análisis estadístico riguroso basado en los datos de oferta inmobiliaria de los últimos tres meses en Cali. Los principales hallazgos son:

  1. Vivienda 1 – Casa en Zona Norte: El modelo de regresión lineal múltiple estimó un precio de aproximadamente 312 millones (estrato 4) o 393 millones (estrato 5). Se identificaron al menos 5 propiedades en la base de datos que cumplen las condiciones solicitadas dentro del presupuesto de $350 millones.

  2. Vivienda 2 – Apartamento en Zona Sur: El modelo estimó un precio de aproximadamente 675 millones (estrato 5) o 736 millones (estrato 6). Se identificaron opciones reales en la base de datos dentro del presupuesto de $850 millones.

5.2 Recomendaciones

  • Ampliar el conjunto de variables: Incorporar variables como el estado del inmueble, los años de construcción, las amenidades y el piso ocupado mejoraría la capacidad predictiva de los modelos.
  • Actualizar la base de datos: Dado el contexto de volatilidad en el mercado inmobiliario de Cali, se recomienda actualizar la base periódicamente.
  • Validación presencial: Los modelos estadísticos proveen estimaciones a partir de características observables. La inspección física de las propiedades es indispensable antes de tomar una decisión de compra.
  • Consultar el reporte del avalúo catastral: Para tener un punto de referencia adicional al precio de mercado estimado.

Informe elaborado con R Markdown. Los datos provienen del paquete paqueteMODELOS del repositorio centromagis. Análisis realizado con fines académicos.